| /* |
| * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "webrtc/modules/remote_bitrate_estimator/aimd_rate_control.h" |
| |
| #include <algorithm> |
| #include <cassert> |
| #include <cmath> |
| |
| #include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h" |
| |
| namespace webrtc { |
| |
| static const uint32_t kDefaultRttMs = 200; |
| static const int64_t kLogIntervalMs = 1000; |
| static const double kWithinIncomingBitrateHysteresis = 1.05; |
| |
| AimdRateControl::AimdRateControl(uint32_t min_bitrate_bps) |
| : min_configured_bitrate_bps_(min_bitrate_bps), |
| max_configured_bitrate_bps_(30000000), |
| current_bitrate_bps_(max_configured_bitrate_bps_), |
| max_hold_rate_bps_(0), |
| avg_max_bitrate_kbps_(-1.0f), |
| var_max_bitrate_kbps_(0.4f), |
| rate_control_state_(kRcHold), |
| came_from_state_(kRcDecrease), |
| rate_control_region_(kRcMaxUnknown), |
| time_last_bitrate_change_(-1), |
| current_input_(kBwNormal, 0, 1.0), |
| updated_(false), |
| time_first_incoming_estimate_(-1), |
| bitrate_is_initialized_(false), |
| beta_(0.9f), |
| rtt_(kDefaultRttMs), |
| time_of_last_log_(-1) {} |
| |
| RateControlType AimdRateControl::GetControlType() const { |
| return kAimdControl; |
| } |
| |
| uint32_t AimdRateControl::GetMinBitrate() const { |
| return min_configured_bitrate_bps_; |
| } |
| |
| bool AimdRateControl::ValidEstimate() const { |
| return bitrate_is_initialized_; |
| } |
| |
| int64_t AimdRateControl::GetFeedbackInterval() const { |
| // Estimate how often we can send RTCP if we allocate up to 5% of bandwidth |
| // to feedback. |
| static const int kRtcpSize = 80; |
| int64_t interval = static_cast<int64_t>( |
| kRtcpSize * 8.0 * 1000.0 / (0.05 * current_bitrate_bps_) + 0.5); |
| const int64_t kMinFeedbackIntervalMs = 200; |
| return std::min(std::max(interval, kMinFeedbackIntervalMs), |
| kMaxFeedbackIntervalMs); |
| } |
| |
| bool AimdRateControl::TimeToReduceFurther(int64_t time_now, |
| uint32_t incoming_bitrate_bps) const { |
| const int bitrate_reduction_interval = std::max(std::min(rtt_, 200u), 10u); |
| if (time_now - time_last_bitrate_change_ >= bitrate_reduction_interval) { |
| return true; |
| } |
| if (ValidEstimate()) { |
| const int threshold = static_cast<int>(kWithinIncomingBitrateHysteresis * |
| incoming_bitrate_bps); |
| const int bitrate_difference = LatestEstimate() - incoming_bitrate_bps; |
| return bitrate_difference > threshold; |
| } |
| return false; |
| } |
| |
| uint32_t AimdRateControl::LatestEstimate() const { |
| return current_bitrate_bps_; |
| } |
| |
| uint32_t AimdRateControl::UpdateBandwidthEstimate(int64_t now_ms) { |
| current_bitrate_bps_ = ChangeBitrate(current_bitrate_bps_, |
| current_input_._incomingBitRate, |
| now_ms); |
| if (now_ms - time_of_last_log_ > kLogIntervalMs) { |
| time_of_last_log_ = now_ms; |
| } |
| return current_bitrate_bps_; |
| } |
| |
| void AimdRateControl::SetRtt(uint32_t rtt) { |
| rtt_ = rtt; |
| } |
| |
| RateControlRegion AimdRateControl::Update(const RateControlInput* input, |
| int64_t now_ms) { |
| assert(input); |
| |
| // Set the initial bit rate value to what we're receiving the first half |
| // second. |
| if (!bitrate_is_initialized_) { |
| if (time_first_incoming_estimate_ < 0) { |
| if (input->_incomingBitRate > 0) { |
| time_first_incoming_estimate_ = now_ms; |
| } |
| } else if (now_ms - time_first_incoming_estimate_ > 500 && |
| input->_incomingBitRate > 0) { |
| current_bitrate_bps_ = input->_incomingBitRate; |
| bitrate_is_initialized_ = true; |
| } |
| } |
| |
| if (updated_ && current_input_._bwState == kBwOverusing) { |
| // Only update delay factor and incoming bit rate. We always want to react |
| // on an over-use. |
| current_input_._noiseVar = input->_noiseVar; |
| current_input_._incomingBitRate = input->_incomingBitRate; |
| } else { |
| updated_ = true; |
| current_input_ = *input; |
| } |
| return rate_control_region_; |
| } |
| |
| void AimdRateControl::SetEstimate(int bitrate_bps, int64_t now_ms) { |
| updated_ = true; |
| bitrate_is_initialized_ = true; |
| current_bitrate_bps_ = ChangeBitrate(bitrate_bps, bitrate_bps, now_ms); |
| } |
| |
| uint32_t AimdRateControl::ChangeBitrate(uint32_t current_bitrate_bps, |
| uint32_t incoming_bitrate_bps, |
| int64_t now_ms) { |
| BWE_TEST_LOGGING_PLOT("estimated_incoming#1", -1, |
| incoming_bitrate_bps / 1000); |
| if (!updated_) { |
| return current_bitrate_bps_; |
| } |
| updated_ = false; |
| ChangeState(current_input_, now_ms); |
| // Calculated here because it's used in multiple places. |
| const float incoming_bitrate_kbps = incoming_bitrate_bps / 1000.0f; |
| // Calculate the max bit rate std dev given the normalized |
| // variance and the current incoming bit rate. |
| const float std_max_bit_rate = sqrt(var_max_bitrate_kbps_ * |
| avg_max_bitrate_kbps_); |
| bool fast_recovery_after_hold = false; |
| switch (rate_control_state_) { |
| case kRcHold: { |
| max_hold_rate_bps_ = std::max(max_hold_rate_bps_, incoming_bitrate_bps); |
| break; |
| } |
| case kRcIncrease: { |
| if (avg_max_bitrate_kbps_ >= 0) { |
| if (incoming_bitrate_kbps > avg_max_bitrate_kbps_ + |
| 3 * std_max_bit_rate) { |
| ChangeRegion(kRcMaxUnknown); |
| avg_max_bitrate_kbps_ = -1.0; |
| } else if (incoming_bitrate_kbps > avg_max_bitrate_kbps_ + |
| 2.5 * std_max_bit_rate) { |
| ChangeRegion(kRcAboveMax); |
| } |
| } |
| if (rate_control_region_ == kRcNearMax) { |
| // Approximate the over-use estimator delay to 100 ms. |
| const uint32_t response_time = rtt_ + 100; |
| uint32_t additive_increase_bps = AdditiveRateIncrease( |
| now_ms, time_last_bitrate_change_, response_time); |
| BWE_TEST_LOGGING_PLOT("add_increase#1", -1, |
| additive_increase_bps / 1000); |
| current_bitrate_bps += additive_increase_bps; |
| |
| } else { |
| uint32_t multiplicative_increase_bps = MultiplicativeRateIncrease( |
| now_ms, time_last_bitrate_change_, current_bitrate_bps); |
| BWE_TEST_LOGGING_PLOT("mult_increase#1", -1, |
| multiplicative_increase_bps / 1000); |
| current_bitrate_bps += multiplicative_increase_bps; |
| } |
| |
| if (max_hold_rate_bps_ > 0 && |
| beta_ * max_hold_rate_bps_ > current_bitrate_bps) { |
| current_bitrate_bps = static_cast<uint32_t>(beta_ * max_hold_rate_bps_); |
| avg_max_bitrate_kbps_ = beta_ * max_hold_rate_bps_ / 1000.0f; |
| ChangeRegion(kRcNearMax); |
| fast_recovery_after_hold = true; |
| } |
| max_hold_rate_bps_ = 0; |
| time_last_bitrate_change_ = now_ms; |
| break; |
| } |
| case kRcDecrease: { |
| if (incoming_bitrate_bps < min_configured_bitrate_bps_) { |
| current_bitrate_bps = min_configured_bitrate_bps_; |
| } else { |
| // Set bit rate to something slightly lower than max |
| // to get rid of any self-induced delay. |
| current_bitrate_bps = static_cast<uint32_t>(beta_ * |
| incoming_bitrate_bps + 0.5); |
| if (current_bitrate_bps > current_bitrate_bps_) { |
| // Avoid increasing the rate when over-using. |
| if (rate_control_region_ != kRcMaxUnknown) { |
| current_bitrate_bps = static_cast<uint32_t>( |
| beta_ * avg_max_bitrate_kbps_ * 1000 + 0.5f); |
| } |
| current_bitrate_bps = std::min(current_bitrate_bps, |
| current_bitrate_bps_); |
| } |
| ChangeRegion(kRcNearMax); |
| |
| if (incoming_bitrate_kbps < avg_max_bitrate_kbps_ - |
| 3 * std_max_bit_rate) { |
| avg_max_bitrate_kbps_ = -1.0f; |
| } |
| |
| UpdateMaxBitRateEstimate(incoming_bitrate_kbps); |
| } |
| // Stay on hold until the pipes are cleared. |
| ChangeState(kRcHold); |
| time_last_bitrate_change_ = now_ms; |
| break; |
| } |
| default: |
| assert(false); |
| } |
| if (!fast_recovery_after_hold && (incoming_bitrate_bps > 100000 || |
| current_bitrate_bps > 150000) && |
| current_bitrate_bps > 1.5 * incoming_bitrate_bps) { |
| // Allow changing the bit rate if we are operating at very low rates |
| // Don't change the bit rate if the send side is too far off |
| current_bitrate_bps = current_bitrate_bps_; |
| time_last_bitrate_change_ = now_ms; |
| } |
| return current_bitrate_bps; |
| } |
| |
| uint32_t AimdRateControl::MultiplicativeRateIncrease( |
| int64_t now_ms, int64_t last_ms, uint32_t current_bitrate_bps) const { |
| double alpha = 1.08; |
| if (last_ms > -1) { |
| int time_since_last_update_ms = std::min(static_cast<int>(now_ms - last_ms), |
| 1000); |
| alpha = pow(alpha, time_since_last_update_ms / 1000.0); |
| } |
| uint32_t multiplicative_increase_bps = std::max( |
| current_bitrate_bps * (alpha - 1.0), 1000.0); |
| return multiplicative_increase_bps; |
| } |
| |
| uint32_t AimdRateControl::AdditiveRateIncrease( |
| int64_t now_ms, int64_t last_ms, uint32_t response_time_ms) const { |
| assert(response_time_ms > 0); |
| double beta = 0.0; |
| if (last_ms > 0) { |
| beta = std::min((now_ms - last_ms) / |
| static_cast<double>(response_time_ms), 1.0); |
| } |
| double bits_per_frame = static_cast<double>(current_bitrate_bps_) / 30.0; |
| double packets_per_frame = std::ceil(bits_per_frame / (8.0 * 1200.0)); |
| double avg_packet_size_bits = bits_per_frame / packets_per_frame; |
| uint32_t additive_increase_bps = std::max( |
| 1000.0, beta * avg_packet_size_bits); |
| return additive_increase_bps; |
| } |
| |
| void AimdRateControl::UpdateMaxBitRateEstimate(float incoming_bitrate_kbps) { |
| const float alpha = 0.05f; |
| if (avg_max_bitrate_kbps_ == -1.0f) { |
| avg_max_bitrate_kbps_ = incoming_bitrate_kbps; |
| } else { |
| avg_max_bitrate_kbps_ = (1 - alpha) * avg_max_bitrate_kbps_ + |
| alpha * incoming_bitrate_kbps; |
| } |
| // Estimate the max bit rate variance and normalize the variance |
| // with the average max bit rate. |
| const float norm = std::max(avg_max_bitrate_kbps_, 1.0f); |
| var_max_bitrate_kbps_ = (1 - alpha) * var_max_bitrate_kbps_ + |
| alpha * (avg_max_bitrate_kbps_ - incoming_bitrate_kbps) * |
| (avg_max_bitrate_kbps_ - incoming_bitrate_kbps) / norm; |
| // 0.4 ~= 14 kbit/s at 500 kbit/s |
| if (var_max_bitrate_kbps_ < 0.4f) { |
| var_max_bitrate_kbps_ = 0.4f; |
| } |
| // 2.5f ~= 35 kbit/s at 500 kbit/s |
| if (var_max_bitrate_kbps_ > 2.5f) { |
| var_max_bitrate_kbps_ = 2.5f; |
| } |
| } |
| |
| void AimdRateControl::ChangeState(const RateControlInput& input, |
| int64_t now_ms) { |
| switch (current_input_._bwState) { |
| case kBwNormal: |
| if (rate_control_state_ == kRcHold) { |
| time_last_bitrate_change_ = now_ms; |
| ChangeState(kRcIncrease); |
| } |
| break; |
| case kBwOverusing: |
| if (rate_control_state_ != kRcDecrease) { |
| ChangeState(kRcDecrease); |
| } |
| break; |
| case kBwUnderusing: |
| ChangeState(kRcHold); |
| break; |
| default: |
| assert(false); |
| } |
| } |
| |
| void AimdRateControl::ChangeRegion(RateControlRegion region) { |
| rate_control_region_ = region; |
| switch (rate_control_region_) { |
| case kRcAboveMax: |
| case kRcMaxUnknown: |
| beta_ = 0.9f; |
| break; |
| case kRcNearMax: |
| beta_ = 0.95f; |
| break; |
| default: |
| assert(false); |
| } |
| } |
| |
| void AimdRateControl::ChangeState(RateControlState new_state) { |
| came_from_state_ = rate_control_state_; |
| rate_control_state_ = new_state; |
| } |
| } // namespace webrtc |