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