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