1 /*
2 * Copyright (c) 2014 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 #include "modules/remote_bitrate_estimator/aimd_rate_control.h"
12
13 #include <inttypes.h>
14
15 #include <algorithm>
16 #include <cassert>
17 #include <cmath>
18 #include <cstdio>
19 #include <string>
20
21 #include "absl/strings/match.h"
22 #include "api/transport/network_types.h"
23 #include "api/units/data_rate.h"
24 #include "modules/remote_bitrate_estimator/include/bwe_defines.h"
25 #include "modules/remote_bitrate_estimator/overuse_detector.h"
26 #include "rtc_base/checks.h"
27 #include "rtc_base/experiments/field_trial_parser.h"
28 #include "rtc_base/logging.h"
29 #include "rtc_base/numerics/safe_minmax.h"
30
31 namespace webrtc {
32 namespace {
33
34 constexpr TimeDelta kDefaultRtt = TimeDelta::Millis(200);
35 constexpr double kDefaultBackoffFactor = 0.85;
36
37 constexpr char kBweBackOffFactorExperiment[] = "WebRTC-BweBackOffFactor";
38
IsEnabled(const WebRtcKeyValueConfig & field_trials,absl::string_view key)39 bool IsEnabled(const WebRtcKeyValueConfig& field_trials,
40 absl::string_view key) {
41 return absl::StartsWith(field_trials.Lookup(key), "Enabled");
42 }
43
IsNotDisabled(const WebRtcKeyValueConfig & field_trials,absl::string_view key)44 bool IsNotDisabled(const WebRtcKeyValueConfig& field_trials,
45 absl::string_view key) {
46 return !absl::StartsWith(field_trials.Lookup(key), "Disabled");
47 }
48
ReadBackoffFactor(const WebRtcKeyValueConfig & key_value_config)49 double ReadBackoffFactor(const WebRtcKeyValueConfig& key_value_config) {
50 std::string experiment_string =
51 key_value_config.Lookup(kBweBackOffFactorExperiment);
52 double backoff_factor;
53 int parsed_values =
54 sscanf(experiment_string.c_str(), "Enabled-%lf", &backoff_factor);
55 if (parsed_values == 1) {
56 if (backoff_factor >= 1.0) {
57 RTC_LOG(WARNING) << "Back-off factor must be less than 1.";
58 } else if (backoff_factor <= 0.0) {
59 RTC_LOG(WARNING) << "Back-off factor must be greater than 0.";
60 } else {
61 return backoff_factor;
62 }
63 }
64 RTC_LOG(LS_WARNING) << "Failed to parse parameters for AimdRateControl "
65 "experiment from field trial string. Using default.";
66 return kDefaultBackoffFactor;
67 }
68
69 } // namespace
70
AimdRateControl(const WebRtcKeyValueConfig * key_value_config)71 AimdRateControl::AimdRateControl(const WebRtcKeyValueConfig* key_value_config)
72 : AimdRateControl(key_value_config, /* send_side =*/false) {}
73
AimdRateControl(const WebRtcKeyValueConfig * key_value_config,bool send_side)74 AimdRateControl::AimdRateControl(const WebRtcKeyValueConfig* key_value_config,
75 bool send_side)
76 : min_configured_bitrate_(congestion_controller::GetMinBitrate()),
77 max_configured_bitrate_(DataRate::KilobitsPerSec(30000)),
78 current_bitrate_(max_configured_bitrate_),
79 latest_estimated_throughput_(current_bitrate_),
80 link_capacity_(),
81 rate_control_state_(kRcHold),
82 time_last_bitrate_change_(Timestamp::MinusInfinity()),
83 time_last_bitrate_decrease_(Timestamp::MinusInfinity()),
84 time_first_throughput_estimate_(Timestamp::MinusInfinity()),
85 bitrate_is_initialized_(false),
86 beta_(IsEnabled(*key_value_config, kBweBackOffFactorExperiment)
87 ? ReadBackoffFactor(*key_value_config)
88 : kDefaultBackoffFactor),
89 in_alr_(false),
90 rtt_(kDefaultRtt),
91 send_side_(send_side),
92 in_experiment_(!AdaptiveThresholdExperimentIsDisabled(*key_value_config)),
93 no_bitrate_increase_in_alr_(
94 IsEnabled(*key_value_config,
95 "WebRTC-DontIncreaseDelayBasedBweInAlr")),
96 estimate_bounded_backoff_(
97 IsNotDisabled(*key_value_config,
98 "WebRTC-Bwe-EstimateBoundedBackoff")),
99 estimate_bounded_increase_(
100 IsNotDisabled(*key_value_config,
101 "WebRTC-Bwe-EstimateBoundedIncrease")),
102 initial_backoff_interval_("initial_backoff_interval"),
103 link_capacity_fix_("link_capacity_fix") {
104 // E.g
105 // WebRTC-BweAimdRateControlConfig/initial_backoff_interval:100ms/
106 ParseFieldTrial({&initial_backoff_interval_, &link_capacity_fix_},
107 key_value_config->Lookup("WebRTC-BweAimdRateControlConfig"));
108 if (initial_backoff_interval_) {
109 RTC_LOG(LS_INFO) << "Using aimd rate control with initial back-off interval"
110 " "
111 << ToString(*initial_backoff_interval_) << ".";
112 }
113 RTC_LOG(LS_INFO) << "Using aimd rate control with back off factor " << beta_;
114 }
115
~AimdRateControl()116 AimdRateControl::~AimdRateControl() {}
117
SetStartBitrate(DataRate start_bitrate)118 void AimdRateControl::SetStartBitrate(DataRate start_bitrate) {
119 current_bitrate_ = start_bitrate;
120 latest_estimated_throughput_ = current_bitrate_;
121 bitrate_is_initialized_ = true;
122 }
123
SetMinBitrate(DataRate min_bitrate)124 void AimdRateControl::SetMinBitrate(DataRate min_bitrate) {
125 min_configured_bitrate_ = min_bitrate;
126 current_bitrate_ = std::max(min_bitrate, current_bitrate_);
127 }
128
ValidEstimate() const129 bool AimdRateControl::ValidEstimate() const {
130 return bitrate_is_initialized_;
131 }
132
GetFeedbackInterval() const133 TimeDelta AimdRateControl::GetFeedbackInterval() const {
134 // Estimate how often we can send RTCP if we allocate up to 5% of bandwidth
135 // to feedback.
136 const DataSize kRtcpSize = DataSize::Bytes(80);
137 const DataRate rtcp_bitrate = current_bitrate_ * 0.05;
138 const TimeDelta interval = kRtcpSize / rtcp_bitrate;
139 const TimeDelta kMinFeedbackInterval = TimeDelta::Millis(200);
140 const TimeDelta kMaxFeedbackInterval = TimeDelta::Millis(1000);
141 return interval.Clamped(kMinFeedbackInterval, kMaxFeedbackInterval);
142 }
143
TimeToReduceFurther(Timestamp at_time,DataRate estimated_throughput) const144 bool AimdRateControl::TimeToReduceFurther(Timestamp at_time,
145 DataRate estimated_throughput) const {
146 const TimeDelta bitrate_reduction_interval =
147 rtt_.Clamped(TimeDelta::Millis(10), TimeDelta::Millis(200));
148 if (at_time - time_last_bitrate_change_ >= bitrate_reduction_interval) {
149 return true;
150 }
151 if (ValidEstimate()) {
152 // TODO(terelius/holmer): Investigate consequences of increasing
153 // the threshold to 0.95 * LatestEstimate().
154 const DataRate threshold = 0.5 * LatestEstimate();
155 return estimated_throughput < threshold;
156 }
157 return false;
158 }
159
InitialTimeToReduceFurther(Timestamp at_time) const160 bool AimdRateControl::InitialTimeToReduceFurther(Timestamp at_time) const {
161 if (!initial_backoff_interval_) {
162 return ValidEstimate() &&
163 TimeToReduceFurther(at_time,
164 LatestEstimate() / 2 - DataRate::BitsPerSec(1));
165 }
166 // TODO(terelius): We could use the RTT (clamped to suitable limits) instead
167 // of a fixed bitrate_reduction_interval.
168 if (time_last_bitrate_decrease_.IsInfinite() ||
169 at_time - time_last_bitrate_decrease_ >= *initial_backoff_interval_) {
170 return true;
171 }
172 return false;
173 }
174
LatestEstimate() const175 DataRate AimdRateControl::LatestEstimate() const {
176 return current_bitrate_;
177 }
178
SetRtt(TimeDelta rtt)179 void AimdRateControl::SetRtt(TimeDelta rtt) {
180 rtt_ = rtt;
181 }
182
Update(const RateControlInput * input,Timestamp at_time)183 DataRate AimdRateControl::Update(const RateControlInput* input,
184 Timestamp at_time) {
185 RTC_CHECK(input);
186
187 // Set the initial bit rate value to what we're receiving the first half
188 // second.
189 // TODO(bugs.webrtc.org/9379): The comment above doesn't match to the code.
190 if (!bitrate_is_initialized_) {
191 const TimeDelta kInitializationTime = TimeDelta::Seconds(5);
192 RTC_DCHECK_LE(kBitrateWindowMs, kInitializationTime.ms());
193 if (time_first_throughput_estimate_.IsInfinite()) {
194 if (input->estimated_throughput)
195 time_first_throughput_estimate_ = at_time;
196 } else if (at_time - time_first_throughput_estimate_ >
197 kInitializationTime &&
198 input->estimated_throughput) {
199 current_bitrate_ = *input->estimated_throughput;
200 bitrate_is_initialized_ = true;
201 }
202 }
203
204 ChangeBitrate(*input, at_time);
205 return current_bitrate_;
206 }
207
SetInApplicationLimitedRegion(bool in_alr)208 void AimdRateControl::SetInApplicationLimitedRegion(bool in_alr) {
209 in_alr_ = in_alr;
210 }
211
SetEstimate(DataRate bitrate,Timestamp at_time)212 void AimdRateControl::SetEstimate(DataRate bitrate, Timestamp at_time) {
213 bitrate_is_initialized_ = true;
214 DataRate prev_bitrate = current_bitrate_;
215 current_bitrate_ = ClampBitrate(bitrate);
216 time_last_bitrate_change_ = at_time;
217 if (current_bitrate_ < prev_bitrate) {
218 time_last_bitrate_decrease_ = at_time;
219 }
220 }
221
SetNetworkStateEstimate(const absl::optional<NetworkStateEstimate> & estimate)222 void AimdRateControl::SetNetworkStateEstimate(
223 const absl::optional<NetworkStateEstimate>& estimate) {
224 network_estimate_ = estimate;
225 }
226
GetNearMaxIncreaseRateBpsPerSecond() const227 double AimdRateControl::GetNearMaxIncreaseRateBpsPerSecond() const {
228 RTC_DCHECK(!current_bitrate_.IsZero());
229 const TimeDelta kFrameInterval = TimeDelta::Seconds(1) / 30;
230 DataSize frame_size = current_bitrate_ * kFrameInterval;
231 const DataSize kPacketSize = DataSize::Bytes(1200);
232 double packets_per_frame = std::ceil(frame_size / kPacketSize);
233 DataSize avg_packet_size = frame_size / packets_per_frame;
234
235 // Approximate the over-use estimator delay to 100 ms.
236 TimeDelta response_time = rtt_ + TimeDelta::Millis(100);
237 if (in_experiment_)
238 response_time = response_time * 2;
239 double increase_rate_bps_per_second =
240 (avg_packet_size / response_time).bps<double>();
241 double kMinIncreaseRateBpsPerSecond = 4000;
242 return std::max(kMinIncreaseRateBpsPerSecond, increase_rate_bps_per_second);
243 }
244
GetExpectedBandwidthPeriod() const245 TimeDelta AimdRateControl::GetExpectedBandwidthPeriod() const {
246 const TimeDelta kMinPeriod = TimeDelta::Seconds(2);
247 const TimeDelta kDefaultPeriod = TimeDelta::Seconds(3);
248 const TimeDelta kMaxPeriod = TimeDelta::Seconds(50);
249
250 double increase_rate_bps_per_second = GetNearMaxIncreaseRateBpsPerSecond();
251 if (!last_decrease_)
252 return kDefaultPeriod;
253 double time_to_recover_decrease_seconds =
254 last_decrease_->bps() / increase_rate_bps_per_second;
255 TimeDelta period = TimeDelta::Seconds(time_to_recover_decrease_seconds);
256 return period.Clamped(kMinPeriod, kMaxPeriod);
257 }
258
ChangeBitrate(const RateControlInput & input,Timestamp at_time)259 void AimdRateControl::ChangeBitrate(const RateControlInput& input,
260 Timestamp at_time) {
261 absl::optional<DataRate> new_bitrate;
262 DataRate estimated_throughput =
263 input.estimated_throughput.value_or(latest_estimated_throughput_);
264 if (input.estimated_throughput)
265 latest_estimated_throughput_ = *input.estimated_throughput;
266
267 // An over-use should always trigger us to reduce the bitrate, even though
268 // we have not yet established our first estimate. By acting on the over-use,
269 // we will end up with a valid estimate.
270 if (!bitrate_is_initialized_ &&
271 input.bw_state != BandwidthUsage::kBwOverusing)
272 return;
273
274 ChangeState(input, at_time);
275
276 // We limit the new bitrate based on the troughput to avoid unlimited bitrate
277 // increases. We allow a bit more lag at very low rates to not too easily get
278 // stuck if the encoder produces uneven outputs.
279 const DataRate troughput_based_limit =
280 1.5 * estimated_throughput + DataRate::KilobitsPerSec(10);
281
282 switch (rate_control_state_) {
283 case kRcHold:
284 break;
285
286 case kRcIncrease:
287 if (estimated_throughput > link_capacity_.UpperBound())
288 link_capacity_.Reset();
289
290 // Do not increase the delay based estimate in alr since the estimator
291 // will not be able to get transport feedback necessary to detect if
292 // the new estimate is correct.
293 // If we have previously increased above the limit (for instance due to
294 // probing), we don't allow further changes.
295 if (current_bitrate_ < troughput_based_limit &&
296 !(send_side_ && in_alr_ && no_bitrate_increase_in_alr_)) {
297 DataRate increased_bitrate = DataRate::MinusInfinity();
298 if (link_capacity_.has_estimate()) {
299 // The link_capacity estimate is reset if the measured throughput
300 // is too far from the estimate. We can therefore assume that our
301 // target rate is reasonably close to link capacity and use additive
302 // increase.
303 DataRate additive_increase =
304 AdditiveRateIncrease(at_time, time_last_bitrate_change_);
305 increased_bitrate = current_bitrate_ + additive_increase;
306 } else {
307 // If we don't have an estimate of the link capacity, use faster ramp
308 // up to discover the capacity.
309 DataRate multiplicative_increase = MultiplicativeRateIncrease(
310 at_time, time_last_bitrate_change_, current_bitrate_);
311 increased_bitrate = current_bitrate_ + multiplicative_increase;
312 }
313 new_bitrate = std::min(increased_bitrate, troughput_based_limit);
314 }
315
316 time_last_bitrate_change_ = at_time;
317 break;
318
319 case kRcDecrease: {
320 DataRate decreased_bitrate = DataRate::PlusInfinity();
321
322 // Set bit rate to something slightly lower than the measured throughput
323 // to get rid of any self-induced delay.
324 decreased_bitrate = estimated_throughput * beta_;
325 if (decreased_bitrate > current_bitrate_ && !link_capacity_fix_) {
326 // TODO(terelius): The link_capacity estimate may be based on old
327 // throughput measurements. Relying on them may lead to unnecessary
328 // BWE drops.
329 if (link_capacity_.has_estimate()) {
330 decreased_bitrate = beta_ * link_capacity_.estimate();
331 }
332 }
333 if (estimate_bounded_backoff_ && network_estimate_) {
334 decreased_bitrate = std::max(
335 decreased_bitrate, network_estimate_->link_capacity_lower * beta_);
336 }
337
338 // Avoid increasing the rate when over-using.
339 if (decreased_bitrate < current_bitrate_) {
340 new_bitrate = decreased_bitrate;
341 }
342
343 if (bitrate_is_initialized_ && estimated_throughput < current_bitrate_) {
344 if (!new_bitrate.has_value()) {
345 last_decrease_ = DataRate::Zero();
346 } else {
347 last_decrease_ = current_bitrate_ - *new_bitrate;
348 }
349 }
350 if (estimated_throughput < link_capacity_.LowerBound()) {
351 // The current throughput is far from the estimated link capacity. Clear
352 // the estimate to allow an immediate update in OnOveruseDetected.
353 link_capacity_.Reset();
354 }
355
356 bitrate_is_initialized_ = true;
357 link_capacity_.OnOveruseDetected(estimated_throughput);
358 // Stay on hold until the pipes are cleared.
359 rate_control_state_ = kRcHold;
360 time_last_bitrate_change_ = at_time;
361 time_last_bitrate_decrease_ = at_time;
362 break;
363 }
364 default:
365 assert(false);
366 }
367
368 current_bitrate_ = ClampBitrate(new_bitrate.value_or(current_bitrate_));
369 }
370
ClampBitrate(DataRate new_bitrate) const371 DataRate AimdRateControl::ClampBitrate(DataRate new_bitrate) const {
372 if (estimate_bounded_increase_ && network_estimate_) {
373 DataRate upper_bound = network_estimate_->link_capacity_upper;
374 new_bitrate = std::min(new_bitrate, upper_bound);
375 }
376 new_bitrate = std::max(new_bitrate, min_configured_bitrate_);
377 return new_bitrate;
378 }
379
MultiplicativeRateIncrease(Timestamp at_time,Timestamp last_time,DataRate current_bitrate) const380 DataRate AimdRateControl::MultiplicativeRateIncrease(
381 Timestamp at_time,
382 Timestamp last_time,
383 DataRate current_bitrate) const {
384 double alpha = 1.08;
385 if (last_time.IsFinite()) {
386 auto time_since_last_update = at_time - last_time;
387 alpha = pow(alpha, std::min(time_since_last_update.seconds<double>(), 1.0));
388 }
389 DataRate multiplicative_increase =
390 std::max(current_bitrate * (alpha - 1.0), DataRate::BitsPerSec(1000));
391 return multiplicative_increase;
392 }
393
AdditiveRateIncrease(Timestamp at_time,Timestamp last_time) const394 DataRate AimdRateControl::AdditiveRateIncrease(Timestamp at_time,
395 Timestamp last_time) const {
396 double time_period_seconds = (at_time - last_time).seconds<double>();
397 double data_rate_increase_bps =
398 GetNearMaxIncreaseRateBpsPerSecond() * time_period_seconds;
399 return DataRate::BitsPerSec(data_rate_increase_bps);
400 }
401
ChangeState(const RateControlInput & input,Timestamp at_time)402 void AimdRateControl::ChangeState(const RateControlInput& input,
403 Timestamp at_time) {
404 switch (input.bw_state) {
405 case BandwidthUsage::kBwNormal:
406 if (rate_control_state_ == kRcHold) {
407 time_last_bitrate_change_ = at_time;
408 rate_control_state_ = kRcIncrease;
409 }
410 break;
411 case BandwidthUsage::kBwOverusing:
412 if (rate_control_state_ != kRcDecrease) {
413 rate_control_state_ = kRcDecrease;
414 }
415 break;
416 case BandwidthUsage::kBwUnderusing:
417 rate_control_state_ = kRcHold;
418 break;
419 default:
420 assert(false);
421 }
422 }
423
424 } // namespace webrtc
425