• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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