Do not allow estimate to increase above the estimate when HOLD started.

To ensure padding, we increase 1 bit instead of 1kbps to avoid that 1kbps adds up over time.
Not have unit test for this, but did manual/hamrit tests many times.

Bug: webrtc:12707
Change-Id: I9b3160ab1808cb3a21ff0609446359a4ec3a4949
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/325520
Commit-Queue: Diep Bui <diepbp@webrtc.org>
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41056}
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 15921cc..445be08 100644
--- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc
+++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc
@@ -132,7 +132,7 @@
   instant_upper_bound_temporal_weights_.resize(
       config_->observation_window_size);
   CalculateTemporalWeights();
-  hold_duration_ = kInitHoldDuration;
+  last_hold_info_.duration = kInitHoldDuration;
 }
 
 bool LossBasedBweV2::IsEnabled() const {
@@ -305,7 +305,7 @@
               current_best_estimate_.loss_limited_bandwidth) {
         best_candidate.loss_limited_bandwidth =
             current_best_estimate_.loss_limited_bandwidth +
-            DataRate::KilobitsPerSec(1);
+            DataRate::BitsPerSec(1);
       }
     }
   }
@@ -340,12 +340,12 @@
   }
 
   if (loss_based_result_.state == LossBasedState::kDecreasing &&
-      last_hold_timestamp_ > last_send_time_most_recent_observation_ &&
+      last_hold_info_.timestamp > last_send_time_most_recent_observation_ &&
       bounded_bandwidth_estimate < delay_based_estimate_) {
-    // BWE is not allowed to increase during the HOLD duration. The purpose of
+    // BWE is not allowed to increase above the HOLD rate. The purpose of
     // HOLD is to not immediately ramp up BWE to a rate that may cause loss.
-    loss_based_result_.bandwidth_estimate = std::min(
-        loss_based_result_.bandwidth_estimate, bounded_bandwidth_estimate);
+    loss_based_result_.bandwidth_estimate =
+        std::min(last_hold_info_.rate, bounded_bandwidth_estimate);
     return;
   }
 
@@ -372,18 +372,23 @@
       RTC_LOG(LS_INFO) << this << " "
                        << "Switch to HOLD. Bounded BWE: "
                        << bounded_bandwidth_estimate.kbps()
-                       << ", duration: " << hold_duration_.seconds();
-      last_hold_timestamp_ =
-          last_send_time_most_recent_observation_ + hold_duration_;
-      hold_duration_ = std::min(kMaxHoldDuration,
-                                hold_duration_ * config_->hold_duration_factor);
+                       << ", duration: " << last_hold_info_.duration.ms();
+      last_hold_info_ = {
+          .timestamp = last_send_time_most_recent_observation_ +
+                       last_hold_info_.duration,
+          .duration =
+              std::min(kMaxHoldDuration, last_hold_info_.duration *
+                                             config_->hold_duration_factor),
+          .rate = bounded_bandwidth_estimate};
     }
     last_padding_info_ = PaddingInfo();
     loss_based_result_.state = LossBasedState::kDecreasing;
   } else {
-    // Reset the HOLD duration if delay based estimate works to avoid getting
+    // Reset the HOLD info if delay based estimate works to avoid getting
     // stuck in low bitrate.
-    hold_duration_ = kInitHoldDuration;
+    last_hold_info_ = {.timestamp = Timestamp::MinusInfinity(),
+                       .duration = kInitHoldDuration,
+                       .rate = DataRate::PlusInfinity()};
     last_padding_info_ = PaddingInfo();
     loss_based_result_.state = LossBasedState::kDelayBasedEstimate;
   }
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 70da76d..228d598 100644
--- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h
+++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h
@@ -155,6 +155,12 @@
     Timestamp padding_timestamp = Timestamp::MinusInfinity();
   };
 
+  struct HoldInfo {
+    Timestamp timestamp = Timestamp::MinusInfinity();
+    TimeDelta duration = TimeDelta::Zero();
+    DataRate rate = DataRate::PlusInfinity();
+  };
+
   static absl::optional<Config> CreateConfig(
       const FieldTrialsView* key_value_config);
   bool IsConfigValid() const;
@@ -207,8 +213,7 @@
   DataRate max_bitrate_ = DataRate::PlusInfinity();
   DataRate delay_based_estimate_ = DataRate::PlusInfinity();
   LossBasedBweV2::Result loss_based_result_ = LossBasedBweV2::Result();
-  Timestamp last_hold_timestamp_ = Timestamp::MinusInfinity();
-  TimeDelta hold_duration_ = TimeDelta::Zero();
+  HoldInfo last_hold_info_ = HoldInfo();
   PaddingInfo last_padding_info_ = PaddingInfo();
 };
 
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 77d3f02..16aefcc 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
@@ -853,8 +853,7 @@
 
   ASSERT_EQ(result.state, LossBasedState::kIncreasing);
   // The estimate increases by 1kbps.
-  EXPECT_EQ(result.bandwidth_estimate,
-            estimate_1 + DataRate::KilobitsPerSec(1));
+  EXPECT_EQ(result.bandwidth_estimate, estimate_1 + DataRate::BitsPerSec(1));
 }
 
 // After loss based bwe backs off, the estimate is bounded during the delayed
@@ -1678,7 +1677,7 @@
 
 TEST_F(LossBasedBweV2Test, IncreaseEstimateAfterHoldDuration) {
   ExplicitKeyValueConfig key_value_config(
-      ShortObservationConfig("HoldDurationFactor:3"));
+      ShortObservationConfig("HoldDurationFactor:10"));
   LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
   loss_based_bandwidth_estimator.SetBandwidthEstimate(
       DataRate::KilobitsPerSec(2500));
@@ -1727,36 +1726,50 @@
       /*in_alr=*/false);
   EXPECT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state,
             LossBasedState::kDecreasing);
-  estimate =
+  DataRate estimate_at_hold =
       loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate;
 
-  // During the hold duration, e.g. next 900ms, the estimate cannot increase.
+  // In the hold duration, e.g. next 3s, the estimate cannot increase above the
+  // hold rate. Get some lost packets to get lower estimate than the HOLD rate.
   for (int i = 4; i <= 6; ++i) {
     loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
-        CreatePacketResultsWithReceivedPackets(
+        CreatePacketResultsWith100pLossRate(
             /*first_packet_timestamp=*/Timestamp::Zero() +
             kObservationDurationLowerBound * i),
         /*delay_based_estimate=*/DataRate::PlusInfinity(),
         /*in_alr=*/false);
     EXPECT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state,
               LossBasedState::kDecreasing);
-    EXPECT_EQ(
+    EXPECT_LT(
         loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
-        estimate);
+        estimate_at_hold);
   }
 
-  // After the hold duration, the estimate can increase again.
-  loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
-      CreatePacketResultsWithReceivedPackets(
-          /*first_packet_timestamp=*/Timestamp::Zero() +
-          kObservationDurationLowerBound * 7),
-      /*delay_based_estimate=*/DataRate::PlusInfinity(),
-      /*in_alr=*/false);
-  EXPECT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state,
-            LossBasedState::kIncreasing);
-  EXPECT_GE(
-      loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
-      estimate);
+  int feedback_id = 7;
+  while (loss_based_bandwidth_estimator.GetLossBasedResult().state !=
+         LossBasedState::kIncreasing) {
+    loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+        CreatePacketResultsWithReceivedPackets(
+            /*first_packet_timestamp=*/Timestamp::Zero() +
+            kObservationDurationLowerBound * feedback_id),
+        /*delay_based_estimate=*/DataRate::PlusInfinity(),
+        /*in_alr=*/false);
+    if (loss_based_bandwidth_estimator.GetLossBasedResult().state ==
+        LossBasedState::kDecreasing) {
+      // In the hold duration, the estimate can not go higher than estimate at
+      // hold.
+      EXPECT_LE(loss_based_bandwidth_estimator.GetLossBasedResult()
+                    .bandwidth_estimate,
+                estimate_at_hold);
+    } else if (loss_based_bandwidth_estimator.GetLossBasedResult().state ==
+               LossBasedState::kIncreasing) {
+      // After the hold duration, the estimate can increase again.
+      EXPECT_GT(loss_based_bandwidth_estimator.GetLossBasedResult()
+                    .bandwidth_estimate,
+                estimate_at_hold);
+    }
+    feedback_id++;
+  }
 }
 
 TEST_F(LossBasedBweV2Test, EndHoldDurationIfDelayBasedEstimateWorks) {