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 a938e936ba..46a2d4eb9b 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc @@ -164,11 +164,14 @@ LossBasedBweV2::Result LossBasedBweV2::GetLossBasedResult() const { if (IsValid(delay_based_estimate_)) { result.bandwidth_estimate = - std::min({current_estimate_.loss_limited_bandwidth, - GetInstantUpperBound(), delay_based_estimate_}); + std::max(GetInstantLowerBound(), + std::min({current_estimate_.loss_limited_bandwidth, + GetInstantUpperBound(), delay_based_estimate_})); } else { - result.bandwidth_estimate = std::min( - current_estimate_.loss_limited_bandwidth, GetInstantUpperBound()); + result.bandwidth_estimate = + std::max(GetInstantLowerBound(), + std::min(current_estimate_.loss_limited_bandwidth, + GetInstantUpperBound())); } return result; } @@ -176,6 +179,7 @@ LossBasedBweV2::Result LossBasedBweV2::GetLossBasedResult() const { void LossBasedBweV2::SetAcknowledgedBitrate(DataRate acknowledged_bitrate) { if (IsValid(acknowledged_bitrate)) { acknowledged_bitrate_ = acknowledged_bitrate; + CalculateInstantLowerBound(); } else { RTC_LOG(LS_WARNING) << "The acknowledged bitrate must be finite: " << ToString(acknowledged_bitrate); @@ -195,6 +199,7 @@ void LossBasedBweV2::SetMinMaxBitrate(DataRate min_bitrate, DataRate max_bitrate) { if (IsValid(min_bitrate)) { min_bitrate_ = min_bitrate; + CalculateInstantLowerBound(); } else { RTC_LOG(LS_WARNING) << "The min bitrate must be finite: " << ToString(min_bitrate); @@ -393,6 +398,8 @@ absl::optional LossBasedBweV2::CreateConfig( true); FieldTrialParameter use_in_start_phase("UseInStartPhase", false); FieldTrialParameter min_num_observations("MinNumObservations", 3); + FieldTrialParameter lower_bound_by_acked_rate_factor( + "LowerBoundByAckedRateFactor", 0.0); if (key_value_config) { ParseFieldTrial({&enabled, &bandwidth_rampup_upper_bound_factor, @@ -428,7 +435,8 @@ absl::optional LossBasedBweV2::CreateConfig( &slope_of_bwe_high_loss_func, ¬_use_acked_rate_in_alr, &use_in_start_phase, - &min_num_observations}, + &min_num_observations, + &lower_bound_by_acked_rate_factor}, key_value_config->Lookup("WebRTC-Bwe-LossBasedBweV2")); } @@ -488,6 +496,8 @@ absl::optional LossBasedBweV2::CreateConfig( config->not_use_acked_rate_in_alr = not_use_acked_rate_in_alr.Get(); config->use_in_start_phase = use_in_start_phase.Get(); config->min_num_observations = min_num_observations.Get(); + config->lower_bound_by_acked_rate_factor = + lower_bound_by_acked_rate_factor.Get(); return config; } @@ -673,6 +683,13 @@ bool LossBasedBweV2::IsConfigValid() const { << config_->min_num_observations; valid = false; } + if (config_->lower_bound_by_acked_rate_factor < 0.0) { + RTC_LOG(LS_WARNING) + << "The estimate lower bound by acknowledged rate factor must be " + "non-negative: " + << config_->lower_bound_by_acked_rate_factor; + valid = false; + } return valid; } @@ -913,6 +930,24 @@ void LossBasedBweV2::CalculateInstantUpperBound() { cached_instant_upper_bound_ = instant_limit; } +DataRate LossBasedBweV2::GetInstantLowerBound() const { + return cached_instant_lower_bound_.value_or(DataRate::Zero()); +} + +void LossBasedBweV2::CalculateInstantLowerBound() { + DataRate instance_lower_bound = DataRate::Zero(); + if (IsValid(acknowledged_bitrate_) && + config_->lower_bound_by_acked_rate_factor > 0.0) { + instance_lower_bound = config_->lower_bound_by_acked_rate_factor * + acknowledged_bitrate_.value(); + } + + if (IsValid(min_bitrate_)) { + instance_lower_bound = std::max(instance_lower_bound, min_bitrate_); + } + cached_instant_lower_bound_ = instance_lower_bound; +} + void LossBasedBweV2::CalculateTemporalWeights() { for (int i = 0; i < config_->observation_window_size; ++i) { temporal_weights_[i] = std::pow(config_->temporal_weight_factor, i); 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 09d4d87d1c..81c2f90200 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h @@ -113,6 +113,7 @@ class LossBasedBweV2 { bool not_use_acked_rate_in_alr = false; bool use_in_start_phase = false; int min_num_observations = 0; + double lower_bound_by_acked_rate_factor = 0.0; }; struct Derivatives { @@ -154,6 +155,8 @@ class LossBasedBweV2 { DataRate GetSendingRate(DataRate instantaneous_sending_rate) const; DataRate GetInstantUpperBound() const; void CalculateInstantUpperBound(); + DataRate GetInstantLowerBound() const; + void CalculateInstantLowerBound(); void CalculateTemporalWeights(); void NewtonsMethodUpdate(ChannelParameters& channel_parameters) const; @@ -173,6 +176,7 @@ class LossBasedBweV2 { Timestamp last_send_time_most_recent_observation_ = Timestamp::PlusInfinity(); Timestamp last_time_estimate_reduced_ = Timestamp::MinusInfinity(); absl::optional cached_instant_upper_bound_; + absl::optional cached_instant_lower_bound_; std::vector instant_upper_bound_temporal_weights_; std::vector temporal_weights_; Timestamp recovering_after_loss_timestamp_ = Timestamp::MinusInfinity(); 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 d1d5fade97..cc2b2c0e78 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 @@ -1185,7 +1185,6 @@ TEST_F(LossBasedBweV2Test, NotBackOffToAckedRateInAlr) { /*first_packet_timestamp=*/Timestamp::Zero()); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( enough_feedback_100p_loss_1, delay_based_estimate, - /*in_alr=*/true); // Make sure that the estimate decreases but higher than acked rate. @@ -1248,5 +1247,55 @@ TEST_F(LossBasedBweV2Test, ReadyToUseInStartPhase) { EXPECT_TRUE(loss_based_bandwidth_estimator.ReadyToUseInStartPhase()); } +TEST_F(LossBasedBweV2Test, BoundEstimateByAckedRate) { + ExplicitKeyValueConfig key_value_config( + ShortObservationConfig("LowerBoundByAckedRateFactor:1.0")); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + loss_based_bandwidth_estimator.SetMinMaxBitrate( + /*min_bitrate=*/DataRate::KilobitsPerSec(10), + /*max_bitrate=*/DataRate::KilobitsPerSec(1000000)); + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.SetAcknowledgedBitrate( + DataRate::KilobitsPerSec(500)); + + std::vector enough_feedback_100p_loss_1 = + CreatePacketResultsWith100pLossRate( + /*first_packet_timestamp=*/Timestamp::Zero()); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_100p_loss_1, + /*delay_based_estimate=*/DataRate::PlusInfinity(), + /*in_alr=*/false); + + EXPECT_EQ( + loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate, + DataRate::KilobitsPerSec(500)); +} + +TEST_F(LossBasedBweV2Test, NotBoundEstimateByAckedRate) { + ExplicitKeyValueConfig key_value_config( + ShortObservationConfig("LowerBoundByAckedRateFactor:0.0")); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + loss_based_bandwidth_estimator.SetMinMaxBitrate( + /*min_bitrate=*/DataRate::KilobitsPerSec(10), + /*max_bitrate=*/DataRate::KilobitsPerSec(1000000)); + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.SetAcknowledgedBitrate( + DataRate::KilobitsPerSec(500)); + + std::vector enough_feedback_100p_loss_1 = + CreatePacketResultsWith100pLossRate( + /*first_packet_timestamp=*/Timestamp::Zero()); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_100p_loss_1, + /*delay_based_estimate=*/DataRate::PlusInfinity(), + /*in_alr=*/false); + + EXPECT_LT( + loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate, + DataRate::KilobitsPerSec(500)); +} + } // namespace } // namespace webrtc