Apply stricter bandwidth cap for high loss.
When loss rate is above a certain threshold, set instant_limit = 500 - 1000 * average_loss_rate, which returns 200kbps at 30% loss rate, or 100kbps at 40% loss rate. When the loss rate is above 50%, use the min_bitrate from send_side_bandwidth_estimation.
The high_loss_rate_threshold is set to 1.0, so the change is not activated by default.
Tested the change with hamrit, when average loss rate is above 50%, bandwidth backed to 10kbps, and it took ~10s to ramp up to 1.5Mbps.
https://screenshot.googleplex.com/7dvPoWa2b5SgMSL
Bug: webrtc:12707
Change-Id: I5eea04ef709a183bdf696246094dbd4a204e48f6
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/272061
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Commit-Queue: Diep Bui <diepbp@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38243}
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 c8c69f3..e671bd2 100644
--- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc
+++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc
@@ -181,6 +181,15 @@
}
}
+void LossBasedBweV2::SetMinBitrate(DataRate min_bitrate) {
+ if (IsValid(min_bitrate)) {
+ min_bitrate_ = min_bitrate;
+ } else {
+ RTC_LOG(LS_WARNING) << "The min bitrate must be finite: "
+ << ToString(min_bitrate);
+ }
+}
+
void LossBasedBweV2::UpdateBandwidthEstimate(
rtc::ArrayView<const PacketResult> packet_results,
DataRate delay_based_estimate,
@@ -324,7 +333,12 @@
FieldTrialParameter<bool>
not_increase_if_inherent_loss_less_than_average_loss(
"NotIncreaseIfInherentLossLessThanAverageLoss", false);
-
+ FieldTrialParameter<double> high_loss_rate_threshold("HighLossRateThreshold",
+ 1.0);
+ FieldTrialParameter<DataRate> bandwidth_cap_at_high_loss_rate(
+ "BandwidthCapAtHighLossRate", DataRate::KilobitsPerSec(500.0));
+ FieldTrialParameter<double> slope_of_bwe_high_loss_func(
+ "SlopeOfBweHighLossFunc", 1000);
if (key_value_config) {
ParseFieldTrial({&enabled,
&bandwidth_rampup_upper_bound_factor,
@@ -356,7 +370,10 @@
&max_increase_factor,
&delayed_increase_window,
&use_acked_bitrate_only_when_overusing,
- ¬_increase_if_inherent_loss_less_than_average_loss},
+ ¬_increase_if_inherent_loss_less_than_average_loss,
+ &high_loss_rate_threshold,
+ &bandwidth_cap_at_high_loss_rate,
+ &slope_of_bwe_high_loss_func},
key_value_config->Lookup("WebRTC-Bwe-LossBasedBweV2"));
}
@@ -412,6 +429,10 @@
use_acked_bitrate_only_when_overusing.Get();
config->not_increase_if_inherent_loss_less_than_average_loss =
not_increase_if_inherent_loss_less_than_average_loss.Get();
+ config->high_loss_rate_threshold = high_loss_rate_threshold.Get();
+ config->bandwidth_cap_at_high_loss_rate =
+ bandwidth_cap_at_high_loss_rate.Get();
+ config->slope_of_bwe_high_loss_func = slope_of_bwe_high_loss_func.Get();
return config;
}
@@ -575,8 +596,8 @@
<< config_->bandwidth_backoff_lower_bound_factor;
valid = false;
}
- if (config_->trendline_observations_window_size < 2) {
- RTC_LOG(LS_WARNING) << "The trendline window size must be at least 2: "
+ if (config_->trendline_observations_window_size < 1) {
+ RTC_LOG(LS_WARNING) << "The trendline window size must be at least 1: "
<< config_->trendline_observations_window_size;
valid = false;
}
@@ -590,6 +611,12 @@
<< config_->delayed_increase_window.ms();
valid = false;
}
+ if (config_->high_loss_rate_threshold <= 0.0 ||
+ config_->high_loss_rate_threshold > 1.0) {
+ RTC_LOG(LS_WARNING) << "The high loss rate threshold must be in (0, 1]: "
+ << config_->high_loss_rate_threshold;
+ valid = false;
+ }
return valid;
}
@@ -598,8 +625,8 @@
return 0.0;
}
- int num_packets = 0;
- int num_lost_packets = 0;
+ double num_packets = 0;
+ double num_lost_packets = 0;
for (const Observation& observation : observations_) {
if (!observation.IsInitialized()) {
continue;
@@ -612,7 +639,7 @@
num_lost_packets += instant_temporal_weight * observation.num_lost_packets;
}
- return static_cast<double>(num_lost_packets) / num_packets;
+ return num_lost_packets / num_packets;
}
DataRate LossBasedBweV2::GetCandidateBandwidthUpperBound(
@@ -843,7 +870,16 @@
instant_limit = config_->instant_upper_bound_bandwidth_balance /
(average_reported_loss_ratio -
config_->instant_upper_bound_loss_offset);
+ if (average_reported_loss_ratio > config_->high_loss_rate_threshold) {
+ instant_limit = std::min(
+ instant_limit, DataRate::KilobitsPerSec(std::max(
+ static_cast<double>(min_bitrate_.kbps()),
+ config_->bandwidth_cap_at_high_loss_rate.kbps() -
+ config_->slope_of_bwe_high_loss_func *
+ average_reported_loss_ratio)));
+ }
}
+
cached_instant_upper_bound_ = instant_limit;
}
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 fdfb440..88cae01 100644
--- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h
+++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h
@@ -48,7 +48,7 @@
void SetAcknowledgedBitrate(DataRate acknowledged_bitrate);
void SetBandwidthEstimate(DataRate bandwidth_estimate);
-
+ void SetMinBitrate(DataRate min_bitrate);
void UpdateBandwidthEstimate(
rtc::ArrayView<const PacketResult> packet_results,
DataRate delay_based_estimate,
@@ -92,6 +92,9 @@
TimeDelta delayed_increase_window = TimeDelta::Zero();
bool use_acked_bitrate_only_when_overusing = false;
bool not_increase_if_inherent_loss_less_than_average_loss = false;
+ double high_loss_rate_threshold = 1.0;
+ DataRate bandwidth_cap_at_high_loss_rate = DataRate::MinusInfinity();
+ double slope_of_bwe_high_loss_func = 1000.0;
};
struct Derivatives {
@@ -168,6 +171,7 @@
Timestamp recovering_after_loss_timestamp_ = Timestamp::MinusInfinity();
DataRate bandwidth_limit_in_current_window_ = DataRate::PlusInfinity();
bool limited_due_to_loss_candidate_ = false;
+ DataRate min_bitrate_ = DataRate::KilobitsPerSec(1);
};
} // 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 9dc6144..0e039fd 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
@@ -913,6 +913,171 @@
DataRate::KilobitsPerSec(600));
}
+TEST_P(LossBasedBweV2Test,
+ StricterBoundUsingHighLossRateThresholdAt10pLossRate) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.0,AckedRateCandidate:false,"
+ "ObservationWindowSize:2,"
+ "DelayBasedCandidate:true,InstantUpperBoundBwBalance:100kbps,"
+ "ObservationDurationLowerBound:200ms,HigherBwBiasFactor:1000,"
+ "HigherLogBwBiasFactor:1000,LossThresholdOfHighBandwidthPreference:0."
+ "05,HighLossRateThreshold:0.09/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetMinBitrate(DataRate::KilobitsPerSec(10));
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+
+ std::vector<PacketResult> enough_feedback_10p_loss_1 =
+ CreatePacketResultsWith10pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_10p_loss_1, delay_based_estimate,
+ BandwidthUsage::kBwNormal);
+
+ std::vector<PacketResult> enough_feedback_10p_loss_2 =
+ CreatePacketResultsWith10pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_10p_loss_2, delay_based_estimate,
+ BandwidthUsage::kBwNormal);
+
+ // At 10% loss rate and high loss rate threshold to be 10%, cap the estimate
+ // to be 500 * 1000-0.1 = 400kbps.
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetBandwidthEstimate(delay_based_estimate),
+ DataRate::KilobitsPerSec(400));
+}
+
+TEST_P(LossBasedBweV2Test,
+ StricterBoundUsingHighLossRateThresholdAt50pLossRate) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.0,AckedRateCandidate:false,"
+ "ObservationWindowSize:2,"
+ "DelayBasedCandidate:true,InstantUpperBoundBwBalance:100kbps,"
+ "ObservationDurationLowerBound:200ms,HigherBwBiasFactor:1000,"
+ "HigherLogBwBiasFactor:1000,LossThresholdOfHighBandwidthPreference:0."
+ "05,HighLossRateThreshold:0.3/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetMinBitrate(DataRate::KilobitsPerSec(10));
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+
+ std::vector<PacketResult> enough_feedback_50p_loss_1 =
+ CreatePacketResultsWith50pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_50p_loss_1, delay_based_estimate,
+ BandwidthUsage::kBwNormal);
+
+ std::vector<PacketResult> enough_feedback_50p_loss_2 =
+ CreatePacketResultsWith50pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_50p_loss_2, delay_based_estimate,
+ BandwidthUsage::kBwNormal);
+
+ // At 50% loss rate and high loss rate threshold to be 30%, cap the estimate
+ // to be the min bitrate.
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetBandwidthEstimate(delay_based_estimate),
+ DataRate::KilobitsPerSec(10));
+}
+
+TEST_P(LossBasedBweV2Test,
+ StricterBoundUsingHighLossRateThresholdAt100pLossRate) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.0,AckedRateCandidate:false,"
+ "ObservationWindowSize:2,"
+ "DelayBasedCandidate:true,InstantUpperBoundBwBalance:100kbps,"
+ "ObservationDurationLowerBound:200ms,HigherBwBiasFactor:1000,"
+ "HigherLogBwBiasFactor:1000,LossThresholdOfHighBandwidthPreference:0."
+ "05,HighLossRateThreshold:0.3/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetMinBitrate(DataRate::KilobitsPerSec(10));
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+
+ std::vector<PacketResult> enough_feedback_100p_loss_1 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_100p_loss_1, delay_based_estimate,
+ BandwidthUsage::kBwNormal);
+
+ std::vector<PacketResult> enough_feedback_100p_loss_2 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_100p_loss_2, delay_based_estimate,
+ BandwidthUsage::kBwNormal);
+
+ // At 100% loss rate and high loss rate threshold to be 30%, cap the estimate
+ // to be the min bitrate.
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetBandwidthEstimate(delay_based_estimate),
+ DataRate::KilobitsPerSec(10));
+}
+
+TEST_P(LossBasedBweV2Test, EstimateRecoversAfterHighLoss) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.1|1.0|0.9,AckedRateCandidate:false,"
+ "ObservationWindowSize:2,"
+ "DelayBasedCandidate:true,InstantUpperBoundBwBalance:100kbps,"
+ "ObservationDurationLowerBound:200ms,HigherBwBiasFactor:1000,"
+ "HigherLogBwBiasFactor:1000,LossThresholdOfHighBandwidthPreference:0."
+ "05,HighLossRateThreshold:0.3/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetMinBitrate(DataRate::KilobitsPerSec(10));
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+
+ std::vector<PacketResult> enough_feedback_100p_loss_1 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_100p_loss_1, delay_based_estimate,
+ BandwidthUsage::kBwNormal);
+
+ // Make sure that the estimate is set to min bitrate because of 100% loss
+ // rate.
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetBandwidthEstimate(delay_based_estimate),
+ DataRate::KilobitsPerSec(10));
+
+ // Create some feedbacks with 0 loss rate to simulate network recovering.
+ std::vector<PacketResult> enough_feedback_0p_loss_1 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_0p_loss_1, delay_based_estimate,
+ BandwidthUsage::kBwNormal);
+
+ std::vector<PacketResult> enough_feedback_0p_loss_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound * 2);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_0p_loss_2, delay_based_estimate,
+ BandwidthUsage::kBwNormal);
+
+ // The estimate increases as network recovers.
+ EXPECT_GT(
+ loss_based_bandwidth_estimator.GetBandwidthEstimate(delay_based_estimate),
+ DataRate::KilobitsPerSec(10));
+}
+
INSTANTIATE_TEST_SUITE_P(LossBasedBweV2Tests,
LossBasedBweV2Test,
::testing::Bool());
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 acf1862..12a7d41 100644
--- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc
+++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc
@@ -244,6 +244,9 @@
}
ParseFieldTrial({&disable_receiver_limit_caps_only_},
key_value_config->Lookup("WebRTC-Bwe-ReceiverLimitCapsOnly"));
+ if (LossBasedBandwidthEstimatorV2Enabled()) {
+ loss_based_bandwidth_estimator_v2_.SetMinBitrate(min_bitrate_configured_);
+ }
}
SendSideBandwidthEstimation::~SendSideBandwidthEstimation() {}