Use acked bitrate as lower bound of loss based BWE.

This cl/ makes sure that the estimate cannot go lower than a factor of acked bitrate. The current flag LowerBoundByAckedRateFactor is set to 0, means we dont use it.


Bug: webrtc:12707
Change-Id: I75d5881f0b85a374af3f7039b82c71aee97fb7b0
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/323881
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Commit-Queue: Diep Bui <diepbp@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40958}
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 a938e93..46a2d4e 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 @@
 
   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 @@
 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 @@
                                       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 @@
                                                       true);
   FieldTrialParameter<bool> use_in_start_phase("UseInStartPhase", false);
   FieldTrialParameter<int> min_num_observations("MinNumObservations", 3);
+  FieldTrialParameter<double> lower_bound_by_acked_rate_factor(
+      "LowerBoundByAckedRateFactor", 0.0);
   if (key_value_config) {
     ParseFieldTrial({&enabled,
                      &bandwidth_rampup_upper_bound_factor,
@@ -428,7 +435,8 @@
                      &slope_of_bwe_high_loss_func,
                      &not_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 @@
   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 @@
                         << 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 @@
   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 09d4d87..81c2f90 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 @@
     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 @@
   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 @@
   Timestamp last_send_time_most_recent_observation_ = Timestamp::PlusInfinity();
   Timestamp last_time_estimate_reduced_ = Timestamp::MinusInfinity();
   absl::optional<DataRate> cached_instant_upper_bound_;
+  absl::optional<DataRate> cached_instant_lower_bound_;
   std::vector<double> instant_upper_bound_temporal_weights_;
   std::vector<double> 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 d1d5fad..cc2b2c0 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 @@
           /*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 @@
   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<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=*/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<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=*/DataRate::PlusInfinity(),
+      /*in_alr=*/false);
+
+  EXPECT_LT(
+      loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+      DataRate::KilobitsPerSec(500));
+}
+
 }  // namespace
 }  // namespace webrtc