Stop changing the requested max bitrate based on protection level.

With the current implementation, whenever we are toggling between
sending/not sending retransmissions, the BitrateAllocator will
toggle the total requested max bitrate that is signalled to the
probing mechanism. The result is that spurious probes are sent
mid-call, at |max_bitrate| and |2*max_bitrate|. This behaviour
is undesirable, and thus removed in this CL. Instead, whenever
the allocation limits actually change, we produce a single
set of probes at |max_bitrate| and |2*max_bitrate|.

This CL does not change how the BitrateAllocator hysteresis is
accounting for protection, since it does not relate to the
spurious probes.

Bug: webrtc:10275
TBR: sprang@webrtc.org
Change-Id: Iab3a690a500372c74772a8ad6217fb838af15ade
Reviewed-on: https://webrtc-review.googlesource.com/c/120808
Commit-Queue: Rasmus Brandt <brandtr@webrtc.org>
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26544}
diff --git a/call/bitrate_allocator.cc b/call/bitrate_allocator.cc
index 172f940..06b74ba 100644
--- a/call/bitrate_allocator.cc
+++ b/call/bitrate_allocator.cc
@@ -27,6 +27,8 @@
 
 namespace webrtc {
 
+namespace {
+
 // Allow packets to be transmitted in up to 2 times max video bitrate if the
 // bandwidth estimate allows it.
 const uint8_t kTransmissionMaxBitrateMultiplier = 2;
@@ -38,8 +40,6 @@
 
 const int64_t kBweLogIntervalMs = 5000;
 
-namespace {
-
 double MediaRatio(uint32_t allocated_bitrate, uint32_t protection_bitrate) {
   RTC_DCHECK_GT(allocated_bitrate, 0);
   if (protection_bitrate == 0)
@@ -48,6 +48,7 @@
   uint32_t media_bitrate = allocated_bitrate - protection_bitrate;
   return media_bitrate / static_cast<double>(allocated_bitrate);
 }
+
 }  // namespace
 
 BitrateAllocator::BitrateAllocator(LimitObserver* limit_observer)
@@ -228,14 +229,7 @@
           std::max(config.MinBitrateWithHysteresis(), stream_padding);
     }
     total_requested_padding_bitrate += stream_padding;
-    uint32_t max_bitrate_bps = config.max_bitrate_bps;
-    if (config.media_ratio < 1.0) {
-      // Account for protection overhead (eg FEC). Assumption is that overhead
-      // is never more than 100%. Don't adjust based exact value as that might
-      // trigger too frequent calls to OnAllocationLimitsChanged().
-      max_bitrate_bps *= 2;
-    }
-    total_requested_max_bitrate += max_bitrate_bps;
+    total_requested_max_bitrate += config.max_bitrate_bps;
   }
 
   if (total_requested_padding_bitrate == total_requested_padding_bitrate_ &&
diff --git a/call/bitrate_allocator_unittest.cc b/call/bitrate_allocator_unittest.cc
index b1df347..9956a88 100644
--- a/call/bitrate_allocator_unittest.cc
+++ b/call/bitrate_allocator_unittest.cc
@@ -17,8 +17,9 @@
 #include "test/gmock.h"
 #include "test/gtest.h"
 
-using ::testing::NiceMock;
 using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::StrictMock;
 
 namespace webrtc {
 // Emulating old interface for test suite compatibility.
@@ -347,27 +348,21 @@
 
   // Add loss and use a part of the bitrate for protection.
   const double kProtectionRatio = 0.4;
-  uint32_t target_bitrate_bps = 200000;
-  const uint32_t kMaxBitrateWithProtectionBps =
-      static_cast<uint32_t>(kMaxBitrateBps * 2);
-  uint8_t fraction_loss = kProtectionRatio * 256;
+  const uint8_t fraction_loss = kProtectionRatio * 256;
   bitrate_observer.SetBitrateProtectionRatio(kProtectionRatio);
-  EXPECT_CALL(limit_observer_,
-              OnAllocationLimitsChanged(0, 0, kMaxBitrateWithProtectionBps));
-  allocator_->OnNetworkChanged(target_bitrate_bps, 0, fraction_loss,
+  allocator_->OnNetworkChanged(200000, 0, fraction_loss,
                                kDefaultProbingIntervalMs);
-  EXPECT_EQ(target_bitrate_bps, bitrate_observer.last_bitrate_bps_);
+  EXPECT_EQ(200000u, bitrate_observer.last_bitrate_bps_);
 
   // Above the min threshold, but not enough given the protection used.
   // Limits changed, as we will video is now off and we need to pad up to the
   // start bitrate.
-  target_bitrate_bps = kMinStartBitrateBps + 1000;
   // Verify the hysteresis is added for the protection.
   const uint32_t kMinStartBitrateWithProtectionBps =
       static_cast<uint32_t>(kMinStartBitrateBps * (1 + kProtectionRatio));
   EXPECT_CALL(limit_observer_,
               OnAllocationLimitsChanged(0, kMinStartBitrateWithProtectionBps,
-                                        kMaxBitrateWithProtectionBps));
+                                        kMaxBitrateBps));
   allocator_->OnNetworkChanged(kMinStartBitrateBps + 1000, 0, fraction_loss,
                                kDefaultProbingIntervalMs);
   EXPECT_EQ(0u, bitrate_observer.last_bitrate_bps_);
@@ -377,9 +372,7 @@
   EXPECT_EQ(0u, bitrate_observer.last_bitrate_bps_);
 
   // Just enough to enable video again.
-  target_bitrate_bps = kMinStartBitrateWithProtectionBps;
-  EXPECT_CALL(limit_observer_,
-              OnAllocationLimitsChanged(0, 0, kMaxBitrateWithProtectionBps));
+  EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 0, kMaxBitrateBps));
   allocator_->OnNetworkChanged(kMinStartBitrateWithProtectionBps, 0,
                                fraction_loss, kDefaultProbingIntervalMs);
   EXPECT_EQ(kMinStartBitrateWithProtectionBps,
@@ -387,7 +380,6 @@
 
   // Remove all protection and make sure video is not paused as earlier.
   bitrate_observer.SetBitrateProtectionRatio(0.0);
-  EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 0, kMaxBitrateBps));
   allocator_->OnNetworkChanged(kMinStartBitrateWithProtectionBps - 1000, 0, 0,
                                kDefaultProbingIntervalMs);
   EXPECT_EQ(kMinStartBitrateWithProtectionBps - 1000,
@@ -401,6 +393,40 @@
   allocator_->RemoveObserver(&bitrate_observer);
 }
 
+TEST_F(BitrateAllocatorTest,
+       TotalAllocationLimitsAreUnaffectedByProtectionRatio) {
+  TestBitrateObserver bitrate_observer;
+
+  const uint32_t kMinBitrateBps = 100000;
+  const uint32_t kMaxBitrateBps = 400000;
+
+  // Register |bitrate_observer| and expect total allocation limits to change.
+  EXPECT_CALL(limit_observer_,
+              OnAllocationLimitsChanged(kMinBitrateBps, 0, kMaxBitrateBps))
+      .Times(1);
+  allocator_->AddObserver(
+      &bitrate_observer,
+      {kMinBitrateBps, kMaxBitrateBps, 0, true, "", kDefaultBitratePriority});
+
+  // Observer uses 20% of it's allocated bitrate for protection.
+  bitrate_observer.SetBitrateProtectionRatio(/*protection_ratio=*/0.2);
+  // Total allocation limits are unaffected by the protection rate change.
+  EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(_, _, _)).Times(0);
+  allocator_->OnNetworkChanged(200000u, 0, 100, kDefaultProbingIntervalMs);
+
+  // Observer uses 0% of it's allocated bitrate for protection.
+  bitrate_observer.SetBitrateProtectionRatio(/*protection_ratio=*/0.0);
+  // Total allocation limits are unaffected by the protection rate change.
+  EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(_, _, _)).Times(0);
+  allocator_->OnNetworkChanged(200000u, 0, 100, kDefaultProbingIntervalMs);
+
+  // Observer again uses 20% of it's allocated bitrate for protection.
+  bitrate_observer.SetBitrateProtectionRatio(/*protection_ratio=*/0.2);
+  // Total allocation limits are unaffected by the protection rate change.
+  EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(_, _, _)).Times(0);
+  allocator_->OnNetworkChanged(200000u, 0, 100, kDefaultProbingIntervalMs);
+}
+
 TEST_F(BitrateAllocatorTestNoEnforceMin, TwoBitrateObserverWithPacketLoss) {
   TestBitrateObserver bitrate_observer_1;
   TestBitrateObserver bitrate_observer_2;
diff --git a/modules/congestion_controller/goog_cc/probe_controller.cc b/modules/congestion_controller/goog_cc/probe_controller.cc
index a71902d..fa7a9eb 100644
--- a/modules/congestion_controller/goog_cc/probe_controller.cc
+++ b/modules/congestion_controller/goog_cc/probe_controller.cc
@@ -154,7 +154,11 @@
       (max_bitrate_bps_ <= 0 || estimated_bitrate_bps_ < max_bitrate_bps_) &&
       estimated_bitrate_bps_ < max_total_allocated_bitrate) {
     max_total_allocated_bitrate_ = max_total_allocated_bitrate;
-    return InitiateProbing(at_time_ms, {max_total_allocated_bitrate}, false);
+    // Also probe at 2x the max bitrate, to account for the transmission max
+    // bitrate multiplier functionality of the BitrateAllocator.
+    return InitiateProbing(
+        at_time_ms,
+        {max_total_allocated_bitrate, 2 * max_total_allocated_bitrate}, false);
   }
   max_total_allocated_bitrate_ = max_total_allocated_bitrate;
   return std::vector<ProbeClusterConfig>();