diff --git a/modules/congestion_controller/goog_cc/BUILD.gn b/modules/congestion_controller/goog_cc/BUILD.gn index 326571e804..5a2d512181 100644 --- a/modules/congestion_controller/goog_cc/BUILD.gn +++ b/modules/congestion_controller/goog_cc/BUILD.gn @@ -179,6 +179,7 @@ rtc_source_set("probe_controller") { "../../../rtc_base:logging", "../../../rtc_base:macromagic", "../../../rtc_base:safe_conversions", + "../../../rtc_base/experiments:field_trial_parser", "../../../rtc_base/system:unused", "../../../system_wrappers:metrics", "//third_party/abseil-cpp/absl/memory:memory", diff --git a/modules/congestion_controller/goog_cc/probe_controller.cc b/modules/congestion_controller/goog_cc/probe_controller.cc index 9128fe78eb..dfcb7f6fd5 100644 --- a/modules/congestion_controller/goog_cc/probe_controller.cc +++ b/modules/congestion_controller/goog_cc/probe_controller.cc @@ -45,15 +45,6 @@ constexpr int kExponentialProbingDisabled = 0; // specify max bitrate. constexpr int64_t kDefaultMaxProbingBitrateBps = 5000000; -// Interval between probes when ALR periodic probing is enabled. -constexpr int64_t kAlrPeriodicProbingIntervalMs = 5000; - -// Minimum probe bitrate percentage to probe further for repeated probes, -// relative to the previous probe. For example, if 1Mbps probe results in -// 80kbps, then we'll probe again at 1.6Mbps. In that case second probe won't be -// sent if we get 600kbps from the first one. -constexpr int kRepeatedProbeMinPercentage = 70; - // If the bitrate drops to a factor |kBitrateDropThreshold| or lower // and we recover within |kBitrateDropTimeoutMs|, then we'll send // a probe at a fraction |kProbeFractionAfterDrop| of the original bitrate. @@ -82,6 +73,9 @@ constexpr char kBweRapidRecoveryExperiment[] = // Never probe higher than configured by OnMaxTotalAllocatedBitrate(). constexpr char kCappedProbingFieldTrialName[] = "WebRTC-BweCappedProbing"; +constexpr char kConfigurableProbingFieldTrialName[] = + "WebRTC-Bwe-ConfigurableProbing"; + void MaybeLogProbeClusterCreated(RtcEventLog* event_log, const ProbeClusterConfig& probe) { RTC_DCHECK(event_log); @@ -98,6 +92,25 @@ void MaybeLogProbeClusterCreated(RtcEventLog* event_log, } // namespace +ProbeControllerConfig::ProbeControllerConfig( + const WebRtcKeyValueConfig* key_value_config) + : first_exponential_probe_scale_("p1", 3.0), + second_exponential_probe_scale_("p2", 6.0), + further_exponential_probe_scale_("step_size", 2), + further_probe_threshold("further_probe_threshold", 0.7), + alr_probing_interval_("alr_interval", TimeDelta::seconds(5)), + alr_probe_scale_("alr_scale", 2) { + ParseFieldTrial( + {&first_exponential_probe_scale_, &second_exponential_probe_scale_, + &further_exponential_probe_scale_, &further_probe_threshold, + &alr_probing_interval_, &alr_probe_scale_}, + key_value_config->Lookup(kConfigurableProbingFieldTrialName)); +} + +ProbeControllerConfig::ProbeControllerConfig(const ProbeControllerConfig&) = + default; +ProbeControllerConfig::~ProbeControllerConfig() = default; + ProbeController::ProbeController(const WebRtcKeyValueConfig* key_value_config, RtcEventLog* event_log) : enable_periodic_alr_probing_(false), @@ -107,7 +120,8 @@ ProbeController::ProbeController(const WebRtcKeyValueConfig* key_value_config, limit_probes_with_allocateable_rate_( key_value_config->Lookup(kCappedProbingFieldTrialName) .find("Disabled") != 0), - event_log_(event_log) { + event_log_(event_log), + config_(ProbeControllerConfig(key_value_config)) { Reset(0); } @@ -204,8 +218,13 @@ std::vector ProbeController::InitiateExponentialProbing( // When probing at 1.8 Mbps ( 6x 300), this represents a threshold of // 1.2 Mbps to continue probing. - return InitiateProbing( - at_time_ms, {3 * start_bitrate_bps_, 6 * start_bitrate_bps_}, true); + std::vector probes = {static_cast( + config_.first_exponential_probe_scale_ * start_bitrate_bps_)}; + if (config_.second_exponential_probe_scale_) { + probes.push_back(config_.second_exponential_probe_scale_.Value() * + start_bitrate_bps_); + } + return InitiateProbing(at_time_ms, probes, true); } std::vector ProbeController::SetEstimatedBitrate( @@ -229,8 +248,11 @@ std::vector ProbeController::SetEstimatedBitrate( if (min_bitrate_to_probe_further_bps_ != kExponentialProbingDisabled && bitrate_bps > min_bitrate_to_probe_further_bps_) { - // Double the probing bitrate. - pending_probes = InitiateProbing(at_time_ms, {2 * bitrate_bps}, true); + pending_probes = InitiateProbing( + at_time_ms, + {static_cast(config_.further_exponential_probe_scale_ * + bitrate_bps)}, + true); } } @@ -291,16 +313,6 @@ std::vector ProbeController::RequestProbe( return std::vector(); } -std::vector ProbeController::InitiateCapacityProbing( - int64_t bitrate_bps, - int64_t at_time_ms) { - if (state_ != State::kWaitingForProbingResult) { - RTC_DCHECK(network_available_); - return InitiateProbing(at_time_ms, {2 * bitrate_bps}, true); - } - return std::vector(); -} - void ProbeController::SetMaxBitrate(int64_t max_bitrate_bps) { max_bitrate_bps_ = max_bitrate_bps; } @@ -339,9 +351,12 @@ std::vector ProbeController::Process(int64_t at_time_ms) { if (alr_start_time_ms_ && estimated_bitrate_bps_ > 0) { int64_t next_probe_time_ms = std::max(*alr_start_time_ms_, time_last_probing_initiated_ms_) + - kAlrPeriodicProbingIntervalMs; + config_.alr_probing_interval_->ms(); if (at_time_ms >= next_probe_time_ms) { - return InitiateProbing(at_time_ms, {estimated_bitrate_bps_ * 2}, true); + return InitiateProbing(at_time_ms, + {static_cast(estimated_bitrate_bps_ * + config_.alr_probe_scale_)}, + true); } } } @@ -350,7 +365,7 @@ std::vector ProbeController::Process(int64_t at_time_ms) { std::vector ProbeController::InitiateProbing( int64_t now_ms, - std::initializer_list bitrates_to_probe, + std::vector bitrates_to_probe, bool probe_further) { int64_t max_probe_bitrate_bps = max_bitrate_bps_ > 0 ? max_bitrate_bps_ : kDefaultMaxProbingBitrateBps; @@ -389,7 +404,7 @@ std::vector ProbeController::InitiateProbing( if (probe_further) { state_ = State::kWaitingForProbingResult; min_bitrate_to_probe_further_bps_ = - (*(bitrates_to_probe.end() - 1)) * kRepeatedProbeMinPercentage / 100; + (*(bitrates_to_probe.end() - 1)) * config_.further_probe_threshold; } else { state_ = State::kProbingComplete; min_bitrate_to_probe_further_bps_ = kExponentialProbingDisabled; diff --git a/modules/congestion_controller/goog_cc/probe_controller.h b/modules/congestion_controller/goog_cc/probe_controller.h index f9379878ac..d0efb27d50 100644 --- a/modules/congestion_controller/goog_cc/probe_controller.h +++ b/modules/congestion_controller/goog_cc/probe_controller.h @@ -21,12 +21,34 @@ #include "api/transport/webrtc_key_value_config.h" #include "logging/rtc_event_log/rtc_event_log.h" #include "rtc_base/constructor_magic.h" +#include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/system/unused.h" namespace webrtc { class Clock; +struct ProbeControllerConfig { + explicit ProbeControllerConfig(const WebRtcKeyValueConfig* key_value_config); + ProbeControllerConfig(const ProbeControllerConfig&); + ProbeControllerConfig& operator=(const ProbeControllerConfig&) = default; + ~ProbeControllerConfig(); + + // These parameters configure the initial probes. First we send one or two + // probes of sizes p1 * start_bitrate_bps_ and p2 * start_bitrate_bps_. + // Then whenever we get a bitrate estimate of at least further_probe_threshold + // times the size of the last sent probe we'll send another one of size + // step_size times the new estimate. + FieldTrialParameter first_exponential_probe_scale_; + FieldTrialOptional second_exponential_probe_scale_; + FieldTrialParameter further_exponential_probe_scale_; + FieldTrialParameter further_probe_threshold; + + // Configures how often we send ALR probes and how big they are. + FieldTrialParameter alr_probing_interval_; + FieldTrialParameter alr_probe_scale_; +}; + // This class controls initiation of probing to estimate initial channel // capacity. There is also support for probing during a session when max // bitrate is adjusted by an application. @@ -63,9 +85,6 @@ class ProbeController { RTC_WARN_UNUSED_RESULT std::vector RequestProbe( int64_t at_time_ms); - RTC_WARN_UNUSED_RESULT std::vector - InitiateCapacityProbing(int64_t bitrate_bps, int64_t at_time_ms); - // Sets a new maximum probing bitrate, without generating a new probe cluster. void SetMaxBitrate(int64_t max_bitrate_bps); @@ -90,7 +109,7 @@ class ProbeController { InitiateExponentialProbing(int64_t at_time_ms); RTC_WARN_UNUSED_RESULT std::vector InitiateProbing( int64_t now_ms, - std::initializer_list bitrates_to_probe, + std::vector bitrates_to_probe, bool probe_further); bool network_available_; @@ -118,6 +137,8 @@ class ProbeController { int32_t next_probe_cluster_id_ = 1; + ProbeControllerConfig config_; + RTC_DISALLOW_COPY_AND_ASSIGN(ProbeController); }; diff --git a/modules/congestion_controller/goog_cc/probe_controller_unittest.cc b/modules/congestion_controller/goog_cc/probe_controller_unittest.cc index 112fe647d7..2fa1e22a2c 100644 --- a/modules/congestion_controller/goog_cc/probe_controller_unittest.cc +++ b/modules/congestion_controller/goog_cc/probe_controller_unittest.cc @@ -15,7 +15,9 @@ #include "api/units/timestamp.h" #include "logging/rtc_event_log/mock/mock_rtc_event_log.h" #include "modules/congestion_controller/goog_cc/probe_controller.h" +#include "rtc_base/logging.h" #include "system_wrappers/include/clock.h" +#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" @@ -61,7 +63,7 @@ class ProbeControllerTest : public ::testing::Test { FieldTrialBasedConfig field_trial_config_; SimulatedClock clock_; - MockRtcEventLog mock_rtc_event_log; + NiceMock mock_rtc_event_log; std::unique_ptr probe_controller_; }; @@ -89,6 +91,7 @@ TEST_F(ProbeControllerTest, InitiatesProbingOnMaxBitrateIncrease) { probes = probe_controller_->Process(NowMs()); probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps, kMaxBitrateBps + 100, NowMs()); + EXPECT_EQ(probes.size(), 1u); EXPECT_EQ(probes[0].target_data_rate.bps(), kMaxBitrateBps + 100); } @@ -102,6 +105,7 @@ TEST_F(ProbeControllerTest, InitiatesProbingOnMaxBitrateIncreaseAtMaxBitrate) { probes = probe_controller_->SetEstimatedBitrate(kMaxBitrateBps, NowMs()); probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps, kMaxBitrateBps + 100, NowMs()); + EXPECT_EQ(probes.size(), 1u); EXPECT_EQ(probes[0].target_data_rate.bps(), kMaxBitrateBps + 100); } @@ -115,6 +119,7 @@ TEST_F(ProbeControllerTest, TestExponentialProbing) { EXPECT_EQ(probes.size(), 0u); probes = probe_controller_->SetEstimatedBitrate(1800, NowMs()); + EXPECT_EQ(probes.size(), 1u); EXPECT_EQ(probes[0].target_data_rate.bps(), 2 * 1800); } @@ -252,6 +257,7 @@ TEST_F(ProbeControllerTest, PeriodicProbingAfterReset) { // until SetEstimatedBitrate is called with an updated estimate. clock_.AdvanceTimeMilliseconds(10000); probes = probe_controller_->Process(NowMs()); + EXPECT_EQ(probes.size(), 1u); EXPECT_EQ(probes[0].target_data_rate.bps(), kStartBitrateBps * 2); } @@ -262,6 +268,7 @@ TEST_F(ProbeControllerTest, TestExponentialProbingOverflow) { // Verify that probe bitrate is capped at the specified max bitrate. probes = probe_controller_->SetEstimatedBitrate(60 * kMbpsMultiplier, NowMs()); + EXPECT_EQ(probes.size(), 1u); EXPECT_EQ(probes[0].target_data_rate.bps(), 100 * kMbpsMultiplier); // Verify that repeated probes aren't sent. probes = @@ -293,6 +300,7 @@ TEST_F(ProbeControllerTest, TestAllocatedBitrateCap) { // Probes such as ALR capped at 2x the max allocation limit. clock_.AdvanceTimeMilliseconds(5000); probes = probe_controller_->Process(NowMs()); + EXPECT_EQ(probes.size(), 1u); EXPECT_EQ(probes[0].target_data_rate.bps(), 2 * max_allocated_bps); // Remove allocation limit. @@ -300,8 +308,31 @@ TEST_F(ProbeControllerTest, TestAllocatedBitrateCap) { probe_controller_->OnMaxTotalAllocatedBitrate(0, NowMs()).empty()); clock_.AdvanceTimeMilliseconds(5000); probes = probe_controller_->Process(NowMs()); + EXPECT_EQ(probes.size(), 1u); EXPECT_EQ(probes[0].target_data_rate.bps(), estimated_bitrate_bps * 2); } +TEST_F(ProbeControllerTest, ConfigurableProbingFieldTrial) { + auto trials = test::ScopedFieldTrials( + "WebRTC-Bwe-ConfigurableProbing/" + "p1:2,p2:5,step_size:3,further_probe_threshold:0.8/"); + probe_controller_.reset( + new ProbeController(&field_trial_config_, &mock_rtc_event_log)); + auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps, + kMaxBitrateBps, NowMs()); + EXPECT_EQ(probes.size(), 2u); + EXPECT_EQ(probes[0].target_data_rate.bps(), 600); + EXPECT_EQ(probes[1].target_data_rate.bps(), 1500); + + // Repeated probe should only be sent when estimated bitrate climbs above + // 0.8 * 5 * kStartBitrateBps = 1200. + probes = probe_controller_->SetEstimatedBitrate(1100, NowMs()); + EXPECT_EQ(probes.size(), 0u); + + probes = probe_controller_->SetEstimatedBitrate(1250, NowMs()); + EXPECT_EQ(probes.size(), 1u); + EXPECT_EQ(probes[0].target_data_rate.bps(), 3 * 1250); +} + } // namespace test } // namespace webrtc