blob: dac85fc18955b7d3ead976272ddfbdfe5109b267 [file] [log] [blame]
Tommid3807da2020-05-22 17:36:36 +02001/*
2 * Copyright (c) 2016 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
Markus Handell06a2bf02021-07-22 15:09:39 +020011#include "modules/video_coding/nack_requester.h"
Tommid3807da2020-05-22 17:36:36 +020012
13#include <algorithm>
14#include <limits>
15
Markus Handell0e62f7a2021-07-20 13:32:02 +020016#include "api/sequence_checker.h"
17#include "api/task_queue/task_queue_base.h"
Tommid3807da2020-05-22 17:36:36 +020018#include "api/units/timestamp.h"
Tommid3807da2020-05-22 17:36:36 +020019#include "rtc_base/checks.h"
20#include "rtc_base/experiments/field_trial_parser.h"
21#include "rtc_base/logging.h"
Tommi63673fe2020-05-27 12:55:38 +020022#include "rtc_base/task_queue.h"
Tommid3807da2020-05-22 17:36:36 +020023#include "system_wrappers/include/field_trial.h"
24
25namespace webrtc {
26
27namespace {
28const int kMaxPacketAge = 10000;
29const int kMaxNackPackets = 1000;
30const int kDefaultRttMs = 100;
31const int kMaxNackRetries = 10;
Tommid3807da2020-05-22 17:36:36 +020032const int kMaxReorderedPackets = 128;
33const int kNumReorderingBuckets = 10;
34const int kDefaultSendNackDelayMs = 0;
35
36int64_t GetSendNackDelay() {
37 int64_t delay_ms = strtol(
38 webrtc::field_trial::FindFullName("WebRTC-SendNackDelayMs").c_str(),
39 nullptr, 10);
40 if (delay_ms > 0 && delay_ms <= 20) {
41 RTC_LOG(LS_INFO) << "SendNackDelay is set to " << delay_ms;
42 return delay_ms;
43 }
44 return kDefaultSendNackDelayMs;
45}
46} // namespace
47
Markus Handell0e62f7a2021-07-20 13:32:02 +020048constexpr TimeDelta NackPeriodicProcessor::kUpdateInterval;
49
50NackPeriodicProcessor::NackPeriodicProcessor(TimeDelta update_interval)
51 : update_interval_(update_interval) {}
52
53NackPeriodicProcessor::~NackPeriodicProcessor() {}
54
Markus Handell06a2bf02021-07-22 15:09:39 +020055void NackPeriodicProcessor::RegisterNackModule(NackRequesterBase* module) {
Markus Handell0e62f7a2021-07-20 13:32:02 +020056 RTC_DCHECK_RUN_ON(&sequence_);
57 modules_.push_back(module);
58 if (modules_.size() != 1)
59 return;
60 repeating_task_ = RepeatingTaskHandle::DelayedStart(
61 TaskQueueBase::Current(), update_interval_, [this] {
62 RTC_DCHECK_RUN_ON(&sequence_);
63 ProcessNackModules();
64 return update_interval_;
65 });
66}
67
Markus Handell06a2bf02021-07-22 15:09:39 +020068void NackPeriodicProcessor::UnregisterNackModule(NackRequesterBase* module) {
Markus Handell0e62f7a2021-07-20 13:32:02 +020069 RTC_DCHECK_RUN_ON(&sequence_);
70 auto it = std::find(modules_.begin(), modules_.end(), module);
71 RTC_DCHECK(it != modules_.end());
72 modules_.erase(it);
73 if (modules_.empty())
74 repeating_task_.Stop();
75}
76
77// RTC_RUN_ON(sequence_)
78void NackPeriodicProcessor::ProcessNackModules() {
Markus Handell06a2bf02021-07-22 15:09:39 +020079 for (NackRequesterBase* module : modules_)
Markus Handell0e62f7a2021-07-20 13:32:02 +020080 module->ProcessNacks();
81}
82
83ScopedNackPeriodicProcessorRegistration::
Markus Handell06a2bf02021-07-22 15:09:39 +020084 ScopedNackPeriodicProcessorRegistration(NackRequesterBase* module,
Markus Handell0e62f7a2021-07-20 13:32:02 +020085 NackPeriodicProcessor* processor)
86 : module_(module), processor_(processor) {
87 processor_->RegisterNackModule(module_);
88}
89
90ScopedNackPeriodicProcessorRegistration::
91 ~ScopedNackPeriodicProcessorRegistration() {
92 processor_->UnregisterNackModule(module_);
93}
Tommi63673fe2020-05-27 12:55:38 +020094
Markus Handell06a2bf02021-07-22 15:09:39 +020095NackRequester::NackInfo::NackInfo()
Tommid3807da2020-05-22 17:36:36 +020096 : seq_num(0), send_at_seq_num(0), sent_at_time(-1), retries(0) {}
97
Markus Handell06a2bf02021-07-22 15:09:39 +020098NackRequester::NackInfo::NackInfo(uint16_t seq_num,
99 uint16_t send_at_seq_num,
100 int64_t created_at_time)
Tommid3807da2020-05-22 17:36:36 +0200101 : seq_num(seq_num),
102 send_at_seq_num(send_at_seq_num),
103 created_at_time(created_at_time),
104 sent_at_time(-1),
105 retries(0) {}
106
Markus Handell06a2bf02021-07-22 15:09:39 +0200107NackRequester::BackoffSettings::BackoffSettings(TimeDelta min_retry,
108 TimeDelta max_rtt,
109 double base)
Tommid3807da2020-05-22 17:36:36 +0200110 : min_retry_interval(min_retry), max_rtt(max_rtt), base(base) {}
111
Markus Handell06a2bf02021-07-22 15:09:39 +0200112absl::optional<NackRequester::BackoffSettings>
113NackRequester::BackoffSettings::ParseFromFieldTrials() {
Tommid3807da2020-05-22 17:36:36 +0200114 // Matches magic number in RTPSender::OnReceivedNack().
115 const TimeDelta kDefaultMinRetryInterval = TimeDelta::Millis(5);
116 // Upper bound on link-delay considered for exponential backoff.
117 // Selected so that cumulative delay with 1.25 base and 10 retries ends up
118 // below 3s, since above that there will be a FIR generated instead.
119 const TimeDelta kDefaultMaxRtt = TimeDelta::Millis(160);
120 // Default base for exponential backoff, adds 25% RTT delay for each retry.
121 const double kDefaultBase = 1.25;
122
123 FieldTrialParameter<bool> enabled("enabled", false);
124 FieldTrialParameter<TimeDelta> min_retry("min_retry",
125 kDefaultMinRetryInterval);
126 FieldTrialParameter<TimeDelta> max_rtt("max_rtt", kDefaultMaxRtt);
127 FieldTrialParameter<double> base("base", kDefaultBase);
128 ParseFieldTrial({&enabled, &min_retry, &max_rtt, &base},
129 field_trial::FindFullName("WebRTC-ExponentialNackBackoff"));
130
131 if (enabled) {
Markus Handell06a2bf02021-07-22 15:09:39 +0200132 return NackRequester::BackoffSettings(min_retry.Get(), max_rtt.Get(),
133 base.Get());
Tommid3807da2020-05-22 17:36:36 +0200134 }
135 return absl::nullopt;
136}
137
Markus Handell06a2bf02021-07-22 15:09:39 +0200138NackRequester::NackRequester(TaskQueueBase* current_queue,
139 NackPeriodicProcessor* periodic_processor,
140 Clock* clock,
141 NackSender* nack_sender,
142 KeyFrameRequestSender* keyframe_request_sender)
Tommi63673fe2020-05-27 12:55:38 +0200143 : worker_thread_(current_queue),
Tommi63673fe2020-05-27 12:55:38 +0200144 clock_(clock),
Tommid3807da2020-05-22 17:36:36 +0200145 nack_sender_(nack_sender),
146 keyframe_request_sender_(keyframe_request_sender),
147 reordering_histogram_(kNumReorderingBuckets, kMaxReorderedPackets),
148 initialized_(false),
149 rtt_ms_(kDefaultRttMs),
150 newest_seq_num_(0),
Tommid3807da2020-05-22 17:36:36 +0200151 send_nack_delay_ms_(GetSendNackDelay()),
Markus Handell0e62f7a2021-07-20 13:32:02 +0200152 backoff_settings_(BackoffSettings::ParseFromFieldTrials()),
153 processor_registration_(this, periodic_processor) {
Tommid3807da2020-05-22 17:36:36 +0200154 RTC_DCHECK(clock_);
155 RTC_DCHECK(nack_sender_);
156 RTC_DCHECK(keyframe_request_sender_);
Tommi63673fe2020-05-27 12:55:38 +0200157 RTC_DCHECK(worker_thread_);
158 RTC_DCHECK(worker_thread_->IsCurrent());
Tommi63673fe2020-05-27 12:55:38 +0200159}
160
Markus Handell06a2bf02021-07-22 15:09:39 +0200161NackRequester::~NackRequester() {
Tommi63673fe2020-05-27 12:55:38 +0200162 RTC_DCHECK_RUN_ON(worker_thread_);
Markus Handell0e62f7a2021-07-20 13:32:02 +0200163}
164
Markus Handell06a2bf02021-07-22 15:09:39 +0200165void NackRequester::ProcessNacks() {
Markus Handell0e62f7a2021-07-20 13:32:02 +0200166 RTC_DCHECK_RUN_ON(worker_thread_);
167 std::vector<uint16_t> nack_batch = GetNackBatch(kTimeOnly);
168 if (!nack_batch.empty()) {
169 // This batch of NACKs is triggered externally; there is no external
170 // initiator who can batch them with other feedback messages.
171 nack_sender_->SendNack(nack_batch, /*buffering_allowed=*/false);
172 }
Tommid3807da2020-05-22 17:36:36 +0200173}
174
Markus Handell06a2bf02021-07-22 15:09:39 +0200175int NackRequester::OnReceivedPacket(uint16_t seq_num, bool is_keyframe) {
Tommi63673fe2020-05-27 12:55:38 +0200176 RTC_DCHECK_RUN_ON(worker_thread_);
Tommid3807da2020-05-22 17:36:36 +0200177 return OnReceivedPacket(seq_num, is_keyframe, false);
178}
179
Markus Handell06a2bf02021-07-22 15:09:39 +0200180int NackRequester::OnReceivedPacket(uint16_t seq_num,
181 bool is_keyframe,
182 bool is_recovered) {
Tommi63673fe2020-05-27 12:55:38 +0200183 RTC_DCHECK_RUN_ON(worker_thread_);
Tommid3807da2020-05-22 17:36:36 +0200184 // TODO(philipel): When the packet includes information whether it is
185 // retransmitted or not, use that value instead. For
186 // now set it to true, which will cause the reordering
187 // statistics to never be updated.
188 bool is_retransmitted = true;
189
190 if (!initialized_) {
191 newest_seq_num_ = seq_num;
192 if (is_keyframe)
193 keyframe_list_.insert(seq_num);
194 initialized_ = true;
195 return 0;
196 }
197
Artem Titovdcd7fc72021-08-09 13:02:57 +0200198 // Since the `newest_seq_num_` is a packet we have actually received we know
Tommid3807da2020-05-22 17:36:36 +0200199 // that packet has never been Nacked.
200 if (seq_num == newest_seq_num_)
201 return 0;
202
203 if (AheadOf(newest_seq_num_, seq_num)) {
204 // An out of order packet has been received.
205 auto nack_list_it = nack_list_.find(seq_num);
206 int nacks_sent_for_packet = 0;
207 if (nack_list_it != nack_list_.end()) {
208 nacks_sent_for_packet = nack_list_it->second.retries;
209 nack_list_.erase(nack_list_it);
210 }
211 if (!is_retransmitted)
212 UpdateReorderingStatistics(seq_num);
213 return nacks_sent_for_packet;
214 }
215
216 // Keep track of new keyframes.
217 if (is_keyframe)
218 keyframe_list_.insert(seq_num);
219
220 // And remove old ones so we don't accumulate keyframes.
221 auto it = keyframe_list_.lower_bound(seq_num - kMaxPacketAge);
222 if (it != keyframe_list_.begin())
223 keyframe_list_.erase(keyframe_list_.begin(), it);
224
225 if (is_recovered) {
226 recovered_list_.insert(seq_num);
227
228 // Remove old ones so we don't accumulate recovered packets.
229 auto it = recovered_list_.lower_bound(seq_num - kMaxPacketAge);
230 if (it != recovered_list_.begin())
231 recovered_list_.erase(recovered_list_.begin(), it);
232
233 // Do not send nack for packets recovered by FEC or RTX.
234 return 0;
235 }
236
237 AddPacketsToNack(newest_seq_num_ + 1, seq_num);
238 newest_seq_num_ = seq_num;
239
240 // Are there any nacks that are waiting for this seq_num.
241 std::vector<uint16_t> nack_batch = GetNackBatch(kSeqNumOnly);
242 if (!nack_batch.empty()) {
243 // This batch of NACKs is triggered externally; the initiator can
244 // batch them with other feedback messages.
245 nack_sender_->SendNack(nack_batch, /*buffering_allowed=*/true);
246 }
247
248 return 0;
249}
250
Markus Handell06a2bf02021-07-22 15:09:39 +0200251void NackRequester::ClearUpTo(uint16_t seq_num) {
Tommi63673fe2020-05-27 12:55:38 +0200252 // Called via RtpVideoStreamReceiver2::FrameContinuous on the network thread.
253 worker_thread_->PostTask(ToQueuedTask(task_safety_, [seq_num, this]() {
254 RTC_DCHECK_RUN_ON(worker_thread_);
255 nack_list_.erase(nack_list_.begin(), nack_list_.lower_bound(seq_num));
256 keyframe_list_.erase(keyframe_list_.begin(),
257 keyframe_list_.lower_bound(seq_num));
258 recovered_list_.erase(recovered_list_.begin(),
259 recovered_list_.lower_bound(seq_num));
260 }));
Tommid3807da2020-05-22 17:36:36 +0200261}
262
Markus Handell06a2bf02021-07-22 15:09:39 +0200263void NackRequester::UpdateRtt(int64_t rtt_ms) {
Tommi63673fe2020-05-27 12:55:38 +0200264 RTC_DCHECK_RUN_ON(worker_thread_);
Tommid3807da2020-05-22 17:36:36 +0200265 rtt_ms_ = rtt_ms;
266}
267
Markus Handell06a2bf02021-07-22 15:09:39 +0200268bool NackRequester::RemovePacketsUntilKeyFrame() {
Tommi63673fe2020-05-27 12:55:38 +0200269 // Called on worker_thread_.
Tommid3807da2020-05-22 17:36:36 +0200270 while (!keyframe_list_.empty()) {
271 auto it = nack_list_.lower_bound(*keyframe_list_.begin());
272
273 if (it != nack_list_.begin()) {
274 // We have found a keyframe that actually is newer than at least one
275 // packet in the nack list.
276 nack_list_.erase(nack_list_.begin(), it);
277 return true;
278 }
279
280 // If this keyframe is so old it does not remove any packets from the list,
281 // remove it from the list of keyframes and try the next keyframe.
282 keyframe_list_.erase(keyframe_list_.begin());
283 }
284 return false;
285}
286
Markus Handell06a2bf02021-07-22 15:09:39 +0200287void NackRequester::AddPacketsToNack(uint16_t seq_num_start,
288 uint16_t seq_num_end) {
Tommi63673fe2020-05-27 12:55:38 +0200289 // Called on worker_thread_.
Tommid3807da2020-05-22 17:36:36 +0200290 // Remove old packets.
291 auto it = nack_list_.lower_bound(seq_num_end - kMaxPacketAge);
292 nack_list_.erase(nack_list_.begin(), it);
293
294 // If the nack list is too large, remove packets from the nack list until
295 // the latest first packet of a keyframe. If the list is still too large,
296 // clear it and request a keyframe.
297 uint16_t num_new_nacks = ForwardDiff(seq_num_start, seq_num_end);
298 if (nack_list_.size() + num_new_nacks > kMaxNackPackets) {
299 while (RemovePacketsUntilKeyFrame() &&
300 nack_list_.size() + num_new_nacks > kMaxNackPackets) {
301 }
302
303 if (nack_list_.size() + num_new_nacks > kMaxNackPackets) {
304 nack_list_.clear();
305 RTC_LOG(LS_WARNING) << "NACK list full, clearing NACK"
306 " list and requesting keyframe.";
307 keyframe_request_sender_->RequestKeyFrame();
308 return;
309 }
310 }
311
312 for (uint16_t seq_num = seq_num_start; seq_num != seq_num_end; ++seq_num) {
313 // Do not send nack for packets that are already recovered by FEC or RTX
314 if (recovered_list_.find(seq_num) != recovered_list_.end())
315 continue;
316 NackInfo nack_info(seq_num, seq_num + WaitNumberOfPackets(0.5),
317 clock_->TimeInMilliseconds());
318 RTC_DCHECK(nack_list_.find(seq_num) == nack_list_.end());
319 nack_list_[seq_num] = nack_info;
320 }
321}
322
Markus Handell06a2bf02021-07-22 15:09:39 +0200323std::vector<uint16_t> NackRequester::GetNackBatch(NackFilterOptions options) {
Tommi63673fe2020-05-27 12:55:38 +0200324 // Called on worker_thread_.
325
Tommid3807da2020-05-22 17:36:36 +0200326 bool consider_seq_num = options != kTimeOnly;
327 bool consider_timestamp = options != kSeqNumOnly;
328 Timestamp now = clock_->CurrentTime();
329 std::vector<uint16_t> nack_batch;
330 auto it = nack_list_.begin();
331 while (it != nack_list_.end()) {
332 TimeDelta resend_delay = TimeDelta::Millis(rtt_ms_);
333 if (backoff_settings_) {
334 resend_delay =
335 std::max(resend_delay, backoff_settings_->min_retry_interval);
336 if (it->second.retries > 1) {
337 TimeDelta exponential_backoff =
338 std::min(TimeDelta::Millis(rtt_ms_), backoff_settings_->max_rtt) *
339 std::pow(backoff_settings_->base, it->second.retries - 1);
340 resend_delay = std::max(resend_delay, exponential_backoff);
341 }
342 }
343
344 bool delay_timed_out =
345 now.ms() - it->second.created_at_time >= send_nack_delay_ms_;
346 bool nack_on_rtt_passed =
347 now.ms() - it->second.sent_at_time >= resend_delay.ms();
348 bool nack_on_seq_num_passed =
349 it->second.sent_at_time == -1 &&
350 AheadOrAt(newest_seq_num_, it->second.send_at_seq_num);
351 if (delay_timed_out && ((consider_seq_num && nack_on_seq_num_passed) ||
352 (consider_timestamp && nack_on_rtt_passed))) {
353 nack_batch.emplace_back(it->second.seq_num);
354 ++it->second.retries;
355 it->second.sent_at_time = now.ms();
356 if (it->second.retries >= kMaxNackRetries) {
357 RTC_LOG(LS_WARNING) << "Sequence number " << it->second.seq_num
358 << " removed from NACK list due to max retries.";
359 it = nack_list_.erase(it);
360 } else {
361 ++it;
362 }
363 continue;
364 }
365 ++it;
366 }
367 return nack_batch;
368}
369
Markus Handell06a2bf02021-07-22 15:09:39 +0200370void NackRequester::UpdateReorderingStatistics(uint16_t seq_num) {
Tommi63673fe2020-05-27 12:55:38 +0200371 // Running on worker_thread_.
Tommid3807da2020-05-22 17:36:36 +0200372 RTC_DCHECK(AheadOf(newest_seq_num_, seq_num));
373 uint16_t diff = ReverseDiff(newest_seq_num_, seq_num);
374 reordering_histogram_.Add(diff);
375}
376
Markus Handell06a2bf02021-07-22 15:09:39 +0200377int NackRequester::WaitNumberOfPackets(float probability) const {
Tommi63673fe2020-05-27 12:55:38 +0200378 // Called on worker_thread_;
Tommid3807da2020-05-22 17:36:36 +0200379 if (reordering_histogram_.NumValues() == 0)
380 return 0;
381 return reordering_histogram_.InverseCdf(probability);
382}
383
384} // namespace webrtc