From c907d4f223ebb49780bdee23840a618604f317c4 Mon Sep 17 00:00:00 2001 From: Jonas Olsson Date: Wed, 11 Dec 2019 13:18:21 +0000 Subject: [PATCH] Revert "Ensure loss-based controller is always enabled." This reverts commit 60ec3703cd1f87081c0e4becde5d9ef210a6d44a. Reason for revert: Needs back-end test before always enabling. Original change's description: > Ensure loss-based controller is always enabled. > > The new default parameters are the ones that were used in the Chrome > Finch trial. The deleted unit test is invalidated by these changes. > > Bug: chromium:941413 > Change-Id: I597f4b0defaebe5bb3a6710b071fae2ee5c6f461 > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/160652 > Commit-Queue: Jonas Olsson > Reviewed-by: Sebastian Jansson > Cr-Commit-Position: refs/heads/master@{#30049} TBR=srte@webrtc.org,crodbro@webrtc.org,jonasolsson@webrtc.org # Not skipping CQ checks because original CL landed > 1 day ago. Bug: chromium:941413 Change-Id: I5da4676ad8be2569ad7eed99e954e0d0b624110b Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/161902 Reviewed-by: Jonas Olsson Commit-Queue: Jonas Olsson Cr-Commit-Position: refs/heads/master@{#30061} --- .../goog_cc_network_control_unittest.cc | 154 +++++++++++------- .../loss_based_bandwidth_estimation.cc | 13 +- .../goog_cc/loss_based_bandwidth_estimation.h | 2 + .../goog_cc/send_side_bandwidth_estimation.cc | 86 +++++++++- ...send_side_bandwidth_estimation_unittest.cc | 53 ++++++ 5 files changed, 234 insertions(+), 74 deletions(-) diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc b/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc index 1f43d5773f..c6537aa5f8 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc @@ -71,6 +71,56 @@ CallClient* CreateVideoSendingClient( s->CreateVideoStream(route->forward(), VideoStreamConfig()); return client; } + +void UpdatesTargetRateBasedOnLinkCapacity(std::string test_name = "") { + ScopedFieldTrials trial("WebRTC-SendSideBwe-WithOverhead/Enabled/"); + auto factory = CreateFeedbackOnlyFactory(); + Scenario s("googcc_unit/target_capacity" + test_name, false); + CallClientConfig config; + config.transport.cc_factory = &factory; + config.transport.rates.min_rate = DataRate::kbps(10); + config.transport.rates.max_rate = DataRate::kbps(1500); + config.transport.rates.start_rate = DataRate::kbps(300); + auto send_net = s.CreateMutableSimulationNode([](NetworkSimulationConfig* c) { + c->bandwidth = DataRate::kbps(500); + c->delay = TimeDelta::ms(100); + c->loss_rate = 0.0; + }); + auto ret_net = s.CreateMutableSimulationNode( + [](NetworkSimulationConfig* c) { c->delay = TimeDelta::ms(100); }); + StatesPrinter* truth = s.CreatePrinter( + "send.truth.txt", TimeDelta::PlusInfinity(), {send_net->ConfigPrinter()}); + + auto* client = CreateVideoSendingClient(&s, config, {send_net->node()}, + {ret_net->node()}); + + truth->PrintRow(); + s.RunFor(TimeDelta::seconds(25)); + truth->PrintRow(); + EXPECT_NEAR(client->target_rate().kbps(), 450, 100); + + send_net->UpdateConfig([](NetworkSimulationConfig* c) { + c->bandwidth = DataRate::kbps(800); + c->delay = TimeDelta::ms(100); + }); + + truth->PrintRow(); + s.RunFor(TimeDelta::seconds(20)); + truth->PrintRow(); + EXPECT_NEAR(client->target_rate().kbps(), 750, 150); + + send_net->UpdateConfig([](NetworkSimulationConfig* c) { + c->bandwidth = DataRate::kbps(100); + c->delay = TimeDelta::ms(200); + }); + ret_net->UpdateConfig( + [](NetworkSimulationConfig* c) { c->delay = TimeDelta::ms(200); }); + + truth->PrintRow(); + s.RunFor(TimeDelta::seconds(50)); + truth->PrintRow(); + EXPECT_NEAR(client->target_rate().kbps(), 90, 25); +} } // namespace class GoogCcNetworkControllerTest : public ::testing::Test { @@ -366,53 +416,7 @@ TEST_F(GoogCcNetworkControllerTest, LimitsToFloorIfRttIsHighInTrial) { } TEST_F(GoogCcNetworkControllerTest, UpdatesTargetRateBasedOnLinkCapacity) { - ScopedFieldTrials trial("WebRTC-SendSideBwe-WithOverhead/Enabled/"); - auto factory = CreateFeedbackOnlyFactory(); - Scenario s("googcc_unit/target_capacity", false); - CallClientConfig config; - config.transport.cc_factory = &factory; - config.transport.rates.min_rate = DataRate::kbps(10); - config.transport.rates.max_rate = DataRate::kbps(1500); - config.transport.rates.start_rate = DataRate::kbps(300); - auto send_net = s.CreateMutableSimulationNode([](NetworkSimulationConfig* c) { - c->bandwidth = DataRate::kbps(500); - c->delay = TimeDelta::ms(100); - c->loss_rate = 0.0; - }); - auto ret_net = s.CreateMutableSimulationNode( - [](NetworkSimulationConfig* c) { c->delay = TimeDelta::ms(100); }); - StatesPrinter* truth = s.CreatePrinter( - "send.truth.txt", TimeDelta::PlusInfinity(), {send_net->ConfigPrinter()}); - - auto* client = CreateVideoSendingClient(&s, config, {send_net->node()}, - {ret_net->node()}); - - truth->PrintRow(); - s.RunFor(TimeDelta::seconds(25)); - truth->PrintRow(); - EXPECT_NEAR(client->target_rate().kbps(), 450, 100); - - send_net->UpdateConfig([](NetworkSimulationConfig* c) { - c->bandwidth = DataRate::kbps(800); - c->delay = TimeDelta::ms(100); - }); - - truth->PrintRow(); - s.RunFor(TimeDelta::seconds(20)); - truth->PrintRow(); - EXPECT_NEAR(client->target_rate().kbps(), 750, 150); - - send_net->UpdateConfig([](NetworkSimulationConfig* c) { - c->bandwidth = DataRate::kbps(100); - c->delay = TimeDelta::ms(200); - }); - ret_net->UpdateConfig( - [](NetworkSimulationConfig* c) { c->delay = TimeDelta::ms(200); }); - - truth->PrintRow(); - s.RunFor(TimeDelta::seconds(50)); - truth->PrintRow(); - EXPECT_NEAR(client->target_rate().kbps(), 90, 25); + UpdatesTargetRateBasedOnLinkCapacity(); } TEST_F(GoogCcNetworkControllerTest, StableEstimateDoesNotVaryInSteadyState) { @@ -452,7 +456,16 @@ TEST_F(GoogCcNetworkControllerTest, StableEstimateDoesNotVaryInSteadyState) { EXPECT_GE(min_stable_target / max_stable_target, min_target / max_target); } -TEST_F(GoogCcNetworkControllerTest, DoesModestBackoffToHighLoss) { +TEST_F(GoogCcNetworkControllerTest, + LossBasedControlUpdatesTargetRateBasedOnLinkCapacity) { + ScopedFieldTrials trial("WebRTC-Bwe-LossBasedControl/Enabled/"); + // TODO(srte): Should the behavior be unaffected at low loss rates? + UpdatesTargetRateBasedOnLinkCapacity("_loss_based"); +} + +TEST_F(GoogCcNetworkControllerTest, + LossBasedControlDoesModestBackoffToHighLoss) { + ScopedFieldTrials trial("WebRTC-Bwe-LossBasedControl/Enabled/"); Scenario s("googcc_unit/high_loss_channel", false); CallClientConfig config; config.transport.rates.min_rate = DataRate::kbps(10); @@ -469,11 +482,12 @@ TEST_F(GoogCcNetworkControllerTest, DoesModestBackoffToHighLoss) { auto* client = CreateVideoSendingClient(&s, config, {send_net}, {ret_net}); s.RunFor(TimeDelta::seconds(120)); + // Without LossBasedControl trial, bandwidth drops to ~10 kbps. EXPECT_GT(client->target_rate().kbps(), 100); } -TEST_F(GoogCcNetworkControllerTest, RecoversAfterCrossInducedLoss) { - Scenario s("googcc_unit/cross_loss_based", false); +DataRate AverageBitrateAfterCrossInducedLoss(std::string name) { + Scenario s(name, false); NetworkSimulationConfig net_conf; net_conf.bandwidth = DataRate::kbps(1000); net_conf.delay = TimeDelta::ms(100); @@ -499,16 +513,33 @@ TEST_F(GoogCcNetworkControllerTest, RecoversAfterCrossInducedLoss) { s.net()->StopCrossTraffic(tcp_traffic); s.RunFor(TimeDelta::seconds(20)); } - DataRate average_bitrate = - DataSize::bytes( - video->receive()->GetStats().rtp_stats.packet_counter.TotalBytes()) / - s.TimeSinceStart(); - - // We recover bitrate when subject to loss spikes from cross traffic. - RTC_DCHECK_GE(average_bitrate, DataRate::kbps(720)); + return DataSize::bytes(video->receive() + ->GetStats() + .rtp_stats.packet_counter.TotalBytes()) / + s.TimeSinceStart(); } -TEST_F(GoogCcNetworkControllerTest, CapsRateAtModerateLoss) { +TEST_F(GoogCcNetworkControllerTest, + NoLossBasedRecoversSlowerAfterCrossInducedLoss) { + // This test acts as a reference for the test below, showing that wihtout the + // trial, we have worse behavior. + DataRate average_bitrate = + AverageBitrateAfterCrossInducedLoss("googcc_unit/no_cross_loss_based"); + RTC_DCHECK_LE(average_bitrate, DataRate::kbps(650)); +} + +TEST_F(GoogCcNetworkControllerTest, + LossBasedRecoversFasterAfterCrossInducedLoss) { + // We recover bitrate better when subject to loss spikes from cross traffic + // when loss based controller is used. + ScopedFieldTrials trial("WebRTC-Bwe-LossBasedControl/Enabled/"); + DataRate average_bitrate = + AverageBitrateAfterCrossInducedLoss("googcc_unit/cross_loss_based"); + RTC_DCHECK_GE(average_bitrate, DataRate::kbps(750)); +} + +TEST_F(GoogCcNetworkControllerTest, LossBasedEstimatorCapsRateAtModerateLoss) { + ScopedFieldTrials trial("WebRTC-Bwe-LossBasedControl/Enabled/"); Scenario s("googcc_unit/moderate_loss_channel", false); CallClientConfig config; config.transport.rates.min_rate = DataRate::kbps(10); @@ -529,11 +560,13 @@ TEST_F(GoogCcNetworkControllerTest, CapsRateAtModerateLoss) { s.CreateVideoStream(route->forward(), VideoStreamConfig()); // Allow the controller to stabilize at the lower bitrate. s.RunFor(TimeDelta::seconds(1)); + // This increase in capacity would cause the target bitrate to increase to + // over 4000 kbps without LossBasedControl. send_net->UpdateConfig( [](NetworkSimulationConfig* c) { c->bandwidth = DataRate::kbps(5000); }); s.RunFor(TimeDelta::seconds(20)); - // The bitrate will not increase over 2500 kbps since we have detected - // moderate loss. + // Using LossBasedControl, the bitrate will not increase over 2500 kbps since + // we have detected moderate loss. EXPECT_LT(client->target_rate().kbps(), 2500); } @@ -662,6 +695,7 @@ TEST_F(GoogCcNetworkControllerTest, } TEST_F(GoogCcNetworkControllerTest, NoBandwidthTogglingInLossControlTrial) { + ScopedFieldTrials trial("WebRTC-Bwe-LossBasedControl/Enabled/"); Scenario s("googcc_unit/no_toggling"); auto* send_net = s.CreateSimulationNode([&](NetworkSimulationConfig* c) { c->bandwidth = DataRate::kbps(2000); diff --git a/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc b/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc index 8add0a2b55..c39ae21ef0 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc +++ b/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc @@ -74,19 +74,20 @@ double ExponentialUpdate(TimeDelta window, TimeDelta interval) { } // namespace LossBasedControlConfig::LossBasedControlConfig() - : min_increase_factor("min_incr", 1.02), + : enabled(field_trial::IsEnabled(kBweLossBasedControl)), + min_increase_factor("min_incr", 1.02), max_increase_factor("max_incr", 1.08), increase_low_rtt("incr_low_rtt", TimeDelta::ms(200)), increase_high_rtt("incr_high_rtt", TimeDelta::ms(800)), - decrease_factor("decr", 0.85), + decrease_factor("decr", 0.99), loss_window("loss_win", TimeDelta::ms(800)), loss_max_window("loss_max_win", TimeDelta::ms(800)), acknowledged_rate_max_window("ackrate_max_win", TimeDelta::ms(800)), increase_offset("incr_offset", DataRate::bps(1000)), - loss_bandwidth_balance_increase("balance_incr", DataRate::kbps(5)), - loss_bandwidth_balance_decrease("balance_decr", DataRate::kbps(24)), - loss_bandwidth_balance_exponent("exponent", 0.7), - allow_resets("resets", true), + loss_bandwidth_balance_increase("balance_incr", DataRate::kbps(0.5)), + loss_bandwidth_balance_decrease("balance_decr", DataRate::kbps(4)), + loss_bandwidth_balance_exponent("exponent", 0.5), + allow_resets("resets", false), decrease_interval("decr_intvl", TimeDelta::ms(300)), loss_report_timeout("timeout", TimeDelta::ms(6000)) { std::string trial_string = field_trial::FindFullName(kBweLossBasedControl); diff --git a/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h b/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h index 76a7ea5960..b63363cadd 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h +++ b/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h @@ -26,6 +26,7 @@ struct LossBasedControlConfig { LossBasedControlConfig(const LossBasedControlConfig&); LossBasedControlConfig& operator=(const LossBasedControlConfig&) = default; ~LossBasedControlConfig(); + bool enabled; FieldTrialParameter min_increase_factor; FieldTrialParameter max_increase_factor; FieldTrialParameter increase_low_rtt; @@ -53,6 +54,7 @@ class LossBasedBandwidthEstimation { Timestamp at_time); void MaybeReset(DataRate bitrate); void SetInitialBitrate(DataRate bitrate); + bool Enabled() const { return config_.enabled; } void UpdateLossStatistics(const std::vector& packet_results, Timestamp at_time); DataRate GetEstimate() const { return loss_based_bitrate_; } diff --git a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc index e47c889395..c16c83eabb 100644 --- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc +++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc @@ -28,6 +28,7 @@ namespace webrtc { namespace { constexpr TimeDelta kBweIncreaseInterval = TimeDelta::Millis<1000>(); +constexpr TimeDelta kBweDecreaseInterval = TimeDelta::Millis<300>(); constexpr TimeDelta kStartPhase = TimeDelta::Millis<2000>(); constexpr TimeDelta kBweConverganceTime = TimeDelta::Millis<20000>(); constexpr int kLimitNumPackets = 20; @@ -271,8 +272,9 @@ void SendSideBandwidthEstimation::SetSendBitrate(DataRate bitrate, RTC_DCHECK_GT(bitrate, DataRate::Zero()); // Reset to avoid being capped by the estimate. delay_based_limit_ = DataRate::PlusInfinity(); - loss_based_bandwidth_estimation_.MaybeReset(bitrate); - + if (loss_based_bandwidth_estimation_.Enabled()) { + loss_based_bandwidth_estimation_.MaybeReset(bitrate); + } UpdateTargetBitrate(bitrate, at_time); // Clear last sent bitrate history so the new value can be used directly // and not capped. @@ -323,7 +325,7 @@ void SendSideBandwidthEstimation::SetAcknowledgedRate( absl::optional acknowledged_rate, Timestamp at_time) { acknowledged_rate_ = acknowledged_rate; - if (acknowledged_rate) { + if (acknowledged_rate && loss_based_bandwidth_estimation_.Enabled()) { loss_based_bandwidth_estimation_.UpdateAcknowledgedBitrate( *acknowledged_rate, at_time); } @@ -331,8 +333,10 @@ void SendSideBandwidthEstimation::SetAcknowledgedRate( void SendSideBandwidthEstimation::IncomingPacketFeedbackVector( const TransportPacketsFeedback& report) { - loss_based_bandwidth_estimation_.UpdateLossStatistics(report.packet_feedbacks, - report.feedback_time); + if (loss_based_bandwidth_estimation_.Enabled()) { + loss_based_bandwidth_estimation_.UpdateLossStatistics( + report.packet_feedbacks, report.feedback_time); + } } void SendSideBandwidthEstimation::UpdatePacketsLost(int packets_lost, @@ -436,11 +440,18 @@ void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) { new_bitrate = std::max(receiver_limit_, new_bitrate); if (delay_based_limit_.IsFinite()) new_bitrate = std::max(delay_based_limit_, new_bitrate); - loss_based_bandwidth_estimation_.SetInitialBitrate(new_bitrate); + if (loss_based_bandwidth_estimation_.Enabled()) { + loss_based_bandwidth_estimation_.SetInitialBitrate(new_bitrate); + } if (new_bitrate != current_target_) { min_bitrate_history_.clear(); - min_bitrate_history_.push_back(std::make_pair(at_time, new_bitrate)); + if (loss_based_bandwidth_estimation_.Enabled()) { + min_bitrate_history_.push_back(std::make_pair(at_time, new_bitrate)); + } else { + min_bitrate_history_.push_back( + std::make_pair(at_time, current_target_)); + } UpdateTargetBitrate(new_bitrate, at_time); return; } @@ -453,10 +464,68 @@ void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) { return; } + if (loss_based_bandwidth_estimation_.Enabled()) { loss_based_bandwidth_estimation_.Update( at_time, min_bitrate_history_.front().second, last_round_trip_time_); DataRate new_bitrate = MaybeRampupOrBackoff(current_target_, at_time); UpdateTargetBitrate(new_bitrate, at_time); + return; + } + + TimeDelta time_since_loss_packet_report = at_time - last_loss_packet_report_; + if (time_since_loss_packet_report < 1.2 * kMaxRtcpFeedbackInterval) { + // We only care about loss above a given bitrate threshold. + float loss = last_fraction_loss_ / 256.0f; + // We only make decisions based on loss when the bitrate is above a + // threshold. This is a crude way of handling loss which is uncorrelated + // to congestion. + if (current_target_ < bitrate_threshold_ || loss <= low_loss_threshold_) { + // Loss < 2%: Increase rate by 8% of the min bitrate in the last + // kBweIncreaseInterval. + // Note that by remembering the bitrate over the last second one can + // rampup up one second faster than if only allowed to start ramping + // at 8% per second rate now. E.g.: + // If sending a constant 100kbps it can rampup immediately to 108kbps + // whenever a receiver report is received with lower packet loss. + // If instead one would do: current_bitrate_ *= 1.08^(delta time), + // it would take over one second since the lower packet loss to achieve + // 108kbps. + DataRate new_bitrate = + DataRate::bps(min_bitrate_history_.front().second.bps() * 1.08 + 0.5); + + // Add 1 kbps extra, just to make sure that we do not get stuck + // (gives a little extra increase at low rates, negligible at higher + // rates). + new_bitrate += DataRate::bps(1000); + UpdateTargetBitrate(new_bitrate, at_time); + return; + } else if (current_target_ > bitrate_threshold_) { + if (loss <= high_loss_threshold_) { + // Loss between 2% - 10%: Do nothing. + } else { + // Loss > 10%: Limit the rate decreases to once a kBweDecreaseInterval + // + rtt. + if (!has_decreased_since_last_fraction_loss_ && + (at_time - time_last_decrease_) >= + (kBweDecreaseInterval + last_round_trip_time_)) { + time_last_decrease_ = at_time; + + // Reduce rate: + // newRate = rate * (1 - 0.5*lossRate); + // where packetLoss = 256*lossRate; + DataRate new_bitrate = + DataRate::bps((current_target_.bps() * + static_cast(512 - last_fraction_loss_)) / + 512.0); + has_decreased_since_last_fraction_loss_ = true; + UpdateTargetBitrate(new_bitrate, at_time); + return; + } + } + } + } + // TODO(srte): This is likely redundant in most cases. + ApplyTargetLimits(at_time); } void SendSideBandwidthEstimation::UpdatePropagationRtt( @@ -511,7 +580,8 @@ DataRate SendSideBandwidthEstimation::MaybeRampupOrBackoff(DataRate new_bitrate, DataRate SendSideBandwidthEstimation::GetUpperLimit() const { DataRate upper_limit = std::min(delay_based_limit_, receiver_limit_); upper_limit = std::min(upper_limit, max_bitrate_configured_); - if (loss_based_bandwidth_estimation_.GetEstimate() > DataRate::Zero()) { + if (loss_based_bandwidth_estimation_.Enabled() && + loss_based_bandwidth_estimation_.GetEstimate() > DataRate::Zero()) { upper_limit = std::min(upper_limit, loss_based_bandwidth_estimation_.GetEstimate()); } diff --git a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc index 9c3aeba7c5..710c71f9cd 100644 --- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc +++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc @@ -79,6 +79,59 @@ TEST(SendSideBweTest, InitialDelayBasedBweWithProbing) { TestProbing(true); } +TEST(SendSideBweTest, DoesntReapplyBitrateDecreaseWithoutFollowingRemb) { + MockRtcEventLog event_log; + EXPECT_CALL(event_log, LogProxy(LossBasedBweUpdateWithBitrateOnly())) + .Times(1); + EXPECT_CALL(event_log, + LogProxy(LossBasedBweUpdateWithBitrateAndLossFraction())) + .Times(1); + SendSideBandwidthEstimation bwe(&event_log); + static const int kMinBitrateBps = 100000; + static const int kInitialBitrateBps = 1000000; + int64_t now_ms = 1000; + bwe.SetMinMaxBitrate(DataRate::bps(kMinBitrateBps), DataRate::bps(1500000)); + bwe.SetSendBitrate(DataRate::bps(kInitialBitrateBps), Timestamp::ms(now_ms)); + + static const uint8_t kFractionLoss = 128; + static const int64_t kRttMs = 50; + now_ms += 10000; + + EXPECT_EQ(kInitialBitrateBps, bwe.target_rate().bps()); + EXPECT_EQ(0, bwe.fraction_loss()); + EXPECT_EQ(0, bwe.round_trip_time().ms()); + + // Signal heavy loss to go down in bitrate. + bwe.UpdatePacketsLost(/*packets_lost=*/50, /*number_of_packets=*/100, + Timestamp::ms(now_ms)); + bwe.UpdateRtt(TimeDelta::ms(kRttMs), Timestamp::ms(now_ms)); + + // Trigger an update 2 seconds later to not be rate limited. + now_ms += 1000; + bwe.UpdateEstimate(Timestamp::ms(now_ms)); + EXPECT_LT(bwe.target_rate().bps(), kInitialBitrateBps); + // Verify that the obtained bitrate isn't hitting the min bitrate, or this + // test doesn't make sense. If this ever happens, update the thresholds or + // loss rates so that it doesn't hit min bitrate after one bitrate update. + EXPECT_GT(bwe.target_rate().bps(), kMinBitrateBps); + EXPECT_EQ(kFractionLoss, bwe.fraction_loss()); + EXPECT_EQ(kRttMs, bwe.round_trip_time().ms()); + + // Triggering an update shouldn't apply further downgrade nor upgrade since + // there's no intermediate receiver block received indicating whether this is + // currently good or not. + int last_bitrate_bps = bwe.target_rate().bps(); + // Trigger an update 2 seconds later to not be rate limited (but it still + // shouldn't update). + now_ms += 1000; + bwe.UpdateEstimate(Timestamp::ms(now_ms)); + + EXPECT_EQ(last_bitrate_bps, bwe.target_rate().bps()); + // The old loss rate should still be applied though. + EXPECT_EQ(kFractionLoss, bwe.fraction_loss()); + EXPECT_EQ(kRttMs, bwe.round_trip_time().ms()); +} + TEST(SendSideBweTest, SettingSendBitrateOverridesDelayBasedEstimate) { ::testing::NiceMock event_log; SendSideBandwidthEstimation bwe(&event_log);