packet_buffer.cc (8305B)
1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 // This is the implementation of the PacketBuffer class. It is mostly based on 12 // an STL list. The list is kept sorted at all times so that the next packet to 13 // decode is at the beginning of the list. 14 15 #include "modules/audio_coding/neteq/packet_buffer.h" 16 17 #include <algorithm> 18 #include <cstddef> 19 #include <cstdint> 20 #include <list> 21 #include <memory> 22 #include <optional> 23 #include <utility> 24 25 #include "api/audio_codecs/audio_decoder.h" 26 #include "api/neteq/tick_timer.h" 27 #include "modules/audio_coding/neteq/decoder_database.h" 28 #include "modules/audio_coding/neteq/packet.h" 29 #include "modules/audio_coding/neteq/statistics_calculator.h" 30 #include "rtc_base/checks.h" 31 #include "rtc_base/logging.h" 32 #include "rtc_base/numerics/safe_conversions.h" 33 34 namespace webrtc { 35 namespace { 36 // Predicate used when inserting packets in the buffer list. 37 // Operator() returns true when `packet` goes before `new_packet`. 38 class NewTimestampIsLarger { 39 public: 40 explicit NewTimestampIsLarger(const Packet& new_packet) 41 : new_packet_(new_packet) {} 42 bool operator()(const Packet& packet) { return (new_packet_ >= packet); } 43 44 private: 45 const Packet& new_packet_; 46 }; 47 48 } // namespace 49 50 PacketBuffer::PacketBuffer(size_t max_number_of_packets, 51 const TickTimer* tick_timer, 52 StatisticsCalculator* stats) 53 : max_number_of_packets_(max_number_of_packets), 54 tick_timer_(tick_timer), 55 stats_(stats) {} 56 57 // Destructor. All packets in the buffer will be destroyed. 58 PacketBuffer::~PacketBuffer() { 59 buffer_.clear(); 60 } 61 62 // Flush the buffer. All packets in the buffer will be destroyed. 63 void PacketBuffer::Flush() { 64 for (auto& p : buffer_) { 65 LogPacketDiscarded(p.priority.codec_level); 66 } 67 buffer_.clear(); 68 stats_->FlushedPacketBuffer(); 69 } 70 71 bool PacketBuffer::Empty() const { 72 return buffer_.empty(); 73 } 74 75 int PacketBuffer::InsertPacket(Packet&& packet) { 76 if (packet.empty()) { 77 RTC_LOG(LS_WARNING) << "InsertPacket invalid packet"; 78 return kInvalidPacket; 79 } 80 81 RTC_DCHECK_GE(packet.priority.codec_level, 0); 82 RTC_DCHECK_GE(packet.priority.red_level, 0); 83 84 int return_val = kOK; 85 86 packet.waiting_time = tick_timer_->GetNewStopwatch(); 87 88 if (buffer_.size() >= max_number_of_packets_) { 89 // Buffer is full. 90 Flush(); 91 return_val = kFlushed; 92 RTC_LOG(LS_WARNING) << "Packet buffer flushed."; 93 } 94 95 // Get an iterator pointing to the place in the buffer where the new packet 96 // should be inserted. The list is searched from the back, since the most 97 // likely case is that the new packet should be near the end of the list. 98 PacketList::reverse_iterator rit = std::find_if( 99 buffer_.rbegin(), buffer_.rend(), NewTimestampIsLarger(packet)); 100 101 // The new packet is to be inserted to the right of `rit`. If it has the same 102 // timestamp as `rit`, which has a higher priority, do not insert the new 103 // packet to list. 104 if (rit != buffer_.rend() && packet.timestamp == rit->timestamp) { 105 LogPacketDiscarded(packet.priority.codec_level); 106 return return_val; 107 } 108 109 // The new packet is to be inserted to the left of `it`. If it has the same 110 // timestamp as `it`, which has a lower priority, replace `it` with the new 111 // packet. 112 PacketList::iterator it = rit.base(); 113 if (it != buffer_.end() && packet.timestamp == it->timestamp) { 114 LogPacketDiscarded(it->priority.codec_level); 115 it = buffer_.erase(it); 116 } 117 buffer_.insert(it, std::move(packet)); // Insert the packet at that position. 118 119 return return_val; 120 } 121 122 int PacketBuffer::NextTimestamp(uint32_t* next_timestamp) const { 123 if (Empty()) { 124 return kBufferEmpty; 125 } 126 if (!next_timestamp) { 127 return kInvalidPointer; 128 } 129 *next_timestamp = buffer_.front().timestamp; 130 return kOK; 131 } 132 133 int PacketBuffer::NextHigherTimestamp(uint32_t timestamp, 134 uint32_t* next_timestamp) const { 135 if (Empty()) { 136 return kBufferEmpty; 137 } 138 if (!next_timestamp) { 139 return kInvalidPointer; 140 } 141 PacketList::const_iterator it; 142 for (it = buffer_.begin(); it != buffer_.end(); ++it) { 143 if (it->timestamp >= timestamp) { 144 // Found a packet matching the search. 145 *next_timestamp = it->timestamp; 146 return kOK; 147 } 148 } 149 return kNotFound; 150 } 151 152 const Packet* PacketBuffer::PeekNextPacket() const { 153 return buffer_.empty() ? nullptr : &buffer_.front(); 154 } 155 156 std::optional<Packet> PacketBuffer::GetNextPacket() { 157 if (Empty()) { 158 // Buffer is empty. 159 return std::nullopt; 160 } 161 162 std::optional<Packet> packet(std::move(buffer_.front())); 163 // Assert that the packet sanity checks in InsertPacket method works. 164 RTC_DCHECK(!packet->empty()); 165 buffer_.pop_front(); 166 167 return packet; 168 } 169 170 int PacketBuffer::DiscardNextPacket() { 171 if (Empty()) { 172 return kBufferEmpty; 173 } 174 // Assert that the packet sanity checks in InsertPacket method works. 175 const Packet& packet = buffer_.front(); 176 RTC_DCHECK(!packet.empty()); 177 LogPacketDiscarded(packet.priority.codec_level); 178 buffer_.pop_front(); 179 return kOK; 180 } 181 182 void PacketBuffer::DiscardOldPackets(uint32_t timestamp_limit, 183 uint32_t horizon_samples) { 184 buffer_.remove_if([this, timestamp_limit, horizon_samples](const Packet& p) { 185 if (timestamp_limit == p.timestamp || 186 !IsObsoleteTimestamp(p.timestamp, timestamp_limit, horizon_samples)) { 187 return false; 188 } 189 LogPacketDiscarded(p.priority.codec_level); 190 return true; 191 }); 192 } 193 194 void PacketBuffer::DiscardAllOldPackets(uint32_t timestamp_limit) { 195 DiscardOldPackets(timestamp_limit, 0); 196 } 197 198 void PacketBuffer::DiscardPacketsWithPayloadType(uint8_t payload_type) { 199 buffer_.remove_if([this, payload_type](const Packet& p) { 200 if (p.payload_type != payload_type) { 201 return false; 202 } 203 LogPacketDiscarded(p.priority.codec_level); 204 return true; 205 }); 206 } 207 208 size_t PacketBuffer::NumPacketsInBuffer() const { 209 return buffer_.size(); 210 } 211 212 size_t PacketBuffer::NumSamplesInBuffer(size_t last_decoded_length) const { 213 size_t num_samples = 0; 214 size_t last_duration = last_decoded_length; 215 for (const Packet& packet : buffer_) { 216 if (packet.frame) { 217 // TODO(hlundin): Verify that it's fine to count all packets and remove 218 // this check. 219 if (packet.priority != Packet::Priority(0, 0)) { 220 continue; 221 } 222 size_t duration = packet.frame->Duration(); 223 if (duration > 0) { 224 last_duration = duration; // Save the most up-to-date (valid) duration. 225 } 226 } 227 num_samples += last_duration; 228 } 229 return num_samples; 230 } 231 232 size_t PacketBuffer::GetSpanSamples(size_t last_decoded_length, 233 size_t sample_rate, 234 bool count_waiting_time) const { 235 if (buffer_.empty()) { 236 return 0; 237 } 238 239 size_t span = buffer_.back().timestamp - buffer_.front().timestamp; 240 size_t waiting_time_samples = dchecked_cast<size_t>( 241 buffer_.back().waiting_time->ElapsedMs() * (sample_rate / 1000)); 242 if (count_waiting_time) { 243 span += waiting_time_samples; 244 } else if (buffer_.back().frame && buffer_.back().frame->Duration() > 0) { 245 size_t duration = buffer_.back().frame->Duration(); 246 if (buffer_.back().frame->IsDtxPacket()) { 247 duration = std::max(duration, waiting_time_samples); 248 } 249 span += duration; 250 } else { 251 span += last_decoded_length; 252 } 253 return span; 254 } 255 256 bool PacketBuffer::ContainsDtxOrCngPacket( 257 const DecoderDatabase* decoder_database) const { 258 RTC_DCHECK(decoder_database); 259 for (const Packet& packet : buffer_) { 260 if ((packet.frame && packet.frame->IsDtxPacket()) || 261 decoder_database->IsComfortNoise(packet.payload_type)) { 262 return true; 263 } 264 } 265 return false; 266 } 267 268 void PacketBuffer::LogPacketDiscarded(int codec_level) { 269 if (codec_level > 0) { 270 stats_->SecondaryPacketsDiscarded(1); 271 } else { 272 stats_->PacketsDiscarded(1); 273 } 274 } 275 276 } // namespace webrtc