xref: /aosp_15_r20/external/webrtc/modules/video_coding/nack_requester.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker  *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker  *
4*d9f75844SAndroid Build Coastguard Worker  *  Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker  *  that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker  *  tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker  *  in the file PATENTS.  All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker  *  be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker  */
10*d9f75844SAndroid Build Coastguard Worker 
11*d9f75844SAndroid Build Coastguard Worker #include "modules/video_coding/nack_requester.h"
12*d9f75844SAndroid Build Coastguard Worker 
13*d9f75844SAndroid Build Coastguard Worker #include <algorithm>
14*d9f75844SAndroid Build Coastguard Worker #include <limits>
15*d9f75844SAndroid Build Coastguard Worker 
16*d9f75844SAndroid Build Coastguard Worker #include "api/sequence_checker.h"
17*d9f75844SAndroid Build Coastguard Worker #include "api/task_queue/task_queue_base.h"
18*d9f75844SAndroid Build Coastguard Worker #include "api/units/timestamp.h"
19*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
20*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/experiments/field_trial_parser.h"
21*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/logging.h"
22*d9f75844SAndroid Build Coastguard Worker 
23*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
24*d9f75844SAndroid Build Coastguard Worker 
25*d9f75844SAndroid Build Coastguard Worker namespace {
26*d9f75844SAndroid Build Coastguard Worker constexpr int kMaxPacketAge = 10'000;
27*d9f75844SAndroid Build Coastguard Worker constexpr int kMaxNackPackets = 1000;
28*d9f75844SAndroid Build Coastguard Worker constexpr TimeDelta kDefaultRtt = TimeDelta::Millis(100);
29*d9f75844SAndroid Build Coastguard Worker constexpr int kMaxNackRetries = 10;
30*d9f75844SAndroid Build Coastguard Worker constexpr int kMaxReorderedPackets = 128;
31*d9f75844SAndroid Build Coastguard Worker constexpr int kNumReorderingBuckets = 10;
32*d9f75844SAndroid Build Coastguard Worker constexpr TimeDelta kDefaultSendNackDelay = TimeDelta::Zero();
33*d9f75844SAndroid Build Coastguard Worker 
GetSendNackDelay(const FieldTrialsView & field_trials)34*d9f75844SAndroid Build Coastguard Worker TimeDelta GetSendNackDelay(const FieldTrialsView& field_trials) {
35*d9f75844SAndroid Build Coastguard Worker   int64_t delay_ms = strtol(
36*d9f75844SAndroid Build Coastguard Worker       field_trials.Lookup("WebRTC-SendNackDelayMs").c_str(), nullptr, 10);
37*d9f75844SAndroid Build Coastguard Worker   if (delay_ms > 0 && delay_ms <= 20) {
38*d9f75844SAndroid Build Coastguard Worker     RTC_LOG(LS_INFO) << "SendNackDelay is set to " << delay_ms;
39*d9f75844SAndroid Build Coastguard Worker     return TimeDelta::Millis(delay_ms);
40*d9f75844SAndroid Build Coastguard Worker   }
41*d9f75844SAndroid Build Coastguard Worker   return kDefaultSendNackDelay;
42*d9f75844SAndroid Build Coastguard Worker }
43*d9f75844SAndroid Build Coastguard Worker }  // namespace
44*d9f75844SAndroid Build Coastguard Worker 
45*d9f75844SAndroid Build Coastguard Worker constexpr TimeDelta NackPeriodicProcessor::kUpdateInterval;
46*d9f75844SAndroid Build Coastguard Worker 
NackPeriodicProcessor(TimeDelta update_interval)47*d9f75844SAndroid Build Coastguard Worker NackPeriodicProcessor::NackPeriodicProcessor(TimeDelta update_interval)
48*d9f75844SAndroid Build Coastguard Worker     : update_interval_(update_interval) {}
49*d9f75844SAndroid Build Coastguard Worker 
~NackPeriodicProcessor()50*d9f75844SAndroid Build Coastguard Worker NackPeriodicProcessor::~NackPeriodicProcessor() {}
51*d9f75844SAndroid Build Coastguard Worker 
RegisterNackModule(NackRequesterBase * module)52*d9f75844SAndroid Build Coastguard Worker void NackPeriodicProcessor::RegisterNackModule(NackRequesterBase* module) {
53*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_RUN_ON(&sequence_);
54*d9f75844SAndroid Build Coastguard Worker   modules_.push_back(module);
55*d9f75844SAndroid Build Coastguard Worker   if (modules_.size() != 1)
56*d9f75844SAndroid Build Coastguard Worker     return;
57*d9f75844SAndroid Build Coastguard Worker   repeating_task_ = RepeatingTaskHandle::DelayedStart(
58*d9f75844SAndroid Build Coastguard Worker       TaskQueueBase::Current(), update_interval_, [this] {
59*d9f75844SAndroid Build Coastguard Worker         RTC_DCHECK_RUN_ON(&sequence_);
60*d9f75844SAndroid Build Coastguard Worker         ProcessNackModules();
61*d9f75844SAndroid Build Coastguard Worker         return update_interval_;
62*d9f75844SAndroid Build Coastguard Worker       });
63*d9f75844SAndroid Build Coastguard Worker }
64*d9f75844SAndroid Build Coastguard Worker 
UnregisterNackModule(NackRequesterBase * module)65*d9f75844SAndroid Build Coastguard Worker void NackPeriodicProcessor::UnregisterNackModule(NackRequesterBase* module) {
66*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_RUN_ON(&sequence_);
67*d9f75844SAndroid Build Coastguard Worker   auto it = std::find(modules_.begin(), modules_.end(), module);
68*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(it != modules_.end());
69*d9f75844SAndroid Build Coastguard Worker   modules_.erase(it);
70*d9f75844SAndroid Build Coastguard Worker   if (modules_.empty())
71*d9f75844SAndroid Build Coastguard Worker     repeating_task_.Stop();
72*d9f75844SAndroid Build Coastguard Worker }
73*d9f75844SAndroid Build Coastguard Worker 
ProcessNackModules()74*d9f75844SAndroid Build Coastguard Worker void NackPeriodicProcessor::ProcessNackModules() {
75*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_RUN_ON(&sequence_);
76*d9f75844SAndroid Build Coastguard Worker   for (NackRequesterBase* module : modules_)
77*d9f75844SAndroid Build Coastguard Worker     module->ProcessNacks();
78*d9f75844SAndroid Build Coastguard Worker }
79*d9f75844SAndroid Build Coastguard Worker 
80*d9f75844SAndroid Build Coastguard Worker ScopedNackPeriodicProcessorRegistration::
ScopedNackPeriodicProcessorRegistration(NackRequesterBase * module,NackPeriodicProcessor * processor)81*d9f75844SAndroid Build Coastguard Worker     ScopedNackPeriodicProcessorRegistration(NackRequesterBase* module,
82*d9f75844SAndroid Build Coastguard Worker                                             NackPeriodicProcessor* processor)
83*d9f75844SAndroid Build Coastguard Worker     : module_(module), processor_(processor) {
84*d9f75844SAndroid Build Coastguard Worker   processor_->RegisterNackModule(module_);
85*d9f75844SAndroid Build Coastguard Worker }
86*d9f75844SAndroid Build Coastguard Worker 
87*d9f75844SAndroid Build Coastguard Worker ScopedNackPeriodicProcessorRegistration::
~ScopedNackPeriodicProcessorRegistration()88*d9f75844SAndroid Build Coastguard Worker     ~ScopedNackPeriodicProcessorRegistration() {
89*d9f75844SAndroid Build Coastguard Worker   processor_->UnregisterNackModule(module_);
90*d9f75844SAndroid Build Coastguard Worker }
91*d9f75844SAndroid Build Coastguard Worker 
NackInfo()92*d9f75844SAndroid Build Coastguard Worker NackRequester::NackInfo::NackInfo()
93*d9f75844SAndroid Build Coastguard Worker     : seq_num(0),
94*d9f75844SAndroid Build Coastguard Worker       send_at_seq_num(0),
95*d9f75844SAndroid Build Coastguard Worker       created_at_time(Timestamp::MinusInfinity()),
96*d9f75844SAndroid Build Coastguard Worker       sent_at_time(Timestamp::MinusInfinity()),
97*d9f75844SAndroid Build Coastguard Worker       retries(0) {}
98*d9f75844SAndroid Build Coastguard Worker 
NackInfo(uint16_t seq_num,uint16_t send_at_seq_num,Timestamp created_at_time)99*d9f75844SAndroid Build Coastguard Worker NackRequester::NackInfo::NackInfo(uint16_t seq_num,
100*d9f75844SAndroid Build Coastguard Worker                                   uint16_t send_at_seq_num,
101*d9f75844SAndroid Build Coastguard Worker                                   Timestamp created_at_time)
102*d9f75844SAndroid Build Coastguard Worker     : seq_num(seq_num),
103*d9f75844SAndroid Build Coastguard Worker       send_at_seq_num(send_at_seq_num),
104*d9f75844SAndroid Build Coastguard Worker       created_at_time(created_at_time),
105*d9f75844SAndroid Build Coastguard Worker       sent_at_time(Timestamp::MinusInfinity()),
106*d9f75844SAndroid Build Coastguard Worker       retries(0) {}
107*d9f75844SAndroid Build Coastguard Worker 
NackRequester(TaskQueueBase * current_queue,NackPeriodicProcessor * periodic_processor,Clock * clock,NackSender * nack_sender,KeyFrameRequestSender * keyframe_request_sender,const FieldTrialsView & field_trials)108*d9f75844SAndroid Build Coastguard Worker NackRequester::NackRequester(TaskQueueBase* current_queue,
109*d9f75844SAndroid Build Coastguard Worker                              NackPeriodicProcessor* periodic_processor,
110*d9f75844SAndroid Build Coastguard Worker                              Clock* clock,
111*d9f75844SAndroid Build Coastguard Worker                              NackSender* nack_sender,
112*d9f75844SAndroid Build Coastguard Worker                              KeyFrameRequestSender* keyframe_request_sender,
113*d9f75844SAndroid Build Coastguard Worker                              const FieldTrialsView& field_trials)
114*d9f75844SAndroid Build Coastguard Worker     : worker_thread_(current_queue),
115*d9f75844SAndroid Build Coastguard Worker       clock_(clock),
116*d9f75844SAndroid Build Coastguard Worker       nack_sender_(nack_sender),
117*d9f75844SAndroid Build Coastguard Worker       keyframe_request_sender_(keyframe_request_sender),
118*d9f75844SAndroid Build Coastguard Worker       reordering_histogram_(kNumReorderingBuckets, kMaxReorderedPackets),
119*d9f75844SAndroid Build Coastguard Worker       initialized_(false),
120*d9f75844SAndroid Build Coastguard Worker       rtt_(kDefaultRtt),
121*d9f75844SAndroid Build Coastguard Worker       newest_seq_num_(0),
122*d9f75844SAndroid Build Coastguard Worker       send_nack_delay_(GetSendNackDelay(field_trials)),
123*d9f75844SAndroid Build Coastguard Worker       processor_registration_(this, periodic_processor) {
124*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(clock_);
125*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(nack_sender_);
126*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(keyframe_request_sender_);
127*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(worker_thread_);
128*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(worker_thread_->IsCurrent());
129*d9f75844SAndroid Build Coastguard Worker }
130*d9f75844SAndroid Build Coastguard Worker 
~NackRequester()131*d9f75844SAndroid Build Coastguard Worker NackRequester::~NackRequester() {
132*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_RUN_ON(worker_thread_);
133*d9f75844SAndroid Build Coastguard Worker }
134*d9f75844SAndroid Build Coastguard Worker 
ProcessNacks()135*d9f75844SAndroid Build Coastguard Worker void NackRequester::ProcessNacks() {
136*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_RUN_ON(worker_thread_);
137*d9f75844SAndroid Build Coastguard Worker   std::vector<uint16_t> nack_batch = GetNackBatch(kTimeOnly);
138*d9f75844SAndroid Build Coastguard Worker   if (!nack_batch.empty()) {
139*d9f75844SAndroid Build Coastguard Worker     // This batch of NACKs is triggered externally; there is no external
140*d9f75844SAndroid Build Coastguard Worker     // initiator who can batch them with other feedback messages.
141*d9f75844SAndroid Build Coastguard Worker     nack_sender_->SendNack(nack_batch, /*buffering_allowed=*/false);
142*d9f75844SAndroid Build Coastguard Worker   }
143*d9f75844SAndroid Build Coastguard Worker }
144*d9f75844SAndroid Build Coastguard Worker 
OnReceivedPacket(uint16_t seq_num,bool is_keyframe)145*d9f75844SAndroid Build Coastguard Worker int NackRequester::OnReceivedPacket(uint16_t seq_num, bool is_keyframe) {
146*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_RUN_ON(worker_thread_);
147*d9f75844SAndroid Build Coastguard Worker   return OnReceivedPacket(seq_num, is_keyframe, false);
148*d9f75844SAndroid Build Coastguard Worker }
149*d9f75844SAndroid Build Coastguard Worker 
OnReceivedPacket(uint16_t seq_num,bool is_keyframe,bool is_recovered)150*d9f75844SAndroid Build Coastguard Worker int NackRequester::OnReceivedPacket(uint16_t seq_num,
151*d9f75844SAndroid Build Coastguard Worker                                     bool is_keyframe,
152*d9f75844SAndroid Build Coastguard Worker                                     bool is_recovered) {
153*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_RUN_ON(worker_thread_);
154*d9f75844SAndroid Build Coastguard Worker   // TODO(philipel): When the packet includes information whether it is
155*d9f75844SAndroid Build Coastguard Worker   //                 retransmitted or not, use that value instead. For
156*d9f75844SAndroid Build Coastguard Worker   //                 now set it to true, which will cause the reordering
157*d9f75844SAndroid Build Coastguard Worker   //                 statistics to never be updated.
158*d9f75844SAndroid Build Coastguard Worker   bool is_retransmitted = true;
159*d9f75844SAndroid Build Coastguard Worker 
160*d9f75844SAndroid Build Coastguard Worker   if (!initialized_) {
161*d9f75844SAndroid Build Coastguard Worker     newest_seq_num_ = seq_num;
162*d9f75844SAndroid Build Coastguard Worker     if (is_keyframe)
163*d9f75844SAndroid Build Coastguard Worker       keyframe_list_.insert(seq_num);
164*d9f75844SAndroid Build Coastguard Worker     initialized_ = true;
165*d9f75844SAndroid Build Coastguard Worker     return 0;
166*d9f75844SAndroid Build Coastguard Worker   }
167*d9f75844SAndroid Build Coastguard Worker 
168*d9f75844SAndroid Build Coastguard Worker   // Since the `newest_seq_num_` is a packet we have actually received we know
169*d9f75844SAndroid Build Coastguard Worker   // that packet has never been Nacked.
170*d9f75844SAndroid Build Coastguard Worker   if (seq_num == newest_seq_num_)
171*d9f75844SAndroid Build Coastguard Worker     return 0;
172*d9f75844SAndroid Build Coastguard Worker 
173*d9f75844SAndroid Build Coastguard Worker   if (AheadOf(newest_seq_num_, seq_num)) {
174*d9f75844SAndroid Build Coastguard Worker     // An out of order packet has been received.
175*d9f75844SAndroid Build Coastguard Worker     auto nack_list_it = nack_list_.find(seq_num);
176*d9f75844SAndroid Build Coastguard Worker     int nacks_sent_for_packet = 0;
177*d9f75844SAndroid Build Coastguard Worker     if (nack_list_it != nack_list_.end()) {
178*d9f75844SAndroid Build Coastguard Worker       nacks_sent_for_packet = nack_list_it->second.retries;
179*d9f75844SAndroid Build Coastguard Worker       nack_list_.erase(nack_list_it);
180*d9f75844SAndroid Build Coastguard Worker     }
181*d9f75844SAndroid Build Coastguard Worker     if (!is_retransmitted)
182*d9f75844SAndroid Build Coastguard Worker       UpdateReorderingStatistics(seq_num);
183*d9f75844SAndroid Build Coastguard Worker     return nacks_sent_for_packet;
184*d9f75844SAndroid Build Coastguard Worker   }
185*d9f75844SAndroid Build Coastguard Worker 
186*d9f75844SAndroid Build Coastguard Worker   // Keep track of new keyframes.
187*d9f75844SAndroid Build Coastguard Worker   if (is_keyframe)
188*d9f75844SAndroid Build Coastguard Worker     keyframe_list_.insert(seq_num);
189*d9f75844SAndroid Build Coastguard Worker 
190*d9f75844SAndroid Build Coastguard Worker   // And remove old ones so we don't accumulate keyframes.
191*d9f75844SAndroid Build Coastguard Worker   auto it = keyframe_list_.lower_bound(seq_num - kMaxPacketAge);
192*d9f75844SAndroid Build Coastguard Worker   if (it != keyframe_list_.begin())
193*d9f75844SAndroid Build Coastguard Worker     keyframe_list_.erase(keyframe_list_.begin(), it);
194*d9f75844SAndroid Build Coastguard Worker 
195*d9f75844SAndroid Build Coastguard Worker   if (is_recovered) {
196*d9f75844SAndroid Build Coastguard Worker     recovered_list_.insert(seq_num);
197*d9f75844SAndroid Build Coastguard Worker 
198*d9f75844SAndroid Build Coastguard Worker     // Remove old ones so we don't accumulate recovered packets.
199*d9f75844SAndroid Build Coastguard Worker     auto it = recovered_list_.lower_bound(seq_num - kMaxPacketAge);
200*d9f75844SAndroid Build Coastguard Worker     if (it != recovered_list_.begin())
201*d9f75844SAndroid Build Coastguard Worker       recovered_list_.erase(recovered_list_.begin(), it);
202*d9f75844SAndroid Build Coastguard Worker 
203*d9f75844SAndroid Build Coastguard Worker     // Do not send nack for packets recovered by FEC or RTX.
204*d9f75844SAndroid Build Coastguard Worker     return 0;
205*d9f75844SAndroid Build Coastguard Worker   }
206*d9f75844SAndroid Build Coastguard Worker 
207*d9f75844SAndroid Build Coastguard Worker   AddPacketsToNack(newest_seq_num_ + 1, seq_num);
208*d9f75844SAndroid Build Coastguard Worker   newest_seq_num_ = seq_num;
209*d9f75844SAndroid Build Coastguard Worker 
210*d9f75844SAndroid Build Coastguard Worker   // Are there any nacks that are waiting for this seq_num.
211*d9f75844SAndroid Build Coastguard Worker   std::vector<uint16_t> nack_batch = GetNackBatch(kSeqNumOnly);
212*d9f75844SAndroid Build Coastguard Worker   if (!nack_batch.empty()) {
213*d9f75844SAndroid Build Coastguard Worker     // This batch of NACKs is triggered externally; the initiator can
214*d9f75844SAndroid Build Coastguard Worker     // batch them with other feedback messages.
215*d9f75844SAndroid Build Coastguard Worker     nack_sender_->SendNack(nack_batch, /*buffering_allowed=*/true);
216*d9f75844SAndroid Build Coastguard Worker   }
217*d9f75844SAndroid Build Coastguard Worker 
218*d9f75844SAndroid Build Coastguard Worker   return 0;
219*d9f75844SAndroid Build Coastguard Worker }
220*d9f75844SAndroid Build Coastguard Worker 
ClearUpTo(uint16_t seq_num)221*d9f75844SAndroid Build Coastguard Worker void NackRequester::ClearUpTo(uint16_t seq_num) {
222*d9f75844SAndroid Build Coastguard Worker   // Called via RtpVideoStreamReceiver2::FrameContinuous on the network thread.
223*d9f75844SAndroid Build Coastguard Worker   worker_thread_->PostTask(SafeTask(task_safety_.flag(), [seq_num, this]() {
224*d9f75844SAndroid Build Coastguard Worker     RTC_DCHECK_RUN_ON(worker_thread_);
225*d9f75844SAndroid Build Coastguard Worker     nack_list_.erase(nack_list_.begin(), nack_list_.lower_bound(seq_num));
226*d9f75844SAndroid Build Coastguard Worker     keyframe_list_.erase(keyframe_list_.begin(),
227*d9f75844SAndroid Build Coastguard Worker                          keyframe_list_.lower_bound(seq_num));
228*d9f75844SAndroid Build Coastguard Worker     recovered_list_.erase(recovered_list_.begin(),
229*d9f75844SAndroid Build Coastguard Worker                           recovered_list_.lower_bound(seq_num));
230*d9f75844SAndroid Build Coastguard Worker   }));
231*d9f75844SAndroid Build Coastguard Worker }
232*d9f75844SAndroid Build Coastguard Worker 
UpdateRtt(int64_t rtt_ms)233*d9f75844SAndroid Build Coastguard Worker void NackRequester::UpdateRtt(int64_t rtt_ms) {
234*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_RUN_ON(worker_thread_);
235*d9f75844SAndroid Build Coastguard Worker   rtt_ = TimeDelta::Millis(rtt_ms);
236*d9f75844SAndroid Build Coastguard Worker }
237*d9f75844SAndroid Build Coastguard Worker 
RemovePacketsUntilKeyFrame()238*d9f75844SAndroid Build Coastguard Worker bool NackRequester::RemovePacketsUntilKeyFrame() {
239*d9f75844SAndroid Build Coastguard Worker   // Called on worker_thread_.
240*d9f75844SAndroid Build Coastguard Worker   while (!keyframe_list_.empty()) {
241*d9f75844SAndroid Build Coastguard Worker     auto it = nack_list_.lower_bound(*keyframe_list_.begin());
242*d9f75844SAndroid Build Coastguard Worker 
243*d9f75844SAndroid Build Coastguard Worker     if (it != nack_list_.begin()) {
244*d9f75844SAndroid Build Coastguard Worker       // We have found a keyframe that actually is newer than at least one
245*d9f75844SAndroid Build Coastguard Worker       // packet in the nack list.
246*d9f75844SAndroid Build Coastguard Worker       nack_list_.erase(nack_list_.begin(), it);
247*d9f75844SAndroid Build Coastguard Worker       return true;
248*d9f75844SAndroid Build Coastguard Worker     }
249*d9f75844SAndroid Build Coastguard Worker 
250*d9f75844SAndroid Build Coastguard Worker     // If this keyframe is so old it does not remove any packets from the list,
251*d9f75844SAndroid Build Coastguard Worker     // remove it from the list of keyframes and try the next keyframe.
252*d9f75844SAndroid Build Coastguard Worker     keyframe_list_.erase(keyframe_list_.begin());
253*d9f75844SAndroid Build Coastguard Worker   }
254*d9f75844SAndroid Build Coastguard Worker   return false;
255*d9f75844SAndroid Build Coastguard Worker }
256*d9f75844SAndroid Build Coastguard Worker 
AddPacketsToNack(uint16_t seq_num_start,uint16_t seq_num_end)257*d9f75844SAndroid Build Coastguard Worker void NackRequester::AddPacketsToNack(uint16_t seq_num_start,
258*d9f75844SAndroid Build Coastguard Worker                                      uint16_t seq_num_end) {
259*d9f75844SAndroid Build Coastguard Worker   // Called on worker_thread_.
260*d9f75844SAndroid Build Coastguard Worker   // Remove old packets.
261*d9f75844SAndroid Build Coastguard Worker   auto it = nack_list_.lower_bound(seq_num_end - kMaxPacketAge);
262*d9f75844SAndroid Build Coastguard Worker   nack_list_.erase(nack_list_.begin(), it);
263*d9f75844SAndroid Build Coastguard Worker 
264*d9f75844SAndroid Build Coastguard Worker   // If the nack list is too large, remove packets from the nack list until
265*d9f75844SAndroid Build Coastguard Worker   // the latest first packet of a keyframe. If the list is still too large,
266*d9f75844SAndroid Build Coastguard Worker   // clear it and request a keyframe.
267*d9f75844SAndroid Build Coastguard Worker   uint16_t num_new_nacks = ForwardDiff(seq_num_start, seq_num_end);
268*d9f75844SAndroid Build Coastguard Worker   if (nack_list_.size() + num_new_nacks > kMaxNackPackets) {
269*d9f75844SAndroid Build Coastguard Worker     while (RemovePacketsUntilKeyFrame() &&
270*d9f75844SAndroid Build Coastguard Worker            nack_list_.size() + num_new_nacks > kMaxNackPackets) {
271*d9f75844SAndroid Build Coastguard Worker     }
272*d9f75844SAndroid Build Coastguard Worker 
273*d9f75844SAndroid Build Coastguard Worker     if (nack_list_.size() + num_new_nacks > kMaxNackPackets) {
274*d9f75844SAndroid Build Coastguard Worker       nack_list_.clear();
275*d9f75844SAndroid Build Coastguard Worker       RTC_LOG(LS_WARNING) << "NACK list full, clearing NACK"
276*d9f75844SAndroid Build Coastguard Worker                              " list and requesting keyframe.";
277*d9f75844SAndroid Build Coastguard Worker       keyframe_request_sender_->RequestKeyFrame();
278*d9f75844SAndroid Build Coastguard Worker       return;
279*d9f75844SAndroid Build Coastguard Worker     }
280*d9f75844SAndroid Build Coastguard Worker   }
281*d9f75844SAndroid Build Coastguard Worker 
282*d9f75844SAndroid Build Coastguard Worker   for (uint16_t seq_num = seq_num_start; seq_num != seq_num_end; ++seq_num) {
283*d9f75844SAndroid Build Coastguard Worker     // Do not send nack for packets that are already recovered by FEC or RTX
284*d9f75844SAndroid Build Coastguard Worker     if (recovered_list_.find(seq_num) != recovered_list_.end())
285*d9f75844SAndroid Build Coastguard Worker       continue;
286*d9f75844SAndroid Build Coastguard Worker     NackInfo nack_info(seq_num, seq_num + WaitNumberOfPackets(0.5),
287*d9f75844SAndroid Build Coastguard Worker                        clock_->CurrentTime());
288*d9f75844SAndroid Build Coastguard Worker     RTC_DCHECK(nack_list_.find(seq_num) == nack_list_.end());
289*d9f75844SAndroid Build Coastguard Worker     nack_list_[seq_num] = nack_info;
290*d9f75844SAndroid Build Coastguard Worker   }
291*d9f75844SAndroid Build Coastguard Worker }
292*d9f75844SAndroid Build Coastguard Worker 
GetNackBatch(NackFilterOptions options)293*d9f75844SAndroid Build Coastguard Worker std::vector<uint16_t> NackRequester::GetNackBatch(NackFilterOptions options) {
294*d9f75844SAndroid Build Coastguard Worker   // Called on worker_thread_.
295*d9f75844SAndroid Build Coastguard Worker 
296*d9f75844SAndroid Build Coastguard Worker   bool consider_seq_num = options != kTimeOnly;
297*d9f75844SAndroid Build Coastguard Worker   bool consider_timestamp = options != kSeqNumOnly;
298*d9f75844SAndroid Build Coastguard Worker   Timestamp now = clock_->CurrentTime();
299*d9f75844SAndroid Build Coastguard Worker   std::vector<uint16_t> nack_batch;
300*d9f75844SAndroid Build Coastguard Worker   auto it = nack_list_.begin();
301*d9f75844SAndroid Build Coastguard Worker   while (it != nack_list_.end()) {
302*d9f75844SAndroid Build Coastguard Worker     bool delay_timed_out = now - it->second.created_at_time >= send_nack_delay_;
303*d9f75844SAndroid Build Coastguard Worker     bool nack_on_rtt_passed = now - it->second.sent_at_time >= rtt_;
304*d9f75844SAndroid Build Coastguard Worker     bool nack_on_seq_num_passed =
305*d9f75844SAndroid Build Coastguard Worker         it->second.sent_at_time.IsInfinite() &&
306*d9f75844SAndroid Build Coastguard Worker         AheadOrAt(newest_seq_num_, it->second.send_at_seq_num);
307*d9f75844SAndroid Build Coastguard Worker     if (delay_timed_out && ((consider_seq_num && nack_on_seq_num_passed) ||
308*d9f75844SAndroid Build Coastguard Worker                             (consider_timestamp && nack_on_rtt_passed))) {
309*d9f75844SAndroid Build Coastguard Worker       nack_batch.emplace_back(it->second.seq_num);
310*d9f75844SAndroid Build Coastguard Worker       ++it->second.retries;
311*d9f75844SAndroid Build Coastguard Worker       it->second.sent_at_time = now;
312*d9f75844SAndroid Build Coastguard Worker       if (it->second.retries >= kMaxNackRetries) {
313*d9f75844SAndroid Build Coastguard Worker         RTC_LOG(LS_WARNING) << "Sequence number " << it->second.seq_num
314*d9f75844SAndroid Build Coastguard Worker                             << " removed from NACK list due to max retries.";
315*d9f75844SAndroid Build Coastguard Worker         it = nack_list_.erase(it);
316*d9f75844SAndroid Build Coastguard Worker       } else {
317*d9f75844SAndroid Build Coastguard Worker         ++it;
318*d9f75844SAndroid Build Coastguard Worker       }
319*d9f75844SAndroid Build Coastguard Worker       continue;
320*d9f75844SAndroid Build Coastguard Worker     }
321*d9f75844SAndroid Build Coastguard Worker     ++it;
322*d9f75844SAndroid Build Coastguard Worker   }
323*d9f75844SAndroid Build Coastguard Worker   return nack_batch;
324*d9f75844SAndroid Build Coastguard Worker }
325*d9f75844SAndroid Build Coastguard Worker 
UpdateReorderingStatistics(uint16_t seq_num)326*d9f75844SAndroid Build Coastguard Worker void NackRequester::UpdateReorderingStatistics(uint16_t seq_num) {
327*d9f75844SAndroid Build Coastguard Worker   // Running on worker_thread_.
328*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(AheadOf(newest_seq_num_, seq_num));
329*d9f75844SAndroid Build Coastguard Worker   uint16_t diff = ReverseDiff(newest_seq_num_, seq_num);
330*d9f75844SAndroid Build Coastguard Worker   reordering_histogram_.Add(diff);
331*d9f75844SAndroid Build Coastguard Worker }
332*d9f75844SAndroid Build Coastguard Worker 
WaitNumberOfPackets(float probability) const333*d9f75844SAndroid Build Coastguard Worker int NackRequester::WaitNumberOfPackets(float probability) const {
334*d9f75844SAndroid Build Coastguard Worker   // Called on worker_thread_;
335*d9f75844SAndroid Build Coastguard Worker   if (reordering_histogram_.NumValues() == 0)
336*d9f75844SAndroid Build Coastguard Worker     return 0;
337*d9f75844SAndroid Build Coastguard Worker   return reordering_histogram_.InverseCdf(probability);
338*d9f75844SAndroid Build Coastguard Worker }
339*d9f75844SAndroid Build Coastguard Worker 
340*d9f75844SAndroid Build Coastguard Worker }  // namespace webrtc
341