diff --git a/api/transport/network_types.h b/api/transport/network_types.h index 258a3a4350..46470d6b5d 100644 --- a/api/transport/network_types.h +++ b/api/transport/network_types.h @@ -46,6 +46,10 @@ struct StreamsConfig { ~StreamsConfig(); Timestamp at_time = Timestamp::PlusInfinity(); absl::optional requests_alr_probing; + // If `initial_probe_to_max_bitrate` is set to true, the first probe + // may probe up to the max configured bitrate and can ignore + // max_total_allocated_bitrate. + absl::optional initial_probe_to_max_bitrate; absl::optional pacing_factor; // TODO(srte): Use BitrateAllocationLimits here. diff --git a/call/rtp_transport_controller_send.cc b/call/rtp_transport_controller_send.cc index 0ee6751035..d739ed1ddd 100644 --- a/call/rtp_transport_controller_send.cc +++ b/call/rtp_transport_controller_send.cc @@ -263,6 +263,11 @@ void RtpTransportControllerSend::ReconfigureBandwidthEstimation( RTC_DCHECK_RUN_ON(&sequence_checker_); bwe_settings_ = settings; + bool allow_probe_without_media = bwe_settings_.allow_probe_without_media && + packet_router_.SupportsRtxPayloadPadding(); + streams_config_.initial_probe_to_max_bitrate = allow_probe_without_media; + pacer_.SetAllowProbeWithoutMediaPacket(allow_probe_without_media); + if (controller_) { // Recreate the controller and handler. control_handler_ = nullptr; @@ -276,9 +281,6 @@ void RtpTransportControllerSend::ReconfigureBandwidthEstimation( UpdateNetworkAvailability(); } } - pacer_.SetAllowProbeWithoutMediaPacket( - bwe_settings_.allow_probe_without_media && - packet_router_.SupportsRtxPayloadPadding()); } void RtpTransportControllerSend::RegisterTargetTransferRateObserver( diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc index d8a0ce9d64..815520ace2 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc @@ -218,6 +218,10 @@ NetworkControlUpdate GoogCcNetworkController::OnProcessInterval( probe_controller_->EnablePeriodicAlrProbing( *initial_config_->stream_based_config.requests_alr_probing); } + if (initial_config_->stream_based_config.initial_probe_to_max_bitrate) { + probe_controller_->SetFirstProbeToMaxBitrate( + *initial_config_->stream_based_config.initial_probe_to_max_bitrate); + } absl::optional total_bitrate = initial_config_->stream_based_config.max_total_allocated_bitrate; if (total_bitrate) { diff --git a/modules/congestion_controller/goog_cc/probe_controller.cc b/modules/congestion_controller/goog_cc/probe_controller.cc index 3fc8677e87..36d61bf9fc 100644 --- a/modules/congestion_controller/goog_cc/probe_controller.cc +++ b/modules/congestion_controller/goog_cc/probe_controller.cc @@ -272,6 +272,22 @@ std::vector ProbeController::OnNetworkAvailability( return std::vector(); } +void ProbeController::UpdateState(State new_state) { + switch (new_state) { + case State::kInit: + state_ = State::kInit; + break; + case State::kWaitingForProbingResult: + state_ = State::kWaitingForProbingResult; + break; + case State::kProbingComplete: + state_ = State::kProbingComplete; + waiting_for_initial_probe_result_ = false; + min_bitrate_to_probe_further_ = DataRate::PlusInfinity(); + break; + } +} + std::vector ProbeController::InitiateExponentialProbing( Timestamp at_time) { RTC_DCHECK(network_available_); @@ -287,6 +303,8 @@ std::vector ProbeController::InitiateExponentialProbing( probes.push_back(config_.second_exponential_probe_scale.Value() * start_bitrate_); } + waiting_for_initial_probe_result_ = true; + return InitiateProbing(at_time, probes, true); } @@ -307,6 +325,7 @@ std::vector ProbeController::SetEstimatedBitrate( if (config_.abort_further_probe_if_max_lower_than_current && (bitrate > max_bitrate_ || (!max_total_allocated_bitrate_.IsZero() && + !(waiting_for_initial_probe_result_ && first_probe_to_max_bitrate_) && bitrate > 2 * max_total_allocated_bitrate_))) { // No need to continue probing. min_bitrate_to_probe_further_ = DataRate::PlusInfinity(); @@ -335,6 +354,11 @@ void ProbeController::EnablePeriodicAlrProbing(bool enable) { enable_periodic_alr_probing_ = enable; } +void ProbeController::SetFirstProbeToMaxBitrate( + bool first_probe_to_max_bitrate) { + first_probe_to_max_bitrate_ = first_probe_to_max_bitrate; +} + void ProbeController::SetAlrStartTimeMs( absl::optional alr_start_time_ms) { if (alr_start_time_ms) { @@ -391,6 +415,7 @@ void ProbeController::SetNetworkStateEstimate( 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(); @@ -452,8 +477,7 @@ std::vector ProbeController::Process(Timestamp at_time) { kMaxWaitingTimeForProbingResult) { if (state_ == State::kWaitingForProbingResult) { RTC_LOG(LS_INFO) << "kWaitingForProbingResult: timeout"; - state_ = State::kProbingComplete; - min_bitrate_to_probe_further_ = DataRate::PlusInfinity(); + UpdateState(State::kProbingComplete); } } if (estimated_bitrate_.IsZero() || state_ != State::kProbingComplete) { @@ -480,14 +504,14 @@ std::vector ProbeController::InitiateProbing( : std::min(max_total_allocated_bitrate_, max_bitrate_); if (std::min(network_estimate, estimated_bitrate_) > config_.skip_if_estimate_larger_than_fraction_of_max * max_probe_rate) { - state_ = State::kProbingComplete; - min_bitrate_to_probe_further_ = DataRate::PlusInfinity(); + UpdateState(State::kProbingComplete); return {}; } } DataRate max_probe_bitrate = max_bitrate_; - if (max_total_allocated_bitrate_ > DataRate::Zero()) { + if (max_total_allocated_bitrate_ > DataRate::Zero() && + !(first_probe_to_max_bitrate_ && waiting_for_initial_probe_result_)) { // 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 @@ -555,15 +579,14 @@ std::vector ProbeController::InitiateProbing( } time_last_probing_initiated_ = now; if (probe_further) { - state_ = State::kWaitingForProbingResult; + UpdateState(State::kWaitingForProbingResult); // Dont expect probe results to be larger than a fraction of the actual // probe rate. min_bitrate_to_probe_further_ = std::min(estimate_capped_bitrate, (*(bitrates_to_probe.end() - 1))) * config_.further_probe_threshold; } else { - state_ = State::kProbingComplete; - min_bitrate_to_probe_further_ = DataRate::PlusInfinity(); + UpdateState(State::kProbingComplete); } return pending_probes; } diff --git a/modules/congestion_controller/goog_cc/probe_controller.h b/modules/congestion_controller/goog_cc/probe_controller.h index cec6157851..ec078adbc1 100644 --- a/modules/congestion_controller/goog_cc/probe_controller.h +++ b/modules/congestion_controller/goog_cc/probe_controller.h @@ -121,6 +121,9 @@ class ProbeController { Timestamp at_time); void EnablePeriodicAlrProbing(bool enable); + // The first initial probe ignores allocated bitrate constraints and probe up + // to max configured bitrate configured via SetBitrates. + void SetFirstProbeToMaxBitrate(bool first_probe_to_max_bitrate); void SetAlrStartTimeMs(absl::optional alr_start_time); void SetAlrEndedTimeMs(int64_t alr_end_time); @@ -148,6 +151,7 @@ class ProbeController { kProbingComplete, }; + void UpdateState(State new_state); ABSL_MUST_USE_RESULT std::vector InitiateExponentialProbing(Timestamp at_time); ABSL_MUST_USE_RESULT std::vector InitiateProbing( @@ -158,6 +162,8 @@ class ProbeController { bool TimeForNetworkStateProbe(Timestamp at_time) const; bool network_available_; + bool waiting_for_initial_probe_result_ = false; + bool first_probe_to_max_bitrate_ = false; BandwidthLimitedCause bandwidth_limited_cause_ = BandwidthLimitedCause::kDelayBasedLimited; State state_; diff --git a/modules/congestion_controller/goog_cc/probe_controller_unittest.cc b/modules/congestion_controller/goog_cc/probe_controller_unittest.cc index aa62c476d5..8024fec4dd 100644 --- a/modules/congestion_controller/goog_cc/probe_controller_unittest.cc +++ b/modules/congestion_controller/goog_cc/probe_controller_unittest.cc @@ -327,6 +327,32 @@ TEST(ProbeControllerTest, TestExponentialProbing) { EXPECT_EQ(probes[0].target_data_rate.bps(), 2 * 1800); } +TEST(ProbeControllerTest, ExponentialProbingStopIfMaxBitrateLow) { + ProbeControllerFixture fixture( + "WebRTC-Bwe-ProbingConfiguration/abort_further:true/"); + std::unique_ptr 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))); + + // Repeated probe normally is sent when estimated bitrate climbs above + // 0.7 * 6 * kStartBitrate = 1260. But since max bitrate is low, expect + // exponential probing to stop. + probes = probe_controller->SetBitrates(kMinBitrate, kStartBitrate, + /*max_bitrate=*/kStartBitrate, + fixture.CurrentTime()); + EXPECT_THAT(probes, IsEmpty()); + + probes = probe_controller->SetEstimatedBitrate( + DataRate::BitsPerSec(1800), BandwidthLimitedCause::kDelayBasedLimited, + fixture.CurrentTime()); + EXPECT_THAT(probes, IsEmpty()); +} + TEST(ProbeControllerTest, ExponentialProbingStopIfMaxAllocatedBitrateLow) { ProbeControllerFixture fixture( "WebRTC-Bwe-ProbingConfiguration/abort_further:true/"); @@ -352,6 +378,66 @@ TEST(ProbeControllerTest, ExponentialProbingStopIfMaxAllocatedBitrateLow) { EXPECT_THAT(probes, IsEmpty()); } +TEST(ProbeControllerTest, + InitialProbingIgnoreLowMaxAllocatedbitrateIfSetFirstProbeToMaxBitrate) { + ProbeControllerFixture fixture( + "WebRTC-Bwe-ProbingConfiguration/abort_further:true/"); + std::unique_ptr 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->SetFirstProbeToMaxBitrate(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, + InitialProbingToLowMaxAllocatedbitrateIfNotSetFirstProbeToMaxBitrate) { + ProbeControllerFixture fixture; + std::unique_ptr 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))); + + // Repeated probe is sent when estimated bitrate climbs above + // 0.7 * 6 * kStartBitrate = 1260. + probes = probe_controller->OnMaxTotalAllocatedBitrate(kStartBitrate, + fixture.CurrentTime()); + EXPECT_THAT(probes, IsEmpty()); + + // If the inital probe result is received, a new probe is sent at 2x the + // needed max bitrate. + probes = probe_controller->SetEstimatedBitrate( + DataRate::BitsPerSec(1800), BandwidthLimitedCause::kDelayBasedLimited, + fixture.CurrentTime()); + ASSERT_EQ(probes.size(), 1u); + EXPECT_EQ(probes[0].target_data_rate.bps(), 2 * kStartBitrate.bps()); +} + TEST(ProbeControllerTest, TestExponentialProbingTimeout) { ProbeControllerFixture fixture; std::unique_ptr probe_controller =