aimd_rate_control.cc
444 lines
/*
/*
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
*
*
* Use of this source code is governed by a BSD-style license
* 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
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
* be found in the AUTHORS file in the root of the source tree.
*/
*/
#define MS_CLASS "webrtc::AimdRateControl"
// #define MS_LOG_DEV_LEVEL 3
#include "modules/remote_bitrate_estimator/aimd_rate_control.h"
#include "modules/remote_bitrate_estimator/aimd_rate_control.h"
Text moved from lines 23-30
#include <inttypes.h>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <string>
#include "absl/strings/match.h"
#include "api/transport/network_types.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/data_rate.h"
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "modules/remote_bitrate_estimator/overuse_detector.h"
#include "modules/remote_bitrate_estimator/overuse_detector.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_minmax.h"
#include "rtc_base/numerics/safe_minmax.h"
#include "Logger.hpp"
Text moved to lines 13-20
#include "MediaSoupErrors.hpp"
#include <inttypes.h>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <string>
namespace webrtc {
namespace webrtc {
namespace {
namespace {
constexpr TimeDelta kDefaultRtt = TimeDelta::Millis<200>();
constexpr TimeDelta kDefaultRtt = TimeDelta::Millis(200);
constexpr double kDefaultBackoffFactor = 0.85;
constexpr double kDefaultBackoffFactor = 0.85;
constexpr char kBweBackOffFactorExperiment[] = "WebRTC-BweBackOffFactor";
constexpr char kBweBackOffFactorExperiment[] = "WebRTC-BweBackOffFactor";
bool IsEnabled(const WebRtcKeyValueConfig& field_trials,
bool IsEnabled(const WebRtcKeyValueConfig& field_trials,
absl::string_view key) {
absl::string_view key) {
return field_trials.Lookup(key).find("Enabled") == 0;
return absl::StartsWith(field_trials.Lookup(key), "Enabled");
}
bool IsNotDisabled(const WebRtcKeyValueConfig& field_trials,
absl::string_view key) {
return !absl::StartsWith(field_trials.Lookup(key), "Disabled");
}
}
double ReadBackoffFactor(const WebRtcKeyValueConfig& key_value_config) {
double ReadBackoffFactor(const WebRtcKeyValueConfig& key_value_config) {
std::string experiment_string =
std::string experiment_string =
key_value_config.Lookup(kBweBackOffFactorExperiment);
key_value_config.Lookup(kBweBackOffFactorExperiment);
double backoff_factor;
double backoff_factor;
int parsed_values =
int parsed_values =
sscanf(experiment_string.c_str(), "Enabled-%lf", &backoff_factor);
sscanf(experiment_string.c_str(), "Enabled-%lf", &backoff_factor);
if (parsed_values == 1) {
if (parsed_values == 1) {
if (backoff_factor >= 1.0) {
if (backoff_factor >= 1.0) {
MS_WARN_TAG(bwe, "Back-off factor must be less than 1.");
RTC_LOG(WARNING) << "Back-off factor must be less than 1.";
} else if (backoff_factor <= 0.0) {
} else if (backoff_factor <= 0.0) {
MS_WARN_TAG(bwe, "Back-off factor must be greater than 0.");
RTC_LOG(WARNING) << "Back-off factor must be greater than 0.";
} else {
} else {
return backoff_factor;
return backoff_factor;
}
}
}
}
RTC_LOG(LS_WARNING) << "Failed to parse parameters for AimdRateControl "
MS_WARN_TAG(bwe, "Failed to parse parameters for AimdRateControl experiment from field trial string. Using default.");
"experiment from field trial string. Using default.";
return kDefaultBackoffFactor;
return kDefaultBackoffFactor;
}
}
} // namespace
} // namespace
AimdRateControl::AimdRateControl(const WebRtcKeyValueConfig* key_value_config)
AimdRateControl::AimdRateControl(const WebRtcKeyValueConfig* key_value_config)
: AimdRateControl(key_value_config, /* send_side =*/false) {}
: AimdRateControl(key_value_config, /* send_side =*/false) {}
AimdRateControl::AimdRateControl(const WebRtcKeyValueConfig* key_value_config,
AimdRateControl::AimdRateControl(const WebRtcKeyValueConfig* key_value_config,
bool send_side)
bool send_side)
: min_configured_bitrate_(congestion_controller::GetMinBitrate()),
: min_configured_bitrate_(congestion_controller::GetMinBitrate()),
max_configured_bitrate_(DataRate::kbps(30000)),
max_configured_bitrate_(DataRate::KilobitsPerSec(30000)),
current_bitrate_(max_configured_bitrate_),
current_bitrate_(max_configured_bitrate_),
latest_estimated_throughput_(current_bitrate_),
latest_estimated_throughput_(current_bitrate_),
link_capacity_(),
link_capacity_(),
rate_control_state_(kRcHold),
rate_control_state_(kRcHold),
time_last_bitrate_change_(Timestamp::MinusInfinity()),
time_last_bitrate_change_(Timestamp::MinusInfinity()),
time_last_bitrate_decrease_(Timestamp::MinusInfinity()),
time_last_bitrate_decrease_(Timestamp::MinusInfinity()),
time_first_throughput_estimate_(Timestamp::MinusInfinity()),
time_first_throughput_estimate_(Timestamp::MinusInfinity()),
bitrate_is_initialized_(false),
bitrate_is_initialized_(false),
beta_(IsEnabled(*key_value_config, kBweBackOffFactorExperiment)
beta_(IsEnabled(*key_value_config, kBweBackOffFactorExperiment)
? ReadBackoffFactor(*key_value_config)
? ReadBackoffFactor(*key_value_config)
: kDefaultBackoffFactor),
: kDefaultBackoffFactor),
in_alr_(false),
in_alr_(false),
rtt_(kDefaultRtt),
rtt_(kDefaultRtt),
send_side_(send_side),
send_side_(send_side),
in_experiment_(!AdaptiveThresholdExperimentIsDisabled(*key_value_config)),
in_experiment_(!AdaptiveThresholdExperimentIsDisabled(*key_value_config)),
no_bitrate_increase_in_alr_(
no_bitrate_increase_in_alr_(
IsEnabled(*key_value_config,
IsEnabled(*key_value_config,
"WebRTC-DontIncreaseDelayBasedBweInAlr")),
"WebRTC-DontIncreaseDelayBasedBweInAlr")),
smoothing_experiment_(false),
estimate_bounded_backoff_(
estimate_bounded_backoff_(
IsEnabled(*key_value_config, "WebRTC-Bwe-EstimateBoundedBackoff")),
IsNotDisabled(*key_value_config,
"WebRTC-Bwe-EstimateBoundedBackoff")),
estimate_bounded_increase_(
estimate_bounded_increase_(
IsEnabled(*key_value_config, "WebRTC-Bwe-EstimateBoundedIncrease")),
IsNotDisabled(*key_value_config,
"WebRTC-Bwe-EstimateBoundedIncrease")),
initial_backoff_interval_("initial_backoff_interval"),
initial_backoff_interval_("initial_backoff_interval"),
low_throughput_threshold_("low_throughput", DataRate::Zero()),
link_capacity_fix_("link_capacity_fix") {
capacity_deviation_ratio_threshold_("cap_thr", 0.2),
capacity_limit_deviation_factor_("cap_lim", 1) {
// E.g
// E.g
// WebRTC-BweAimdRateControlConfig/initial_backoff_interval:100ms,
// WebRTC-BweAimdRateControlConfig/initial_backoff_interval:100ms/
// low_throughput:50kbps/
ParseFieldTrial({&initial_backoff_interval_, &link_capacity_fix_},
ParseFieldTrial({&initial_backoff_interval_, &low_throughput_threshold_},
key_value_config->Lookup("WebRTC-BweAimdRateControlConfig"));
key_value_config->Lookup("WebRTC-BweAimdRateControlConfig"));
if (initial_backoff_interval_) {
if (initial_backoff_interval_) {
MS_DEBUG_TAG(bwe, "Using aimd rate control with initial back-off interval: %s",
RTC_LOG(LS_INFO) << "Using aimd rate control with initial back-off interval"
ToString(*initial_backoff_interval_).c_str());
" "
<< ToString(*initial_backoff_interval_) << ".";
}
}
MS_DEBUG_TAG(bwe, "Using aimd rate control with back off factor: %f ", beta_);
RTC_LOG(LS_INFO) << "Using aimd rate control with back off factor " << beta_;
ParseFieldTrial(
{&capacity_deviation_ratio_threshold_, &capacity_limit_deviation_factor_},
key_value_config->Lookup("WebRTC-Bwe-AimdRateControl-NetworkState"));
}
}
AimdRateControl::~AimdRateControl() {}
AimdRateControl::~AimdRateControl() {}
void AimdRateControl::SetStartBitrate(DataRate start_bitrate) {
void AimdRateControl::SetStartBitrate(DataRate start_bitrate) {
current_bitrate_ = start_bitrate;
current_bitrate_ = start_bitrate;
latest_estimated_throughput_ = current_bitrate_;
latest_estimated_throughput_ = current_bitrate_;
bitrate_is_initialized_ = true;
bitrate_is_initialized_ = true;
}
}
void AimdRateControl::SetMinBitrate(DataRate min_bitrate) {
void AimdRateControl::SetMinBitrate(DataRate min_bitrate) {
MS_DEBUG_DEV("[min_bitrate:%" PRIi64 "]", min_bitrate.bps());
min_configured_bitrate_ = min_bitrate;
min_configured_bitrate_ = min_bitrate;
current_bitrate_ = std::max(min_bitrate, current_bitrate_);
current_bitrate_ = std::max(min_bitrate, current_bitrate_);
}
}
bool AimdRateControl::ValidEstimate() const {
bool AimdRateControl::ValidEstimate() const {
return bitrate_is_initialized_;
return bitrate_is_initialized_;
}
}
TimeDelta AimdRateControl::GetFeedbackInterval() const {
TimeDelta AimdRateControl::GetFeedbackInterval() const {
// Estimate how often we can send RTCP if we allocate up to 5% of bandwidth
// Estimate how often we can send RTCP if we allocate up to 5% of bandwidth
// to feedback.
// to feedback.
const DataSize kRtcpSize = DataSize::bytes(80);
const DataSize kRtcpSize = DataSize::Bytes(80);
const DataRate rtcp_bitrate = current_bitrate_ * 0.05;
const DataRate rtcp_bitrate = current_bitrate_ * 0.05;
const TimeDelta interval = kRtcpSize / rtcp_bitrate;
const TimeDelta interval = kRtcpSize / rtcp_bitrate;
const TimeDelta kMinFeedbackInterval = TimeDelta::ms(200);
const TimeDelta kMinFeedbackInterval = TimeDelta::Millis(200);
const TimeDelta kMaxFeedbackInterval = TimeDelta::ms(1000);
const TimeDelta kMaxFeedbackInterval = TimeDelta::Millis(1000);
return interval.Clamped(kMinFeedbackInterval, kMaxFeedbackInterval);
return interval.Clamped(kMinFeedbackInterval, kMaxFeedbackInterval);
}
}
bool AimdRateControl::TimeToReduceFurther(Timestamp at_time,
bool AimdRateControl::TimeToReduceFurther(Timestamp at_time,
DataRate estimated_throughput) const {
DataRate estimated_throughput) const {
const TimeDelta bitrate_reduction_interval =
const TimeDelta bitrate_reduction_interval =
rtt_.Clamped(TimeDelta::ms(10), TimeDelta::ms(200));
rtt_.Clamped(TimeDelta::Millis(10), TimeDelta::Millis(200));
if (at_time - time_last_bitrate_change_ >= bitrate_reduction_interval) {
if (at_time - time_last_bitrate_change_ >= bitrate_reduction_interval) {
return true;
return true;
}
}
if (ValidEstimate()) {
if (ValidEstimate()) {
// TODO(terelius/holmer): Investigate consequences of increasing
// TODO(terelius/holmer): Investigate consequences of increasing
// the threshold to 0.95 * LatestEstimate().
// the threshold to 0.95 * LatestEstimate().
const DataRate threshold = 0.5 * LatestEstimate();
const DataRate threshold = 0.5 * LatestEstimate();
return estimated_throughput < threshold;
return estimated_throughput < threshold;
}
}
return false;
return false;
}
}
bool AimdRateControl::InitialTimeToReduceFurther(Timestamp at_time) const {
bool AimdRateControl::InitialTimeToReduceFurther(Timestamp at_time) const {
if (!initial_backoff_interval_) {
if (!initial_backoff_interval_) {
return ValidEstimate() &&
return ValidEstimate() &&
TimeToReduceFurther(at_time,
TimeToReduceFurther(at_time,
LatestEstimate() / 2 - DataRate::bps(1));
LatestEstimate() / 2 - DataRate::BitsPerSec(1));
}
}
// TODO(terelius): We could use the RTT (clamped to suitable limits) instead
// TODO(terelius): We could use the RTT (clamped to suitable limits) instead
// of a fixed bitrate_reduction_interval.
// of a fixed bitrate_reduction_interval.
if (time_last_bitrate_decrease_.IsInfinite() ||
if (time_last_bitrate_decrease_.IsInfinite() ||
at_time - time_last_bitrate_decrease_ >= *initial_backoff_interval_) {
at_time - time_last_bitrate_decrease_ >= *initial_backoff_interval_) {
return true;
return true;
}
}
return false;
return false;
}
}
DataRate AimdRateControl::LatestEstimate() const {
DataRate AimdRateControl::LatestEstimate() const {
return current_bitrate_;
return current_bitrate_;
}
}
void AimdRateControl::SetRtt(TimeDelta rtt) {
void AimdRateControl::SetRtt(TimeDelta rtt) {
rtt_ = rtt;
rtt_ = rtt;
}
}
DataRate AimdRateControl::Update(const RateControlInput* input,
DataRate AimdRateControl::Update(const RateControlInput* input,
Timestamp at_time) {
Timestamp at_time) {
// RTC_CHECK(input);
RTC_CHECK(input);
// Set the initial bit rate value to what we're receiving the first half
// Set the initial bit rate value to what we're receiving the first half
// second.
// second.
// TODO(bugs.webrtc.org/9379): The comment above doesn't match to the code.
// TODO(bugs.webrtc.org/9379): The comment above doesn't match to the code.
if (!bitrate_is_initialized_) {
if (!bitrate_is_initialized_) {
const TimeDelta kInitializationTime = TimeDelta::seconds(5);
const TimeDelta kInitializationTime = TimeDelta::Seconds(5);
// RTC_DCHECK_LE(kBitrateWindowMs, kInitializationTime.ms());
RTC_DCHECK_LE(kBitrateWindowMs, kInitializationTime.ms());
if (time_first_throughput_estimate_.IsInfinite()) {
if (time_first_throughput_estimate_.IsInfinite()) {
if (input->estimated_throughput)
if (input->estimated_throughput)
time_first_throughput_estimate_ = at_time;
time_first_throughput_estimate_ = at_time;
} else if (at_time - time_first_throughput_estimate_ >
} else if (at_time - time_first_throughput_estimate_ >
kInitializationTime &&
kInitializationTime &&
input->estimated_throughput) {
input->estimated_throughput) {
current_bitrate_ = *input->estimated_throughput;
current_bitrate_ = *input->estimated_throughput;
bitrate_is_initialized_ = true;
bitrate_is_initialized_ = true;
}
}
}
}
current_bitrate_ = ChangeBitrate(current_bitrate_, *input, at_time);
ChangeBitrate(*input, at_time);
return current_bitrate_;
return current_bitrate_;
}
}
void AimdRateControl::SetInApplicationLimitedRegion(bool in_alr) {
void AimdRateControl::SetInApplicationLimitedRegion(bool in_alr) {
in_alr_ = in_alr;
in_alr_ = in_alr;
}
}
void AimdRateControl::SetEstimate(DataRate bitrate, Timestamp at_time) {
void AimdRateControl::SetEstimate(DataRate bitrate, Timestamp at_time) {
bitrate_is_initialized_ = true;
bitrate_is_initialized_ = true;
DataRate prev_bitrate = current_bitrate_;
DataRate prev_bitrate = current_bitrate_;
current_bitrate_ = ClampBitrate(bitrate, bitrate);
current_bitrate_ = ClampBitrate(bitrate);
time_last_bitrate_change_ = at_time;
time_last_bitrate_change_ = at_time;
if (current_bitrate_ < prev_bitrate) {
if (current_bitrate_ < prev_bitrate) {
time_last_bitrate_decrease_ = at_time;
time_last_bitrate_decrease_ = at_time;
}
}
}
}
void AimdRateControl::SetNetworkStateEstimate(
void AimdRateControl::SetNetworkStateEstimate(
const absl::optional<NetworkStateEstimate>& estimate) {
const absl::optional<NetworkStateEstimate>& estimate) {
network_estimate_ = estimate;
network_estimate_ = estimate;
}
}
double AimdRateControl::GetNearMaxIncreaseRateBpsPerSecond() const {
double AimdRateControl::GetNearMaxIncreaseRateBpsPerSecond() const {
// RTC_DCHECK(!current_bitrate_.IsZero());
RTC_DCHECK(!current_bitrate_.IsZero());
const TimeDelta kFrameInterval = TimeDelta::seconds(1) / 30;
const TimeDelta kFrameInterval = TimeDelta::Seconds(1) / 30;
DataSize frame_size = current_bitrate_ * kFrameInterval;
DataSize frame_size = current_bitrate_ * kFrameInterval;
const DataSize kPacketSize = DataSize::bytes(1200);
const DataSize kPacketSize = DataSize::Bytes(1200);
double packets_per_frame = std::ceil(frame_size / kPacketSize);
double packets_per_frame = std::ceil(frame_size / kPacketSize);
DataSize avg_packet_size = frame_size / packets_per_frame;
DataSize avg_packet_size = frame_size / packets_per_frame;
// Approximate the over-use estimator delay to 100 ms.
// Approximate the over-use estimator delay to 100 ms.
TimeDelta response_time = rtt_ + TimeDelta::ms(100);
TimeDelta response_time = rtt_ + TimeDelta::Millis(100);
if (in_experiment_)
if (in_experiment_)
response_time = response_time * 2;
response_time = response_time * 2;
double increase_rate_bps_per_second =
double increase_rate_bps_per_second =
(avg_packet_size / response_time).bps<double>();
(avg_packet_size / response_time).bps<double>();
double kMinIncreaseRateBpsPerSecond = 4000;
double kMinIncreaseRateBpsPerSecond = 4000;
return std::max(kMinIncreaseRateBpsPerSecond, increase_rate_bps_per_second);
return std::max(kMinIncreaseRateBpsPerSecond, increase_rate_bps_per_second);
}
}
TimeDelta AimdRateControl::GetExpectedBandwidthPeriod() const {
TimeDelta AimdRateControl::GetExpectedBandwidthPeriod() const {
const TimeDelta kMinPeriod =
const TimeDelta kMinPeriod = TimeDelta::Seconds(2);
smoothing_experiment_ ? TimeDelta::ms(500) : TimeDelta::seconds(2);
const TimeDelta kDefaultPeriod = TimeDelta::Seconds(3);
const TimeDelta kDefaultPeriod = TimeDelta::seconds(3);
const TimeDelta kMaxPeriod = TimeDelta::Seconds(50);
const TimeDelta kMaxPeriod = TimeDelta::seconds(50);
double increase_rate_bps_per_second = GetNearMaxIncreaseRateBpsPerSecond();
double increase_rate_bps_per_second = GetNearMaxIncreaseRateBpsPerSecond();
if (!last_decrease_)
if (!last_decrease_)
return smoothing_experiment_ ? kMinPeriod : kDefaultPeriod;
return kDefaultPeriod;
double time_to_recover_decrease_seconds =
double time_to_recover_decrease_seconds =
last_decrease_->bps() / increase_rate_bps_per_second;
last_decrease_->bps() / increase_rate_bps_per_second;
TimeDelta period = TimeDelta::seconds(time_to_recover_decrease_seconds);
TimeDelta period = TimeDelta::Seconds(time_to_recover_decrease_seconds);
return period.Clamped(kMinPeriod, kMaxPeriod);
return period.Clamped(kMinPeriod, kMaxPeriod);
}
}
DataRate AimdRateControl::ChangeBitrate(DataRate new_bitrate,
void AimdRateControl::ChangeBitrate(const RateControlInput& input,
const RateControlInput& input,
Timestamp at_time) {
Timestamp at_time) {
absl::optional<DataRate> new_bitrate;
DataRate estimated_throughput =
DataRate estimated_throughput =
input.estimated_throughput.value_or(latest_estimated_throughput_);
input.estimated_throughput.value_or(latest_estimated_throughput_);
if (input.estimated_throughput)
if (input.estimated_throughput)
latest_estimated_throughput_ = *input.estimated_throughput;
latest_estimated_throughput_ = *input.estimated_throughput;
// An over-use should always trigger us to reduce the bitrate, even though
// An over-use should always trigger us to reduce the bitrate, even though
// we have not yet established our first estimate. By acting on the over-use,
// we have not yet established our first estimate. By acting on the over-use,
// we will end up with a valid estimate.
// we will end up with a valid estimate.
if (!bitrate_is_initialized_ &&
if (!bitrate_is_initialized_ &&
input.bw_state != BandwidthUsage::kBwOverusing)
input.bw_state != BandwidthUsage::kBwOverusing)
return current_bitrate_;
return;
ChangeState(input, at_time);
ChangeState(input, at_time);
// We limit the new bitrate based on the troughput to avoid unlimited bitrate
// increases. We allow a bit more lag at very low rates to not too easily get
// stuck if the encoder produces uneven outputs.
const DataRate troughput_based_limit =
1.5 * estimated_throughput + DataRate::KilobitsPerSec(10);
switch (rate_control_state_) {
switch (rate_control_state_) {
case kRcHold:
case kRcHold:
break;
break;
case kRcIncrease:
case kRcIncrease:
if (estimated_throughput > link_capacity_.UpperBound())
if (estimated_throughput > link_capacity_.UpperBound())
link_capacity_.Reset();
link_capacity_.Reset();
// Do not increase the delay based estimate in alr since the estimator
// Do not increase the delay based estimate in alr since the estimator
// will not be able to get transport feedback necessary to detect if
// will not be able to get transport feedback necessary to detect if
// the new estimate is correct.
// the new estimate is correct.
if (!(send_side_ && in_alr_ && no_bitrate_increase_in_alr_)) {
// If we have previously increased above the limit (for instance due to
// probing), we don't allow further changes.
if (current_bitrate_ < troughput_based_limit &&
!(send_side_ && in_alr_ && no_bitrate_increase_in_alr_)) {
DataRate increased_bitrate = DataRate::MinusInfinity();
if (link_capacity_.has_estimate()) {
if (link_capacity_.has_estimate()) {
// The link_capacity estimate is reset if the measured throughput
// The link_capacity estimate is reset if the measured throughput
// is too far from the estimate. We can therefore assume that our
// is too far from the estimate. We can therefore assume that our
// target rate is reasonably close to link capacity and use additive
// target rate is reasonably close to link capacity and use additive
// increase.
// increase.
DataRate additive_increase =
DataRate additive_increase =
AdditiveRateIncrease(at_time, time_last_bitrate_change_);
AdditiveRateIncrease(at_time, time_last_bitrate_change_);
new_bitrate += additive_increase;
increased_bitrate = current_bitrate_ + additive_increase;
} else {
} else {
// If we don't have an estimate of the link capacity, use faster ramp
// If we don't have an estimate of the link capacity, use faster ramp
// up to discover the capacity.
// up to discover the capacity.
DataRate multiplicative_increase = MultiplicativeRateIncrease(
DataRate multiplicative_increase = MultiplicativeRateIncrease(
at_time, time_last_bitrate_change_, new_bitrate);
at_time, time_last_bitrate_change_, current_bitrate_);
new_bitrate += multiplicative_increase;
increased_bitrate = current_bitrate_ + multiplicative_increase;
}
}
new_bitrate = std::min(increased_bitrate, troughput_based_limit);
}
}
time_last_bitrate_change_ = at_time;
time_last_bitrate_change_ = at_time;
break;
break;
case kRcDecrease:
case kRcDecrease: {
// TODO(srte): Remove when |estimate_bounded_backoff_| has been validated.
DataRate decreased_bitrate = DataRate::PlusInfinity();
if (network_estimate_ && capacity_deviation_ratio_threshold_ &&
!estimate_bounded_backoff_) {
// Set bit rate to something slightly lower than the measured throughput
estimated_throughput = std::max(estimated_throughput,
// to get rid of any self-induced delay.
network_estimate_->link_capacity_lower);
decreased_bitrate = estimated_throughput * beta_;
}
if (decreased_bitrate > current_bitrate_ && !link_capacity_fix_) {
if (estimated_throughput > low_throughput_threshold_) {
// TODO(terelius): The link_capacity estimate may be based on old
// Set bit rate to something slightly lower than the measured throughput
// throughput measurements. Relying on them may lead to unnecessary
// to get rid of any self-induced delay.
// BWE drops.
new_bitrate = estimated_throughput * beta_;
if (new_bitrate > current_bitrate_) {
// Avoid increasing the rate when over-using.
if (link_capacity_.has_estimate()) {
new_bitrate = beta_ * link_capacity_.estimate();
}
}
if (estimate_bounded_backoff_ && network_estimate_) {
new_bitrate = std::max(
new_bitrate, network_estimate_->link_capacity_lower * beta_);
}
} else {
new_bitrate = estimated_throughput;
if (link_capacity_.has_estimate()) {
if (link_capacity_.has_estimate()) {
new_bitrate = std::max(new_bitrate, link_capacity_.estimate());
decreased_bitrate = beta_ * link_capacity_.estimate();
}
}
new_bitrate = std::min(new_bitrate, low_throughput_threshold_.Get());
}
}
new_bitrate = std::min(new_bitrate, current_bitrate_);
if (estimate_bounded_backoff_ && network_estimate_) {
decreased_bitrate = std::max(
decreased_bitrate, network_estimate_->link_capacity_lower * beta_);
}
// Avoid increasing the rate when over-using.
if (decreased_bitrate < current_bitrate_) {
new_bitrate = decreased_bitrate;
}
if (bitrate_is_initialized_ && estimated_throughput < current_bitrate_) {
if (bitrate_is_initialized_ && estimated_throughput < current_bitrate_) {
constexpr double kDegradationFactor = 0.9;
if (!new_bitrate.has_value()) {
if (smoothing_experiment_ &&
last_decrease_ = DataRate::Zero();
new_bitrate < kDegradationFactor * beta_ * current_bitrate_) {
// If bitrate decreases more than a normal back off after overuse, it
// indicates a real network degradation. We do not let such a decrease
// to determine the bandwidth estimation period.
last_decrease_ = absl::nullopt;
} else {
} else {
last_decrease_ = current_bitrate_ - new_bitrate;
last_decrease_ = current_bitrate_ - *new_bitrate;
}
}
}
}
if (estimated_throughput < link_capacity_.LowerBound()) {
if (estimated_throughput < link_capacity_.LowerBound()) {
// The current throughput is far from the estimated link capacity. Clear
// The current throughput is far from the estimated link capacity. Clear
// the estimate to allow an immediate update in OnOveruseDetected.
// the estimate to allow an immediate update in OnOveruseDetected.
link_capacity_.Reset();
link_capacity_.Reset();
}
}
bitrate_is_initialized_ = true;
bitrate_is_initialized_ = true;
link_capacity_.OnOveruseDetected(estimated_throughput);
link_capacity_.OnOveruseDetected(estimated_throughput);
// Stay on hold until the pipes are cleared.
// Stay on hold until the pipes are cleared.
rate_control_state_ = kRcHold;
rate_control_state_ = kRcHold;
time_last_bitrate_change_ = at_time;
time_last_bitrate_change_ = at_time;
time_last_bitrate_decrease_ = at_time;
time_last_bitrate_decrease_ = at_time;
break;
break;
}
default:
default:
MS_THROW_ERROR("unknown rate control state");
assert(false);
}
}
return ClampBitrate(new_bitrate, estimated_throughput);
current_bitrate_ = ClampBitrate(new_bitrate.value_or(current_bitrate_));
}
}
DataRate AimdRateControl::ClampBitrate(DataRate new_bitrate,
DataRate AimdRateControl::ClampBitrate(DataRate new_bitrate) const {
DataRate estimated_throughput) const {
if (estimate_bounded_increase_ && network_estimate_) {
// Allow the estimate to increase as long as alr is not detected to ensure
// that there is no BWE values that can make the estimate stuck at a too
// low bitrate. If an encoder can not produce the bitrate necessary to
// fully use the capacity, alr will sooner or later trigger.
if (!(send_side_ && no_bitrate_increase_in_alr_)) {
// Don't change the bit rate if the send side is too far off.
// We allow a bit more lag at very low rates to not too easily get stuck if
// the encoder produces uneven outputs.
const DataRate max_bitrate =
1.5 * estimated_throughput + DataRate::kbps(10);
if (new_bitrate > current_bitrate_ && new_bitrate > max_bitrate) {
new_bitrate = std::max(current_bitrate_, max_bitrate);
}
}
if (network_estimate_ &&
(estimate_bounded_increase_ || capacity_limit_deviation_factor_)) {
DataRate upper_bound = network_estimate_->link_capacity_upper;
DataRate upper_bound = network_estimate_->link_capacity_upper;
new_bitrate = std::min(new_bitrate, upper_bound);
new_bitrate = std::min(new_bitrate, upper_bound);
}
}
new_bitrate = std::max(new_bitrate, min_configured_bitrate_);
new_bitrate = std::max(new_bitrate, min_configured_bitrate_);
return new_bitrate;
return new_bitrate;
}
}
DataRate AimdRateControl::MultiplicativeRateIncrease(
DataRate AimdRateControl::MultiplicativeRateIncrease(
Timestamp at_time,
Timestamp at_time,
Timestamp last_time,
Timestamp last_time,
DataRate current_bitrate) const {
DataRate current_bitrate) const {
double alpha = 1.08;
double alpha = 1.08;
if (last_time.IsFinite()) {
if (last_time.IsFinite()) {
auto time_since_last_update = at_time - last_time;
auto time_since_last_update = at_time - last_time;
alpha = pow(alpha, std::min(time_since_last_update.seconds<double>(), 1.0));
alpha = pow(alpha, std::min(time_since_last_update.seconds<double>(), 1.0));
}
}
DataRate multiplicative_increase =
DataRate multiplicative_increase =
std::max(current_bitrate * (alpha - 1.0), DataRate::bps(1000));
std::max(current_bitrate * (alpha - 1.0), DataRate::BitsPerSec(1000));
return multiplicative_increase;
return multiplicative_increase;
}
}
DataRate AimdRateControl::AdditiveRateIncrease(Timestamp at_time,
DataRate AimdRateControl::AdditiveRateIncrease(Timestamp at_time,
Timestamp last_time) const {
Timestamp last_time) const {
double time_period_seconds = (at_time - last_time).seconds<double>();
double time_period_seconds = (at_time - last_time).seconds<double>();
double data_rate_increase_bps =
double data_rate_increase_bps =
GetNearMaxIncreaseRateBpsPerSecond() * time_period_seconds;
GetNearMaxIncreaseRateBpsPerSecond() * time_period_seconds;
return DataRate::bps(data_rate_increase_bps);
return DataRate::BitsPerSec(data_rate_increase_bps);
}
}
void AimdRateControl::ChangeState(const RateControlInput& input,
void AimdRateControl::ChangeState(const RateControlInput& input,
Timestamp at_time) {
Timestamp at_time) {
switch (input.bw_state) {
switch (input.bw_state) {
case BandwidthUsage::kBwNormal:
case BandwidthUsage::kBwNormal:
if (rate_control_state_ == kRcHold) {
if (rate_control_state_ == kRcHold) {
time_last_bitrate_change_ = at_time;
time_last_bitrate_change_ = at_time;
rate_control_state_ = kRcIncrease;
rate_control_state_ = kRcIncrease;
}
}
break;
break;
case BandwidthUsage::kBwOverusing:
case BandwidthUsage::kBwOverusing:
if (rate_control_state_ != kRcDecrease) {
if (rate_control_state_ != kRcDecrease) {
rate_control_state_ = kRcDecrease;
rate_control_state_ = kRcDecrease;
}
}
break;
break;
case BandwidthUsage::kBwUnderusing:
case BandwidthUsage::kBwUnderusing:
rate_control_state_ = kRcHold;
rate_control_state_ = kRcHold;
break;
break;
default:
default:
MS_THROW_ERROR("unknown input.bw_state");
assert(false);
}
}
}
}
} // namespace webrtc
} // namespace webrtc