Add field trial to probe if NetworkState drop below a threshold

Change ProbeController field trial to also probe when loss limited but probe at the current estimate.

Bug: webrtc:14392
Change-Id: I8b30e316b935a0f2c375e2204a8e33e6671eb956
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/273901
Reviewed-by: Diep Bui <diepbp@webrtc.org>
Commit-Queue: Per Kjellander <perkj@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38004}
diff --git a/modules/congestion_controller/goog_cc/probe_controller.cc b/modules/congestion_controller/goog_cc/probe_controller.cc
index e60c722..b9e6151 100644
--- a/modules/congestion_controller/goog_cc/probe_controller.cc
+++ b/modules/congestion_controller/goog_cc/probe_controller.cc
@@ -89,6 +89,7 @@
                                               TimeDelta::PlusInfinity()),
       network_state_estimate_fast_rampup_rate("network_state_fast_rampup_rate",
                                               0),
+      network_state_estimate_drop_down_rate("network_state_drop_down_rate", 0),
       network_state_probe_scale("network_state_scale", 1.0),
       network_state_probe_duration("network_state_probe_duration",
                                    TimeDelta::Millis(15)),
@@ -99,16 +100,17 @@
       allocation_probe_max("alloc_probe_max", DataRate::PlusInfinity()),
       min_probe_packets_sent("min_probe_packets_sent", 5),
       min_probe_duration("min_probe_duration", TimeDelta::Millis(15)),
-      probe_if_bwe_limited_due_to_loss("probe_if_bwe_limited_due_to_loss",
-                                       true) {
+      limit_probe_target_rate_to_loss_bwe("limit_probe_target_rate_to_loss_bwe",
+                                          false) {
   ParseFieldTrial(
       {&first_exponential_probe_scale, &second_exponential_probe_scale,
        &further_exponential_probe_scale, &further_probe_threshold,
        &alr_probing_interval, &alr_probe_scale, &first_allocation_probe_scale,
        &second_allocation_probe_scale, &allocation_allow_further_probing,
        &min_probe_duration, &network_state_estimate_probing_interval,
-       &network_state_estimate_fast_rampup_rate, &network_state_probe_scale,
-       &network_state_probe_duration, &probe_if_bwe_limited_due_to_loss},
+       &network_state_estimate_fast_rampup_rate,
+       &network_state_estimate_drop_down_rate, &network_state_probe_scale,
+       &network_state_probe_duration, &limit_probe_target_rate_to_loss_bwe},
       key_value_config->Lookup("WebRTC-Bwe-ProbingConfiguration"));
 
   // Specialized keys overriding subsets of WebRTC-Bwe-ProbingConfiguration
@@ -364,6 +366,15 @@
                network_estimate_->link_capacity_upper)) {
     send_probe_on_next_process_interval_ = true;
   }
+  if (config_.network_state_estimate_drop_down_rate > 0 && network_estimate_ &&
+      (estimated_bitrate_ > estimate.link_capacity_upper ||
+       bwe_limited_due_to_packet_loss_) &&
+      estimate.link_capacity_upper <=
+          config_.network_state_estimate_drop_down_rate *
+              network_estimate_->link_capacity_upper) {
+    send_probe_on_next_process_interval_ = true;
+  }
+
   network_estimate_ = estimate;
 }
 
@@ -434,11 +445,11 @@
     Timestamp now,
     std::vector<DataRate> bitrates_to_probe,
     bool probe_further) {
-  if (bwe_limited_due_to_packet_loss_ &&
-      !config_.probe_if_bwe_limited_due_to_loss) {
-    return {};
-  }
   DataRate max_probe_bitrate = max_bitrate_;
+  if (bwe_limited_due_to_packet_loss_ &&
+      config_.limit_probe_target_rate_to_loss_bwe) {
+    max_probe_bitrate = estimated_bitrate_;
+  }
   if (config_.network_state_estimate_probing_interval->IsFinite() &&
       network_estimate_ &&
       network_estimate_->link_capacity_upper > DataRate::Zero()) {
diff --git a/modules/congestion_controller/goog_cc/probe_controller.h b/modules/congestion_controller/goog_cc/probe_controller.h
index 399b38e..c32f25e 100644
--- a/modules/congestion_controller/goog_cc/probe_controller.h
+++ b/modules/congestion_controller/goog_cc/probe_controller.h
@@ -53,6 +53,9 @@
   // If the network state estimate increase more than this rate, a probe is sent
   // the next process interval.
   FieldTrialParameter<double> network_state_estimate_fast_rampup_rate;
+  // If the network state estimate decreases more than this rate, a probe is
+  // sent the next process interval.
+  FieldTrialParameter<double> network_state_estimate_drop_down_rate;
   FieldTrialParameter<double> network_state_probe_scale;
   // Overrides min_probe_duration if network_state_estimate_probing_interval
   // is set and a network state estimate is known.
@@ -68,8 +71,9 @@
   FieldTrialParameter<int> min_probe_packets_sent;
   // The minimum probing duration.
   FieldTrialParameter<TimeDelta> min_probe_duration;
-
-  FieldTrialParameter<bool> probe_if_bwe_limited_due_to_loss;
+  // Max limit the target rate of a probe to current estimate if BWE is loss
+  // limited.
+  FieldTrialParameter<bool> limit_probe_target_rate_to_loss_bwe;
 };
 
 // This class controls initiation of probing to estimate initial channel
diff --git a/modules/congestion_controller/goog_cc/probe_controller_unittest.cc b/modules/congestion_controller/goog_cc/probe_controller_unittest.cc
index 8860a0f..2907606 100644
--- a/modules/congestion_controller/goog_cc/probe_controller_unittest.cc
+++ b/modules/congestion_controller/goog_cc/probe_controller_unittest.cc
@@ -504,10 +504,10 @@
   EXPECT_EQ(probes[0].target_data_rate.bps(), 400'000);
 }
 
-TEST(ProbeControllerTest, PauseAlrProbeWhenLossBasedBweLimited) {
+TEST(ProbeControllerTest, LimitAlrProbeWhenLossBasedBweLimited) {
   ProbeControllerFixture fixture(
       "WebRTC-Bwe-ProbingConfiguration/"
-      "probe_if_bwe_limited_due_to_loss:false/");
+      "limit_probe_target_rate_to_loss_bwe:true/");
   std::unique_ptr<ProbeController> probe_controller =
       fixture.CreateController();
   probe_controller->EnablePeriodicAlrProbing(true);
@@ -527,40 +527,16 @@
       fixture.CurrentTime());
   fixture.AdvanceTime(TimeDelta::Seconds(6));
   probes = probe_controller->Process(fixture.CurrentTime());
-  EXPECT_TRUE(probes.empty());
-  // ALR probing resumed when estimate is no longer restricted by loss based
-  // BWE.
+  ASSERT_EQ(probes.size(), 1u);
+  EXPECT_EQ(probes[0].target_data_rate, DataRate::BitsPerSec(500));
+
   probes = probe_controller->SetEstimatedBitrate(
       DataRate::BitsPerSec(500), /*bwe_limited_due_to_packet_loss=*/false,
       fixture.CurrentTime());
   fixture.AdvanceTime(TimeDelta::Seconds(6));
   probes = probe_controller->Process(fixture.CurrentTime());
-  EXPECT_TRUE(!probes.empty());
-}
-
-TEST(ProbeControllerTest, AlrProbeStartWhenNotLossBasedBweLimited) {
-  ProbeControllerFixture fixture(
-      "WebRTC-Bwe-ProbingConfiguration/"
-      "probe_if_bwe_limited_due_to_loss:false/");
-  std::unique_ptr<ProbeController> probe_controller =
-      fixture.CreateController();
-  probe_controller->EnablePeriodicAlrProbing(true);
-  auto probes = probe_controller->SetBitrates(
-      kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
-  probes = probe_controller->SetEstimatedBitrate(
-      DataRate::BitsPerSec(500), /*bwe_limited_due_to_packet_loss=*/true,
-      fixture.CurrentTime());
-  // Expect the controller to send a new probe after 5s has passed.
-  probe_controller->SetAlrStartTimeMs(fixture.CurrentTime().ms());
-  fixture.AdvanceTime(TimeDelta::Seconds(5));
-  probes = probe_controller->Process(fixture.CurrentTime());
-  EXPECT_TRUE(probes.empty());
-  probes = probe_controller->SetEstimatedBitrate(
-      DataRate::BitsPerSec(500), /*bwe_limited_due_to_packet_loss*/ false,
-      fixture.CurrentTime());
-  fixture.AdvanceTime(TimeDelta::Seconds(1));
-  probes = probe_controller->Process(fixture.CurrentTime());
-  EXPECT_TRUE(!probes.empty());
+  ASSERT_TRUE(!probes.empty());
+  EXPECT_GT(probes[0].target_data_rate, DataRate::BitsPerSec(500));
 }
 
 TEST(ProbeControllerTest, PeriodicProbeAtUpperNetworkStateEstimate) {
@@ -590,10 +566,10 @@
 }
 
 TEST(ProbeControllerTest,
-     PausePeriodicProbeAtUpperNetworkStateEstimateIfLossBasedLimited) {
+     LimitProbeAtUpperNetworkStateEstimateIfLossBasedLimited) {
   ProbeControllerFixture fixture(
       "WebRTC-Bwe-ProbingConfiguration/"
-      "network_state_interval:5s,probe_if_bwe_limited_due_to_loss:false/");
+      "network_state_interval:5s,limit_probe_target_rate_to_loss_bwe:true/");
   std::unique_ptr<ProbeController> probe_controller =
       fixture.CreateController();
 
@@ -616,14 +592,16 @@
   // Expect the controller to send a new probe after 5s has passed.
   fixture.AdvanceTime(TimeDelta::Seconds(5));
   probes = probe_controller->Process(fixture.CurrentTime());
-  EXPECT_TRUE(probes.empty());
+  ASSERT_TRUE(!probes.empty());
+  EXPECT_EQ(probes[0].target_data_rate, DataRate::BitsPerSec(500));
 
   probes = probe_controller->SetEstimatedBitrate(
       DataRate::BitsPerSec(500), /*bwe_limited_due_to_packet_loss=*/false,
       fixture.CurrentTime());
   fixture.AdvanceTime(TimeDelta::Seconds(5));
   probes = probe_controller->Process(fixture.CurrentTime());
-  EXPECT_FALSE(probes.empty());
+  ASSERT_TRUE(!probes.empty());
+  EXPECT_GT(probes[0].target_data_rate, DataRate::BitsPerSec(500));
 }
 
 TEST(ProbeControllerTest, AlrProbesLimitedByNetworkStateEstimate) {
@@ -677,7 +655,7 @@
   EXPECT_EQ(probes[0].target_duration, TimeDelta::Millis(100));
 }
 
-TEST(ProbeControllerTest, ProbeAfterLargeNetworkStateChange) {
+TEST(ProbeControllerTest, ProbeAfterLargeNetworkStateIncrease) {
   ProbeControllerFixture fixture(
       "WebRTC-Bwe-ProbingConfiguration/"
       "network_state_interval:5s,network_state_fast_rampup_rate:2.0/");
@@ -716,5 +694,81 @@
   EXPECT_EQ(probes.size(), 1u);
 }
 
+TEST(ProbeControllerTest, ProbeAfterLargeNetworkStateDrop) {
+  ProbeControllerFixture fixture(
+      "WebRTC-Bwe-ProbingConfiguration/"
+      "network_state_interval:5s,network_state_drop_down_rate:0.5/");
+  std::unique_ptr<ProbeController> probe_controller =
+      fixture.CreateController();
+
+  auto probes = probe_controller->SetBitrates(
+      kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+  probes = probe_controller->SetEstimatedBitrate(
+      kStartBitrate, /*bwe_limited_due_to_packet_loss=*/false,
+      fixture.CurrentTime());
+  // Need to wait at least one second before process can trigger a new probe.
+  fixture.AdvanceTime(TimeDelta::Millis(1100));
+  probes = probe_controller->Process(fixture.CurrentTime());
+  EXPECT_TRUE(probes.empty());
+
+  NetworkStateEstimate state_estimate;
+  state_estimate.link_capacity_upper = kStartBitrate;
+  probe_controller->SetNetworkStateEstimate(state_estimate);
+  // No probe since NetworkStateEstimate is not lower than the set
+  // estimated bitrate.
+  probes = probe_controller->Process(fixture.CurrentTime());
+  EXPECT_TRUE(probes.empty());
+
+  // If NetworkState decrease just a bit, dont expect the probe to be sent
+  // immediately.
+  state_estimate.link_capacity_upper = kStartBitrate * 0.9;
+  probe_controller->SetNetworkStateEstimate(state_estimate);
+  probes = probe_controller->Process(fixture.CurrentTime());
+  EXPECT_TRUE(probes.empty());
+
+  // If NetworkState decrease dramatically, expect a probe to be sent.
+  state_estimate.link_capacity_upper = kStartBitrate * 0.9 * 0.5;
+  probe_controller->SetNetworkStateEstimate(state_estimate);
+  probes = probe_controller->Process(fixture.CurrentTime());
+  EXPECT_EQ(probes.size(), 1u);
+}
+
+TEST(ProbeControllerTest, ProbeAfterLargeNetworkStateDropLossLimited) {
+  ProbeControllerFixture fixture(
+      "WebRTC-Bwe-ProbingConfiguration/"
+      "network_state_interval:5s,network_state_drop_down_rate:0.5,limit_probe_"
+      "target_rate_to_loss_bwe:true/");
+  std::unique_ptr<ProbeController> probe_controller =
+      fixture.CreateController();
+
+  auto probes = probe_controller->SetBitrates(
+      kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+  probes = probe_controller->SetEstimatedBitrate(
+      kStartBitrate, /*bwe_limited_due_to_packet_loss=*/false,
+      fixture.CurrentTime());
+  // Need to wait at least one second before process can trigger a new probe.
+  fixture.AdvanceTime(TimeDelta::Millis(1100));
+  probes = probe_controller->Process(fixture.CurrentTime());
+  EXPECT_TRUE(probes.empty());
+
+  NetworkStateEstimate state_estimate;
+  state_estimate.link_capacity_upper = kStartBitrate;
+  probe_controller->SetNetworkStateEstimate(state_estimate);
+  probes = probe_controller->Process(fixture.CurrentTime());
+  EXPECT_TRUE(probes.empty());
+
+  // Loss limited.
+  probes = probe_controller->SetEstimatedBitrate(
+      kStartBitrate / 3, /*bwe_limited_due_to_packet_loss=*/true,
+      fixture.CurrentTime());
+  // If NetworkState decrease dramatically, expect a probe to be sent.
+  // But limited to loss based estimate.
+  state_estimate.link_capacity_upper = kStartBitrate / 2;
+  probe_controller->SetNetworkStateEstimate(state_estimate);
+  probes = probe_controller->Process(fixture.CurrentTime());
+  ASSERT_EQ(probes.size(), 1u);
+  EXPECT_EQ(probes[0].target_data_rate, kStartBitrate / 3);
+}
+
 }  // namespace test
 }  // namespace webrtc