Using unit classes in AimdRateControl.
Bug: webrtc:9718 Change-Id: I1efed4e55c9d1ccec3c32ed012cb3cd82d7f4ee8 Reviewed-on: https://webrtc-review.googlesource.com/c/110788 Commit-Queue: Sebastian Jansson <srte@webrtc.org> Reviewed-by: Christoffer Rodbro <crodbro@webrtc.org> Cr-Commit-Position: refs/heads/master@{#25705}
This commit is contained in:

committed by
Commit Bot

parent
50b8426648
commit
5f00995964
@ -175,12 +175,11 @@ DelayBasedBwe::Result DelayBasedBwe::OnLongFeedbackDelay(
|
||||
// Call constructor. An alternative would be to return an empty Result here,
|
||||
// or to estimate the throughput based on the feedback we received.
|
||||
RTC_DCHECK(rate_control_.ValidEstimate());
|
||||
rate_control_.SetEstimate(rate_control_.LatestEstimate() / 2,
|
||||
arrival_time.ms());
|
||||
rate_control_.SetEstimate(rate_control_.LatestEstimate() / 2, arrival_time);
|
||||
Result result;
|
||||
result.updated = true;
|
||||
result.probe = false;
|
||||
result.target_bitrate = DataRate::bps(rate_control_.LatestEstimate());
|
||||
result.target_bitrate = rate_control_.LatestEstimate();
|
||||
RTC_LOG(LS_WARNING) << "Long feedback delay detected, reducing BWE to "
|
||||
<< ToString(result.target_bitrate);
|
||||
return result;
|
||||
@ -239,27 +238,26 @@ DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate(
|
||||
// Currently overusing the bandwidth.
|
||||
if (delay_detector_->State() == BandwidthUsage::kBwOverusing) {
|
||||
if (acked_bitrate &&
|
||||
rate_control_.TimeToReduceFurther(at_time.ms(), acked_bitrate->bps())) {
|
||||
rate_control_.TimeToReduceFurther(at_time, *acked_bitrate)) {
|
||||
result.updated =
|
||||
UpdateEstimate(at_time, acked_bitrate, &result.target_bitrate);
|
||||
} else if (!acked_bitrate && rate_control_.ValidEstimate() &&
|
||||
rate_control_.InitialTimeToReduceFurther(at_time.ms())) {
|
||||
rate_control_.InitialTimeToReduceFurther(at_time)) {
|
||||
// Overusing before we have a measured acknowledged bitrate. Reduce send
|
||||
// rate by 50% every 200 ms.
|
||||
// TODO(tschumim): Improve this and/or the acknowledged bitrate estimator
|
||||
// so that we (almost) always have a bitrate estimate.
|
||||
rate_control_.SetEstimate(rate_control_.LatestEstimate() / 2,
|
||||
at_time.ms());
|
||||
rate_control_.SetEstimate(rate_control_.LatestEstimate() / 2, at_time);
|
||||
result.updated = true;
|
||||
result.probe = false;
|
||||
result.target_bitrate = DataRate::bps(rate_control_.LatestEstimate());
|
||||
result.target_bitrate = rate_control_.LatestEstimate();
|
||||
}
|
||||
} else {
|
||||
if (probe_bitrate) {
|
||||
result.probe = true;
|
||||
result.updated = true;
|
||||
result.target_bitrate = *probe_bitrate;
|
||||
rate_control_.SetEstimate(probe_bitrate->bps(), at_time.ms());
|
||||
rate_control_.SetEstimate(*probe_bitrate, at_time);
|
||||
} else {
|
||||
result.updated =
|
||||
UpdateEstimate(at_time, acked_bitrate, &result.target_bitrate);
|
||||
@ -287,16 +285,13 @@ DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate(
|
||||
bool DelayBasedBwe::UpdateEstimate(Timestamp at_time,
|
||||
absl::optional<DataRate> acked_bitrate,
|
||||
DataRate* target_rate) {
|
||||
absl::optional<int> acked_bitrate_bps;
|
||||
if (acked_bitrate)
|
||||
acked_bitrate_bps = acked_bitrate->bps<int>();
|
||||
const RateControlInput input(delay_detector_->State(), acked_bitrate_bps);
|
||||
*target_rate = DataRate::bps(rate_control_.Update(&input, at_time.ms()));
|
||||
const RateControlInput input(delay_detector_->State(), acked_bitrate);
|
||||
*target_rate = rate_control_.Update(&input, at_time);
|
||||
return rate_control_.ValidEstimate();
|
||||
}
|
||||
|
||||
void DelayBasedBwe::OnRttUpdate(TimeDelta avg_rtt) {
|
||||
rate_control_.SetRtt(avg_rtt.ms());
|
||||
rate_control_.SetRtt(avg_rtt);
|
||||
}
|
||||
|
||||
bool DelayBasedBwe::LatestEstimate(std::vector<uint32_t>* ssrcs,
|
||||
@ -311,23 +306,23 @@ bool DelayBasedBwe::LatestEstimate(std::vector<uint32_t>* ssrcs,
|
||||
return false;
|
||||
|
||||
*ssrcs = {kFixedSsrc};
|
||||
*bitrate = DataRate::bps(rate_control_.LatestEstimate());
|
||||
*bitrate = rate_control_.LatestEstimate();
|
||||
return true;
|
||||
}
|
||||
|
||||
void DelayBasedBwe::SetStartBitrate(DataRate start_bitrate) {
|
||||
RTC_LOG(LS_INFO) << "BWE Setting start bitrate to: "
|
||||
<< ToString(start_bitrate);
|
||||
rate_control_.SetStartBitrate(start_bitrate.bps());
|
||||
rate_control_.SetStartBitrate(start_bitrate);
|
||||
}
|
||||
|
||||
void DelayBasedBwe::SetMinBitrate(DataRate min_bitrate) {
|
||||
// Called from both the configuration thread and the network thread. Shouldn't
|
||||
// be called from the network thread in the future.
|
||||
rate_control_.SetMinBitrate(min_bitrate.bps());
|
||||
rate_control_.SetMinBitrate(min_bitrate);
|
||||
}
|
||||
|
||||
TimeDelta DelayBasedBwe::GetExpectedBwePeriod() const {
|
||||
return TimeDelta::ms(rate_control_.GetExpectedBandwidthPeriodMs());
|
||||
return rate_control_.GetExpectedBandwidthPeriod();
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
@ -42,6 +42,7 @@ rtc_static_library("remote_bitrate_estimator") {
|
||||
deps = [
|
||||
"../..:webrtc_common",
|
||||
"../../api/units:data_rate",
|
||||
"../../api/units:timestamp",
|
||||
"../../modules:module_api",
|
||||
"../../modules:module_api_public",
|
||||
"../../modules/rtp_rtcp:rtp_rtcp_format",
|
||||
|
@ -26,10 +26,9 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
static const int64_t kDefaultRttMs = 200;
|
||||
static const int64_t kMaxFeedbackIntervalMs = 1000;
|
||||
static const float kDefaultBackoffFactor = 0.85f;
|
||||
static const int64_t kDefaultInitialBackOffIntervalMs = 200;
|
||||
constexpr TimeDelta kDefaultRtt = TimeDelta::Millis<200>();
|
||||
constexpr float kDefaultBackoffFactor = 0.85f;
|
||||
constexpr TimeDelta kDefaultInitialBackOffInterval = TimeDelta::Millis<200>();
|
||||
|
||||
const char kBweBackOffFactorExperiment[] = "WebRTC-BweBackOffFactor";
|
||||
const char kBweInitialBackOffIntervalExperiment[] =
|
||||
@ -55,7 +54,7 @@ float ReadBackoffFactor() {
|
||||
return kDefaultBackoffFactor;
|
||||
}
|
||||
|
||||
int64_t ReadInitialBackoffIntervalMs() {
|
||||
TimeDelta ReadInitialBackoffInterval() {
|
||||
std::string experiment_string =
|
||||
webrtc::field_trial::FindFullName(kBweInitialBackOffIntervalExperiment);
|
||||
int64_t backoff_interval;
|
||||
@ -63,7 +62,7 @@ int64_t ReadInitialBackoffIntervalMs() {
|
||||
sscanf(experiment_string.c_str(), "Enabled-%" SCNd64, &backoff_interval);
|
||||
if (parsed_values == 1) {
|
||||
if (10 <= backoff_interval && backoff_interval <= 200) {
|
||||
return backoff_interval;
|
||||
return TimeDelta::ms(backoff_interval);
|
||||
}
|
||||
RTC_LOG(WARNING)
|
||||
<< "Initial back-off interval must be between 10 and 200 ms.";
|
||||
@ -71,188 +70,193 @@ int64_t ReadInitialBackoffIntervalMs() {
|
||||
RTC_LOG(LS_WARNING) << "Failed to parse parameters for "
|
||||
<< kBweInitialBackOffIntervalExperiment
|
||||
<< " experiment. Using default.";
|
||||
return kDefaultInitialBackOffIntervalMs;
|
||||
return kDefaultInitialBackOffInterval;
|
||||
}
|
||||
|
||||
AimdRateControl::AimdRateControl()
|
||||
: min_configured_bitrate_bps_(congestion_controller::GetMinBitrateBps()),
|
||||
max_configured_bitrate_bps_(30000000),
|
||||
current_bitrate_bps_(max_configured_bitrate_bps_),
|
||||
latest_estimated_throughput_bps_(current_bitrate_bps_),
|
||||
: min_configured_bitrate_(congestion_controller::GetMinBitrate()),
|
||||
max_configured_bitrate_(DataRate::kbps(30000)),
|
||||
current_bitrate_(max_configured_bitrate_),
|
||||
latest_estimated_throughput_(current_bitrate_),
|
||||
avg_max_bitrate_kbps_(-1.0f),
|
||||
var_max_bitrate_kbps_(0.4f),
|
||||
rate_control_state_(kRcHold),
|
||||
rate_control_region_(kRcMaxUnknown),
|
||||
time_last_bitrate_change_(-1),
|
||||
time_last_bitrate_decrease_(-1),
|
||||
time_first_throughput_estimate_(-1),
|
||||
time_last_bitrate_change_(Timestamp::MinusInfinity()),
|
||||
time_last_bitrate_decrease_(Timestamp::MinusInfinity()),
|
||||
time_first_throughput_estimate_(Timestamp::MinusInfinity()),
|
||||
bitrate_is_initialized_(false),
|
||||
beta_(webrtc::field_trial::IsEnabled(kBweBackOffFactorExperiment)
|
||||
? ReadBackoffFactor()
|
||||
: kDefaultBackoffFactor),
|
||||
rtt_(kDefaultRttMs),
|
||||
rtt_(kDefaultRtt),
|
||||
in_experiment_(!AdaptiveThresholdExperimentIsDisabled()),
|
||||
smoothing_experiment_(
|
||||
webrtc::field_trial::IsEnabled("WebRTC-Audio-BandwidthSmoothing")),
|
||||
in_initial_backoff_interval_experiment_(
|
||||
webrtc::field_trial::IsEnabled(kBweInitialBackOffIntervalExperiment)),
|
||||
initial_backoff_interval_ms_(kDefaultInitialBackOffIntervalMs) {
|
||||
initial_backoff_interval_(kDefaultInitialBackOffInterval) {
|
||||
if (in_initial_backoff_interval_experiment_) {
|
||||
initial_backoff_interval_ms_ = ReadInitialBackoffIntervalMs();
|
||||
initial_backoff_interval_ = ReadInitialBackoffInterval();
|
||||
RTC_LOG(LS_INFO) << "Using aimd rate control with initial back-off interval"
|
||||
<< " " << initial_backoff_interval_ms_ << " ms.";
|
||||
<< " " << ToString(initial_backoff_interval_) << ".";
|
||||
}
|
||||
RTC_LOG(LS_INFO) << "Using aimd rate control with back off factor " << beta_;
|
||||
}
|
||||
|
||||
AimdRateControl::~AimdRateControl() {}
|
||||
|
||||
void AimdRateControl::SetStartBitrate(int start_bitrate_bps) {
|
||||
current_bitrate_bps_ = start_bitrate_bps;
|
||||
latest_estimated_throughput_bps_ = current_bitrate_bps_;
|
||||
void AimdRateControl::SetStartBitrate(DataRate start_bitrate) {
|
||||
current_bitrate_ = start_bitrate;
|
||||
latest_estimated_throughput_ = current_bitrate_;
|
||||
bitrate_is_initialized_ = true;
|
||||
}
|
||||
|
||||
void AimdRateControl::SetMinBitrate(int min_bitrate_bps) {
|
||||
min_configured_bitrate_bps_ = min_bitrate_bps;
|
||||
current_bitrate_bps_ = std::max<int>(min_bitrate_bps, current_bitrate_bps_);
|
||||
void AimdRateControl::SetMinBitrate(DataRate min_bitrate) {
|
||||
min_configured_bitrate_ = min_bitrate;
|
||||
current_bitrate_ = std::max(min_bitrate, current_bitrate_);
|
||||
}
|
||||
|
||||
bool AimdRateControl::ValidEstimate() const {
|
||||
return bitrate_is_initialized_;
|
||||
}
|
||||
|
||||
int64_t AimdRateControl::GetFeedbackInterval() const {
|
||||
TimeDelta 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;
|
||||
const 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 rtc::SafeClamp(interval, kMinFeedbackIntervalMs,
|
||||
kMaxFeedbackIntervalMs);
|
||||
const DataSize kRtcpSize = DataSize::bytes(80);
|
||||
const DataRate rtcp_bitrate = current_bitrate_ * 0.05;
|
||||
const TimeDelta interval = kRtcpSize / rtcp_bitrate;
|
||||
const TimeDelta kMinFeedbackInterval = TimeDelta::ms(200);
|
||||
const TimeDelta kMaxFeedbackInterval = TimeDelta::ms(1000);
|
||||
return interval.Clamped(kMinFeedbackInterval, kMaxFeedbackInterval);
|
||||
}
|
||||
|
||||
bool AimdRateControl::TimeToReduceFurther(
|
||||
int64_t now_ms,
|
||||
uint32_t estimated_throughput_bps) const {
|
||||
const int64_t bitrate_reduction_interval =
|
||||
std::max<int64_t>(std::min<int64_t>(rtt_, 200), 10);
|
||||
if (now_ms - time_last_bitrate_change_ >= bitrate_reduction_interval) {
|
||||
bool AimdRateControl::TimeToReduceFurther(Timestamp at_time,
|
||||
DataRate estimated_throughput) const {
|
||||
const TimeDelta bitrate_reduction_interval =
|
||||
rtt_.Clamped(TimeDelta::ms(10), TimeDelta::ms(200));
|
||||
if (at_time - time_last_bitrate_change_ >= bitrate_reduction_interval) {
|
||||
return true;
|
||||
}
|
||||
if (ValidEstimate()) {
|
||||
// TODO(terelius/holmer): Investigate consequences of increasing
|
||||
// the threshold to 0.95 * LatestEstimate().
|
||||
const uint32_t threshold = static_cast<uint32_t>(0.5 * LatestEstimate());
|
||||
return estimated_throughput_bps < threshold;
|
||||
const DataRate threshold = 0.5 * LatestEstimate();
|
||||
return estimated_throughput < threshold;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AimdRateControl::InitialTimeToReduceFurther(int64_t now_ms) const {
|
||||
bool AimdRateControl::InitialTimeToReduceFurther(Timestamp at_time) const {
|
||||
if (!in_initial_backoff_interval_experiment_) {
|
||||
return ValidEstimate() &&
|
||||
TimeToReduceFurther(now_ms, LatestEstimate() / 2 - 1);
|
||||
TimeToReduceFurther(at_time,
|
||||
LatestEstimate() / 2 - DataRate::bps(1));
|
||||
}
|
||||
// TODO(terelius): We could use the RTT (clamped to suitable limits) instead
|
||||
// of a fixed bitrate_reduction_interval.
|
||||
if (time_last_bitrate_decrease_ == -1 ||
|
||||
now_ms - time_last_bitrate_decrease_ >= initial_backoff_interval_ms_) {
|
||||
if (time_last_bitrate_decrease_.IsInfinite() ||
|
||||
at_time - time_last_bitrate_decrease_ >= initial_backoff_interval_) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t AimdRateControl::LatestEstimate() const {
|
||||
return current_bitrate_bps_;
|
||||
DataRate AimdRateControl::LatestEstimate() const {
|
||||
return current_bitrate_;
|
||||
}
|
||||
|
||||
void AimdRateControl::SetRtt(int64_t rtt) {
|
||||
void AimdRateControl::SetRtt(TimeDelta rtt) {
|
||||
rtt_ = rtt;
|
||||
}
|
||||
|
||||
uint32_t AimdRateControl::Update(const RateControlInput* input,
|
||||
int64_t now_ms) {
|
||||
DataRate AimdRateControl::Update(const RateControlInput* input,
|
||||
Timestamp at_time) {
|
||||
RTC_CHECK(input);
|
||||
|
||||
// Set the initial bit rate value to what we're receiving the first half
|
||||
// second.
|
||||
// TODO(bugs.webrtc.org/9379): The comment above doesn't match to the code.
|
||||
if (!bitrate_is_initialized_) {
|
||||
const int64_t kInitializationTimeMs = 5000;
|
||||
RTC_DCHECK_LE(kBitrateWindowMs, kInitializationTimeMs);
|
||||
if (time_first_throughput_estimate_ < 0) {
|
||||
if (input->estimated_throughput_bps)
|
||||
time_first_throughput_estimate_ = now_ms;
|
||||
} else if (now_ms - time_first_throughput_estimate_ >
|
||||
kInitializationTimeMs &&
|
||||
input->estimated_throughput_bps) {
|
||||
current_bitrate_bps_ = *input->estimated_throughput_bps;
|
||||
const TimeDelta kInitializationTime = TimeDelta::seconds(5);
|
||||
RTC_DCHECK_LE(kBitrateWindowMs, kInitializationTime.ms());
|
||||
if (time_first_throughput_estimate_.IsInfinite()) {
|
||||
if (input->estimated_throughput)
|
||||
time_first_throughput_estimate_ = at_time;
|
||||
} else if (at_time - time_first_throughput_estimate_ >
|
||||
kInitializationTime &&
|
||||
input->estimated_throughput) {
|
||||
current_bitrate_ = *input->estimated_throughput;
|
||||
bitrate_is_initialized_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
current_bitrate_bps_ = ChangeBitrate(current_bitrate_bps_, *input, now_ms);
|
||||
return current_bitrate_bps_;
|
||||
current_bitrate_ = ChangeBitrate(current_bitrate_, *input, at_time);
|
||||
return current_bitrate_;
|
||||
}
|
||||
|
||||
void AimdRateControl::SetEstimate(int bitrate_bps, int64_t now_ms) {
|
||||
void AimdRateControl::SetEstimate(DataRate bitrate, Timestamp at_time) {
|
||||
bitrate_is_initialized_ = true;
|
||||
uint32_t prev_bitrate_bps = current_bitrate_bps_;
|
||||
current_bitrate_bps_ = ClampBitrate(bitrate_bps, bitrate_bps);
|
||||
time_last_bitrate_change_ = now_ms;
|
||||
if (current_bitrate_bps_ < prev_bitrate_bps) {
|
||||
time_last_bitrate_decrease_ = now_ms;
|
||||
DataRate prev_bitrate = current_bitrate_;
|
||||
current_bitrate_ = ClampBitrate(bitrate, bitrate);
|
||||
time_last_bitrate_change_ = at_time;
|
||||
if (current_bitrate_ < prev_bitrate) {
|
||||
time_last_bitrate_decrease_ = at_time;
|
||||
}
|
||||
}
|
||||
|
||||
int AimdRateControl::GetNearMaxIncreaseRateBps() const {
|
||||
RTC_DCHECK_GT(current_bitrate_bps_, 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;
|
||||
double AimdRateControl::GetNearMaxIncreaseRateBpsPerSecond() const {
|
||||
RTC_DCHECK(!current_bitrate_.IsZero());
|
||||
const TimeDelta kFrameInterval = TimeDelta::seconds(1) / 30;
|
||||
DataSize frame_size = current_bitrate_ * kFrameInterval;
|
||||
const DataSize kPacketSize = DataSize::bytes(1200);
|
||||
double packets_per_frame = std::ceil(frame_size / kPacketSize);
|
||||
DataSize avg_packet_size = frame_size / packets_per_frame;
|
||||
|
||||
// Approximate the over-use estimator delay to 100 ms.
|
||||
const int64_t response_time = in_experiment_ ? (rtt_ + 100) * 2 : rtt_ + 100;
|
||||
constexpr double kMinIncreaseRateBps = 4000;
|
||||
return static_cast<int>(std::max(
|
||||
kMinIncreaseRateBps, (avg_packet_size_bits * 1000) / response_time));
|
||||
TimeDelta response_time = rtt_ + TimeDelta::ms(100);
|
||||
if (in_experiment_)
|
||||
response_time = response_time * 2;
|
||||
double increase_rate_bps_per_second =
|
||||
(avg_packet_size / response_time).bps<double>();
|
||||
double kMinIncreaseRateBpsPerSecond = 4000;
|
||||
return std::max(kMinIncreaseRateBpsPerSecond, increase_rate_bps_per_second);
|
||||
}
|
||||
|
||||
int AimdRateControl::GetExpectedBandwidthPeriodMs() const {
|
||||
const int kMinPeriodMs = smoothing_experiment_ ? 500 : 2000;
|
||||
constexpr int kDefaultPeriodMs = 3000;
|
||||
constexpr int kMaxPeriodMs = 50000;
|
||||
TimeDelta AimdRateControl::GetExpectedBandwidthPeriod() const {
|
||||
const TimeDelta kMinPeriod =
|
||||
smoothing_experiment_ ? TimeDelta::ms(500) : TimeDelta::seconds(2);
|
||||
const TimeDelta kDefaultPeriod = TimeDelta::seconds(3);
|
||||
const TimeDelta kMaxPeriod = TimeDelta::seconds(50);
|
||||
|
||||
int increase_rate = GetNearMaxIncreaseRateBps();
|
||||
double increase_rate_bps_per_second = GetNearMaxIncreaseRateBpsPerSecond();
|
||||
if (!last_decrease_)
|
||||
return smoothing_experiment_ ? kMinPeriodMs : kDefaultPeriodMs;
|
||||
|
||||
return std::min(kMaxPeriodMs,
|
||||
std::max<int>(1000 * static_cast<int64_t>(*last_decrease_) /
|
||||
increase_rate,
|
||||
kMinPeriodMs));
|
||||
return smoothing_experiment_ ? kMinPeriod : kDefaultPeriod;
|
||||
double time_to_recover_decrease_seconds =
|
||||
last_decrease_->bps() / increase_rate_bps_per_second;
|
||||
TimeDelta period = TimeDelta::seconds(time_to_recover_decrease_seconds);
|
||||
return period.Clamped(kMinPeriod, kMaxPeriod);
|
||||
}
|
||||
|
||||
uint32_t AimdRateControl::ChangeBitrate(uint32_t new_bitrate_bps,
|
||||
DataRate AimdRateControl::ChangeBitrate(DataRate new_bitrate,
|
||||
const RateControlInput& input,
|
||||
int64_t now_ms) {
|
||||
uint32_t estimated_throughput_bps =
|
||||
input.estimated_throughput_bps.value_or(latest_estimated_throughput_bps_);
|
||||
if (input.estimated_throughput_bps)
|
||||
latest_estimated_throughput_bps_ = *input.estimated_throughput_bps;
|
||||
Timestamp at_time) {
|
||||
DataRate estimated_throughput =
|
||||
input.estimated_throughput.value_or(latest_estimated_throughput_);
|
||||
if (input.estimated_throughput)
|
||||
latest_estimated_throughput_ = *input.estimated_throughput;
|
||||
|
||||
// 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 will end up with a valid estimate.
|
||||
if (!bitrate_is_initialized_ &&
|
||||
input.bw_state != BandwidthUsage::kBwOverusing)
|
||||
return current_bitrate_bps_;
|
||||
return current_bitrate_;
|
||||
|
||||
ChangeState(input, now_ms);
|
||||
ChangeState(input, at_time);
|
||||
// Calculated here because it's used in multiple places.
|
||||
const float estimated_throughput_kbps = estimated_throughput_bps / 1000.0f;
|
||||
const float estimated_throughput_kbps = estimated_throughput.kbps<double>();
|
||||
// Calculate the max bit rate std dev given the normalized
|
||||
// variance and the current throughput bitrate.
|
||||
const float std_max_bit_rate =
|
||||
@ -270,45 +274,41 @@ uint32_t AimdRateControl::ChangeBitrate(uint32_t new_bitrate_bps,
|
||||
avg_max_bitrate_kbps_ = -1.0;
|
||||
}
|
||||
if (rate_control_region_ == kRcNearMax) {
|
||||
uint32_t additive_increase_bps =
|
||||
AdditiveRateIncrease(now_ms, time_last_bitrate_change_);
|
||||
new_bitrate_bps += additive_increase_bps;
|
||||
DataRate additive_increase =
|
||||
AdditiveRateIncrease(at_time, time_last_bitrate_change_);
|
||||
new_bitrate += additive_increase;
|
||||
} else {
|
||||
uint32_t multiplicative_increase_bps = MultiplicativeRateIncrease(
|
||||
now_ms, time_last_bitrate_change_, new_bitrate_bps);
|
||||
new_bitrate_bps += multiplicative_increase_bps;
|
||||
DataRate multiplicative_increase = MultiplicativeRateIncrease(
|
||||
at_time, time_last_bitrate_change_, new_bitrate);
|
||||
new_bitrate += multiplicative_increase;
|
||||
}
|
||||
|
||||
time_last_bitrate_change_ = now_ms;
|
||||
time_last_bitrate_change_ = at_time;
|
||||
break;
|
||||
|
||||
case kRcDecrease:
|
||||
// Set bit rate to something slightly lower than max
|
||||
// to get rid of any self-induced delay.
|
||||
new_bitrate_bps =
|
||||
static_cast<uint32_t>(beta_ * estimated_throughput_bps + 0.5);
|
||||
if (new_bitrate_bps > current_bitrate_bps_) {
|
||||
new_bitrate = estimated_throughput * beta_;
|
||||
if (new_bitrate > current_bitrate_) {
|
||||
// Avoid increasing the rate when over-using.
|
||||
if (rate_control_region_ != kRcMaxUnknown) {
|
||||
new_bitrate_bps = static_cast<uint32_t>(
|
||||
beta_ * avg_max_bitrate_kbps_ * 1000 + 0.5f);
|
||||
new_bitrate = DataRate::kbps(beta_ * avg_max_bitrate_kbps_);
|
||||
}
|
||||
new_bitrate_bps = std::min(new_bitrate_bps, current_bitrate_bps_);
|
||||
new_bitrate = std::min(new_bitrate, current_bitrate_);
|
||||
}
|
||||
rate_control_region_ = kRcNearMax;
|
||||
|
||||
if (bitrate_is_initialized_ &&
|
||||
estimated_throughput_bps < current_bitrate_bps_) {
|
||||
if (bitrate_is_initialized_ && estimated_throughput < current_bitrate_) {
|
||||
constexpr float kDegradationFactor = 0.9f;
|
||||
if (smoothing_experiment_ &&
|
||||
new_bitrate_bps <
|
||||
kDegradationFactor * beta_ * current_bitrate_bps_) {
|
||||
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 {
|
||||
last_decrease_ = current_bitrate_bps_ - new_bitrate_bps;
|
||||
last_decrease_ = current_bitrate_ - new_bitrate;
|
||||
}
|
||||
}
|
||||
if (estimated_throughput_kbps <
|
||||
@ -320,51 +320,49 @@ uint32_t AimdRateControl::ChangeBitrate(uint32_t new_bitrate_bps,
|
||||
UpdateMaxThroughputEstimate(estimated_throughput_kbps);
|
||||
// Stay on hold until the pipes are cleared.
|
||||
rate_control_state_ = kRcHold;
|
||||
time_last_bitrate_change_ = now_ms;
|
||||
time_last_bitrate_decrease_ = now_ms;
|
||||
time_last_bitrate_change_ = at_time;
|
||||
time_last_bitrate_decrease_ = at_time;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return ClampBitrate(new_bitrate_bps, estimated_throughput_bps);
|
||||
return ClampBitrate(new_bitrate, estimated_throughput);
|
||||
}
|
||||
|
||||
uint32_t AimdRateControl::ClampBitrate(
|
||||
uint32_t new_bitrate_bps,
|
||||
uint32_t estimated_throughput_bps) const {
|
||||
DataRate AimdRateControl::ClampBitrate(DataRate new_bitrate,
|
||||
DataRate estimated_throughput) const {
|
||||
// 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 uint32_t max_bitrate_bps =
|
||||
static_cast<uint32_t>(1.5f * estimated_throughput_bps) + 10000;
|
||||
if (new_bitrate_bps > current_bitrate_bps_ &&
|
||||
new_bitrate_bps > max_bitrate_bps) {
|
||||
new_bitrate_bps = std::max(current_bitrate_bps_, max_bitrate_bps);
|
||||
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);
|
||||
}
|
||||
new_bitrate_bps = std::max(new_bitrate_bps, min_configured_bitrate_bps_);
|
||||
return new_bitrate_bps;
|
||||
new_bitrate = std::max(new_bitrate, min_configured_bitrate_);
|
||||
return new_bitrate;
|
||||
}
|
||||
|
||||
uint32_t AimdRateControl::MultiplicativeRateIncrease(
|
||||
int64_t now_ms,
|
||||
int64_t last_ms,
|
||||
uint32_t current_bitrate_bps) const {
|
||||
DataRate AimdRateControl::MultiplicativeRateIncrease(
|
||||
Timestamp at_time,
|
||||
Timestamp last_time,
|
||||
DataRate current_bitrate) const {
|
||||
double alpha = 1.08;
|
||||
if (last_ms > -1) {
|
||||
auto time_since_last_update_ms =
|
||||
rtc::SafeMin<int64_t>(now_ms - last_ms, 1000);
|
||||
alpha = pow(alpha, time_since_last_update_ms / 1000.0);
|
||||
if (last_time.IsFinite()) {
|
||||
auto time_since_last_update = at_time - last_time;
|
||||
alpha = pow(alpha, std::min(time_since_last_update.seconds<double>(), 1.0));
|
||||
}
|
||||
uint32_t multiplicative_increase_bps =
|
||||
std::max(current_bitrate_bps * (alpha - 1.0), 1000.0);
|
||||
return multiplicative_increase_bps;
|
||||
DataRate multiplicative_increase =
|
||||
std::max(current_bitrate * (alpha - 1.0), DataRate::bps(1000));
|
||||
return multiplicative_increase;
|
||||
}
|
||||
|
||||
uint32_t AimdRateControl::AdditiveRateIncrease(int64_t now_ms,
|
||||
int64_t last_ms) const {
|
||||
return static_cast<uint32_t>((now_ms - last_ms) *
|
||||
GetNearMaxIncreaseRateBps() / 1000);
|
||||
DataRate AimdRateControl::AdditiveRateIncrease(Timestamp at_time,
|
||||
Timestamp last_time) const {
|
||||
double time_period_seconds = (at_time - last_time).seconds<double>();
|
||||
double data_rate_increase_bps =
|
||||
GetNearMaxIncreaseRateBpsPerSecond() * time_period_seconds;
|
||||
return DataRate::bps(data_rate_increase_bps);
|
||||
}
|
||||
|
||||
void AimdRateControl::UpdateMaxThroughputEstimate(
|
||||
@ -394,11 +392,11 @@ void AimdRateControl::UpdateMaxThroughputEstimate(
|
||||
}
|
||||
|
||||
void AimdRateControl::ChangeState(const RateControlInput& input,
|
||||
int64_t now_ms) {
|
||||
Timestamp at_time) {
|
||||
switch (input.bw_state) {
|
||||
case BandwidthUsage::kBwNormal:
|
||||
if (rate_control_state_ == kRcHold) {
|
||||
time_last_bitrate_change_ = now_ms;
|
||||
time_last_bitrate_change_ = at_time;
|
||||
rate_control_state_ = kRcIncrease;
|
||||
}
|
||||
break;
|
||||
|
@ -16,6 +16,9 @@
|
||||
#include "absl/types/optional.h"
|
||||
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
|
||||
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/timestamp.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// A rate control implementation based on additive increases of
|
||||
@ -32,28 +35,28 @@ class AimdRateControl {
|
||||
// either if it has been explicitly set via SetStartBitrate/SetEstimate, or if
|
||||
// we have measured a throughput.
|
||||
bool ValidEstimate() const;
|
||||
void SetStartBitrate(int start_bitrate_bps);
|
||||
void SetMinBitrate(int min_bitrate_bps);
|
||||
int64_t GetFeedbackInterval() const;
|
||||
void SetStartBitrate(DataRate start_bitrate);
|
||||
void SetMinBitrate(DataRate min_bitrate);
|
||||
TimeDelta GetFeedbackInterval() const;
|
||||
|
||||
// Returns true if the bitrate estimate hasn't been changed for more than
|
||||
// an RTT, or if the estimated_throughput is less than half of the current
|
||||
// estimate. Should be used to decide if we should reduce the rate further
|
||||
// when over-using.
|
||||
bool TimeToReduceFurther(int64_t now_ms,
|
||||
uint32_t estimated_throughput_bps) const;
|
||||
bool TimeToReduceFurther(Timestamp at_time,
|
||||
DataRate estimated_throughput) const;
|
||||
// As above. To be used if overusing before we have measured a throughput.
|
||||
bool InitialTimeToReduceFurther(int64_t now_ms) const;
|
||||
bool InitialTimeToReduceFurther(Timestamp at_time) const;
|
||||
|
||||
uint32_t LatestEstimate() const;
|
||||
void SetRtt(int64_t rtt);
|
||||
uint32_t Update(const RateControlInput* input, int64_t now_ms);
|
||||
void SetEstimate(int bitrate_bps, int64_t now_ms);
|
||||
DataRate LatestEstimate() const;
|
||||
void SetRtt(TimeDelta rtt);
|
||||
DataRate Update(const RateControlInput* input, Timestamp at_time);
|
||||
void SetEstimate(DataRate bitrate, Timestamp at_time);
|
||||
|
||||
// Returns the increase rate when used bandwidth is near the link capacity.
|
||||
int GetNearMaxIncreaseRateBps() const;
|
||||
double GetNearMaxIncreaseRateBpsPerSecond() const;
|
||||
// Returns the expected time between overuse signals (assuming steady state).
|
||||
int GetExpectedBandwidthPeriodMs() const;
|
||||
TimeDelta GetExpectedBandwidthPeriod() const;
|
||||
|
||||
private:
|
||||
friend class GoogCcStatePrinter;
|
||||
@ -64,41 +67,41 @@ class AimdRateControl {
|
||||
// in the "decrease" state the bitrate will be decreased to slightly below the
|
||||
// current throughput. When in the "hold" state the bitrate will be kept
|
||||
// constant to allow built up queues to drain.
|
||||
uint32_t ChangeBitrate(uint32_t current_bitrate,
|
||||
DataRate ChangeBitrate(DataRate current_bitrate,
|
||||
const RateControlInput& input,
|
||||
int64_t now_ms);
|
||||
// Clamps new_bitrate_bps to within the configured min bitrate and a linear
|
||||
Timestamp at_time);
|
||||
// Clamps new_bitrate to within the configured min bitrate and a linear
|
||||
// function of the throughput, so that the new bitrate can't grow too
|
||||
// large compared to the bitrate actually being received by the other end.
|
||||
uint32_t ClampBitrate(uint32_t new_bitrate_bps,
|
||||
uint32_t estimated_throughput_bps) const;
|
||||
uint32_t MultiplicativeRateIncrease(int64_t now_ms,
|
||||
int64_t last_ms,
|
||||
uint32_t current_bitrate_bps) const;
|
||||
uint32_t AdditiveRateIncrease(int64_t now_ms, int64_t last_ms) const;
|
||||
void UpdateChangePeriod(int64_t now_ms);
|
||||
DataRate ClampBitrate(DataRate new_bitrate,
|
||||
DataRate estimated_throughput) const;
|
||||
DataRate MultiplicativeRateIncrease(Timestamp at_time,
|
||||
Timestamp last_ms,
|
||||
DataRate current_bitrate) const;
|
||||
DataRate AdditiveRateIncrease(Timestamp at_time, Timestamp last_time) const;
|
||||
void UpdateChangePeriod(Timestamp at_time);
|
||||
void UpdateMaxThroughputEstimate(float estimated_throughput_kbps);
|
||||
void ChangeState(const RateControlInput& input, int64_t now_ms);
|
||||
void ChangeState(const RateControlInput& input, Timestamp at_time);
|
||||
|
||||
uint32_t min_configured_bitrate_bps_;
|
||||
uint32_t max_configured_bitrate_bps_;
|
||||
uint32_t current_bitrate_bps_;
|
||||
uint32_t latest_estimated_throughput_bps_;
|
||||
DataRate min_configured_bitrate_;
|
||||
DataRate max_configured_bitrate_;
|
||||
DataRate current_bitrate_;
|
||||
DataRate latest_estimated_throughput_;
|
||||
float avg_max_bitrate_kbps_;
|
||||
float var_max_bitrate_kbps_;
|
||||
RateControlState rate_control_state_;
|
||||
RateControlRegion rate_control_region_;
|
||||
int64_t time_last_bitrate_change_;
|
||||
int64_t time_last_bitrate_decrease_;
|
||||
int64_t time_first_throughput_estimate_;
|
||||
Timestamp time_last_bitrate_change_;
|
||||
Timestamp time_last_bitrate_decrease_;
|
||||
Timestamp time_first_throughput_estimate_;
|
||||
bool bitrate_is_initialized_;
|
||||
float beta_;
|
||||
int64_t rtt_;
|
||||
TimeDelta rtt_;
|
||||
const bool in_experiment_;
|
||||
const bool smoothing_experiment_;
|
||||
const bool in_initial_backoff_interval_experiment_;
|
||||
int64_t initial_backoff_interval_ms_;
|
||||
absl::optional<int> last_decrease_;
|
||||
TimeDelta initial_backoff_interval_;
|
||||
absl::optional<DataRate> last_decrease_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
|
@ -40,13 +40,26 @@ AimdRateControlStates CreateAimdRateControlStates() {
|
||||
states.simulated_clock.reset(new SimulatedClock(kClockInitialTime));
|
||||
return states;
|
||||
}
|
||||
|
||||
absl::optional<DataRate> OptionalRateFromOptionalBps(
|
||||
absl::optional<int> bitrate_bps) {
|
||||
if (bitrate_bps) {
|
||||
return DataRate::bps(*bitrate_bps);
|
||||
} else {
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
void UpdateRateControl(const AimdRateControlStates& states,
|
||||
const BandwidthUsage& bandwidth_usage,
|
||||
absl::optional<uint32_t> throughput_estimate,
|
||||
int64_t now_ms) {
|
||||
RateControlInput input(bandwidth_usage, throughput_estimate);
|
||||
states.aimd_rate_control->Update(&input, now_ms);
|
||||
RateControlInput input(bandwidth_usage,
|
||||
OptionalRateFromOptionalBps(throughput_estimate));
|
||||
states.aimd_rate_control->Update(&input, Timestamp::ms(now_ms));
|
||||
}
|
||||
void SetEstimate(const AimdRateControlStates& states, int bitrate_bps) {
|
||||
states.aimd_rate_control->SetEstimate(
|
||||
DataRate::bps(bitrate_bps),
|
||||
Timestamp::ms(states.simulated_clock->TimeInMilliseconds()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -54,40 +67,40 @@ void UpdateRateControl(const AimdRateControlStates& states,
|
||||
TEST(AimdRateControlTest, MinNearMaxIncreaseRateOnLowBandwith) {
|
||||
auto states = CreateAimdRateControlStates();
|
||||
constexpr int kBitrate = 30000;
|
||||
states.aimd_rate_control->SetEstimate(
|
||||
kBitrate, states.simulated_clock->TimeInMilliseconds());
|
||||
EXPECT_EQ(4000, states.aimd_rate_control->GetNearMaxIncreaseRateBps());
|
||||
SetEstimate(states, kBitrate);
|
||||
EXPECT_EQ(4000,
|
||||
states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond());
|
||||
}
|
||||
|
||||
TEST(AimdRateControlTest, NearMaxIncreaseRateIs5kbpsOn90kbpsAnd200msRtt) {
|
||||
auto states = CreateAimdRateControlStates();
|
||||
constexpr int kBitrate = 90000;
|
||||
states.aimd_rate_control->SetEstimate(
|
||||
kBitrate, states.simulated_clock->TimeInMilliseconds());
|
||||
EXPECT_EQ(5000, states.aimd_rate_control->GetNearMaxIncreaseRateBps());
|
||||
SetEstimate(states, kBitrate);
|
||||
EXPECT_EQ(5000,
|
||||
states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond());
|
||||
}
|
||||
|
||||
TEST(AimdRateControlTest, NearMaxIncreaseRateIs5kbpsOn60kbpsAnd100msRtt) {
|
||||
auto states = CreateAimdRateControlStates();
|
||||
constexpr int kBitrate = 60000;
|
||||
states.aimd_rate_control->SetEstimate(
|
||||
kBitrate, states.simulated_clock->TimeInMilliseconds());
|
||||
states.aimd_rate_control->SetRtt(100);
|
||||
EXPECT_EQ(5000, states.aimd_rate_control->GetNearMaxIncreaseRateBps());
|
||||
SetEstimate(states, kBitrate);
|
||||
states.aimd_rate_control->SetRtt(TimeDelta::ms(100));
|
||||
EXPECT_EQ(5000,
|
||||
states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond());
|
||||
}
|
||||
|
||||
TEST(AimdRateControlTest, GetIncreaseRateAndBandwidthPeriod) {
|
||||
// Smoothing experiment disabled
|
||||
auto states = CreateAimdRateControlStates();
|
||||
constexpr int kBitrate = 300000;
|
||||
states.aimd_rate_control->SetEstimate(
|
||||
kBitrate, states.simulated_clock->TimeInMilliseconds());
|
||||
SetEstimate(states, kBitrate);
|
||||
UpdateRateControl(states, BandwidthUsage::kBwOverusing, kBitrate,
|
||||
states.simulated_clock->TimeInMilliseconds());
|
||||
EXPECT_NEAR(14000, states.aimd_rate_control->GetNearMaxIncreaseRateBps(),
|
||||
EXPECT_NEAR(14000,
|
||||
states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond(),
|
||||
1000);
|
||||
EXPECT_EQ(kDefaultPeriodMsNoSmoothingExp,
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriodMs());
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriod().ms());
|
||||
}
|
||||
|
||||
TEST(AimdRateControlTest, GetIncreaseRateAndBandwidthPeriodSmoothingExp) {
|
||||
@ -95,21 +108,20 @@ TEST(AimdRateControlTest, GetIncreaseRateAndBandwidthPeriodSmoothingExp) {
|
||||
test::ScopedFieldTrials override_field_trials(kSmoothingExpFieldTrial);
|
||||
auto states = CreateAimdRateControlStates();
|
||||
constexpr int kBitrate = 300000;
|
||||
states.aimd_rate_control->SetEstimate(
|
||||
kBitrate, states.simulated_clock->TimeInMilliseconds());
|
||||
SetEstimate(states, kBitrate);
|
||||
UpdateRateControl(states, BandwidthUsage::kBwOverusing, kBitrate,
|
||||
states.simulated_clock->TimeInMilliseconds());
|
||||
EXPECT_NEAR(14000, states.aimd_rate_control->GetNearMaxIncreaseRateBps(),
|
||||
EXPECT_NEAR(14000,
|
||||
states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond(),
|
||||
1000);
|
||||
EXPECT_EQ(kMinBwePeriodMsSmoothingExp,
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriodMs());
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriod().ms());
|
||||
}
|
||||
|
||||
TEST(AimdRateControlTest, BweLimitedByAckedBitrate) {
|
||||
auto states = CreateAimdRateControlStates();
|
||||
constexpr int kAckedBitrate = 10000;
|
||||
states.aimd_rate_control->SetEstimate(
|
||||
kAckedBitrate, states.simulated_clock->TimeInMilliseconds());
|
||||
SetEstimate(states, kAckedBitrate);
|
||||
while (states.simulated_clock->TimeInMilliseconds() - kClockInitialTime <
|
||||
20000) {
|
||||
UpdateRateControl(states, BandwidthUsage::kBwNormal, kAckedBitrate,
|
||||
@ -118,14 +130,13 @@ TEST(AimdRateControlTest, BweLimitedByAckedBitrate) {
|
||||
}
|
||||
ASSERT_TRUE(states.aimd_rate_control->ValidEstimate());
|
||||
EXPECT_EQ(static_cast<uint32_t>(1.5 * kAckedBitrate + 10000),
|
||||
states.aimd_rate_control->LatestEstimate());
|
||||
states.aimd_rate_control->LatestEstimate().bps());
|
||||
}
|
||||
|
||||
TEST(AimdRateControlTest, BweNotLimitedByDecreasingAckedBitrate) {
|
||||
auto states = CreateAimdRateControlStates();
|
||||
constexpr int kAckedBitrate = 100000;
|
||||
states.aimd_rate_control->SetEstimate(
|
||||
kAckedBitrate, states.simulated_clock->TimeInMilliseconds());
|
||||
SetEstimate(states, kAckedBitrate);
|
||||
while (states.simulated_clock->TimeInMilliseconds() - kClockInitialTime <
|
||||
20000) {
|
||||
UpdateRateControl(states, BandwidthUsage::kBwNormal, kAckedBitrate,
|
||||
@ -135,10 +146,10 @@ TEST(AimdRateControlTest, BweNotLimitedByDecreasingAckedBitrate) {
|
||||
ASSERT_TRUE(states.aimd_rate_control->ValidEstimate());
|
||||
// If the acked bitrate decreases the BWE shouldn't be reduced to 1.5x
|
||||
// what's being acked, but also shouldn't get to increase more.
|
||||
uint32_t prev_estimate = states.aimd_rate_control->LatestEstimate();
|
||||
uint32_t prev_estimate = states.aimd_rate_control->LatestEstimate().bps();
|
||||
UpdateRateControl(states, BandwidthUsage::kBwNormal, kAckedBitrate / 2,
|
||||
states.simulated_clock->TimeInMilliseconds());
|
||||
uint32_t new_estimate = states.aimd_rate_control->LatestEstimate();
|
||||
uint32_t new_estimate = states.aimd_rate_control->LatestEstimate().bps();
|
||||
EXPECT_NEAR(new_estimate, static_cast<uint32_t>(1.5 * kAckedBitrate + 10000),
|
||||
2000);
|
||||
EXPECT_EQ(new_estimate, prev_estimate);
|
||||
@ -147,35 +158,34 @@ TEST(AimdRateControlTest, BweNotLimitedByDecreasingAckedBitrate) {
|
||||
TEST(AimdRateControlTest, DefaultPeriodUntilFirstOveruse) {
|
||||
// Smoothing experiment disabled
|
||||
auto states = CreateAimdRateControlStates();
|
||||
states.aimd_rate_control->SetStartBitrate(300000);
|
||||
states.aimd_rate_control->SetStartBitrate(DataRate::kbps(300));
|
||||
EXPECT_EQ(kDefaultPeriodMsNoSmoothingExp,
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriodMs());
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriod().ms());
|
||||
states.simulated_clock->AdvanceTimeMilliseconds(100);
|
||||
UpdateRateControl(states, BandwidthUsage::kBwOverusing, 280000,
|
||||
states.simulated_clock->TimeInMilliseconds());
|
||||
EXPECT_NE(kDefaultPeriodMsNoSmoothingExp,
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriodMs());
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriod().ms());
|
||||
}
|
||||
|
||||
TEST(AimdRateControlTest, MinPeriodUntilFirstOveruseSmoothingExp) {
|
||||
// Smoothing experiment enabled
|
||||
test::ScopedFieldTrials override_field_trials(kSmoothingExpFieldTrial);
|
||||
auto states = CreateAimdRateControlStates();
|
||||
states.aimd_rate_control->SetStartBitrate(300000);
|
||||
states.aimd_rate_control->SetStartBitrate(DataRate::kbps(300));
|
||||
EXPECT_EQ(kMinBwePeriodMsSmoothingExp,
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriodMs());
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriod().ms());
|
||||
states.simulated_clock->AdvanceTimeMilliseconds(100);
|
||||
UpdateRateControl(states, BandwidthUsage::kBwOverusing, 280000,
|
||||
states.simulated_clock->TimeInMilliseconds());
|
||||
EXPECT_NE(kMinBwePeriodMsSmoothingExp,
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriodMs());
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriod().ms());
|
||||
}
|
||||
|
||||
TEST(AimdRateControlTest, ExpectedPeriodAfter20kbpsDropAnd5kbpsIncrease) {
|
||||
auto states = CreateAimdRateControlStates();
|
||||
constexpr int kInitialBitrate = 110000;
|
||||
states.aimd_rate_control->SetEstimate(
|
||||
kInitialBitrate, states.simulated_clock->TimeInMilliseconds());
|
||||
SetEstimate(states, kInitialBitrate);
|
||||
states.simulated_clock->AdvanceTimeMilliseconds(100);
|
||||
// Make the bitrate drop by 20 kbps to get to 90 kbps.
|
||||
// The rate increase at 90 kbps should be 5 kbps, so the period should be 4 s.
|
||||
@ -183,8 +193,9 @@ TEST(AimdRateControlTest, ExpectedPeriodAfter20kbpsDropAnd5kbpsIncrease) {
|
||||
(kInitialBitrate - 20000) / kFractionAfterOveruse;
|
||||
UpdateRateControl(states, BandwidthUsage::kBwOverusing, kAckedBitrate,
|
||||
states.simulated_clock->TimeInMilliseconds());
|
||||
EXPECT_EQ(5000, states.aimd_rate_control->GetNearMaxIncreaseRateBps());
|
||||
EXPECT_EQ(4000, states.aimd_rate_control->GetExpectedBandwidthPeriodMs());
|
||||
EXPECT_EQ(5000,
|
||||
states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond());
|
||||
EXPECT_EQ(4000, states.aimd_rate_control->GetExpectedBandwidthPeriod().ms());
|
||||
}
|
||||
|
||||
TEST(AimdRateControlTest, MinPeriodAfterLargeBitrateDecreaseSmoothingExp) {
|
||||
@ -192,8 +203,7 @@ TEST(AimdRateControlTest, MinPeriodAfterLargeBitrateDecreaseSmoothingExp) {
|
||||
test::ScopedFieldTrials override_field_trials(kSmoothingExpFieldTrial);
|
||||
auto states = CreateAimdRateControlStates();
|
||||
constexpr int kInitialBitrate = 110000;
|
||||
states.aimd_rate_control->SetEstimate(
|
||||
kInitialBitrate, states.simulated_clock->TimeInMilliseconds());
|
||||
SetEstimate(states, kInitialBitrate);
|
||||
states.simulated_clock->AdvanceTimeMilliseconds(100);
|
||||
// Make such a large drop in bitrate that should be treated as network
|
||||
// degradation.
|
||||
@ -201,20 +211,19 @@ TEST(AimdRateControlTest, MinPeriodAfterLargeBitrateDecreaseSmoothingExp) {
|
||||
UpdateRateControl(states, BandwidthUsage::kBwOverusing, kAckedBitrate,
|
||||
states.simulated_clock->TimeInMilliseconds());
|
||||
EXPECT_EQ(kMinBwePeriodMsSmoothingExp,
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriodMs());
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriod().ms());
|
||||
}
|
||||
|
||||
TEST(AimdRateControlTest, BandwidthPeriodIsNotBelowMin) {
|
||||
auto states = CreateAimdRateControlStates();
|
||||
constexpr int kInitialBitrate = 10000;
|
||||
states.aimd_rate_control->SetEstimate(
|
||||
kInitialBitrate, states.simulated_clock->TimeInMilliseconds());
|
||||
SetEstimate(states, kInitialBitrate);
|
||||
states.simulated_clock->AdvanceTimeMilliseconds(100);
|
||||
// Make a small (1.5 kbps) bitrate drop to 8.5 kbps.
|
||||
UpdateRateControl(states, BandwidthUsage::kBwOverusing, kInitialBitrate - 1,
|
||||
states.simulated_clock->TimeInMilliseconds());
|
||||
EXPECT_EQ(kMinBwePeriodMsNoSmoothingExp,
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriodMs());
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriod().ms());
|
||||
}
|
||||
|
||||
TEST(AimdRateControlTest, BandwidthPeriodIsNotAboveMaxSmoothingExp) {
|
||||
@ -222,29 +231,27 @@ TEST(AimdRateControlTest, BandwidthPeriodIsNotAboveMaxSmoothingExp) {
|
||||
test::ScopedFieldTrials override_field_trials(kSmoothingExpFieldTrial);
|
||||
auto states = CreateAimdRateControlStates();
|
||||
constexpr int kInitialBitrate = 50000000;
|
||||
states.aimd_rate_control->SetEstimate(
|
||||
kInitialBitrate, states.simulated_clock->TimeInMilliseconds());
|
||||
SetEstimate(states, kInitialBitrate);
|
||||
states.simulated_clock->AdvanceTimeMilliseconds(100);
|
||||
// Make a large (10 Mbps) bitrate drop to 10 kbps.
|
||||
constexpr int kAckedBitrate = 40000000 / kFractionAfterOveruse;
|
||||
UpdateRateControl(states, BandwidthUsage::kBwOverusing, kAckedBitrate,
|
||||
states.simulated_clock->TimeInMilliseconds());
|
||||
EXPECT_EQ(kMaxBwePeriodMs,
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriodMs());
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriod().ms());
|
||||
}
|
||||
|
||||
TEST(AimdRateControlTest, BandwidthPeriodIsNotAboveMaxNoSmoothingExp) {
|
||||
auto states = CreateAimdRateControlStates();
|
||||
constexpr int kInitialBitrate = 10010000;
|
||||
states.aimd_rate_control->SetEstimate(
|
||||
kInitialBitrate, states.simulated_clock->TimeInMilliseconds());
|
||||
SetEstimate(states, kInitialBitrate);
|
||||
states.simulated_clock->AdvanceTimeMilliseconds(100);
|
||||
// Make a large (10 Mbps) bitrate drop to 10 kbps.
|
||||
constexpr int kAckedBitrate = 10000 / kFractionAfterOveruse;
|
||||
UpdateRateControl(states, BandwidthUsage::kBwOverusing, kAckedBitrate,
|
||||
states.simulated_clock->TimeInMilliseconds());
|
||||
EXPECT_EQ(kMaxBwePeriodMs,
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriodMs());
|
||||
states.aimd_rate_control->GetExpectedBandwidthPeriod().ms());
|
||||
}
|
||||
|
||||
TEST(AimdRateControlTest, SendingRateBoundedWhenThroughputNotEstimated) {
|
||||
@ -265,7 +272,7 @@ TEST(AimdRateControlTest, SendingRateBoundedWhenThroughputNotEstimated) {
|
||||
states.simulated_clock->TimeInMilliseconds());
|
||||
states.simulated_clock->AdvanceTimeMilliseconds(100);
|
||||
}
|
||||
EXPECT_LE(states.aimd_rate_control->LatestEstimate(),
|
||||
EXPECT_LE(states.aimd_rate_control->LatestEstimate().bps(),
|
||||
kInitialBitrateBps * 1.5 + 10000);
|
||||
}
|
||||
|
||||
|
@ -34,8 +34,8 @@ DataRate GetMinBitrate() {
|
||||
|
||||
RateControlInput::RateControlInput(
|
||||
BandwidthUsage bw_state,
|
||||
const absl::optional<uint32_t>& estimated_throughput_bps)
|
||||
: bw_state(bw_state), estimated_throughput_bps(estimated_throughput_bps) {}
|
||||
const absl::optional<DataRate>& estimated_throughput)
|
||||
: bw_state(bw_state), estimated_throughput(estimated_throughput) {}
|
||||
|
||||
RateControlInput::~RateControlInput() = default;
|
||||
|
||||
|
@ -51,11 +51,11 @@ enum RateControlRegion { kRcNearMax, kRcAboveMax, kRcMaxUnknown };
|
||||
|
||||
struct RateControlInput {
|
||||
RateControlInput(BandwidthUsage bw_state,
|
||||
const absl::optional<uint32_t>& estimated_throughput_bps);
|
||||
const absl::optional<DataRate>& estimated_throughput);
|
||||
~RateControlInput();
|
||||
|
||||
BandwidthUsage bw_state;
|
||||
absl::optional<uint32_t> estimated_throughput_bps;
|
||||
absl::optional<DataRate> estimated_throughput;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
|
@ -22,6 +22,16 @@
|
||||
#include "system_wrappers/include/metrics.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
absl::optional<DataRate> OptionalRateFromOptionalBps(
|
||||
absl::optional<int> bitrate_bps) {
|
||||
if (bitrate_bps) {
|
||||
return DataRate::bps(*bitrate_bps);
|
||||
} else {
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
enum {
|
||||
kTimestampGroupLengthMs = 5,
|
||||
@ -188,7 +198,8 @@ RemoteBitrateEstimatorAbsSendTime::ProcessClusters(int64_t now_ms) {
|
||||
<< " bps. Mean send delta: " << best_it->send_mean_ms
|
||||
<< " ms, mean recv delta: " << best_it->recv_mean_ms
|
||||
<< " ms, num probes: " << best_it->count;
|
||||
remote_rate_.SetEstimate(probe_bitrate_bps, now_ms);
|
||||
remote_rate_.SetEstimate(DataRate::bps(probe_bitrate_bps),
|
||||
Timestamp::ms(now_ms));
|
||||
return ProbeResult::kBitrateUpdated;
|
||||
}
|
||||
}
|
||||
@ -205,7 +216,7 @@ bool RemoteBitrateEstimatorAbsSendTime::IsBitrateImproving(
|
||||
bool initial_probe = !remote_rate_.ValidEstimate() && new_bitrate_bps > 0;
|
||||
bool bitrate_above_estimate =
|
||||
remote_rate_.ValidEstimate() &&
|
||||
new_bitrate_bps > static_cast<int>(remote_rate_.LatestEstimate());
|
||||
new_bitrate_bps > remote_rate_.LatestEstimate().bps<int>();
|
||||
return initial_probe || bitrate_above_estimate;
|
||||
}
|
||||
|
||||
@ -316,13 +327,14 @@ void RemoteBitrateEstimatorAbsSendTime::IncomingPacketInfo(
|
||||
// Check if it's time for a periodic update or if we should update because
|
||||
// of an over-use.
|
||||
if (last_update_ms_ == -1 ||
|
||||
now_ms - last_update_ms_ > remote_rate_.GetFeedbackInterval()) {
|
||||
now_ms - last_update_ms_ > remote_rate_.GetFeedbackInterval().ms()) {
|
||||
update_estimate = true;
|
||||
} else if (detector_.State() == BandwidthUsage::kBwOverusing) {
|
||||
absl::optional<uint32_t> incoming_rate =
|
||||
incoming_bitrate_.Rate(arrival_time_ms);
|
||||
if (incoming_rate &&
|
||||
remote_rate_.TimeToReduceFurther(now_ms, *incoming_rate)) {
|
||||
remote_rate_.TimeToReduceFurther(Timestamp::ms(now_ms),
|
||||
DataRate::bps(*incoming_rate))) {
|
||||
update_estimate = true;
|
||||
}
|
||||
}
|
||||
@ -332,9 +344,11 @@ void RemoteBitrateEstimatorAbsSendTime::IncomingPacketInfo(
|
||||
// The first overuse should immediately trigger a new estimate.
|
||||
// We also have to update the estimate immediately if we are overusing
|
||||
// and the target bitrate is too high compared to what we are receiving.
|
||||
const RateControlInput input(detector_.State(),
|
||||
incoming_bitrate_.Rate(arrival_time_ms));
|
||||
target_bitrate_bps = remote_rate_.Update(&input, now_ms);
|
||||
const RateControlInput input(
|
||||
detector_.State(),
|
||||
OptionalRateFromOptionalBps(incoming_bitrate_.Rate(arrival_time_ms)));
|
||||
target_bitrate_bps =
|
||||
remote_rate_.Update(&input, Timestamp::ms(now_ms)).bps<uint32_t>();
|
||||
update_estimate = remote_rate_.ValidEstimate();
|
||||
ssrcs = Keys(ssrcs_);
|
||||
}
|
||||
@ -374,7 +388,7 @@ void RemoteBitrateEstimatorAbsSendTime::TimeoutStreams(int64_t now_ms) {
|
||||
void RemoteBitrateEstimatorAbsSendTime::OnRttUpdate(int64_t avg_rtt_ms,
|
||||
int64_t max_rtt_ms) {
|
||||
rtc::CritScope lock(&crit_);
|
||||
remote_rate_.SetRtt(avg_rtt_ms);
|
||||
remote_rate_.SetRtt(TimeDelta::ms(avg_rtt_ms));
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorAbsSendTime::RemoveStream(uint32_t ssrc) {
|
||||
@ -399,7 +413,7 @@ bool RemoteBitrateEstimatorAbsSendTime::LatestEstimate(
|
||||
if (ssrcs_.empty()) {
|
||||
*bitrate_bps = 0;
|
||||
} else {
|
||||
*bitrate_bps = remote_rate_.LatestEstimate();
|
||||
*bitrate_bps = remote_rate_.LatestEstimate().bps<uint32_t>();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -408,6 +422,6 @@ void RemoteBitrateEstimatorAbsSendTime::SetMinBitrate(int min_bitrate_bps) {
|
||||
// Called from both the configuration thread and the network thread. Shouldn't
|
||||
// be called from the network thread in the future.
|
||||
rtc::CritScope lock(&crit_);
|
||||
remote_rate_.SetMinBitrate(min_bitrate_bps);
|
||||
remote_rate_.SetMinBitrate(DataRate::bps(min_bitrate_bps));
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
@ -27,6 +27,16 @@
|
||||
#include "system_wrappers/include/metrics.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
absl::optional<DataRate> OptionalRateFromOptionalBps(
|
||||
absl::optional<int> bitrate_bps) {
|
||||
if (bitrate_bps) {
|
||||
return DataRate::bps(*bitrate_bps);
|
||||
} else {
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
enum { kTimestampGroupLengthMs = 5 };
|
||||
static const double kTimestampToMs = 1.0 / 90.0;
|
||||
@ -133,7 +143,8 @@ void RemoteBitrateEstimatorSingleStream::IncomingPacket(
|
||||
incoming_bitrate_.Rate(now_ms);
|
||||
if (incoming_bitrate_bps &&
|
||||
(prior_state != BandwidthUsage::kBwOverusing ||
|
||||
GetRemoteRate()->TimeToReduceFurther(now_ms, *incoming_bitrate_bps))) {
|
||||
GetRemoteRate()->TimeToReduceFurther(
|
||||
Timestamp::ms(now_ms), DataRate::bps(*incoming_bitrate_bps)))) {
|
||||
// The first overuse should immediately trigger a new estimate.
|
||||
// We also have to update the estimate immediately if we are overusing
|
||||
// and the target bitrate is too high compared to what we are receiving.
|
||||
@ -187,10 +198,12 @@ void RemoteBitrateEstimatorSingleStream::UpdateEstimate(int64_t now_ms) {
|
||||
}
|
||||
AimdRateControl* remote_rate = GetRemoteRate();
|
||||
|
||||
const RateControlInput input(bw_state, incoming_bitrate_.Rate(now_ms));
|
||||
uint32_t target_bitrate = remote_rate->Update(&input, now_ms);
|
||||
const RateControlInput input(
|
||||
bw_state, OptionalRateFromOptionalBps(incoming_bitrate_.Rate(now_ms)));
|
||||
uint32_t target_bitrate =
|
||||
remote_rate->Update(&input, Timestamp::ms(now_ms)).bps<uint32_t>();
|
||||
if (remote_rate->ValidEstimate()) {
|
||||
process_interval_ms_ = remote_rate->GetFeedbackInterval();
|
||||
process_interval_ms_ = remote_rate->GetFeedbackInterval().ms();
|
||||
RTC_DCHECK_GT(process_interval_ms_, 0);
|
||||
std::vector<uint32_t> ssrcs;
|
||||
GetSsrcs(&ssrcs);
|
||||
@ -202,7 +215,7 @@ void RemoteBitrateEstimatorSingleStream::UpdateEstimate(int64_t now_ms) {
|
||||
void RemoteBitrateEstimatorSingleStream::OnRttUpdate(int64_t avg_rtt_ms,
|
||||
int64_t max_rtt_ms) {
|
||||
rtc::CritScope cs(&crit_sect_);
|
||||
GetRemoteRate()->SetRtt(avg_rtt_ms);
|
||||
GetRemoteRate()->SetRtt(TimeDelta::ms(avg_rtt_ms));
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorSingleStream::RemoveStream(unsigned int ssrc) {
|
||||
@ -226,7 +239,7 @@ bool RemoteBitrateEstimatorSingleStream::LatestEstimate(
|
||||
if (ssrcs->empty())
|
||||
*bitrate_bps = 0;
|
||||
else
|
||||
*bitrate_bps = remote_rate_->LatestEstimate();
|
||||
*bitrate_bps = remote_rate_->LatestEstimate().bps<uint32_t>();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -249,7 +262,7 @@ AimdRateControl* RemoteBitrateEstimatorSingleStream::GetRemoteRate() {
|
||||
|
||||
void RemoteBitrateEstimatorSingleStream::SetMinBitrate(int min_bitrate_bps) {
|
||||
rtc::CritScope cs(&crit_sect_);
|
||||
remote_rate_->SetMinBitrate(min_bitrate_bps);
|
||||
remote_rate_->SetMinBitrate(DataRate::bps(min_bitrate_bps));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -55,7 +55,7 @@ TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropThreeStreamsWrap) {
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropThirteenStreamsWrap) {
|
||||
CapacityDropTestHelper(13, true, 733, 0);
|
||||
CapacityDropTestHelper(13, true, 567, 0);
|
||||
}
|
||||
|
||||
TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropNineteenStreamsWrap) {
|
||||
|
Reference in New Issue
Block a user