Change behaviour of repeated initial probes

Repeated initial probes are sent every second until
ProbeController::OnMaxAllocatedBitrate is invoked (Media is beeing sent) or 5s has passed.
Each probe has a duration of 100ms, sent in packet bursts every 20ms.

ProbeController::waiting_for_initial_probe_result_ is no longer needed
and is removed.
Setting field trial for duration between probe packets bursts are moved
from BitrateProber to ProbeController.

Bug: webrtc:14928
Change-Id: I3170533f679fc2cc2aa5402e248fa1f6996d3ccd
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/350640
Reviewed-by: Diep Bui <diepbp@google.com>
Commit-Queue: Per Kjellander <perkj@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42323}
diff --git a/api/transport/network_types.h b/api/transport/network_types.h
index c922cb3..9001e74 100644
--- a/api/transport/network_types.h
+++ b/api/transport/network_types.h
@@ -214,7 +214,10 @@
 struct ProbeClusterConfig {
   Timestamp at_time = Timestamp::PlusInfinity();
   DataRate target_data_rate = DataRate::Zero();
+  // Duration of a probe.
   TimeDelta target_duration = TimeDelta::Zero();
+  // Delta time between sent bursts of packets during probe.
+  TimeDelta min_probe_delta = TimeDelta::Millis(2);
   int32_t target_probe_count = 0;
   int32_t id = 0;
 };
diff --git a/modules/congestion_controller/goog_cc/probe_controller.cc b/modules/congestion_controller/goog_cc/probe_controller.cc
index 1a20807..44cb0a7 100644
--- a/modules/congestion_controller/goog_cc/probe_controller.cc
+++ b/modules/congestion_controller/goog_cc/probe_controller.cc
@@ -89,8 +89,10 @@
       further_exponential_probe_scale("step_size", 2),
       further_probe_threshold("further_probe_threshold", 0.7),
       abort_further_probe_if_max_lower_than_current("abort_further", false),
-      repeated_initial_probing_duration("initial_probing_duration",
-                                        TimeDelta::Seconds(5)),
+      repeated_initial_probing_time_period("initial_probing",
+                                           TimeDelta::Seconds(5)),
+      initial_probe_duration("initial_probe_duration", TimeDelta::Millis(100)),
+      initial_min_probe_delta("initial_min_probe_delta", TimeDelta::Millis(20)),
       alr_probing_interval("alr_interval", TimeDelta::Seconds(5)),
       alr_probe_scale("alr_scale", 2),
       network_state_estimate_probing_interval("network_state_interval",
@@ -104,13 +106,13 @@
       network_state_probe_scale("network_state_scale", 1.0),
       network_state_probe_duration("network_state_probe_duration",
                                    TimeDelta::Millis(15)),
-
       probe_on_max_allocated_bitrate_change("probe_max_allocation", true),
       first_allocation_probe_scale("alloc_p1", 1),
       second_allocation_probe_scale("alloc_p2", 2),
       allocation_probe_limit_by_current_scale("alloc_current_bwe_limit"),
       min_probe_packets_sent("min_probe_packets_sent", 5),
       min_probe_duration("min_probe_duration", TimeDelta::Millis(15)),
+      min_probe_delta("min_probe_delta", TimeDelta::Millis(2)),
       loss_limited_probe_scale("loss_limited_scale", 1.5),
       skip_if_estimate_larger_than_fraction_of_max(
           "skip_if_est_larger_than_fraction_of_max",
@@ -120,7 +122,8 @@
                    &further_exponential_probe_scale,
                    &further_probe_threshold,
                    &abort_further_probe_if_max_lower_than_current,
-                   &repeated_initial_probing_duration,
+                   &repeated_initial_probing_time_period,
+                   &initial_probe_duration,
                    &alr_probing_interval,
                    &alr_probe_scale,
                    &probe_on_max_allocated_bitrate_change,
@@ -128,6 +131,8 @@
                    &second_allocation_probe_scale,
                    &allocation_probe_limit_by_current_scale,
                    &min_probe_duration,
+                   &min_probe_delta,
+                   &initial_min_probe_delta,
                    &network_state_estimate_probing_interval,
                    &probe_if_estimate_lower_than_network_state_estimate_ratio,
                    &estimate_lower_than_network_state_estimate_probing_interval,
@@ -217,7 +222,6 @@
     Timestamp at_time) {
   const bool in_alr = alr_start_time_.has_value();
   const bool allow_allocation_probe = in_alr;
-
   if (config_.probe_on_max_allocated_bitrate_change &&
       state_ == State::kProbingComplete &&
       max_total_allocated_bitrate != max_total_allocated_bitrate_ &&
@@ -257,6 +261,10 @@
 
     return InitiateProbing(at_time, probes, allow_further_probing);
   }
+  if (!max_total_allocated_bitrate.IsZero()) {
+    last_allowed_repeated_initial_probe_ = at_time;
+  }
+
   max_total_allocated_bitrate_ = max_total_allocated_bitrate;
   return std::vector<ProbeClusterConfig>();
 }
@@ -285,7 +293,6 @@
       break;
     case State::kProbingComplete:
       state_ = State::kProbingComplete;
-      waiting_for_initial_probe_result_ = false;
       min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
       break;
   }
@@ -306,13 +313,13 @@
     probes.push_back(config_.second_exponential_probe_scale.Value() *
                      start_bitrate_);
   }
-  waiting_for_initial_probe_result_ = true;
-  if (repeated_initial_probing_enabled_) {
+  if (repeated_initial_probing_enabled_ &&
+      max_total_allocated_bitrate_.IsZero()) {
     last_allowed_repeated_initial_probe_ =
-        at_time + config_.repeated_initial_probing_duration;
+        at_time + config_.repeated_initial_probing_time_period;
     RTC_LOG(LS_INFO) << "Repeated initial probing enabled, last allowed probe: "
                      << last_allowed_repeated_initial_probe_
-                     << "now: " << at_time;
+                     << " now: " << at_time;
   }
 
   return InitiateProbing(at_time, probes, true);
@@ -335,8 +342,6 @@
     if (config_.abort_further_probe_if_max_lower_than_current &&
         (bitrate > max_bitrate_ ||
          (!max_total_allocated_bitrate_.IsZero() &&
-          !(waiting_for_initial_probe_result_ &&
-            repeated_initial_probing_enabled_) &&
           bitrate > 2 * max_total_allocated_bitrate_))) {
       // No need to continue probing.
       min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
@@ -425,7 +430,6 @@
 void ProbeController::Reset(Timestamp at_time) {
   bandwidth_limited_cause_ = BandwidthLimitedCause::kDelayBasedLimited;
   state_ = State::kInit;
-  waiting_for_initial_probe_result_ = false;
   min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
   time_last_probing_initiated_ = Timestamp::Zero();
   estimated_bitrate_ = DataRate::Zero();
@@ -437,7 +441,6 @@
   alr_end_time_.reset();
   time_of_last_large_drop_ = now;
   bitrate_before_last_large_drop_ = DataRate::Zero();
-  max_total_allocated_bitrate_ = DataRate::Zero();
 }
 
 bool ProbeController::TimeForAlrProbe(Timestamp at_time) const {
@@ -506,7 +509,6 @@
     return {};
   }
   if (TimeForNextRepeatedInitialProbe(at_time)) {
-    waiting_for_initial_probe_result_ = true;
     return InitiateProbing(
         at_time, {estimated_bitrate_ * config_.first_exponential_probe_scale},
         true);
@@ -518,6 +520,29 @@
   return std::vector<ProbeClusterConfig>();
 }
 
+ProbeClusterConfig ProbeController::CreateProbeClusterConfig(Timestamp at_time,
+                                                             DataRate bitrate) {
+  ProbeClusterConfig config;
+  config.at_time = at_time;
+  config.target_data_rate = bitrate;
+  if (network_estimate_ &&
+      config_.network_state_estimate_probing_interval->IsFinite()) {
+    config.target_duration = config_.network_state_probe_duration;
+    config.min_probe_delta = config_.min_probe_delta;
+  } else if (at_time < last_allowed_repeated_initial_probe_) {
+    config.target_duration = config_.initial_probe_duration;
+    config.min_probe_delta = config_.initial_min_probe_delta;
+  } else {
+    config.target_duration = config_.min_probe_duration;
+    config.min_probe_delta = config_.min_probe_delta;
+  }
+  config.target_probe_count = config_.min_probe_packets_sent;
+  config.id = next_probe_cluster_id_;
+  next_probe_cluster_id_++;
+  MaybeLogProbeClusterCreated(event_log_, config);
+  return config;
+}
+
 std::vector<ProbeClusterConfig> ProbeController::InitiateProbing(
     Timestamp now,
     std::vector<DataRate> bitrates_to_probe,
@@ -538,9 +563,7 @@
   }
 
   DataRate max_probe_bitrate = max_bitrate_;
-  if (max_total_allocated_bitrate_ > DataRate::Zero() &&
-      !(repeated_initial_probing_enabled_ &&
-        waiting_for_initial_probe_result_)) {
+  if (max_total_allocated_bitrate_ > DataRate::Zero()) {
     // If a max allocated bitrate has been configured, allow probing up to 2x
     // that rate. This allows some overhead to account for bursty streams,
     // which otherwise would have to ramp up when the overshoot is already in
@@ -556,7 +579,8 @@
     case BandwidthLimitedCause::kRttBasedBackOffHighRtt:
     case BandwidthLimitedCause::kDelayBasedLimitedDelayIncreased:
     case BandwidthLimitedCause::kLossLimitedBwe:
-      RTC_LOG(LS_INFO) << "Not sending probe in bandwidth limited state.";
+      RTC_LOG(LS_INFO) << "Not sending probe in bandwidth limited state. "
+                       << static_cast<int>(bandwidth_limited_cause_);
       return {};
     case BandwidthLimitedCause::kLossLimitedBweIncreasing:
       estimate_capped_bitrate =
@@ -590,21 +614,7 @@
       probe_further = false;
     }
 
-    ProbeClusterConfig config;
-    config.at_time = now;
-    config.target_data_rate = bitrate;
-    if (network_estimate_ &&
-        config_.network_state_estimate_probing_interval->IsFinite()) {
-      config.target_duration = config_.network_state_probe_duration;
-    } else {
-      config.target_duration = config_.min_probe_duration;
-    }
-
-    config.target_probe_count = config_.min_probe_packets_sent;
-    config.id = next_probe_cluster_id_;
-    next_probe_cluster_id_++;
-    MaybeLogProbeClusterCreated(event_log_, config);
-    pending_probes.push_back(config);
+    pending_probes.push_back(CreateProbeClusterConfig(now, bitrate));
   }
   time_last_probing_initiated_ = now;
   if (probe_further) {
diff --git a/modules/congestion_controller/goog_cc/probe_controller.h b/modules/congestion_controller/goog_cc/probe_controller.h
index 4a13d17..96c6091 100644
--- a/modules/congestion_controller/goog_cc/probe_controller.h
+++ b/modules/congestion_controller/goog_cc/probe_controller.h
@@ -45,12 +45,16 @@
   FieldTrialParameter<bool> abort_further_probe_if_max_lower_than_current;
   // Duration of time from the first initial probe where repeated initial probes
   // are sent if repeated initial probing is enabled.
-  FieldTrialParameter<TimeDelta> repeated_initial_probing_duration;
-
+  FieldTrialParameter<TimeDelta> repeated_initial_probing_time_period;
+  // The minimum probing duration of an individual probe during
+  // the repeated_initial_probing_time_period.
+  FieldTrialParameter<TimeDelta> initial_probe_duration;
+  // Delta time between sent bursts of packets in a probe during
+  // the repeated_initial_probing_time_period.
+  FieldTrialParameter<TimeDelta> initial_min_probe_delta;
   // Configures how often we send ALR probes and how big they are.
   FieldTrialParameter<TimeDelta> alr_probing_interval;
   FieldTrialParameter<double> alr_probe_scale;
-
   // Configures how often we send probes if NetworkStateEstimate is available.
   FieldTrialParameter<TimeDelta> network_state_estimate_probing_interval;
   // Periodically probe as long as the ratio between current estimate and
@@ -74,6 +78,8 @@
   FieldTrialParameter<int> min_probe_packets_sent;
   // The minimum probing duration.
   FieldTrialParameter<TimeDelta> min_probe_duration;
+  // Delta time between sent bursts of packets in a probe.
+  FieldTrialParameter<TimeDelta> min_probe_delta;
   FieldTrialParameter<double> loss_limited_probe_scale;
   // Don't send a probe if min(estimate, network state estimate) is larger than
   // this fraction of the set max bitrate.
@@ -83,7 +89,7 @@
 // Reason that bandwidth estimate is limited. Bandwidth estimate can be limited
 // by either delay based bwe, or loss based bwe when it increases/decreases the
 // estimate.
-enum class BandwidthLimitedCause {
+enum class BandwidthLimitedCause : int {
   kLossLimitedBweIncreasing = 0,
   kLossLimitedBwe = 1,
   kDelayBasedLimited = 2,
@@ -126,8 +132,10 @@
   void EnablePeriodicAlrProbing(bool enable);
 
   // Probes are sent periodically every 1s during the first 5s after the network
-  // becomes available. The probes ignores allocated bitrate constraints and
-  // probe up to max configured bitrate configured via SetBitrates.
+  // becomes available or until OnMaxTotalAllocatedBitrate is invoked with a
+  // none zero max_total_allocated_bitrate (there are active streams being
+  // sent.) Probe rate is up to max configured bitrate configured via
+  // SetBitrates.
   void EnableRepeatedInitialProbing(bool enable);
 
   void SetAlrStartTimeMs(absl::optional<int64_t> alr_start_time);
@@ -139,8 +147,9 @@
   void SetNetworkStateEstimate(webrtc::NetworkStateEstimate estimate);
 
   // Resets the ProbeController to a state equivalent to as if it was just
-  // created EXCEPT for `enable_periodic_alr_probing_` and
-  // `network_available_`.
+  // created EXCEPT for configuration settings like
+  // `enable_periodic_alr_probing_` `network_available_` and
+  // `max_total_allocated_bitrate_`.
   void Reset(Timestamp at_time);
 
   ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig> Process(
@@ -166,9 +175,10 @@
   bool TimeForAlrProbe(Timestamp at_time) const;
   bool TimeForNetworkStateProbe(Timestamp at_time) const;
   bool TimeForNextRepeatedInitialProbe(Timestamp at_time) const;
+  ProbeClusterConfig CreateProbeClusterConfig(Timestamp at_time,
+                                              DataRate bitrate);
 
   bool network_available_;
-  bool waiting_for_initial_probe_result_ = false;
   bool repeated_initial_probing_enabled_ = false;
   Timestamp last_allowed_repeated_initial_probe_ = Timestamp::MinusInfinity();
   BandwidthLimitedCause bandwidth_limited_cause_ =
diff --git a/modules/congestion_controller/goog_cc/probe_controller_unittest.cc b/modules/congestion_controller/goog_cc/probe_controller_unittest.cc
index 4821485..e75c2c8 100644
--- a/modules/congestion_controller/goog_cc/probe_controller_unittest.cc
+++ b/modules/congestion_controller/goog_cc/probe_controller_unittest.cc
@@ -378,37 +378,6 @@
   EXPECT_THAT(probes, IsEmpty());
 }
 
-TEST(ProbeControllerTest, RepeatedInitialProbingIgnoreLowMaxAllocatedbitrate) {
-  ProbeControllerFixture fixture;
-  std::unique_ptr<ProbeController> probe_controller =
-      fixture.CreateController();
-  ASSERT_THAT(
-      probe_controller->OnNetworkAvailability({.network_available = true}),
-      IsEmpty());
-  auto probes = probe_controller->SetBitrates(
-      kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
-  ASSERT_THAT(probes, SizeIs(Gt(0)));
-  probe_controller->EnableRepeatedInitialProbing(true);
-
-  // Repeated probe is sent when estimated bitrate climbs above
-  // 0.7 * 6 * kStartBitrate = 1260. During the initial probe, we ignore the
-  // allocation limit and probe up to the max.
-  probes = probe_controller->OnMaxTotalAllocatedBitrate(kStartBitrate,
-                                                        fixture.CurrentTime());
-  EXPECT_THAT(probes, IsEmpty());
-
-  probes = probe_controller->SetEstimatedBitrate(
-      DataRate::BitsPerSec(1800), BandwidthLimitedCause::kDelayBasedLimited,
-      fixture.CurrentTime());
-  EXPECT_EQ(probes.size(), 1u);
-  EXPECT_EQ(probes[0].target_data_rate.bps(), 2 * 1800);
-
-  probes = probe_controller->SetEstimatedBitrate(
-      probes[0].target_data_rate, BandwidthLimitedCause::kDelayBasedLimited,
-      fixture.CurrentTime());
-  EXPECT_EQ(probes.size(), 1u);
-}
-
 TEST(ProbeControllerTest, InitialProbingToLowMaxAllocatedbitrate) {
   ProbeControllerFixture fixture;
   std::unique_ptr<ProbeController> probe_controller =
@@ -475,6 +444,8 @@
       // Expect a probe every second.
       EXPECT_EQ(fixture.CurrentTime() - last_probe_time,
                 TimeDelta::Seconds(1.1));
+      EXPECT_EQ(probes[0].min_probe_delta, TimeDelta::Millis(20));
+      EXPECT_EQ(probes[0].target_duration, TimeDelta::Millis(100));
       last_probe_time = fixture.CurrentTime();
     } else {
       EXPECT_LT(fixture.CurrentTime() - last_probe_time,
@@ -486,6 +457,28 @@
   EXPECT_THAT(probe_controller->Process(fixture.CurrentTime()), IsEmpty());
 }
 
+TEST(ProbeControllerTest, RepeatedInitialProbingStopIfMaxAllocatedBitrateSet) {
+  ProbeControllerFixture fixture;
+  std::unique_ptr<ProbeController> probe_controller =
+      fixture.CreateController();
+  probe_controller->EnableRepeatedInitialProbing(true);
+  ASSERT_THAT(
+      probe_controller->OnNetworkAvailability({.network_available = true}),
+      IsEmpty());
+  auto probes = probe_controller->SetBitrates(
+      kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+  EXPECT_THAT(probes, SizeIs(Gt(0)));
+
+  fixture.AdvanceTime(TimeDelta::Millis(1100));
+  probes = probe_controller->Process(fixture.CurrentTime());
+  EXPECT_THAT(probes, SizeIs(1));
+  probes = probe_controller->OnMaxTotalAllocatedBitrate(kMinBitrate,
+                                                        fixture.CurrentTime());
+  fixture.AdvanceTime(TimeDelta::Millis(1100));
+  probes = probe_controller->Process(fixture.CurrentTime());
+  EXPECT_THAT(probes, IsEmpty());
+}
+
 TEST(ProbeControllerTest, RequestProbeInAlr) {
   ProbeControllerFixture fixture;
   std::unique_ptr<ProbeController> probe_controller =
@@ -1274,6 +1267,27 @@
   EXPECT_TRUE(probes.empty());
 }
 
+TEST(ProbeControllerTest, MaxAllocatedBitrateNotReset) {
+  ProbeControllerFixture fixture;
+  std::unique_ptr<ProbeController> probe_controller =
+      fixture.CreateController();
+  ASSERT_THAT(
+      probe_controller->OnNetworkAvailability({.network_available = true}),
+      IsEmpty());
+  auto probes = probe_controller->SetBitrates(
+      kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+  ASSERT_FALSE(probes.empty());
+
+  probes = probe_controller->OnMaxTotalAllocatedBitrate(kStartBitrate / 4,
+                                                        fixture.CurrentTime());
+  probe_controller->Reset(fixture.CurrentTime());
+
+  probes = probe_controller->SetBitrates(kMinBitrate, kStartBitrate,
+                                         kMaxBitrate, fixture.CurrentTime());
+  ASSERT_FALSE(probes.empty());
+  EXPECT_EQ(probes[0].target_data_rate, kStartBitrate / 4 * 2);
+}
+
 TEST(ProbeControllerTest, SkipAlrProbeIfEstimateLargerThanMaxProbe) {
   ProbeControllerFixture fixture(
       "WebRTC-Bwe-ProbingConfiguration/"
diff --git a/modules/pacing/BUILD.gn b/modules/pacing/BUILD.gn
index a24bccd..e8e19b1 100644
--- a/modules/pacing/BUILD.gn
+++ b/modules/pacing/BUILD.gn
@@ -98,6 +98,7 @@
       ":interval_budget",
       ":pacing",
       "../../api/task_queue:task_queue",
+      "../../api/transport:field_trial_based_config",
       "../../api/transport:network_control",
       "../../api/units:data_rate",
       "../../api/units:data_size",
diff --git a/modules/pacing/bitrate_prober.cc b/modules/pacing/bitrate_prober.cc
index 17729b5..29b4f4c 100644
--- a/modules/pacing/bitrate_prober.cc
+++ b/modules/pacing/bitrate_prober.cc
@@ -12,7 +12,9 @@
 
 #include <algorithm>
 
+#include "api/field_trials_view.h"
 #include "api/units/data_size.h"
+#include "api/units/time_delta.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 
@@ -26,10 +28,9 @@
 
 BitrateProberConfig::BitrateProberConfig(
     const FieldTrialsView* key_value_config)
-    : min_probe_delta("min_probe_delta", TimeDelta::Millis(2)),
-      max_probe_delay("max_probe_delay", TimeDelta::Millis(10)),
+    : max_probe_delay("max_probe_delay", TimeDelta::Millis(10)),
       min_packet_size("min_packet_size", DataSize::Bytes(200)) {
-  ParseFieldTrial({&min_probe_delta, &max_probe_delay, &min_packet_size},
+  ParseFieldTrial({&max_probe_delay, &min_packet_size},
                   key_value_config->Lookup("WebRTC-Bwe-ProbingBehavior"));
 }
 
@@ -93,6 +94,7 @@
 void BitrateProber::CreateProbeCluster(
     const ProbeClusterConfig& cluster_config) {
   RTC_DCHECK(probing_state_ != ProbingState::kDisabled);
+  RTC_DCHECK(cluster_config.min_probe_delta > TimeDelta::Zero());
 
   while (!clusters_.empty() &&
          (cluster_config.at_time - clusters_.front().requested_at >
@@ -109,6 +111,7 @@
       (cluster_config.target_data_rate * cluster_config.target_duration)
           .bytes();
   RTC_DCHECK_GE(cluster.pace_info.probe_cluster_min_bytes, 0);
+  cluster.min_probe_delta = cluster_config.min_probe_delta;
   cluster.pace_info.send_bitrate = cluster_config.target_data_rate;
   cluster.pace_info.probe_cluster_id = cluster_config.id;
   clusters_.push(cluster);
@@ -164,7 +167,7 @@
     return DataSize::Zero();
   }
   DataRate send_rate = clusters_.front().pace_info.send_bitrate;
-  return send_rate * config_.min_probe_delta;
+  return send_rate * clusters_.front().min_probe_delta;
 }
 
 void BitrateProber::ProbeSent(Timestamp now, DataSize size) {
diff --git a/modules/pacing/bitrate_prober.h b/modules/pacing/bitrate_prober.h
index 821bbf3..149d24f 100644
--- a/modules/pacing/bitrate_prober.h
+++ b/modules/pacing/bitrate_prober.h
@@ -16,8 +16,10 @@
 
 #include <queue>
 
-#include "api/transport/field_trial_based_config.h"
+#include "api/field_trials_view.h"
 #include "api/transport/network_types.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
 #include "rtc_base/experiments/field_trial_parser.h"
 
 namespace webrtc {
@@ -29,8 +31,6 @@
   BitrateProberConfig& operator=(const BitrateProberConfig&) = default;
   ~BitrateProberConfig() = default;
 
-  // A minimum interval between probes to allow scheduling to be feasible.
-  FieldTrialParameter<TimeDelta> min_probe_delta;
   // Maximum amount of time each probe can be delayed.
   FieldTrialParameter<TimeDelta> max_probe_delay;
   // This is used to start sending a probe after a large enough packet.
@@ -103,9 +103,9 @@
 
     int sent_probes = 0;
     int sent_bytes = 0;
+    TimeDelta min_probe_delta = TimeDelta::Zero();
     Timestamp requested_at = Timestamp::MinusInfinity();
     Timestamp started_at = Timestamp::MinusInfinity();
-    int retries = 0;
   };
 
   Timestamp CalculateNextProbeTime(const ProbeCluster& cluster) const;
diff --git a/modules/pacing/bitrate_prober_unittest.cc b/modules/pacing/bitrate_prober_unittest.cc
index 3c1c93f..2954398 100644
--- a/modules/pacing/bitrate_prober_unittest.cc
+++ b/modules/pacing/bitrate_prober_unittest.cc
@@ -12,6 +12,7 @@
 
 #include <algorithm>
 
+#include "api/transport/field_trial_based_config.h"
 #include "api/transport/network_types.h"
 #include "api/units/data_rate.h"
 #include "api/units/time_delta.h"
@@ -39,11 +40,13 @@
   prober.CreateProbeCluster({.at_time = now,
                              .target_data_rate = kTestBitrate1,
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 0});
   prober.CreateProbeCluster({.at_time = now,
                              .target_data_rate = kTestBitrate2,
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 1});
   EXPECT_FALSE(prober.is_probing());
@@ -100,6 +103,7 @@
   prober.CreateProbeCluster({.at_time = now,
                              .target_data_rate = DataRate::KilobitsPerSec(900),
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 0});
   EXPECT_FALSE(prober.is_probing());
@@ -125,6 +129,7 @@
   prober.CreateProbeCluster({.at_time = now,
                              .target_data_rate = DataRate::KilobitsPerSec(900),
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 0});
 
@@ -154,6 +159,7 @@
   prober.CreateProbeCluster({.at_time = now,
                              .target_data_rate = DataRate::KilobitsPerSec(900),
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 0});
   prober.OnIncomingPacket(kProbeSize);
@@ -165,6 +171,7 @@
         {.at_time = now,
          .target_data_rate = DataRate::KilobitsPerSec(900),
          .target_duration = TimeDelta::Millis(15),
+         .min_probe_delta = TimeDelta::Millis(2),
          .target_probe_count = 5,
          .id = i});
     prober.OnIncomingPacket(kProbeSize);
@@ -190,6 +197,7 @@
   prober.CreateProbeCluster({.at_time = Timestamp::Zero(),
                              .target_data_rate = DataRate::KilobitsPerSec(1000),
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 0});
   prober.OnIncomingPacket(DataSize::Bytes(100));
@@ -208,6 +216,7 @@
   prober.CreateProbeCluster({.at_time = Timestamp::Zero(),
                              .target_data_rate = DataRate::KilobitsPerSec(1000),
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 0});
   prober.OnIncomingPacket(DataSize::Bytes(10));
@@ -224,6 +233,7 @@
   prober.CreateProbeCluster({.at_time = Timestamp::Zero(),
                              .target_data_rate = kHighBitrate,
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 0});
   // Probe size should ensure a minimum of 1 ms interval.
@@ -231,10 +241,9 @@
             kHighBitrate * TimeDelta::Millis(1));
 }
 
-TEST(BitrateProberTest, ProbeSizeCanBeSetWithFieldTrial) {
-  const test::ExplicitKeyValueConfig trials(
-      "WebRTC-Bwe-ProbingBehavior/min_probe_delta:20ms/");
-  BitrateProber prober(trials);
+TEST(BitrateProberTest, ProbeSizeCanBeSetInProbeClusterConfig) {
+  const FieldTrialBasedConfig config;
+  BitrateProber prober(config);
   prober.SetEnabled(true);
 
   const DataRate kHighBitrate = DataRate::KilobitsPerSec(10000);  // 10 Mbps
@@ -242,6 +251,7 @@
   prober.CreateProbeCluster({.at_time = Timestamp::Zero(),
                              .target_data_rate = kHighBitrate,
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(20),
                              .target_probe_count = 5,
                              .id = 0});
   EXPECT_EQ(prober.RecommendedMinProbeSize(),
@@ -267,6 +277,7 @@
   prober.CreateProbeCluster({.at_time = Timestamp::Zero(),
                              .target_data_rate = kBitrate,
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 0});
 
@@ -290,6 +301,7 @@
   prober.CreateProbeCluster({.at_time = Timestamp::Zero(),
                              .target_data_rate = kBitrate,
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 0});
   prober.OnIncomingPacket(kPacketSize);
@@ -314,6 +326,7 @@
   prober.CreateProbeCluster({.at_time = Timestamp::Zero(),
                              .target_data_rate = kBitrate,
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 0});
   prober.OnIncomingPacket(kPacketSize);
@@ -340,6 +353,7 @@
   prober.CreateProbeCluster({.at_time = now,
                              .target_data_rate = kBitrate,
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 0});
   prober.OnIncomingPacket(kSmallPacketSize);
@@ -348,6 +362,7 @@
   prober.CreateProbeCluster({.at_time = now,
                              .target_data_rate = kBitrate / 10,
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 1});
   prober.OnIncomingPacket(kSmallPacketSize);
@@ -356,6 +371,7 @@
   prober.CreateProbeCluster({.at_time = now,
                              .target_data_rate = kBitrate / 10,
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 2});
   prober.OnIncomingPacket(kSmallPacketSize);
@@ -378,6 +394,7 @@
   prober.CreateProbeCluster({.at_time = Timestamp::Zero(),
                              .target_data_rate = DataRate::KilobitsPerSec(300),
                              .target_duration = TimeDelta::Millis(15),
+                             .min_probe_delta = TimeDelta::Millis(2),
                              .target_probe_count = 5,
                              .id = 0});
   EXPECT_TRUE(prober.is_probing());
@@ -392,6 +409,7 @@
       .at_time = Timestamp::Zero(),
       .target_data_rate = DataRate::KilobitsPerSec(300),
       .target_duration = TimeDelta::Millis(15),
+      .min_probe_delta = TimeDelta::Millis(2),
       .target_probe_count = 1,
       .id = 0};
   prober.CreateProbeCluster(cluster_config);
diff --git a/modules/pacing/task_queue_paced_sender_unittest.cc b/modules/pacing/task_queue_paced_sender_unittest.cc
index f0a9ad7..fda6615 100644
--- a/modules/pacing/task_queue_paced_sender_unittest.cc
+++ b/modules/pacing/task_queue_paced_sender_unittest.cc
@@ -443,8 +443,7 @@
 TEST(TaskQueuePacedSenderTest, NoMinSleepTimeWhenProbing) {
   // Set min_probe_delta to be less than kMinSleepTime (1ms).
   const TimeDelta kMinProbeDelta = TimeDelta::Micros(200);
-  ScopedKeyValueConfig trials(
-      "WebRTC-Bwe-ProbingBehavior/min_probe_delta:200us/");
+  ScopedKeyValueConfig trials;
   GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234));
   MockPacketRouter packet_router;
   TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials,
@@ -473,6 +472,7 @@
       {{.at_time = time_controller.GetClock()->CurrentTime(),
         .target_data_rate = kProbingRate,
         .target_duration = TimeDelta::Millis(15),
+        .min_probe_delta = kMinProbeDelta,
         .target_probe_count = 5,
         .id = kProbeClusterId}});
 
diff --git a/test/peer_scenario/tests/bwe_ramp_up_test.cc b/test/peer_scenario/tests/bwe_ramp_up_test.cc
index f8eaa47..909d989 100644
--- a/test/peer_scenario/tests/bwe_ramp_up_test.cc
+++ b/test/peer_scenario/tests/bwe_ramp_up_test.cc
@@ -199,7 +199,7 @@
 
   // Test that 1s after offer/answer exchange finish, we have a BWE estimate,
   // even though no video frames have been sent.
-  s.ProcessMessages(TimeDelta::Seconds(1));
+  s.ProcessMessages(TimeDelta::Seconds(2));
 
   auto callee_inbound_stats =
       GetStatsAndProcess(s, callee)->GetStatsOfType<RTCInboundRtpStreamStats>();