From 4ceea6584839e563ec992db0c2e4f5e081157c57 Mon Sep 17 00:00:00 2001 From: Diep Bui Date: Thu, 17 Mar 2022 11:59:47 +0000 Subject: [PATCH] Integrate trendline estimator into loss based bwe v2. Bug: webrtc:12707 Change-Id: I510d3799c14599344d1714178e42b29e7c0c06d7 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/254380 Reviewed-by: Per Kjellander Commit-Queue: Diep Bui Cr-Commit-Position: refs/heads/main@{#36236} --- .../congestion_controller/goog_cc/BUILD.gn | 3 + .../goog_cc/delay_based_bwe.cc | 31 +-- .../goog_cc/delay_based_bwe.h | 1 + .../goog_cc/goog_cc_network_control.cc | 7 +- .../goog_cc/loss_based_bwe_v2.cc | 138 ++++++++-- .../goog_cc/loss_based_bwe_v2.h | 27 +- .../goog_cc/loss_based_bwe_v2_test.cc | 242 ++++++++++++++++-- .../goog_cc/send_side_bandwidth_estimation.cc | 20 +- .../goog_cc/send_side_bandwidth_estimation.h | 9 +- ...send_side_bandwidth_estimation_unittest.cc | 10 +- 10 files changed, 410 insertions(+), 78 deletions(-) diff --git a/modules/congestion_controller/goog_cc/BUILD.gn b/modules/congestion_controller/goog_cc/BUILD.gn index 9aafedbcb4..2c50c32759 100644 --- a/modules/congestion_controller/goog_cc/BUILD.gn +++ b/modules/congestion_controller/goog_cc/BUILD.gn @@ -153,6 +153,7 @@ rtc_library("loss_based_bwe_v2") { ] deps = [ "../../../api:array_view", + "../../../api:network_state_predictor_api", "../../../api/transport:network_control", "../../../api/transport:webrtc_key_value_config", "../../../api/units:data_rate", @@ -195,6 +196,7 @@ rtc_library("send_side_bwe") { deps = [ ":loss_based_bwe_v1", ":loss_based_bwe_v2", + "../../../api:network_state_predictor_api", "../../../api/rtc_event_log", "../../../api/transport:network_control", "../../../api/transport:webrtc_key_value_config", @@ -325,6 +327,7 @@ if (rtc_include_tests) { ":probe_controller", ":pushback_controller", ":send_side_bwe", + "../../../api:network_state_predictor_api", "../../../api/rtc_event_log", "../../../api/test/network_emulation", "../../../api/test/network_emulation:create_cross_traffic", diff --git a/modules/congestion_controller/goog_cc/delay_based_bwe.cc b/modules/congestion_controller/goog_cc/delay_based_bwe.cc index 01dfbc44e4..95d98b2675 100644 --- a/modules/congestion_controller/goog_cc/delay_based_bwe.cc +++ b/modules/congestion_controller/goog_cc/delay_based_bwe.cc @@ -181,23 +181,22 @@ void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback, } DataSize packet_size = packet_feedback.sent_packet.size; - TimeDelta send_delta = TimeDelta::Zero(); - TimeDelta recv_delta = TimeDelta::Zero(); - int size_delta = 0; + TimeDelta send_delta = TimeDelta::Zero(); + TimeDelta recv_delta = TimeDelta::Zero(); + int size_delta = 0; - InterArrivalDelta* inter_arrival_for_packet = - (separate_audio_.enabled && packet_feedback.sent_packet.audio) - ? video_inter_arrival_delta_.get() - : audio_inter_arrival_delta_.get(); - bool calculated_deltas = inter_arrival_for_packet->ComputeDeltas( - packet_feedback.sent_packet.send_time, packet_feedback.receive_time, - at_time, packet_size.bytes(), &send_delta, &recv_delta, &size_delta); + InterArrivalDelta* inter_arrival_for_packet = + (separate_audio_.enabled && packet_feedback.sent_packet.audio) + ? video_inter_arrival_delta_.get() + : audio_inter_arrival_delta_.get(); + bool calculated_deltas = inter_arrival_for_packet->ComputeDeltas( + packet_feedback.sent_packet.send_time, packet_feedback.receive_time, + at_time, packet_size.bytes(), &send_delta, &recv_delta, &size_delta); - delay_detector_for_packet->Update( - recv_delta.ms(), send_delta.ms(), - packet_feedback.sent_packet.send_time.ms(), - packet_feedback.receive_time.ms(), packet_size.bytes(), - calculated_deltas); + delay_detector_for_packet->Update(recv_delta.ms(), send_delta.ms(), + packet_feedback.sent_packet.send_time.ms(), + packet_feedback.receive_time.ms(), + packet_size.bytes(), calculated_deltas); } DataRate DelayBasedBwe::TriggerOveruse(Timestamp at_time, @@ -266,6 +265,8 @@ DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate( prev_bitrate_ = bitrate; prev_state_ = detector_state; } + + result.delay_detector_state = detector_state; return result; } diff --git a/modules/congestion_controller/goog_cc/delay_based_bwe.h b/modules/congestion_controller/goog_cc/delay_based_bwe.h index 7823f77abe..d37e05f8dd 100644 --- a/modules/congestion_controller/goog_cc/delay_based_bwe.h +++ b/modules/congestion_controller/goog_cc/delay_based_bwe.h @@ -56,6 +56,7 @@ class DelayBasedBwe { DataRate target_bitrate = DataRate::Zero(); bool recovered_from_overuse; bool backoff_in_alr; + BandwidthUsage delay_detector_state; }; explicit DelayBasedBwe(const WebRtcKeyValueConfig* key_value_config, diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc index ba656be234..8ae7f40cb2 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc @@ -495,7 +495,6 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback( auto acknowledged_bitrate = acknowledged_bitrate_estimator_->bitrate(); bandwidth_estimation_->SetAcknowledgedRate(acknowledged_bitrate, report.feedback_time); - bandwidth_estimation_->IncomingPacketFeedbackVector(report); for (const auto& feedback : report.SortedByReceiveTime()) { if (feedback.sent_packet.pacing_info.probe_cluster_id != PacedPacketInfo::kNotAProbe) { @@ -553,11 +552,13 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback( } // Since SetSendBitrate now resets the delay-based estimate, we have to // call UpdateDelayBasedEstimate after SetSendBitrate. - bandwidth_estimation_->UpdateDelayBasedEstimate(report.feedback_time, - result.target_bitrate); + bandwidth_estimation_->UpdateDelayBasedEstimate( + report.feedback_time, result.target_bitrate, + result.delay_detector_state); // Update the estimate in the ProbeController, in case we want to probe. MaybeTriggerOnNetworkChanged(&update, report.feedback_time); } + bandwidth_estimation_->UpdateLossBasedEstimatorFromFeedbackVector(report); recovered_from_overuse = result.recovered_from_overuse; backoff_in_alr = result.backoff_in_alr; diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc index 44041143bf..147c2107ab 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc @@ -21,6 +21,7 @@ #include "absl/algorithm/container.h" #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/network_state_predictor.h" #include "api/transport/network_types.h" #include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" @@ -89,18 +90,11 @@ double GetLossProbability(double inherent_loss, << ToString(loss_limited_bandwidth); } - // We approximate the loss model - // loss_probability = inherent_loss + (1 - inherent_loss) * - // max(0, sending_rate - bandwidth) / sending_rate - // by - // loss_probability = inherent_loss + - // max(0, sending_rate - bandwidth) / sending_rate - // as it allows for simpler calculations and makes little difference in - // practice. double loss_probability = inherent_loss; if (IsValid(sending_rate) && IsValid(loss_limited_bandwidth) && (sending_rate > loss_limited_bandwidth)) { - loss_probability += (sending_rate - loss_limited_bandwidth) / sending_rate; + loss_probability += (1 - inherent_loss) * + (sending_rate - loss_limited_bandwidth) / sending_rate; } return std::min(std::max(loss_probability, 1.0e-6), 1.0 - 1.0e-6); } @@ -138,7 +132,8 @@ bool LossBasedBweV2::IsReady() const { num_observations_ > 0; } -DataRate LossBasedBweV2::GetBandwidthEstimate() const { +DataRate LossBasedBweV2::GetBandwidthEstimate( + DataRate delay_based_limit) const { if (!IsReady()) { if (!IsEnabled()) { RTC_LOG(LS_WARNING) @@ -156,8 +151,14 @@ DataRate LossBasedBweV2::GetBandwidthEstimate() const { return DataRate::PlusInfinity(); } - return std::min(current_estimate_.loss_limited_bandwidth, - GetInstantUpperBound()); + if (delay_based_limit.IsFinite()) { + return std::min({current_estimate_.loss_limited_bandwidth, + GetInstantUpperBound(), + delay_based_limit * config_->delay_based_limit_factor}); + } else { + return std::min(current_estimate_.loss_limited_bandwidth, + GetInstantUpperBound()); + } } void LossBasedBweV2::SetAcknowledgedBitrate(DataRate acknowledged_bitrate) { @@ -180,7 +181,8 @@ void LossBasedBweV2::SetBandwidthEstimate(DataRate bandwidth_estimate) { void LossBasedBweV2::UpdateBandwidthEstimate( rtc::ArrayView packet_results, - DataRate delay_based_estimate) { + DataRate delay_based_estimate, + BandwidthUsage delay_detector_state) { if (!IsEnabled()) { RTC_LOG(LS_WARNING) << "The estimator must be enabled before it can be used."; @@ -192,7 +194,7 @@ void LossBasedBweV2::UpdateBandwidthEstimate( return; } - if (!PushBackObservation(packet_results)) { + if (!PushBackObservation(packet_results, delay_detector_state)) { return; } @@ -264,6 +266,15 @@ absl::optional LossBasedBweV2::CreateConfig( "InstantUpperBoundLossOffset", 0.05); FieldTrialParameter temporal_weight_factor("TemporalWeightFactor", 0.99); + FieldTrialParameter bandwidth_backoff_lower_bound_factor( + "BwBackoffLowerBoundFactor", 0.85); + FieldTrialParameter trendline_integration_enabled( + "TrendlineIntegrationEnabled", false); + FieldTrialParameter delay_based_limit_factor("DelayBasedLimitFactor", + 1.0); + FieldTrialParameter trendline_window_size("TrendlineWindowSize", 20); + FieldTrialParameter backoff_when_overusing("BackoffWhenOverusing", + false); if (key_value_config) { ParseFieldTrial({&enabled, @@ -287,7 +298,12 @@ absl::optional LossBasedBweV2::CreateConfig( &instant_upper_bound_temporal_weight_factor, &instant_upper_bound_bandwidth_balance, &instant_upper_bound_loss_offset, - &temporal_weight_factor}, + &temporal_weight_factor, + &bandwidth_backoff_lower_bound_factor, + &trendline_integration_enabled, + &delay_based_limit_factor, + &trendline_window_size, + &backoff_when_overusing}, key_value_config->Lookup("WebRTC-Bwe-LossBasedBweV2")); } @@ -328,6 +344,12 @@ absl::optional LossBasedBweV2::CreateConfig( config->instant_upper_bound_loss_offset = instant_upper_bound_loss_offset.Get(); config->temporal_weight_factor = temporal_weight_factor.Get(); + config->bandwidth_backoff_lower_bound_factor = + bandwidth_backoff_lower_bound_factor.Get(); + config->trendline_integration_enabled = trendline_integration_enabled.Get(); + config->delay_based_limit_factor = delay_based_limit_factor.Get(); + config->trendline_window_size = trendline_window_size.Get(); + config->backoff_when_overusing = backoff_when_overusing.Get(); return config; } @@ -470,7 +492,24 @@ bool LossBasedBweV2::IsConfigValid() const { << config_->temporal_weight_factor; valid = false; } - + if (config_->bandwidth_backoff_lower_bound_factor > 1.0) { + RTC_LOG(LS_WARNING) + << "The bandwidth backoff lower bound factor must not be greater than " + "1: " + << config_->bandwidth_backoff_lower_bound_factor; + valid = false; + } + if (config_->delay_based_limit_factor < 1.0) { + RTC_LOG(LS_WARNING) + << "The delay based limit factor must not be less than 1: " + << config_->delay_based_limit_factor; + valid = false; + } + if (config_->trendline_window_size < 2) { + RTC_LOG(LS_WARNING) << "The trendline window size must be at least 2: " + << config_->trendline_window_size; + valid = false; + } return valid; } @@ -521,19 +560,35 @@ DataRate LossBasedBweV2::GetCandidateBandwidthUpperBound() const { std::vector LossBasedBweV2::GetCandidates( DataRate delay_based_estimate) const { std::vector bandwidths; + bool can_increase_bitrate = TrendlineEsimateAllowBitrateIncrease(); + bool can_decrease_bitrate = TrendlineEsimateAllowBitrateDecrease(); for (double candidate_factor : config_->candidate_factors) { + if (!can_increase_bitrate && candidate_factor >= 1.0) { + // When the network is overusing, the estimate is forced to decrease + // even if there is no loss yet. + if (candidate_factor > 1 || config_->backoff_when_overusing) { + continue; + } + } + if (!can_decrease_bitrate && candidate_factor < 1.0) { + continue; + } bandwidths.push_back(candidate_factor * current_estimate_.loss_limited_bandwidth); } if (acknowledged_bitrate_.has_value() && - config_->append_acknowledged_rate_candidate) { - bandwidths.push_back(*acknowledged_bitrate_); + config_->append_acknowledged_rate_candidate && can_decrease_bitrate) { + bandwidths.push_back(*acknowledged_bitrate_ * + config_->bandwidth_backoff_lower_bound_factor); } if (IsValid(delay_based_estimate) && config_->append_delay_based_estimate_candidate) { - bandwidths.push_back(delay_based_estimate); + if (can_increase_bitrate && + delay_based_estimate > current_estimate_.loss_limited_bandwidth) { + bandwidths.push_back(delay_based_estimate); + } } const DataRate candidate_bandwidth_upper_bound = @@ -704,8 +759,47 @@ void LossBasedBweV2::NewtonsMethodUpdate( } } +bool LossBasedBweV2::TrendlineEsimateAllowBitrateDecrease() const { + if (!config_->trendline_integration_enabled) { + return true; + } + + for (const auto& detector_state : delay_detector_states_) { + if (detector_state == BandwidthUsage::kBwOverusing) { + return true; + } + } + + for (const auto& detector_state : delay_detector_states_) { + if (detector_state == BandwidthUsage::kBwUnderusing) { + return false; + } + } + return true; +} + +bool LossBasedBweV2::TrendlineEsimateAllowBitrateIncrease() const { + if (!config_->trendline_integration_enabled) { + return true; + } + + for (const auto& detector_state : delay_detector_states_) { + if (detector_state == BandwidthUsage::kBwOverusing) { + return false; + } + } + return true; +} + bool LossBasedBweV2::PushBackObservation( - rtc::ArrayView packet_results) { + rtc::ArrayView packet_results, + BandwidthUsage delay_detector_state) { + delay_detector_states_.push_front(delay_detector_state); + if (static_cast(delay_detector_states_.size()) > + config_->trendline_window_size) { + delay_detector_states_.pop_back(); + } + if (packet_results.empty()) { return false; } @@ -729,7 +823,9 @@ bool LossBasedBweV2::PushBackObservation( last_send_time - last_send_time_most_recent_observation_; // Too small to be meaningful. - if (observation_duration < config_->observation_duration_lower_bound) { + if (observation_duration < config_->observation_duration_lower_bound && + (delay_detector_state == BandwidthUsage::kBwNormal || + !config_->trendline_integration_enabled)) { return false; } diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h index f764892b55..7e42b3e827 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h @@ -12,10 +12,12 @@ #define MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_V2_H_ #include +#include #include #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/network_state_predictor.h" #include "api/transport/network_types.h" #include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" @@ -42,14 +44,15 @@ class LossBasedBweV2 { bool IsReady() const; // Returns `DataRate::PlusInfinity` if no BWE can be calculated. - DataRate GetBandwidthEstimate() const; + DataRate GetBandwidthEstimate(DataRate delay_based_limit) const; void SetAcknowledgedBitrate(DataRate acknowledged_bitrate); void SetBandwidthEstimate(DataRate bandwidth_estimate); void UpdateBandwidthEstimate( rtc::ArrayView packet_results, - DataRate delay_based_estimate); + DataRate delay_based_estimate, + BandwidthUsage delay_detector_state); private: struct ChannelParameters { @@ -80,6 +83,11 @@ class LossBasedBweV2 { DataRate instant_upper_bound_bandwidth_balance = DataRate::MinusInfinity(); double instant_upper_bound_loss_offset = 0.0; double temporal_weight_factor = 0.0; + double bandwidth_backoff_lower_bound_factor = 0.0; + bool trendline_integration_enabled = false; + double delay_based_limit_factor = 1.0; + int trendline_window_size = 0; + bool backoff_when_overusing = false; }; struct Derivatives { @@ -124,9 +132,21 @@ class LossBasedBweV2 { void CalculateTemporalWeights(); void NewtonsMethodUpdate(ChannelParameters& channel_parameters) const; + // Returns true if either + // 1. At least one of states in the window is kBwOverusing, or + // 2. There are no kBwUnderusing states in the window. + bool TrendlineEsimateAllowBitrateDecrease() const; + + // Returns false if there exists an overusing state in the window. + bool TrendlineEsimateAllowBitrateIncrease() const; // Returns false if no observation was created. - bool PushBackObservation(rtc::ArrayView packet_results); + bool PushBackObservation(rtc::ArrayView packet_results, + BandwidthUsage delay_detector_state); + void UpdateTrendlineEstimator( + const std::vector& packet_feedbacks, + Timestamp at_time); + void UpdateDelayDetector(BandwidthUsage delay_detector_state); absl::optional acknowledged_bitrate_; absl::optional config_; @@ -139,6 +159,7 @@ class LossBasedBweV2 { absl::optional cached_instant_upper_bound_; std::vector instant_upper_bound_temporal_weights_; std::vector temporal_weights_; + std::deque delay_detector_states_; }; } // namespace webrtc diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc b/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc index 05334885f2..bf0a7e492e 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc @@ -12,6 +12,7 @@ #include +#include "api/network_state_predictor.h" #include "api/transport/network_types.h" #include "api/units/data_rate.h" #include "api/units/data_size.h" @@ -48,14 +49,16 @@ std::string Config(bool enabled, bool valid) { } config_string - << ",CandidateFactors:0.9|1.1,HigherBwBiasFactor:0.01," + << ",CandidateFactors:1.1|1.0|0.95,HigherBwBiasFactor:0.01," + "DelayBasedCandidate:true," "InherentLossLowerBound:0.001,InherentLossUpperBoundBwBalance:14kbps," "InherentLossUpperBoundOffset:0.9,InitialInherentLossEstimate:0.01," "NewtonIterations:2,NewtonStepSize:0.4,ObservationWindowSize:15," "SendingRateSmoothingFactor:0.01," "InstantUpperBoundTemporalWeightFactor:0.97," "InstantUpperBoundBwBalance:90kbps," - "InstantUpperBoundLossOffset:0.1,TemporalWeightFactor:0.98"; + "InstantUpperBoundLossOffset:0.1,TemporalWeightFactor:0.98," + "BackoffWhenOverusing:true"; config_string.AppendFormat( ",ObservationDurationLowerBound:%dms", @@ -133,10 +136,13 @@ TEST(LossBasedBweV2Test, BandwidthEstimateGivenInitializationAndThenFeedback) { loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback, DataRate::PlusInfinity()); + enough_feedback, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); EXPECT_TRUE(loss_based_bandwidth_estimator.IsReady()); - EXPECT_TRUE(loss_based_bandwidth_estimator.GetBandwidthEstimate().IsFinite()); + EXPECT_TRUE( + loss_based_bandwidth_estimator + .GetBandwidthEstimate(/*delay_based_limit=*/DataRate::PlusInfinity()) + .IsFinite()); } TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNoInitialization) { @@ -156,11 +162,13 @@ TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNoInitialization) { LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback, DataRate::PlusInfinity()); + enough_feedback, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady()); EXPECT_TRUE( - loss_based_bandwidth_estimator.GetBandwidthEstimate().IsPlusInfinity()); + loss_based_bandwidth_estimator + .GetBandwidthEstimate(/*delay_based_limit=*/DataRate::PlusInfinity()) + .IsPlusInfinity()); } TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNotEnoughFeedback) { @@ -186,14 +194,18 @@ TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNotEnoughFeedback) { EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady()); EXPECT_TRUE( - loss_based_bandwidth_estimator.GetBandwidthEstimate().IsPlusInfinity()); + loss_based_bandwidth_estimator + .GetBandwidthEstimate(/*delay_based_limit=*/DataRate::PlusInfinity()) + .IsPlusInfinity()); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - not_enough_feedback, DataRate::PlusInfinity()); + not_enough_feedback, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady()); EXPECT_TRUE( - loss_based_bandwidth_estimator.GetBandwidthEstimate().IsPlusInfinity()); + loss_based_bandwidth_estimator + .GetBandwidthEstimate(/*delay_based_limit=*/DataRate::PlusInfinity()) + .IsPlusInfinity()); } TEST(LossBasedBweV2Test, @@ -227,21 +239,24 @@ TEST(LossBasedBweV2Test, loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback_1, DataRate::PlusInfinity()); + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); - EXPECT_NE(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + EXPECT_NE(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); - EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback_2, DataRate::PlusInfinity()); + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); - EXPECT_NE(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + EXPECT_NE(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(600)); } @@ -279,26 +294,30 @@ TEST(LossBasedBweV2Test, loss_based_bandwidth_estimator_2.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator_1.UpdateBandwidthEstimate( - enough_feedback_1, DataRate::PlusInfinity()); + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); loss_based_bandwidth_estimator_2.UpdateBandwidthEstimate( - enough_feedback_1, DataRate::PlusInfinity()); + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); - EXPECT_EQ(loss_based_bandwidth_estimator_1.GetBandwidthEstimate(), + EXPECT_EQ(loss_based_bandwidth_estimator_1.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(660)); loss_based_bandwidth_estimator_1.SetAcknowledgedBitrate( DataRate::KilobitsPerSec(600)); - EXPECT_EQ(loss_based_bandwidth_estimator_1.GetBandwidthEstimate(), + EXPECT_EQ(loss_based_bandwidth_estimator_1.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(660)); loss_based_bandwidth_estimator_1.UpdateBandwidthEstimate( - enough_feedback_2, DataRate::PlusInfinity()); + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); loss_based_bandwidth_estimator_2.UpdateBandwidthEstimate( - enough_feedback_2, DataRate::PlusInfinity()); + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); - EXPECT_NE(loss_based_bandwidth_estimator_1.GetBandwidthEstimate(), - loss_based_bandwidth_estimator_2.GetBandwidthEstimate()); + EXPECT_NE(loss_based_bandwidth_estimator_1.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + loss_based_bandwidth_estimator_2.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity())); } TEST(LossBasedBweV2Test, @@ -324,12 +343,187 @@ TEST(LossBasedBweV2Test, loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback_no_received_packets, DataRate::PlusInfinity()); + enough_feedback_no_received_packets, DataRate::PlusInfinity(), + BandwidthUsage::kBwNormal); - EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(100)); } +// When network is overusing and flag `BackoffWhenOverusing` is true, +// the bandwidth estimate is forced to decrease even if there is no loss yet. +TEST(LossBasedBweV2Test, BandwidthEstimateDecreasesWhenOverusing) { + PacketResult enough_feedback_1[2]; + PacketResult enough_feedback_2[2]; + enough_feedback_1[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[0].sent_packet.send_time = Timestamp::Zero(); + enough_feedback_1[1].sent_packet.send_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_2[0].sent_packet.send_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[1].sent_packet.send_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_1[0].receive_time = Timestamp::PlusInfinity(); + enough_feedback_1[1].receive_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[0].receive_time = Timestamp::PlusInfinity(); + enough_feedback_2[1].receive_time = + Timestamp::Zero() + 4 * kObservationDurationLowerBound; + + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + loss_based_bandwidth_estimator.SetAcknowledgedBitrate( + DataRate::KilobitsPerSec(300)); + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), + BandwidthUsage::kBwOverusing); + EXPECT_LE(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + DataRate::KilobitsPerSec(600)); + + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + EXPECT_LE(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + DataRate::KilobitsPerSec(600)); +} + +TEST(LossBasedBweV2Test, BandwidthEstimateIncreasesWhenUnderusing) { + PacketResult enough_feedback_1[2]; + PacketResult enough_feedback_2[2]; + enough_feedback_1[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[0].sent_packet.send_time = Timestamp::Zero(); + enough_feedback_1[1].sent_packet.send_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_2[0].sent_packet.send_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[1].sent_packet.send_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_1[0].receive_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_1[1].receive_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[0].receive_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_2[1].receive_time = + Timestamp::Zero() + 4 * kObservationDurationLowerBound; + + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), + BandwidthUsage::kBwUnderusing); + EXPECT_GT(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + EXPECT_GT(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + DataRate::KilobitsPerSec(600)); +} + +TEST(LossBasedBweV2Test, + BandwidthEstimateCappedByDelayBasedEstimateWhenUnderusing) { + PacketResult enough_feedback_1[2]; + PacketResult enough_feedback_2[2]; + enough_feedback_1[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[0].sent_packet.send_time = Timestamp::Zero(); + enough_feedback_1[1].sent_packet.send_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_2[0].sent_packet.send_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[1].sent_packet.send_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_1[0].receive_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_1[1].receive_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[0].receive_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_2[1].receive_time = + Timestamp::Zero() + 4 * kObservationDurationLowerBound; + + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), + BandwidthUsage::kBwUnderusing); + EXPECT_GT(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::KilobitsPerSec(500)), + DataRate::KilobitsPerSec(500)); +} + +TEST(LossBasedBweV2Test, NotUseAckedBitrateInNormalState) { + PacketResult enough_feedback_1[2]; + PacketResult enough_feedback_2[2]; + enough_feedback_1[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[0].sent_packet.send_time = Timestamp::Zero(); + enough_feedback_1[1].sent_packet.send_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_2[0].sent_packet.send_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[1].sent_packet.send_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_1[0].receive_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_1[1].receive_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[0].receive_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_2[1].receive_time = + Timestamp::Zero() + 4 * kObservationDurationLowerBound; + + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + DataRate acked_bitrate = DataRate::KilobitsPerSec(300); + loss_based_bandwidth_estimator.SetAcknowledgedBitrate(acked_bitrate); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + EXPECT_GT(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + acked_bitrate); + + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + EXPECT_GT(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + acked_bitrate); +} + } // namespace } // namespace webrtc 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 5bb145cf20..631ef86c65 100644 --- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc +++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc @@ -17,6 +17,7 @@ #include #include "absl/strings/match.h" +#include "api/network_state_predictor.h" #include "api/rtc_event_log/rtc_event.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/transport/webrtc_key_value_config.h" @@ -229,7 +230,8 @@ SendSideBandwidthEstimation::SendSideBandwidthEstimation( bitrate_threshold_(kDefaultBitrateThreshold), loss_based_bandwidth_estimator_v1_(key_value_config), loss_based_bandwidth_estimator_v2_(key_value_config), - disable_receiver_limit_caps_only_("Disabled") { + disable_receiver_limit_caps_only_("Disabled"), + delay_detector_state_(BandwidthUsage::kBwNormal) { RTC_DCHECK(event_log); if (BweLossExperimentIsEnabled()) { uint32_t bitrate_threshold_kbps; @@ -271,6 +273,7 @@ void SendSideBandwidthEstimation::OnRouteChange() { uma_update_state_ = kNoUpdate; uma_rtt_state_ = kNoUpdate; last_rtc_event_log_ = Timestamp::MinusInfinity(); + delay_detector_state_ = BandwidthUsage::kBwNormal; } void SendSideBandwidthEstimation::SetBitrates( @@ -330,9 +333,12 @@ void SendSideBandwidthEstimation::UpdateReceiverEstimate(Timestamp at_time, ApplyTargetLimits(at_time); } -void SendSideBandwidthEstimation::UpdateDelayBasedEstimate(Timestamp at_time, - DataRate bitrate) { +void SendSideBandwidthEstimation::UpdateDelayBasedEstimate( + Timestamp at_time, + DataRate bitrate, + BandwidthUsage delay_detector_state) { link_capacity_.UpdateDelayBasedEstimate(at_time, bitrate); + delay_detector_state_ = delay_detector_state; // TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no // limitation. delay_based_limit_ = bitrate.IsZero() ? DataRate::PlusInfinity() : bitrate; @@ -356,7 +362,7 @@ void SendSideBandwidthEstimation::SetAcknowledgedRate( } } -void SendSideBandwidthEstimation::IncomingPacketFeedbackVector( +void SendSideBandwidthEstimation::UpdateLossBasedEstimatorFromFeedbackVector( const TransportPacketsFeedback& report) { if (LossBasedBandwidthEstimatorV1Enabled()) { loss_based_bandwidth_estimator_v1_.UpdateLossStatistics( @@ -364,7 +370,7 @@ void SendSideBandwidthEstimation::IncomingPacketFeedbackVector( } if (LossBasedBandwidthEstimatorV2Enabled()) { loss_based_bandwidth_estimator_v2_.UpdateBandwidthEstimate( - report.packet_feedbacks, delay_based_limit_); + report.packet_feedbacks, delay_based_limit_, delay_detector_state_); } } @@ -509,8 +515,8 @@ void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) { if (LossBasedBandwidthEstimatorV2ReadyForUse()) { DataRate new_bitrate = - loss_based_bandwidth_estimator_v2_.GetBandwidthEstimate(); - new_bitrate = std::min(new_bitrate, delay_based_limit_); + loss_based_bandwidth_estimator_v2_.GetBandwidthEstimate( + delay_based_limit_); UpdateTargetBitrate(new_bitrate, at_time); return; } diff --git a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h index f31f30f70d..bb53be873b 100644 --- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h +++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h @@ -20,6 +20,7 @@ #include #include "absl/types/optional.h" +#include "api/network_state_predictor.h" #include "api/transport/network_types.h" #include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" @@ -97,7 +98,9 @@ class SendSideBandwidthEstimation { void UpdateReceiverEstimate(Timestamp at_time, DataRate bandwidth); // Call when a new delay-based estimate is available. - void UpdateDelayBasedEstimate(Timestamp at_time, DataRate bitrate); + void UpdateDelayBasedEstimate(Timestamp at_time, + DataRate bitrate, + BandwidthUsage delay_detector_state); // Call when we receive a RTCP message with a ReceiveBlock. void UpdatePacketsLost(int64_t packets_lost, @@ -116,7 +119,8 @@ class SendSideBandwidthEstimation { int GetMinBitrate() const; void SetAcknowledgedRate(absl::optional acknowledged_rate, Timestamp at_time); - void IncomingPacketFeedbackVector(const TransportPacketsFeedback& report); + void UpdateLossBasedEstimatorFromFeedbackVector( + const TransportPacketsFeedback& report); private: friend class GoogCcStatePrinter; @@ -199,6 +203,7 @@ class SendSideBandwidthEstimation { LossBasedBandwidthEstimation loss_based_bandwidth_estimator_v1_; LossBasedBweV2 loss_based_bandwidth_estimator_v2_; FieldTrialFlag disable_receiver_limit_caps_only_; + BandwidthUsage delay_detector_state_; }; } // namespace webrtc #endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_ 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 85ce401098..e3db866cf7 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 @@ -10,6 +10,7 @@ #include "modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h" +#include "api/network_state_predictor.h" #include "api/rtc_event_log/rtc_event.h" #include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h" #include "logging/rtc_event_log/mock/mock_rtc_event_log.h" @@ -54,7 +55,8 @@ void TestProbing(bool use_delay_based) { // Initial REMB applies immediately. if (use_delay_based) { bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms), - DataRate::BitsPerSec(kRembBps)); + DataRate::BitsPerSec(kRembBps), + BandwidthUsage::kBwNormal); } else { bwe.UpdateReceiverEstimate(Timestamp::Millis(now_ms), DataRate::BitsPerSec(kRembBps)); @@ -66,7 +68,8 @@ void TestProbing(bool use_delay_based) { now_ms += 2001; if (use_delay_based) { bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms), - DataRate::BitsPerSec(kSecondRembBps)); + DataRate::BitsPerSec(kSecondRembBps), + BandwidthUsage::kBwNormal); } else { bwe.UpdateReceiverEstimate(Timestamp::Millis(now_ms), DataRate::BitsPerSec(kSecondRembBps)); @@ -157,7 +160,8 @@ TEST(SendSideBweTest, SettingSendBitrateOverridesDelayBasedEstimate) { Timestamp::Millis(now_ms)); bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms), - DataRate::BitsPerSec(kDelayBasedBitrateBps)); + DataRate::BitsPerSec(kDelayBasedBitrateBps), + BandwidthUsage::kBwNormal); bwe.UpdateEstimate(Timestamp::Millis(now_ms)); EXPECT_GE(bwe.target_rate().bps(), kInitialBitrateBps); EXPECT_LE(bwe.target_rate().bps(), kDelayBasedBitrateBps);