diff --git a/BUILD.gn b/BUILD.gn index edb033c8b2..de5e773036 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -436,6 +436,7 @@ if (rtc_include_tests) { "rtc_base:rtc_task_queue_unittests", "rtc_base:sequenced_task_checker_unittests", "rtc_base:weak_ptr_unittests", + "rtc_base/experiments:experiments_unittests", "system_wrappers:metrics_default", "system_wrappers:runtime_enabled_features_default", ] diff --git a/modules/congestion_controller/bbr/BUILD.gn b/modules/congestion_controller/bbr/BUILD.gn index 985b578854..61ae174afd 100644 --- a/modules/congestion_controller/bbr/BUILD.gn +++ b/modules/congestion_controller/bbr/BUILD.gn @@ -33,6 +33,7 @@ rtc_source_set("bbr_controller") { "../../../api:optional", "../../../rtc_base:checks", "../../../rtc_base:rtc_base_approved", + "../../../rtc_base/experiments:congestion_controller_experiment", "../../../rtc_base/system:fallthrough", "../network_control", ] diff --git a/modules/congestion_controller/bbr/bbr_network_controller.cc b/modules/congestion_controller/bbr/bbr_network_controller.cc index 40f28b95d1..81ca741e84 100644 --- a/modules/congestion_controller/bbr/bbr_network_controller.cc +++ b/modules/congestion_controller/bbr/bbr_network_controller.cc @@ -18,6 +18,7 @@ #include "modules/congestion_controller/network_control/include/network_units.h" #include "modules/congestion_controller/network_control/include/network_units_to_string.h" #include "rtc_base/checks.h" +#include "rtc_base/experiments/congestion_controller_experiment.h" #include "rtc_base/logging.h" #include "rtc_base/system/fallthrough.h" @@ -123,6 +124,40 @@ BbrNetworkController::BbrControllerConfig::DefaultConfig() { return config; } +BbrNetworkController::BbrControllerConfig +BbrNetworkController::BbrControllerConfig::ExperimentConfig() { + auto exp = CongestionControllerExperiment::GetBbrExperimentConfig(); + if (exp) { + BbrControllerConfig config; + config.exit_startup_on_loss = exp->exit_startup_on_loss; + config.exit_startup_rtt_threshold_ms = exp->exit_startup_rtt_threshold_ms; + config.fully_drain_queue = exp->fully_drain_queue; + config.initial_conservation_in_startup = + static_cast(exp->initial_conservation_in_startup); + config.num_startup_rtts = exp->num_startup_rtts; + config.probe_rtt_based_on_bdp = exp->probe_rtt_based_on_bdp; + config.probe_rtt_disabled_if_app_limited = + exp->probe_rtt_disabled_if_app_limited; + config.probe_rtt_skipped_if_similar_rtt = + exp->probe_rtt_skipped_if_similar_rtt; + config.rate_based_recovery = exp->rate_based_recovery; + config.rate_based_startup = exp->rate_based_startup; + config.slower_startup = exp->slower_startup; + config.encoder_rate_gain = exp->encoder_rate_gain; + config.encoder_rate_gain_in_probe_rtt = exp->encoder_rate_gain_in_probe_rtt; + config.max_ack_height_window_multiplier = + exp->max_ack_height_window_multiplier; + config.max_aggregation_bytes_multiplier = + exp->max_aggregation_bytes_multiplier; + config.probe_bw_pacing_gain_offset = exp->probe_bw_pacing_gain_offset; + config.probe_rtt_congestion_window_gain = + exp->probe_rtt_congestion_window_gain; + return config; + } else { + return DefaultConfig(); + } +} + BbrNetworkController::DebugState::DebugState(const BbrNetworkController& sender) : mode(sender.mode_), max_bandwidth(sender.max_bandwidth_.GetBest()), @@ -154,7 +189,7 @@ BbrNetworkController::BbrNetworkController(NetworkControllerObserver* observer, congestion_window_gain_constant_(kProbeBWCongestionWindowGain), rtt_variance_weight_(kBbrRttVariationWeight), recovery_window_(max_congestion_window_) { - config_ = BbrControllerConfig::DefaultConfig(); + config_ = BbrControllerConfig::ExperimentConfig(); if (config.starting_bandwidth.IsFinite()) default_bandwidth_ = config.starting_bandwidth; constraints_ = config.constraints; diff --git a/modules/congestion_controller/bbr/bbr_network_controller.h b/modules/congestion_controller/bbr/bbr_network_controller.h index d39d4a8904..be3be4976d 100644 --- a/modules/congestion_controller/bbr/bbr_network_controller.h +++ b/modules/congestion_controller/bbr/bbr_network_controller.h @@ -117,6 +117,7 @@ class BbrNetworkController : public NetworkControllerInterface { struct BbrControllerConfig { // Default config based on default QUIC config static BbrControllerConfig DefaultConfig(); + static BbrControllerConfig ExperimentConfig(); double probe_bw_pacing_gain_offset; double encoder_rate_gain; diff --git a/modules/congestion_controller/rtp/BUILD.gn b/modules/congestion_controller/rtp/BUILD.gn index a1e15ad25e..bae6053382 100644 --- a/modules/congestion_controller/rtp/BUILD.gn +++ b/modules/congestion_controller/rtp/BUILD.gn @@ -44,6 +44,7 @@ rtc_static_library("congestion_controller") { "../../../rtc_base:rtc_task_queue_api", "../../../rtc_base:safe_minmax", "../../../rtc_base:sequenced_task_checker", + "../../../rtc_base/experiments:congestion_controller_experiment", "../../../system_wrappers", "../../../system_wrappers:field_trial_api", "../../../system_wrappers:metrics_api", @@ -51,6 +52,7 @@ rtc_static_library("congestion_controller") { "../../pacing", "../../remote_bitrate_estimator", "../../rtp_rtcp:rtp_rtcp_format", + "../bbr", "../goog_cc", "../network_control", ] diff --git a/modules/congestion_controller/rtp/include/send_side_congestion_controller.h b/modules/congestion_controller/rtp/include/send_side_congestion_controller.h index 5d2ae0238a..db992030b7 100644 --- a/modules/congestion_controller/rtp/include/send_side_congestion_controller.h +++ b/modules/congestion_controller/rtp/include/send_side_congestion_controller.h @@ -151,6 +151,8 @@ class SendSideCongestionController private: void MaybeCreateControllers() RTC_RUN_ON(task_queue_ptr_); + void MaybeRecreateControllers() RTC_RUN_ON(task_queue_ptr_); + void StartProcessPeriodicTasks() RTC_RUN_ON(task_queue_ptr_); void UpdateControllerWithTimeInterval() RTC_RUN_ON(task_queue_ptr_); void UpdatePacerQueue() RTC_RUN_ON(task_queue_ptr_); @@ -167,7 +169,10 @@ class SendSideCongestionController // TODO(srte): Move all access to feedback adapter to task queue. TransportFeedbackAdapter transport_feedback_adapter_; - const std::unique_ptr controller_factory_; + const std::unique_ptr + controller_factory_with_feedback_ RTC_GUARDED_BY(task_queue_ptr_); + const std::unique_ptr + controller_factory_fallback_ RTC_GUARDED_BY(task_queue_ptr_); const std::unique_ptr pacer_controller_ RTC_GUARDED_BY(task_queue_ptr_); @@ -195,6 +200,8 @@ class SendSideCongestionController std::atomic transport_overhead_bytes_per_packet_; bool network_available_ RTC_GUARDED_BY(task_queue_ptr_); bool periodic_tasks_enabled_ RTC_GUARDED_BY(task_queue_ptr_); + bool packet_feedback_available_ RTC_GUARDED_BY(task_queue_ptr_); + bool feedback_only_controller_ RTC_GUARDED_BY(task_queue_ptr_); send_side_cc_internal::PeriodicTask* pacer_queue_update_task_ RTC_GUARDED_BY(task_queue_ptr_); send_side_cc_internal::PeriodicTask* controller_task_ diff --git a/modules/congestion_controller/rtp/send_side_congestion_controller.cc b/modules/congestion_controller/rtp/send_side_congestion_controller.cc index 898a9dd7d0..4e1a1b1209 100644 --- a/modules/congestion_controller/rtp/send_side_congestion_controller.cc +++ b/modules/congestion_controller/rtp/send_side_congestion_controller.cc @@ -14,12 +14,14 @@ #include #include #include +#include "modules/congestion_controller/bbr/bbr_factory.h" #include "modules/congestion_controller/goog_cc/include/goog_cc_factory.h" #include "modules/congestion_controller/network_control/include/network_types.h" #include "modules/congestion_controller/network_control/include/network_units.h" #include "modules/remote_bitrate_estimator/include/bwe_defines.h" #include "rtc_base/bind.h" #include "rtc_base/checks.h" +#include "rtc_base/experiments/congestion_controller_experiment.h" #include "rtc_base/format_macros.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" @@ -49,9 +51,12 @@ bool IsPacerPushbackExperimentEnabled() { webrtc::runtime_enabled_features::kDualStreamModeFeatureName)); } -NetworkControllerFactoryInterface::uptr ControllerFactory( - RtcEventLog* event_log) { - return rtc::MakeUnique(event_log); +std::unique_ptr MaybeCreateBbrFactory() { + if (CongestionControllerExperiment::BbrControllerEnabled()) { + return rtc::MakeUnique(); + } else { + return nullptr; + } } void SortPacketFeedbackVector(std::vector* input) { @@ -326,15 +331,19 @@ SendSideCongestionController::SendSideCongestionController( : clock_(clock), pacer_(pacer), transport_feedback_adapter_(clock_), - controller_factory_(ControllerFactory(event_log)), + controller_factory_with_feedback_(MaybeCreateBbrFactory()), + controller_factory_fallback_( + rtc::MakeUnique(event_log)), pacer_controller_(MakeUnique(pacer_)), - process_interval_(controller_factory_->GetProcessInterval()), + process_interval_(controller_factory_fallback_->GetProcessInterval()), observer_(nullptr), send_side_bwe_with_overhead_( webrtc::field_trial::IsEnabled("WebRTC-SendSideBwe-WithOverhead")), transport_overhead_bytes_per_packet_(0), network_available_(false), periodic_tasks_enabled_(true), + packet_feedback_available_(false), + feedback_only_controller_(false), pacer_queue_update_task_(nullptr), controller_task_(nullptr), task_queue_(MakeUnique("SendSideCCQueue")) { @@ -357,21 +366,44 @@ SendSideCongestionController::SendSideCongestionController( // bandwidth is set before this class is initialized so the controllers can be // created in the constructor. void SendSideCongestionController::MaybeCreateControllers() { - if (controller_ || !network_available_ || !observer_) + if (!controller_) + MaybeRecreateControllers(); +} + +void SendSideCongestionController::MaybeRecreateControllers() { + if (!network_available_ || !observer_) return; + if (!control_handler_) { + control_handler_ = MakeUnique( + observer_, pacer_controller_.get(), clock_); + } initial_config_.constraints.at_time = Timestamp::ms(clock_->TimeInMilliseconds()); initial_config_.stream_based_config = streams_config_; - control_handler_ = MakeUnique( - observer_, pacer_controller_.get(), clock_); + const bool feedback_only_controller = + packet_feedback_available_ && controller_factory_with_feedback_; - controller_ = - controller_factory_->Create(control_handler_.get(), initial_config_); - UpdateStreamsConfig(); - UpdateControllerWithTimeInterval(); - StartProcessPeriodicTasks(); + if (!controller_ || (feedback_only_controller != feedback_only_controller_)) { + if (feedback_only_controller) { + RTC_LOG(LS_INFO) << "Creating feedback based only controller"; + controller_ = controller_factory_with_feedback_->Create( + control_handler_.get(), initial_config_); + process_interval_ = + controller_factory_with_feedback_->GetProcessInterval(); + } else { + RTC_LOG(LS_INFO) << "Creating fallback controller"; + controller_ = controller_factory_fallback_->Create(control_handler_.get(), + initial_config_); + process_interval_ = controller_factory_fallback_->GetProcessInterval(); + } + feedback_only_controller_ = feedback_only_controller; + UpdateStreamsConfig(); + UpdateControllerWithTimeInterval(); + StartProcessPeriodicTasks(); + } + RTC_DCHECK(controller_); } SendSideCongestionController::~SendSideCongestionController() { @@ -484,7 +516,13 @@ RtcpBandwidthObserver* SendSideCongestionController::GetBandwidthObserver() { } void SendSideCongestionController::SetPerPacketFeedbackAvailable( - bool available) {} + bool available) { + task_queue_->PostTask([this, available]() { + RTC_DCHECK_RUN_ON(task_queue_ptr_); + packet_feedback_available_ = available; + MaybeRecreateControllers(); + }); +} void SendSideCongestionController::EnablePeriodicAlrProbing(bool enable) { task_queue_->PostTask([this, enable]() { diff --git a/rtc_base/experiments/BUILD.gn b/rtc_base/experiments/BUILD.gn index 305ad24a58..0ee402be05 100644 --- a/rtc_base/experiments/BUILD.gn +++ b/rtc_base/experiments/BUILD.gn @@ -19,3 +19,31 @@ rtc_static_library("alr_experiment") { "../../system_wrappers:field_trial_api", ] } + +rtc_static_library("congestion_controller_experiment") { + sources = [ + "congestion_controller_experiment.cc", + "congestion_controller_experiment.h", + ] + deps = [ + "../:rtc_base_approved", + "../../api:optional", + "../../system_wrappers:field_trial_api", + ] +} + +if (rtc_include_tests) { + rtc_source_set("experiments_unittests") { + testonly = true + + sources = [ + "congestion_controller_experiment_unittest.cc", + ] + deps = [ + ":congestion_controller_experiment", + "../:rtc_base_tests_main", + "../:rtc_base_tests_utils", + "../../test:field_trial", + ] + } +} diff --git a/rtc_base/experiments/congestion_controller_experiment.cc b/rtc_base/experiments/congestion_controller_experiment.cc new file mode 100644 index 0000000000..5733fea364 --- /dev/null +++ b/rtc_base/experiments/congestion_controller_experiment.cc @@ -0,0 +1,57 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "rtc_base/experiments/congestion_controller_experiment.h" + +#include + +#include "system_wrappers/include/field_trial.h" + +namespace webrtc { +namespace { + +const char kBbrControllerExperiment[] = "WebRTC-BweCongestionController"; +} // namespace + +bool CongestionControllerExperiment::BbrControllerEnabled() { + std::string trial_string = + webrtc::field_trial::FindFullName(kBbrControllerExperiment); + return trial_string.find("Enabled,BBR") == 0; +} + +rtc::Optional +CongestionControllerExperiment::GetBbrExperimentConfig() { + if (!BbrControllerEnabled()) + return rtc::nullopt; + std::string trial_string = + webrtc::field_trial::FindFullName(kBbrControllerExperiment); + BbrExperimentConfig config; + if (sscanf( + trial_string.c_str(), + "Enabled,BBR," + "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d," + "%lf,%lf,%lf,%lf,%lf,%lf", + &config.exit_startup_on_loss, &config.exit_startup_rtt_threshold_ms, + &config.fully_drain_queue, &config.initial_conservation_in_startup, + &config.num_startup_rtts, &config.probe_rtt_based_on_bdp, + &config.probe_rtt_disabled_if_app_limited, + &config.probe_rtt_skipped_if_similar_rtt, &config.rate_based_recovery, + &config.rate_based_startup, &config.slower_startup, + &config.encoder_rate_gain, &config.encoder_rate_gain_in_probe_rtt, + &config.max_ack_height_window_multiplier, + &config.max_aggregation_bytes_multiplier, + &config.probe_bw_pacing_gain_offset, + &config.probe_rtt_congestion_window_gain) == 17) { + return config; + } else { + return rtc::nullopt; + } +} + +} // namespace webrtc diff --git a/rtc_base/experiments/congestion_controller_experiment.h b/rtc_base/experiments/congestion_controller_experiment.h new file mode 100644 index 0000000000..478d10612d --- /dev/null +++ b/rtc_base/experiments/congestion_controller_experiment.h @@ -0,0 +1,41 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_BASE_EXPERIMENTS_CONGESTION_CONTROLLER_EXPERIMENT_H_ +#define RTC_BASE_EXPERIMENTS_CONGESTION_CONTROLLER_EXPERIMENT_H_ +#include +namespace webrtc { +class CongestionControllerExperiment { + public: + struct BbrExperimentConfig { + int exit_startup_on_loss; + int exit_startup_rtt_threshold_ms; + int fully_drain_queue; + int initial_conservation_in_startup; + int num_startup_rtts; + int probe_rtt_based_on_bdp; + int probe_rtt_disabled_if_app_limited; + int probe_rtt_skipped_if_similar_rtt; + int rate_based_recovery; + int rate_based_startup; + int slower_startup; + double encoder_rate_gain; + double encoder_rate_gain_in_probe_rtt; + double max_ack_height_window_multiplier; + double max_aggregation_bytes_multiplier; + double probe_bw_pacing_gain_offset; + double probe_rtt_congestion_window_gain; + }; + static bool BbrControllerEnabled(); + static rtc::Optional GetBbrExperimentConfig(); +}; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_CONGESTION_CONTROLLER_EXPERIMENT_H_ diff --git a/rtc_base/experiments/congestion_controller_experiment_unittest.cc b/rtc_base/experiments/congestion_controller_experiment_unittest.cc new file mode 100644 index 0000000000..b8e1fffa8a --- /dev/null +++ b/rtc_base/experiments/congestion_controller_experiment_unittest.cc @@ -0,0 +1,89 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/experiments/congestion_controller_experiment.h" +#include "rtc_base/gunit.h" +#include "test/field_trial.h" + +namespace webrtc { +namespace { +void ExpectEquals(CongestionControllerExperiment::BbrExperimentConfig a, + CongestionControllerExperiment::BbrExperimentConfig b) { + EXPECT_EQ(a.exit_startup_on_loss, b.exit_startup_on_loss); + EXPECT_EQ(a.exit_startup_rtt_threshold_ms, b.exit_startup_rtt_threshold_ms); + EXPECT_EQ(a.fully_drain_queue, b.fully_drain_queue); + EXPECT_EQ(a.initial_conservation_in_startup, + b.initial_conservation_in_startup); + EXPECT_EQ(a.num_startup_rtts, b.num_startup_rtts); + EXPECT_EQ(a.probe_rtt_based_on_bdp, b.probe_rtt_based_on_bdp); + EXPECT_EQ(a.probe_rtt_disabled_if_app_limited, + b.probe_rtt_disabled_if_app_limited); + EXPECT_EQ(a.probe_rtt_skipped_if_similar_rtt, + b.probe_rtt_skipped_if_similar_rtt); + EXPECT_EQ(a.rate_based_recovery, b.rate_based_recovery); + EXPECT_EQ(a.rate_based_startup, b.rate_based_startup); + EXPECT_EQ(a.slower_startup, b.slower_startup); + EXPECT_EQ(a.encoder_rate_gain, b.encoder_rate_gain); + EXPECT_EQ(a.encoder_rate_gain_in_probe_rtt, b.encoder_rate_gain_in_probe_rtt); + EXPECT_EQ(a.max_ack_height_window_multiplier, + b.max_ack_height_window_multiplier); + EXPECT_EQ(a.max_aggregation_bytes_multiplier, + b.max_aggregation_bytes_multiplier); + EXPECT_EQ(a.probe_bw_pacing_gain_offset, b.probe_bw_pacing_gain_offset); + EXPECT_EQ(a.probe_rtt_congestion_window_gain, + b.probe_rtt_congestion_window_gain); +} +} // namespace + +TEST(CongestionControllerExperimentTest, BbrDisabledByDefault) { + webrtc::test::ScopedFieldTrials field_trials(""); + EXPECT_FALSE(CongestionControllerExperiment::BbrControllerEnabled()); +} + +TEST(CongestionControllerExperimentTest, BbrEnabledByFieldTrial) { + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-BweCongestionController/Enabled,BBR/"); + EXPECT_TRUE(CongestionControllerExperiment::BbrControllerEnabled()); +} + +TEST(CongestionControllerExperimentTest, BbrBadParametersFails) { + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-BweCongestionController/Enabled,BBR," + "garbage,here/"); + EXPECT_FALSE(CongestionControllerExperiment::GetBbrExperimentConfig()); +} + +TEST(CongestionControllerExperimentTest, BbrZeroParametersParsed) { + CongestionControllerExperiment::BbrExperimentConfig truth = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-BweCongestionController/Enabled,BBR," + "0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0/"); + auto config = CongestionControllerExperiment::GetBbrExperimentConfig(); + EXPECT_TRUE(config); + if (config) + ExpectEquals(truth, *config); +} + +TEST(CongestionControllerExperimentTest, BbrNonZeroParametersParsed) { + CongestionControllerExperiment::BbrExperimentConfig truth = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25}; + + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-BweCongestionController/Enabled,BBR," + "1,1,1,1,1,1,1,1,1,1,1," + "0.25,0.25,0.25,0.25,0.25,0.25/"); + auto config = CongestionControllerExperiment::GetBbrExperimentConfig(); + EXPECT_TRUE(config); + if (config) + ExpectEquals(truth, *config); +} +} // namespace webrtc