/* * Copyright (c) 2016 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 "modules/remote_bitrate_estimator/aimd_rate_control.h" #include #include "api/transport/field_trial_based_config.h" #include "api/units/data_rate.h" #include "system_wrappers/include/clock.h" #include "test/field_trial.h" #include "test/gtest.h" namespace webrtc { namespace { constexpr int64_t kClockInitialTime = 123456; constexpr int kMinBwePeriodMs = 2000; constexpr int kDefaultPeriodMs = 3000; constexpr int kMaxBwePeriodMs = 50000; // After an overuse, we back off to 85% to the received bitrate. constexpr double kFractionAfterOveruse = 0.85; struct AimdRateControlStates { std::unique_ptr aimd_rate_control; std::unique_ptr simulated_clock; FieldTrialBasedConfig field_trials; }; AimdRateControlStates CreateAimdRateControlStates(bool send_side = false) { AimdRateControlStates states; states.aimd_rate_control.reset( new AimdRateControl(&states.field_trials, send_side)); states.simulated_clock.reset(new SimulatedClock(kClockInitialTime)); return states; } absl::optional OptionalRateFromOptionalBps( absl::optional bitrate_bps) { if (bitrate_bps) { return DataRate::BitsPerSec(*bitrate_bps); } else { return absl::nullopt; } } void UpdateRateControl(const AimdRateControlStates& states, const BandwidthUsage& bandwidth_usage, absl::optional throughput_estimate, int64_t now_ms) { RateControlInput input(bandwidth_usage, OptionalRateFromOptionalBps(throughput_estimate)); states.aimd_rate_control->Update(&input, Timestamp::Millis(now_ms)); } void SetEstimate(const AimdRateControlStates& states, int bitrate_bps) { states.aimd_rate_control->SetEstimate(DataRate::BitsPerSec(bitrate_bps), states.simulated_clock->CurrentTime()); } } // namespace TEST(AimdRateControlTest, MinNearMaxIncreaseRateOnLowBandwith) { auto states = CreateAimdRateControlStates(); constexpr int kBitrate = 30000; SetEstimate(states, kBitrate); EXPECT_EQ(4000, states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond()); } TEST(AimdRateControlTest, NearMaxIncreaseRateIs5kbpsOn90kbpsAnd200msRtt) { auto states = CreateAimdRateControlStates(); constexpr int kBitrate = 90000; SetEstimate(states, kBitrate); EXPECT_EQ(5000, states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond()); } TEST(AimdRateControlTest, NearMaxIncreaseRateIs5kbpsOn60kbpsAnd100msRtt) { auto states = CreateAimdRateControlStates(); constexpr int kBitrate = 60000; SetEstimate(states, kBitrate); states.aimd_rate_control->SetRtt(TimeDelta::Millis(100)); EXPECT_EQ(5000, states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond()); } TEST(AimdRateControlTest, GetIncreaseRateAndBandwidthPeriod) { // Smoothing experiment disabled auto states = CreateAimdRateControlStates(); constexpr int kBitrate = 300000; SetEstimate(states, kBitrate); UpdateRateControl(states, BandwidthUsage::kBwOverusing, kBitrate, states.simulated_clock->TimeInMilliseconds()); EXPECT_NEAR(14000, states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond(), 1000); EXPECT_EQ(kDefaultPeriodMs, states.aimd_rate_control->GetExpectedBandwidthPeriod().ms()); } TEST(AimdRateControlTest, BweLimitedByAckedBitrate) { auto states = CreateAimdRateControlStates(); constexpr int kAckedBitrate = 10000; SetEstimate(states, kAckedBitrate); while (states.simulated_clock->TimeInMilliseconds() - kClockInitialTime < 20000) { UpdateRateControl(states, BandwidthUsage::kBwNormal, kAckedBitrate, states.simulated_clock->TimeInMilliseconds()); states.simulated_clock->AdvanceTimeMilliseconds(100); } ASSERT_TRUE(states.aimd_rate_control->ValidEstimate()); EXPECT_EQ(static_cast(1.5 * kAckedBitrate + 10000), states.aimd_rate_control->LatestEstimate().bps()); } TEST(AimdRateControlTest, BweNotLimitedByDecreasingAckedBitrate) { auto states = CreateAimdRateControlStates(); constexpr int kAckedBitrate = 100000; SetEstimate(states, kAckedBitrate); while (states.simulated_clock->TimeInMilliseconds() - kClockInitialTime < 20000) { UpdateRateControl(states, BandwidthUsage::kBwNormal, kAckedBitrate, states.simulated_clock->TimeInMilliseconds()); states.simulated_clock->AdvanceTimeMilliseconds(100); } 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().bps(); UpdateRateControl(states, BandwidthUsage::kBwNormal, kAckedBitrate / 2, states.simulated_clock->TimeInMilliseconds()); uint32_t new_estimate = states.aimd_rate_control->LatestEstimate().bps(); EXPECT_NEAR(new_estimate, static_cast(1.5 * kAckedBitrate + 10000), 2000); EXPECT_EQ(new_estimate, prev_estimate); } TEST(AimdRateControlTest, DefaultPeriodUntilFirstOveruse) { // Smoothing experiment disabled auto states = CreateAimdRateControlStates(); states.aimd_rate_control->SetStartBitrate(DataRate::KilobitsPerSec(300)); EXPECT_EQ(kDefaultPeriodMs, states.aimd_rate_control->GetExpectedBandwidthPeriod().ms()); states.simulated_clock->AdvanceTimeMilliseconds(100); UpdateRateControl(states, BandwidthUsage::kBwOverusing, 280000, states.simulated_clock->TimeInMilliseconds()); EXPECT_NE(kDefaultPeriodMs, states.aimd_rate_control->GetExpectedBandwidthPeriod().ms()); } TEST(AimdRateControlTest, ExpectedPeriodAfter20kbpsDropAnd5kbpsIncrease) { auto states = CreateAimdRateControlStates(); constexpr int kInitialBitrate = 110000; 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. constexpr int kAckedBitrate = (kInitialBitrate - 20000) / kFractionAfterOveruse; UpdateRateControl(states, BandwidthUsage::kBwOverusing, kAckedBitrate, states.simulated_clock->TimeInMilliseconds()); EXPECT_EQ(5000, states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond()); EXPECT_EQ(4000, states.aimd_rate_control->GetExpectedBandwidthPeriod().ms()); } TEST(AimdRateControlTest, BandwidthPeriodIsNotBelowMin) { auto states = CreateAimdRateControlStates(); constexpr int kInitialBitrate = 10000; 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(kMinBwePeriodMs, states.aimd_rate_control->GetExpectedBandwidthPeriod().ms()); } TEST(AimdRateControlTest, BandwidthPeriodIsNotAboveMaxNoSmoothingExp) { auto states = CreateAimdRateControlStates(); constexpr int kInitialBitrate = 10010000; 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->GetExpectedBandwidthPeriod().ms()); } TEST(AimdRateControlTest, SendingRateBoundedWhenThroughputNotEstimated) { auto states = CreateAimdRateControlStates(); constexpr int kInitialBitrateBps = 123000; UpdateRateControl(states, BandwidthUsage::kBwNormal, kInitialBitrateBps, states.simulated_clock->TimeInMilliseconds()); // AimdRateControl sets the initial bit rate to what it receives after // five seconds has passed. // TODO(bugs.webrtc.org/9379): The comment in the AimdRateControl does not // match the constant. constexpr int kInitializationTimeMs = 5000; states.simulated_clock->AdvanceTimeMilliseconds(kInitializationTimeMs + 1); UpdateRateControl(states, BandwidthUsage::kBwNormal, kInitialBitrateBps, states.simulated_clock->TimeInMilliseconds()); for (int i = 0; i < 100; ++i) { UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, states.simulated_clock->TimeInMilliseconds()); states.simulated_clock->AdvanceTimeMilliseconds(100); } EXPECT_LE(states.aimd_rate_control->LatestEstimate().bps(), kInitialBitrateBps * 1.5 + 10000); } TEST(AimdRateControlTest, EstimateDoesNotIncreaseInAlr) { // When alr is detected, the delay based estimator is not allowed to increase // bwe since there will be no feedback from the network if the new estimate // is correct. test::ScopedFieldTrials override_field_trials( "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); auto states = CreateAimdRateControlStates(/*send_side=*/true); constexpr int kInitialBitrateBps = 123000; SetEstimate(states, kInitialBitrateBps); states.aimd_rate_control->SetInApplicationLimitedRegion(true); UpdateRateControl(states, BandwidthUsage::kBwNormal, kInitialBitrateBps, states.simulated_clock->TimeInMilliseconds()); ASSERT_EQ(states.aimd_rate_control->LatestEstimate().bps(), kInitialBitrateBps); for (int i = 0; i < 100; ++i) { UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, states.simulated_clock->TimeInMilliseconds()); states.simulated_clock->AdvanceTimeMilliseconds(100); } EXPECT_EQ(states.aimd_rate_control->LatestEstimate().bps(), kInitialBitrateBps); } TEST(AimdRateControlTest, SetEstimateIncreaseBweInAlr) { test::ScopedFieldTrials override_field_trials( "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); auto states = CreateAimdRateControlStates(/*send_side=*/true); constexpr int kInitialBitrateBps = 123000; SetEstimate(states, kInitialBitrateBps); states.aimd_rate_control->SetInApplicationLimitedRegion(true); ASSERT_EQ(states.aimd_rate_control->LatestEstimate().bps(), kInitialBitrateBps); SetEstimate(states, 2 * kInitialBitrateBps); EXPECT_EQ(states.aimd_rate_control->LatestEstimate().bps(), 2 * kInitialBitrateBps); } TEST(AimdRateControlTest, SetEstimateUpperLimitedByNetworkEstimate) { auto states = CreateAimdRateControlStates(/*send_side=*/true); NetworkStateEstimate network_estimate; network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(400); states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); SetEstimate(states, 500'000); EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), network_estimate.link_capacity_upper); } TEST(AimdRateControlTest, SetEstimateLowerLimitedByNetworkEstimate) { auto states = CreateAimdRateControlStates(/*send_side=*/true); NetworkStateEstimate network_estimate; network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(400); states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); SetEstimate(states, 100'000); // 0.85 is default backoff factor. (`beta_`) EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), network_estimate.link_capacity_lower * 0.85); } TEST(AimdRateControlTest, SetEstimateIgnoredIfLowerThanNetworkEstimateAndCurrent) { auto states = CreateAimdRateControlStates(/*send_side=*/true); SetEstimate(states, 200'000); ASSERT_EQ(states.aimd_rate_control->LatestEstimate().kbps(), 200); NetworkStateEstimate network_estimate; network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(400); states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); // Ignore the next SetEstimate, since the estimate is lower than 85% of // the network estimate. SetEstimate(states, 100'000); EXPECT_EQ(states.aimd_rate_control->LatestEstimate().kbps(), 200); } TEST(AimdRateControlTest, SetEstimateIgnoresNetworkEstimatesLowerThanCurrent) { test::ScopedFieldTrials override_field_trials( "WebRTC-Bwe-EstimateBoundedIncrease/" "ratio:0.85,ignore_acked:true,ignore_decr:true/"); auto states = CreateAimdRateControlStates(/*send_side=*/true); states.aimd_rate_control->SetStartBitrate(DataRate::KilobitsPerSec(30)); NetworkStateEstimate network_estimate; network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(400); states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); SetEstimate(states, 500'000); ASSERT_EQ(states.aimd_rate_control->LatestEstimate(), network_estimate.link_capacity_upper * 0.85); NetworkStateEstimate lower_network_estimate; lower_network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(300); states.aimd_rate_control->SetNetworkStateEstimate(lower_network_estimate); SetEstimate(states, 500'000); EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), network_estimate.link_capacity_upper * 0.85); } TEST(AimdRateControlTest, EstimateIncreaseWhileNotInAlr) { // Allow the estimate to increase as long as alr is not detected to ensure // tha BWE can not get stuck at a certain bitrate. test::ScopedFieldTrials override_field_trials( "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); auto states = CreateAimdRateControlStates(/*send_side=*/true); constexpr int kInitialBitrateBps = 123000; SetEstimate(states, kInitialBitrateBps); states.aimd_rate_control->SetInApplicationLimitedRegion(false); UpdateRateControl(states, BandwidthUsage::kBwNormal, kInitialBitrateBps, states.simulated_clock->TimeInMilliseconds()); for (int i = 0; i < 100; ++i) { UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, states.simulated_clock->TimeInMilliseconds()); states.simulated_clock->AdvanceTimeMilliseconds(100); } EXPECT_GT(states.aimd_rate_control->LatestEstimate().bps(), kInitialBitrateBps); } TEST(AimdRateControlTest, EstimateNotLimitedByNetworkEstimateIfDisabled) { test::ScopedFieldTrials override_field_trials( "WebRTC-Bwe-EstimateBoundedIncrease/Disabled/"); auto states = CreateAimdRateControlStates(/*send_side=*/true); constexpr int kInitialBitrateBps = 123000; SetEstimate(states, kInitialBitrateBps); states.aimd_rate_control->SetInApplicationLimitedRegion(false); NetworkStateEstimate network_estimate; network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(150); states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); for (int i = 0; i < 100; ++i) { UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, states.simulated_clock->TimeInMilliseconds()); states.simulated_clock->AdvanceTimeMilliseconds(100); } EXPECT_GT(states.aimd_rate_control->LatestEstimate(), network_estimate.link_capacity_upper); } TEST(AimdRateControlTest, EstimateSlowlyIncreaseToUpperLinkCapacityEstimateIfConfigured) { // Even if alr is detected, the delay based estimator is allowed to increase // up to a percentage of upper link capacity. test::ScopedFieldTrials override_field_trials( "WebRTC-Bwe-EstimateBoundedIncrease/" "ratio:0.85,ignore_acked:true,immediate_incr:false/" "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); auto states = CreateAimdRateControlStates(/*send_side=*/true); constexpr int kInitialBitrateBps = 123000; SetEstimate(states, kInitialBitrateBps); states.aimd_rate_control->SetInApplicationLimitedRegion(true); NetworkStateEstimate network_estimate; network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(200); states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); for (int i = 0; i < 10; ++i) { UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, states.simulated_clock->TimeInMilliseconds()); states.simulated_clock->AdvanceTimeMilliseconds(100); EXPECT_LT(states.aimd_rate_control->LatestEstimate(), network_estimate.link_capacity_upper * 0.85); } for (int i = 0; i < 50; ++i) { UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, states.simulated_clock->TimeInMilliseconds()); states.simulated_clock->AdvanceTimeMilliseconds(100); } EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), network_estimate.link_capacity_upper * 0.85); } TEST(AimdRateControlTest, EstimateImmediatelyIncreaseToUpperLinkCapacityEstimateIfConfigured) { // Even if alr is detected, the delay based estimator is allowed to increase // up to a percentage of upper link capacity. test::ScopedFieldTrials override_field_trials( "WebRTC-Bwe-EstimateBoundedIncrease/" "ratio:0.85,ignore_acked:true,immediate_incr:true/" "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); auto states = CreateAimdRateControlStates(/*send_side=*/true); constexpr int kInitialBitrateBps = 123000; SetEstimate(states, kInitialBitrateBps); states.aimd_rate_control->SetInApplicationLimitedRegion(true); NetworkStateEstimate network_estimate; network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(200); states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, states.simulated_clock->TimeInMilliseconds()); EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), network_estimate.link_capacity_upper * 0.85); } TEST(AimdRateControlTest, EstimateNotLoweredByNetworkEstimate) { // The delay based estimator is allowed to increase up to a percentage of // upper link capacity but does not decrease unless the delay detector // discover an overuse. test::ScopedFieldTrials override_field_trials( "WebRTC-Bwe-EstimateBoundedIncrease/" "ratio:0.85,ignore_acked:true,ignore_decr:true/" "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); auto states = CreateAimdRateControlStates(/*send_side=*/true); constexpr int kInitialBitrateBps = 123000; constexpr int kEstimatedThroughputBps = 30'000; SetEstimate(states, kInitialBitrateBps); NetworkStateEstimate network_estimate; network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(200); states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); for (int i = 0; i < 100; ++i) { UpdateRateControl(states, BandwidthUsage::kBwNormal, kEstimatedThroughputBps, states.simulated_clock->TimeInMilliseconds()); states.simulated_clock->AdvanceTimeMilliseconds(100); } DataRate estimate_after_increase = states.aimd_rate_control->LatestEstimate(); ASSERT_EQ(estimate_after_increase, network_estimate.link_capacity_upper * 0.85); // A lower network estimate does not decrease the estimate immediately, // but the estimate is not allowed to increase. network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(100); network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(80); states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); for (int i = 0; i < 10; ++i) { UpdateRateControl(states, BandwidthUsage::kBwNormal, kEstimatedThroughputBps, states.simulated_clock->TimeInMilliseconds()); states.simulated_clock->AdvanceTimeMilliseconds(100); EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), estimate_after_increase); } // If the detector detects and overuse, BWE drops to a value relative the // network estimate. UpdateRateControl(states, BandwidthUsage::kBwOverusing, kEstimatedThroughputBps, states.simulated_clock->TimeInMilliseconds()); EXPECT_LT(states.aimd_rate_control->LatestEstimate(), network_estimate.link_capacity_lower); EXPECT_GT(states.aimd_rate_control->LatestEstimate().bps(), kEstimatedThroughputBps); } TEST(AimdRateControlTest, EstimateDoesNotIncreaseInAlrIfNetworkEstimateNotSet) { // When alr is detected, the delay based estimator is not allowed to increase // bwe since there will be no feedback from the network if the new estimate // is correct. test::ScopedFieldTrials override_field_trials( "WebRTC-Bwe-EstimateBoundedIncrease/ratio:0.85,ignore_acked:true/" "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); auto states = CreateAimdRateControlStates(/*send_side=*/true); constexpr int kInitialBitrateBps = 123000; SetEstimate(states, kInitialBitrateBps); states.aimd_rate_control->SetInApplicationLimitedRegion(true); UpdateRateControl(states, BandwidthUsage::kBwNormal, kInitialBitrateBps, states.simulated_clock->TimeInMilliseconds()); ASSERT_EQ(states.aimd_rate_control->LatestEstimate().bps(), kInitialBitrateBps); for (int i = 0; i < 100; ++i) { UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, states.simulated_clock->TimeInMilliseconds()); states.simulated_clock->AdvanceTimeMilliseconds(100); } EXPECT_EQ(states.aimd_rate_control->LatestEstimate().bps(), kInitialBitrateBps); } } // namespace webrtc