xref: /aosp_15_r20/external/webrtc/modules/audio_coding/neteq/packet_buffer.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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 <list>
19 #include <memory>
20 #include <type_traits>
21 #include <utility>
22 
23 #include "api/audio_codecs/audio_decoder.h"
24 #include "api/neteq/tick_timer.h"
25 #include "modules/audio_coding/neteq/decoder_database.h"
26 #include "modules/audio_coding/neteq/statistics_calculator.h"
27 #include "rtc_base/checks.h"
28 #include "rtc_base/experiments/struct_parameters_parser.h"
29 #include "rtc_base/logging.h"
30 #include "rtc_base/numerics/safe_conversions.h"
31 #include "system_wrappers/include/field_trial.h"
32 
33 namespace webrtc {
34 namespace {
35 // Predicate used when inserting packets in the buffer list.
36 // Operator() returns true when `packet` goes before `new_packet`.
37 class NewTimestampIsLarger {
38  public:
NewTimestampIsLarger(const Packet & new_packet)39   explicit NewTimestampIsLarger(const Packet& new_packet)
40       : new_packet_(new_packet) {}
operator ()(const Packet & packet)41   bool operator()(const Packet& packet) { return (new_packet_ >= packet); }
42 
43  private:
44   const Packet& new_packet_;
45 };
46 
47 // Returns true if both payload types are known to the decoder database, and
48 // have the same sample rate.
EqualSampleRates(uint8_t pt1,uint8_t pt2,const DecoderDatabase & decoder_database)49 bool EqualSampleRates(uint8_t pt1,
50                       uint8_t pt2,
51                       const DecoderDatabase& decoder_database) {
52   auto* di1 = decoder_database.GetDecoderInfo(pt1);
53   auto* di2 = decoder_database.GetDecoderInfo(pt2);
54   return di1 && di2 && di1->SampleRateHz() == di2->SampleRateHz();
55 }
56 
LogPacketDiscarded(int codec_level,StatisticsCalculator * stats)57 void LogPacketDiscarded(int codec_level, StatisticsCalculator* stats) {
58   RTC_CHECK(stats);
59   if (codec_level > 0) {
60     stats->SecondaryPacketsDiscarded(1);
61   } else {
62     stats->PacketsDiscarded(1);
63   }
64 }
65 
GetSmartflushingConfig()66 absl::optional<SmartFlushingConfig> GetSmartflushingConfig() {
67   absl::optional<SmartFlushingConfig> result;
68   std::string field_trial_string =
69       field_trial::FindFullName("WebRTC-Audio-NetEqSmartFlushing");
70   result = SmartFlushingConfig();
71   bool enabled = false;
72   auto parser = StructParametersParser::Create(
73       "enabled", &enabled, "target_level_threshold_ms",
74       &result->target_level_threshold_ms, "target_level_multiplier",
75       &result->target_level_multiplier);
76   parser->Parse(field_trial_string);
77   if (!enabled) {
78     return absl::nullopt;
79   }
80   RTC_LOG(LS_INFO) << "Using smart flushing, target_level_threshold_ms: "
81                    << result->target_level_threshold_ms
82                    << ", target_level_multiplier: "
83                    << result->target_level_multiplier;
84   return result;
85 }
86 
87 }  // namespace
88 
PacketBuffer(size_t max_number_of_packets,const TickTimer * tick_timer)89 PacketBuffer::PacketBuffer(size_t max_number_of_packets,
90                            const TickTimer* tick_timer)
91     : smart_flushing_config_(GetSmartflushingConfig()),
92       max_number_of_packets_(max_number_of_packets),
93       tick_timer_(tick_timer) {}
94 
95 // Destructor. All packets in the buffer will be destroyed.
~PacketBuffer()96 PacketBuffer::~PacketBuffer() {
97   buffer_.clear();
98 }
99 
100 // Flush the buffer. All packets in the buffer will be destroyed.
Flush(StatisticsCalculator * stats)101 void PacketBuffer::Flush(StatisticsCalculator* stats) {
102   for (auto& p : buffer_) {
103     LogPacketDiscarded(p.priority.codec_level, stats);
104   }
105   buffer_.clear();
106   stats->FlushedPacketBuffer();
107 }
108 
PartialFlush(int target_level_ms,size_t sample_rate,size_t last_decoded_length,StatisticsCalculator * stats)109 void PacketBuffer::PartialFlush(int target_level_ms,
110                                 size_t sample_rate,
111                                 size_t last_decoded_length,
112                                 StatisticsCalculator* stats) {
113   // Make sure that at least half the packet buffer capacity will be available
114   // after the flush. This is done to avoid getting stuck if the target level is
115   // very high.
116   int target_level_samples =
117       std::min(target_level_ms * sample_rate / 1000,
118                max_number_of_packets_ * last_decoded_length / 2);
119   // We should avoid flushing to very low levels.
120   target_level_samples = std::max(
121       target_level_samples, smart_flushing_config_->target_level_threshold_ms);
122   while (GetSpanSamples(last_decoded_length, sample_rate, true) >
123              static_cast<size_t>(target_level_samples) ||
124          buffer_.size() > max_number_of_packets_ / 2) {
125     LogPacketDiscarded(PeekNextPacket()->priority.codec_level, stats);
126     buffer_.pop_front();
127   }
128 }
129 
Empty() const130 bool PacketBuffer::Empty() const {
131   return buffer_.empty();
132 }
133 
InsertPacket(Packet && packet,StatisticsCalculator * stats,size_t last_decoded_length,size_t sample_rate,int target_level_ms,const DecoderDatabase & decoder_database)134 int PacketBuffer::InsertPacket(Packet&& packet,
135                                StatisticsCalculator* stats,
136                                size_t last_decoded_length,
137                                size_t sample_rate,
138                                int target_level_ms,
139                                const DecoderDatabase& decoder_database) {
140   if (packet.empty()) {
141     RTC_LOG(LS_WARNING) << "InsertPacket invalid packet";
142     return kInvalidPacket;
143   }
144 
145   RTC_DCHECK_GE(packet.priority.codec_level, 0);
146   RTC_DCHECK_GE(packet.priority.red_level, 0);
147 
148   int return_val = kOK;
149 
150   packet.waiting_time = tick_timer_->GetNewStopwatch();
151 
152   // Perform a smart flush if the buffer size exceeds a multiple of the target
153   // level.
154   const size_t span_threshold =
155       smart_flushing_config_
156           ? smart_flushing_config_->target_level_multiplier *
157                 std::max(smart_flushing_config_->target_level_threshold_ms,
158                          target_level_ms) *
159                 sample_rate / 1000
160           : 0;
161   const bool smart_flush =
162       smart_flushing_config_.has_value() &&
163       GetSpanSamples(last_decoded_length, sample_rate, true) >= span_threshold;
164   if (buffer_.size() >= max_number_of_packets_ || smart_flush) {
165     size_t buffer_size_before_flush = buffer_.size();
166     if (smart_flushing_config_.has_value()) {
167       // Flush down to the target level.
168       PartialFlush(target_level_ms, sample_rate, last_decoded_length, stats);
169       return_val = kPartialFlush;
170     } else {
171       // Buffer is full.
172       Flush(stats);
173       return_val = kFlushed;
174     }
175     RTC_LOG(LS_WARNING) << "Packet buffer flushed, "
176                         << (buffer_size_before_flush - buffer_.size())
177                         << " packets discarded.";
178   }
179 
180   // Get an iterator pointing to the place in the buffer where the new packet
181   // should be inserted. The list is searched from the back, since the most
182   // likely case is that the new packet should be near the end of the list.
183   PacketList::reverse_iterator rit = std::find_if(
184       buffer_.rbegin(), buffer_.rend(), NewTimestampIsLarger(packet));
185 
186   // The new packet is to be inserted to the right of `rit`. If it has the same
187   // timestamp as `rit`, which has a higher priority, do not insert the new
188   // packet to list.
189   if (rit != buffer_.rend() && packet.timestamp == rit->timestamp) {
190     LogPacketDiscarded(packet.priority.codec_level, stats);
191     return return_val;
192   }
193 
194   // The new packet is to be inserted to the left of `it`. If it has the same
195   // timestamp as `it`, which has a lower priority, replace `it` with the new
196   // packet.
197   PacketList::iterator it = rit.base();
198   if (it != buffer_.end() && packet.timestamp == it->timestamp) {
199     LogPacketDiscarded(it->priority.codec_level, stats);
200     it = buffer_.erase(it);
201   }
202   buffer_.insert(it, std::move(packet));  // Insert the packet at that position.
203 
204   return return_val;
205 }
206 
InsertPacketList(PacketList * packet_list,const DecoderDatabase & decoder_database,absl::optional<uint8_t> * current_rtp_payload_type,absl::optional<uint8_t> * current_cng_rtp_payload_type,StatisticsCalculator * stats,size_t last_decoded_length,size_t sample_rate,int target_level_ms)207 int PacketBuffer::InsertPacketList(
208     PacketList* packet_list,
209     const DecoderDatabase& decoder_database,
210     absl::optional<uint8_t>* current_rtp_payload_type,
211     absl::optional<uint8_t>* current_cng_rtp_payload_type,
212     StatisticsCalculator* stats,
213     size_t last_decoded_length,
214     size_t sample_rate,
215     int target_level_ms) {
216   RTC_DCHECK(stats);
217   bool flushed = false;
218   for (auto& packet : *packet_list) {
219     if (decoder_database.IsComfortNoise(packet.payload_type)) {
220       if (*current_cng_rtp_payload_type &&
221           **current_cng_rtp_payload_type != packet.payload_type) {
222         // New CNG payload type implies new codec type.
223         *current_rtp_payload_type = absl::nullopt;
224         Flush(stats);
225         flushed = true;
226       }
227       *current_cng_rtp_payload_type = packet.payload_type;
228     } else if (!decoder_database.IsDtmf(packet.payload_type)) {
229       // This must be speech.
230       if ((*current_rtp_payload_type &&
231            **current_rtp_payload_type != packet.payload_type) ||
232           (*current_cng_rtp_payload_type &&
233            !EqualSampleRates(packet.payload_type,
234                              **current_cng_rtp_payload_type,
235                              decoder_database))) {
236         *current_cng_rtp_payload_type = absl::nullopt;
237         Flush(stats);
238         flushed = true;
239       }
240       *current_rtp_payload_type = packet.payload_type;
241     }
242     int return_val =
243         InsertPacket(std::move(packet), stats, last_decoded_length, sample_rate,
244                      target_level_ms, decoder_database);
245     if (return_val == kFlushed) {
246       // The buffer flushed, but this is not an error. We can still continue.
247       flushed = true;
248     } else if (return_val != kOK) {
249       // An error occurred. Delete remaining packets in list and return.
250       packet_list->clear();
251       return return_val;
252     }
253   }
254   packet_list->clear();
255   return flushed ? kFlushed : kOK;
256 }
257 
NextTimestamp(uint32_t * next_timestamp) const258 int PacketBuffer::NextTimestamp(uint32_t* next_timestamp) const {
259   if (Empty()) {
260     return kBufferEmpty;
261   }
262   if (!next_timestamp) {
263     return kInvalidPointer;
264   }
265   *next_timestamp = buffer_.front().timestamp;
266   return kOK;
267 }
268 
NextHigherTimestamp(uint32_t timestamp,uint32_t * next_timestamp) const269 int PacketBuffer::NextHigherTimestamp(uint32_t timestamp,
270                                       uint32_t* next_timestamp) const {
271   if (Empty()) {
272     return kBufferEmpty;
273   }
274   if (!next_timestamp) {
275     return kInvalidPointer;
276   }
277   PacketList::const_iterator it;
278   for (it = buffer_.begin(); it != buffer_.end(); ++it) {
279     if (it->timestamp >= timestamp) {
280       // Found a packet matching the search.
281       *next_timestamp = it->timestamp;
282       return kOK;
283     }
284   }
285   return kNotFound;
286 }
287 
PeekNextPacket() const288 const Packet* PacketBuffer::PeekNextPacket() const {
289   return buffer_.empty() ? nullptr : &buffer_.front();
290 }
291 
GetNextPacket()292 absl::optional<Packet> PacketBuffer::GetNextPacket() {
293   if (Empty()) {
294     // Buffer is empty.
295     return absl::nullopt;
296   }
297 
298   absl::optional<Packet> packet(std::move(buffer_.front()));
299   // Assert that the packet sanity checks in InsertPacket method works.
300   RTC_DCHECK(!packet->empty());
301   buffer_.pop_front();
302 
303   return packet;
304 }
305 
DiscardNextPacket(StatisticsCalculator * stats)306 int PacketBuffer::DiscardNextPacket(StatisticsCalculator* stats) {
307   if (Empty()) {
308     return kBufferEmpty;
309   }
310   // Assert that the packet sanity checks in InsertPacket method works.
311   const Packet& packet = buffer_.front();
312   RTC_DCHECK(!packet.empty());
313   LogPacketDiscarded(packet.priority.codec_level, stats);
314   buffer_.pop_front();
315   return kOK;
316 }
317 
DiscardOldPackets(uint32_t timestamp_limit,uint32_t horizon_samples,StatisticsCalculator * stats)318 void PacketBuffer::DiscardOldPackets(uint32_t timestamp_limit,
319                                      uint32_t horizon_samples,
320                                      StatisticsCalculator* stats) {
321   buffer_.remove_if([timestamp_limit, horizon_samples, stats](const Packet& p) {
322     if (timestamp_limit == p.timestamp ||
323         !IsObsoleteTimestamp(p.timestamp, timestamp_limit, horizon_samples)) {
324       return false;
325     }
326     LogPacketDiscarded(p.priority.codec_level, stats);
327     return true;
328   });
329 }
330 
DiscardAllOldPackets(uint32_t timestamp_limit,StatisticsCalculator * stats)331 void PacketBuffer::DiscardAllOldPackets(uint32_t timestamp_limit,
332                                         StatisticsCalculator* stats) {
333   DiscardOldPackets(timestamp_limit, 0, stats);
334 }
335 
DiscardPacketsWithPayloadType(uint8_t payload_type,StatisticsCalculator * stats)336 void PacketBuffer::DiscardPacketsWithPayloadType(uint8_t payload_type,
337                                                  StatisticsCalculator* stats) {
338   buffer_.remove_if([payload_type, stats](const Packet& p) {
339     if (p.payload_type != payload_type) {
340       return false;
341     }
342     LogPacketDiscarded(p.priority.codec_level, stats);
343     return true;
344   });
345 }
346 
NumPacketsInBuffer() const347 size_t PacketBuffer::NumPacketsInBuffer() const {
348   return buffer_.size();
349 }
350 
NumSamplesInBuffer(size_t last_decoded_length) const351 size_t PacketBuffer::NumSamplesInBuffer(size_t last_decoded_length) const {
352   size_t num_samples = 0;
353   size_t last_duration = last_decoded_length;
354   for (const Packet& packet : buffer_) {
355     if (packet.frame) {
356       // TODO(hlundin): Verify that it's fine to count all packets and remove
357       // this check.
358       if (packet.priority != Packet::Priority(0, 0)) {
359         continue;
360       }
361       size_t duration = packet.frame->Duration();
362       if (duration > 0) {
363         last_duration = duration;  // Save the most up-to-date (valid) duration.
364       }
365     }
366     num_samples += last_duration;
367   }
368   return num_samples;
369 }
370 
GetSpanSamples(size_t last_decoded_length,size_t sample_rate,bool count_dtx_waiting_time) const371 size_t PacketBuffer::GetSpanSamples(size_t last_decoded_length,
372                                     size_t sample_rate,
373                                     bool count_dtx_waiting_time) const {
374   if (buffer_.size() == 0) {
375     return 0;
376   }
377 
378   size_t span = buffer_.back().timestamp - buffer_.front().timestamp;
379   if (buffer_.back().frame && buffer_.back().frame->Duration() > 0) {
380     size_t duration = buffer_.back().frame->Duration();
381     if (count_dtx_waiting_time && buffer_.back().frame->IsDtxPacket()) {
382       size_t waiting_time_samples = rtc::dchecked_cast<size_t>(
383           buffer_.back().waiting_time->ElapsedMs() * (sample_rate / 1000));
384       duration = std::max(duration, waiting_time_samples);
385     }
386     span += duration;
387   } else {
388     span += last_decoded_length;
389   }
390   return span;
391 }
392 
ContainsDtxOrCngPacket(const DecoderDatabase * decoder_database) const393 bool PacketBuffer::ContainsDtxOrCngPacket(
394     const DecoderDatabase* decoder_database) const {
395   RTC_DCHECK(decoder_database);
396   for (const Packet& packet : buffer_) {
397     if ((packet.frame && packet.frame->IsDtxPacket()) ||
398         decoder_database->IsComfortNoise(packet.payload_type)) {
399       return true;
400     }
401   }
402   return false;
403 }
404 
405 }  // namespace webrtc
406