| /* |
| * 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 "api/units/data_rate.h" |
| #include "api/units/time_delta.h" |
| #include "api/units/timestamp.h" |
| #include "test/explicit_key_value_config.h" |
| #include "test/gtest.h" |
| |
| namespace webrtc { |
| namespace { |
| |
| using ::webrtc::test::ExplicitKeyValueConfig; |
| |
| constexpr Timestamp kInitialTime = Timestamp::Millis(123'456); |
| |
| constexpr TimeDelta kMinBwePeriod = TimeDelta::Seconds(2); |
| constexpr TimeDelta kDefaultPeriod = TimeDelta::Seconds(3); |
| constexpr TimeDelta kMaxBwePeriod = TimeDelta::Seconds(50); |
| |
| // After an overuse, we back off to 85% to the received bitrate. |
| constexpr double kFractionAfterOveruse = 0.85; |
| |
| } // namespace |
| |
| TEST(AimdRateControlTest, MinNearMaxIncreaseRateOnLowBandwith) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig("")); |
| aimd_rate_control.SetEstimate(DataRate::BitsPerSec(30'000), kInitialTime); |
| EXPECT_EQ(aimd_rate_control.GetNearMaxIncreaseRateBpsPerSecond(), 4'000); |
| } |
| |
| TEST(AimdRateControlTest, NearMaxIncreaseRateIs5kbpsOn90kbpsAnd200msRtt) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig("")); |
| aimd_rate_control.SetEstimate(DataRate::BitsPerSec(90'000), kInitialTime); |
| EXPECT_EQ(aimd_rate_control.GetNearMaxIncreaseRateBpsPerSecond(), 5'000); |
| } |
| |
| TEST(AimdRateControlTest, NearMaxIncreaseRateIs5kbpsOn60kbpsAnd100msRtt) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig("")); |
| aimd_rate_control.SetEstimate(DataRate::BitsPerSec(60'000), kInitialTime); |
| aimd_rate_control.SetRtt(TimeDelta::Millis(100)); |
| EXPECT_EQ(aimd_rate_control.GetNearMaxIncreaseRateBpsPerSecond(), 5'000); |
| } |
| |
| TEST(AimdRateControlTest, GetIncreaseRateAndBandwidthPeriod) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig("")); |
| constexpr DataRate kBitrate = DataRate::BitsPerSec(300'000); |
| aimd_rate_control.SetEstimate(kBitrate, kInitialTime); |
| aimd_rate_control.Update({BandwidthUsage::kBwOverusing, kBitrate}, |
| kInitialTime); |
| EXPECT_NEAR(aimd_rate_control.GetNearMaxIncreaseRateBpsPerSecond(), 14'000, |
| 1'000); |
| EXPECT_EQ(aimd_rate_control.GetExpectedBandwidthPeriod(), kDefaultPeriod); |
| } |
| |
| TEST(AimdRateControlTest, BweLimitedByAckedBitrate) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig("")); |
| constexpr DataRate kAckedBitrate = DataRate::BitsPerSec(10'000); |
| Timestamp now = kInitialTime; |
| aimd_rate_control.SetEstimate(kAckedBitrate, now); |
| while (now - kInitialTime < TimeDelta::Seconds(20)) { |
| aimd_rate_control.Update({BandwidthUsage::kBwNormal, kAckedBitrate}, now); |
| now += TimeDelta::Millis(100); |
| } |
| ASSERT_TRUE(aimd_rate_control.ValidEstimate()); |
| EXPECT_EQ(aimd_rate_control.LatestEstimate(), |
| 1.5 * kAckedBitrate + DataRate::BitsPerSec(10'000)); |
| } |
| |
| TEST(AimdRateControlTest, BweNotLimitedByDecreasingAckedBitrate) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig("")); |
| constexpr DataRate kAckedBitrate = DataRate::BitsPerSec(10'000); |
| Timestamp now = kInitialTime; |
| aimd_rate_control.SetEstimate(kAckedBitrate, now); |
| while (now - kInitialTime < TimeDelta::Seconds(20)) { |
| aimd_rate_control.Update({BandwidthUsage::kBwNormal, kAckedBitrate}, now); |
| now += TimeDelta::Millis(100); |
| } |
| ASSERT_TRUE(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. |
| DataRate prev_estimate = aimd_rate_control.LatestEstimate(); |
| aimd_rate_control.Update({BandwidthUsage::kBwNormal, kAckedBitrate / 2}, now); |
| DataRate new_estimate = aimd_rate_control.LatestEstimate(); |
| EXPECT_EQ(new_estimate, prev_estimate); |
| EXPECT_NEAR(new_estimate.bps(), |
| (1.5 * kAckedBitrate + DataRate::BitsPerSec(10'000)).bps(), |
| 2'000); |
| } |
| |
| TEST(AimdRateControlTest, DefaultPeriodUntilFirstOveruse) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig("")); |
| aimd_rate_control.SetStartBitrate(DataRate::KilobitsPerSec(300)); |
| EXPECT_EQ(aimd_rate_control.GetExpectedBandwidthPeriod(), kDefaultPeriod); |
| aimd_rate_control.Update( |
| {BandwidthUsage::kBwOverusing, DataRate::KilobitsPerSec(280)}, |
| kInitialTime); |
| EXPECT_NE(aimd_rate_control.GetExpectedBandwidthPeriod(), kDefaultPeriod); |
| } |
| |
| TEST(AimdRateControlTest, ExpectedPeriodAfterTypicalDrop) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig("")); |
| // The rate increase at 216 kbps should be 12 kbps. If we drop from |
| // 216 + 4*12 = 264 kbps, it should take 4 seconds to recover. Since we |
| // back off to 0.85*acked_rate-5kbps, the acked bitrate needs to be 260 |
| // kbps to end up at 216 kbps. |
| constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(264'000); |
| constexpr DataRate kUpdatedBitrate = DataRate::BitsPerSec(216'000); |
| const DataRate kAckedBitrate = |
| (kUpdatedBitrate + DataRate::BitsPerSec(5'000)) / kFractionAfterOveruse; |
| Timestamp now = kInitialTime; |
| aimd_rate_control.SetEstimate(kInitialBitrate, now); |
| now += TimeDelta::Millis(100); |
| aimd_rate_control.Update({BandwidthUsage::kBwOverusing, kAckedBitrate}, now); |
| EXPECT_EQ(aimd_rate_control.LatestEstimate(), kUpdatedBitrate); |
| EXPECT_EQ(aimd_rate_control.GetNearMaxIncreaseRateBpsPerSecond(), 12'000); |
| EXPECT_EQ(aimd_rate_control.GetExpectedBandwidthPeriod(), |
| TimeDelta::Seconds(4)); |
| } |
| |
| TEST(AimdRateControlTest, BandwidthPeriodIsNotBelowMin) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig("")); |
| constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(10'000); |
| Timestamp now = kInitialTime; |
| aimd_rate_control.SetEstimate(kInitialBitrate, now); |
| now += TimeDelta::Millis(100); |
| // Make a small (1.5 kbps) bitrate drop to 8.5 kbps. |
| aimd_rate_control.Update( |
| {BandwidthUsage::kBwOverusing, kInitialBitrate - DataRate::BitsPerSec(1)}, |
| now); |
| EXPECT_EQ(aimd_rate_control.GetExpectedBandwidthPeriod(), kMinBwePeriod); |
| } |
| |
| TEST(AimdRateControlTest, BandwidthPeriodIsNotAboveMaxNoSmoothingExp) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig("")); |
| constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(10'010'000); |
| Timestamp now = kInitialTime; |
| aimd_rate_control.SetEstimate(kInitialBitrate, now); |
| now += TimeDelta::Millis(100); |
| // Make a large (10 Mbps) bitrate drop to 10 kbps. |
| const DataRate kAckedBitrate = |
| DataRate::BitsPerSec(10'000) / kFractionAfterOveruse; |
| aimd_rate_control.Update({BandwidthUsage::kBwOverusing, kAckedBitrate}, now); |
| EXPECT_EQ(aimd_rate_control.GetExpectedBandwidthPeriod(), kMaxBwePeriod); |
| } |
| |
| TEST(AimdRateControlTest, SendingRateBoundedWhenThroughputNotEstimated) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig("")); |
| constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(123'000); |
| Timestamp now = kInitialTime; |
| aimd_rate_control.Update({BandwidthUsage::kBwNormal, kInitialBitrate}, now); |
| // 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 TimeDelta kInitializationTime = TimeDelta::Seconds(5); |
| now += (kInitializationTime + TimeDelta::Millis(1)); |
| aimd_rate_control.Update({BandwidthUsage::kBwNormal, kInitialBitrate}, now); |
| for (int i = 0; i < 100; ++i) { |
| aimd_rate_control.Update({BandwidthUsage::kBwNormal, std::nullopt}, now); |
| now += TimeDelta::Millis(100); |
| } |
| EXPECT_LE(aimd_rate_control.LatestEstimate(), |
| kInitialBitrate * 1.5 + DataRate::BitsPerSec(10'000)); |
| } |
| |
| 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. |
| ExplicitKeyValueConfig field_trials( |
| "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); |
| AimdRateControl aimd_rate_control(field_trials, /*send_side=*/true); |
| Timestamp now = kInitialTime; |
| constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(123'000); |
| aimd_rate_control.SetEstimate(kInitialBitrate, now); |
| aimd_rate_control.SetInApplicationLimitedRegion(true); |
| aimd_rate_control.Update({BandwidthUsage::kBwNormal, kInitialBitrate}, now); |
| ASSERT_EQ(aimd_rate_control.LatestEstimate(), kInitialBitrate); |
| |
| for (int i = 0; i < 100; ++i) { |
| aimd_rate_control.Update({BandwidthUsage::kBwNormal, std::nullopt}, now); |
| now += TimeDelta::Millis(100); |
| } |
| EXPECT_EQ(aimd_rate_control.LatestEstimate(), kInitialBitrate); |
| } |
| |
| TEST(AimdRateControlTest, SetEstimateIncreaseBweInAlr) { |
| ExplicitKeyValueConfig field_trials( |
| "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); |
| AimdRateControl aimd_rate_control(field_trials, /*send_side=*/true); |
| constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(123'000); |
| aimd_rate_control.SetEstimate(kInitialBitrate, kInitialTime); |
| aimd_rate_control.SetInApplicationLimitedRegion(true); |
| ASSERT_EQ(aimd_rate_control.LatestEstimate(), kInitialBitrate); |
| aimd_rate_control.SetEstimate(2 * kInitialBitrate, kInitialTime); |
| EXPECT_EQ(aimd_rate_control.LatestEstimate(), 2 * kInitialBitrate); |
| } |
| |
| TEST(AimdRateControlTest, SetEstimateUpperLimitedByNetworkEstimate) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""), |
| /*send_side=*/true); |
| aimd_rate_control.SetEstimate(DataRate::BitsPerSec(300'000), kInitialTime); |
| NetworkStateEstimate network_estimate; |
| network_estimate.link_capacity_upper = DataRate::BitsPerSec(400'000); |
| aimd_rate_control.SetNetworkStateEstimate(network_estimate); |
| aimd_rate_control.SetEstimate(DataRate::BitsPerSec(500'000), kInitialTime); |
| EXPECT_EQ(aimd_rate_control.LatestEstimate(), |
| network_estimate.link_capacity_upper); |
| } |
| |
| TEST(AimdRateControlTest, |
| SetEstimateDefaultUpperLimitedByCurrentBitrateIfNetworkEstimateIsLow) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""), |
| /*send_side=*/true); |
| aimd_rate_control.SetEstimate(DataRate::BitsPerSec(500'000), kInitialTime); |
| ASSERT_EQ(aimd_rate_control.LatestEstimate(), DataRate::BitsPerSec(500'000)); |
| |
| NetworkStateEstimate network_estimate; |
| network_estimate.link_capacity_upper = DataRate::BitsPerSec(300'000); |
| aimd_rate_control.SetNetworkStateEstimate(network_estimate); |
| aimd_rate_control.SetEstimate(DataRate::BitsPerSec(700'000), kInitialTime); |
| EXPECT_EQ(aimd_rate_control.LatestEstimate(), DataRate::BitsPerSec(500'000)); |
| } |
| |
| TEST(AimdRateControlTest, |
| SetEstimateNotUpperLimitedByCurrentBitrateIfNetworkEstimateIsLowIf) { |
| AimdRateControl aimd_rate_control( |
| ExplicitKeyValueConfig( |
| "WebRTC-Bwe-EstimateBoundedIncrease/c_upper:false/"), |
| /*send_side=*/true); |
| |
| aimd_rate_control.SetEstimate(DataRate::BitsPerSec(500'000), kInitialTime); |
| ASSERT_EQ(aimd_rate_control.LatestEstimate(), DataRate::BitsPerSec(500'000)); |
| |
| NetworkStateEstimate network_estimate; |
| network_estimate.link_capacity_upper = DataRate::BitsPerSec(300'000); |
| aimd_rate_control.SetNetworkStateEstimate(network_estimate); |
| aimd_rate_control.SetEstimate(DataRate::BitsPerSec(700'000), kInitialTime); |
| EXPECT_EQ(aimd_rate_control.LatestEstimate(), DataRate::BitsPerSec(300'000)); |
| } |
| |
| TEST(AimdRateControlTest, SetEstimateLowerLimitedByNetworkEstimate) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""), |
| /*send_side=*/true); |
| NetworkStateEstimate network_estimate; |
| network_estimate.link_capacity_lower = DataRate::BitsPerSec(400'000); |
| aimd_rate_control.SetNetworkStateEstimate(network_estimate); |
| aimd_rate_control.SetEstimate(DataRate::BitsPerSec(100'000), kInitialTime); |
| // 0.85 is default backoff factor. (`beta_`) |
| EXPECT_EQ(aimd_rate_control.LatestEstimate(), |
| network_estimate.link_capacity_lower * 0.85); |
| } |
| |
| TEST(AimdRateControlTest, |
| SetEstimateIgnoredIfLowerThanNetworkEstimateAndCurrent) { |
| AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""), |
| /*send_side=*/true); |
| aimd_rate_control.SetEstimate(DataRate::KilobitsPerSec(200), kInitialTime); |
| ASSERT_EQ(aimd_rate_control.LatestEstimate().kbps(), 200); |
| NetworkStateEstimate network_estimate; |
| network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(400); |
| aimd_rate_control.SetNetworkStateEstimate(network_estimate); |
| // Ignore the next SetEstimate, since the estimate is lower than 85% of |
| // the network estimate. |
| aimd_rate_control.SetEstimate(DataRate::KilobitsPerSec(100), kInitialTime); |
| EXPECT_EQ(aimd_rate_control.LatestEstimate().kbps(), 200); |
| } |
| |
| 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. |
| ExplicitKeyValueConfig field_trials( |
| "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); |
| AimdRateControl aimd_rate_control(field_trials, /*send_side=*/true); |
| Timestamp now = kInitialTime; |
| constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(123'000); |
| aimd_rate_control.SetEstimate(kInitialBitrate, now); |
| aimd_rate_control.SetInApplicationLimitedRegion(false); |
| aimd_rate_control.Update({BandwidthUsage::kBwNormal, kInitialBitrate}, now); |
| for (int i = 0; i < 100; ++i) { |
| aimd_rate_control.Update({BandwidthUsage::kBwNormal, std::nullopt}, now); |
| now += TimeDelta::Millis(100); |
| } |
| EXPECT_GT(aimd_rate_control.LatestEstimate(), kInitialBitrate); |
| } |
| |
| TEST(AimdRateControlTest, EstimateNotLimitedByNetworkEstimateIfDisabled) { |
| ExplicitKeyValueConfig field_trials( |
| "WebRTC-Bwe-EstimateBoundedIncrease/Disabled/"); |
| AimdRateControl aimd_rate_control(field_trials, /*send_side=*/true); |
| Timestamp now = kInitialTime; |
| constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(123'000); |
| aimd_rate_control.SetEstimate(kInitialBitrate, now); |
| aimd_rate_control.SetInApplicationLimitedRegion(false); |
| NetworkStateEstimate network_estimate; |
| network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(150); |
| aimd_rate_control.SetNetworkStateEstimate(network_estimate); |
| |
| for (int i = 0; i < 100; ++i) { |
| aimd_rate_control.Update({BandwidthUsage::kBwNormal, std::nullopt}, now); |
| now += TimeDelta::Millis(100); |
| } |
| EXPECT_GT(aimd_rate_control.LatestEstimate(), |
| network_estimate.link_capacity_upper); |
| } |
| |
| } // namespace webrtc |