SCReAM: Add cwnd pushback calculation to ScreamNetworkController Calculate cwnd_reduce_ratio in ScreamNetworkController based on pacer queue size and data in flight exceeding max_data_in_flight. - Add min_pacing_delay_for_pushback (default 100ms) and max_pacing_delay_for_pushback (default 500ms) field trial parameters to ScreamV2Parameters. - Calculate cwnd_reduce_ratio using pacing_rate and pacer_queue_size_ or data_in_flight > max_data_in_flight. - Add unit tests verifying cwnd_reduce_ratio behavior when pacer queue grows/shrinks and when congestion window is full. Bug: webrtc:447037083 Change-Id: Ic7983241d7d41fdecc0eabbcf9fc384558631575 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/490860 Reviewed-by: Björn Terelius <terelius@webrtc.org> Commit-Queue: Per Kjellander <perkj@webrtc.org> Cr-Commit-Position: refs/heads/main@{#48225}
diff --git a/modules/congestion_controller/scream/scream_network_controller.cc b/modules/congestion_controller/scream/scream_network_controller.cc index 40b66c5..8cc68ce 100644 --- a/modules/congestion_controller/scream/scream_network_controller.cc +++ b/modules/congestion_controller/scream/scream_network_controller.cc
@@ -11,6 +11,7 @@ #include "modules/congestion_controller/scream/scream_network_controller.h" #include <algorithm> +#include <cmath> #include <memory> #include <optional> #include <utility> @@ -119,9 +120,10 @@ NetworkControlUpdate ScreamNetworkController::OnProcessInterval( ProcessInterval msg) { - NetworkControlUpdate update; - update.pacer_config = MaybeCreatePacerConfig(msg.at_time); - return update; + if (msg.pacer_queue) { + pacer_queue_size_ = *msg.pacer_queue; + } + return CreateUpdate(msg.at_time); } NetworkControlUpdate ScreamNetworkController::OnRemoteBitrateReport( @@ -139,6 +141,7 @@ NetworkControlUpdate ScreamNetworkController::OnSentPacket(SentPacket msg) { scream_->OnPacketSent(msg.data_in_flight); + data_in_flight_ = msg.data_in_flight; if (msg.data_in_flight > scream_->max_data_in_flight() || scream_->delay_based_congestion_control().IsQueueDelayDetected()) { return CreateUpdate(msg.send_time); @@ -195,19 +198,44 @@ NetworkControlUpdate ScreamNetworkController::OnTransportPacketsFeedback( TransportPacketsFeedback msg) { scream_->OnTransportPacketsFeedback(msg); + data_in_flight_ = msg.data_in_flight; return CreateUpdate(msg.feedback_time); } +double ScreamNetworkController::CalculateCwndReduceRatio() const { + if (data_in_flight_ > scream_->max_data_in_flight()) { + return 1.0; + } + + double cwnd_reduce_ratio = 0.0; + if (scream_->pacing_rate() > DataRate::Zero() && + !pacer_queue_size_.IsZero()) { + TimeDelta pacing_delay = pacer_queue_size_ / scream_->pacing_rate(); + TimeDelta min_delay = params_.min_pacing_delay_for_pushback.Get(); + TimeDelta max_delay = params_.max_pacing_delay_for_pushback.Get(); + if (max_delay > min_delay) { + double ratio = (pacing_delay - min_delay) / (max_delay - min_delay); + cwnd_reduce_ratio += std::clamp(ratio, 0.0, 1.0); + } + } + return std::clamp(cwnd_reduce_ratio, 0.0, 1.0); +} + NetworkControlUpdate ScreamNetworkController::CreateUpdate(Timestamp now) { NetworkControlUpdate update; bool is_bandwidth_limited = !scream_->is_application_limited(); + double cwnd_reduce_ratio = CalculateCwndReduceRatio(); + if (scream_->target_rate() != reported_target_rate_ || - is_bandwidth_limited != reported_is_bandwidth_limited_) { + is_bandwidth_limited != reported_is_bandwidth_limited_ || + std::abs(cwnd_reduce_ratio - reported_cwnd_reduce_ratio_) > 0.1) { reported_target_rate_ = scream_->target_rate(); reported_is_bandwidth_limited_ = is_bandwidth_limited; + reported_cwnd_reduce_ratio_ = cwnd_reduce_ratio; TargetTransferRate target_rate_msg; target_rate_msg.at_time = now; target_rate_msg.target_rate = scream_->target_rate(); + target_rate_msg.cwnd_reduce_ratio = cwnd_reduce_ratio; target_rate_msg.network_estimate.at_time = now; target_rate_msg.network_estimate.round_trip_time = scream_->rtt(); target_rate_msg.is_bandwidth_limited = is_bandwidth_limited;
diff --git a/modules/congestion_controller/scream/scream_network_controller.h b/modules/congestion_controller/scream/scream_network_controller.h index ed4c6ba..af9adfb 100644 --- a/modules/congestion_controller/scream/scream_network_controller.h +++ b/modules/congestion_controller/scream/scream_network_controller.h
@@ -17,6 +17,7 @@ #include "api/transport/network_control.h" #include "api/transport/network_types.h" #include "api/units/data_rate.h" +#include "api/units/data_size.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "modules/congestion_controller/scream/scream_v2.h" @@ -51,6 +52,12 @@ NetworkControlUpdate CreateFirstUpdate(Timestamp now); NetworkControlUpdate CreateUpdate(Timestamp now); std::optional<PacerConfig> MaybeCreatePacerConfig(Timestamp now); + // Calculates a ratio in [0.0, 1.0] indicating how much the video encoder + // should reduce its target bitrate (pushback) due to network or pacer queue + // build-up. Returns 1.0 if data in flight exceeds max_data_in_flight. + // Otherwise, if pacer queue delay exceeds min_pacing_delay_for_pushback, + // the ratio scales linearly up to 1.0 at max_pacing_delay_for_pushback. + double CalculateCwndReduceRatio() const; Environment env_; const ScreamV2Parameters params_; @@ -68,6 +75,8 @@ DataRate max_seen_total_allocated_bitrate_ = DataRate::Zero(); Timestamp initial_bwe_probe_end_time_ = Timestamp::MinusInfinity(); Timestamp padding_interval_end_time_ = Timestamp::MinusInfinity(); + DataSize pacer_queue_size_ = DataSize::Zero(); + DataSize data_in_flight_ = DataSize::Zero(); // Values last reported in a NetworkControlUpdate. Used for finding out if an // update needs to be reported. @@ -75,6 +84,7 @@ DataRate reported_padding_rate_; DataRate reported_pacing_rate_; bool reported_is_bandwidth_limited_ = true; + double reported_cwnd_reduce_ratio_ = 0.0; }; } // namespace webrtc
diff --git a/modules/congestion_controller/scream/scream_network_controller_unittest.cc b/modules/congestion_controller/scream/scream_network_controller_unittest.cc index 0461527..1960fba 100644 --- a/modules/congestion_controller/scream/scream_network_controller_unittest.cc +++ b/modules/congestion_controller/scream/scream_network_controller_unittest.cc
@@ -890,5 +890,60 @@ EXPECT_GE(target_rate, DataRate::KilobitsPerSec(9800)); } +TEST(ScreamControllerTest, CwndReduceRatioSetWhenPacerQueueGrowsAndShrinks) { + SimulatedClock clock(Timestamp::Seconds(1'234)); + Environment env = CreateTestEnvironment({.time = &clock}); + NetworkControllerConfig config(env); + config.constraints.starting_rate = DataRate::KilobitsPerSec(1000); + ScreamNetworkController scream_controller(config); + + scream_controller.OnNetworkAvailability( + {.at_time = clock.CurrentTime(), .network_available = true}); + + // Starting rate is 1000 kbps, so pacing rate = 1.1 * 1000 kbps = 1100 kbps + // (137,500 bytes/sec). Pacer queue of 41250 bytes gives (41250/137500) = + // 300ms pacing delay. Expected ratio: (300ms - 100ms) / (500ms - 100ms) = 200 + // / 400 = 0.5. + ProcessInterval msg1; + msg1.at_time = clock.CurrentTime(); + msg1.pacer_queue = DataSize::Bytes(41250); + + NetworkControlUpdate update1 = scream_controller.OnProcessInterval(msg1); + ASSERT_TRUE(update1.target_rate.has_value()); + EXPECT_NEAR(update1.target_rate->cwnd_reduce_ratio, 0.5, 0.05); + + // Send ProcessInterval with empty pacer queue -> cwnd_reduce_ratio drops back + // to 0.0. + clock.AdvanceTime(TimeDelta::Millis(100)); + ProcessInterval msg2; + msg2.at_time = clock.CurrentTime(); + msg2.pacer_queue = DataSize::Zero(); + + NetworkControlUpdate update2 = scream_controller.OnProcessInterval(msg2); + ASSERT_TRUE(update2.target_rate.has_value()); + EXPECT_EQ(update2.target_rate->cwnd_reduce_ratio, 0.0); +} + +TEST(ScreamControllerTest, CwndReduceRatioSetToOneWhenCongestionWindowIsFull) { + SimulatedClock clock(Timestamp::Seconds(1'234)); + Environment env = CreateTestEnvironment({.time = &clock}); + NetworkControllerConfig config(env); + config.constraints.starting_rate = DataRate::KilobitsPerSec(1000); + ScreamNetworkController scream_controller(config); + + scream_controller.OnNetworkAvailability( + {.at_time = clock.CurrentTime(), .network_available = true}); + + // Send a packet where data_in_flight (12000 bytes) exceeds max_data_in_flight + // (~10000 bytes). + SentPacket sent_packet; + sent_packet.send_time = clock.CurrentTime(); + sent_packet.data_in_flight = DataSize::Bytes(12000); + + NetworkControlUpdate update = scream_controller.OnSentPacket(sent_packet); + ASSERT_TRUE(update.target_rate.has_value()); + EXPECT_EQ(update.target_rate->cwnd_reduce_ratio, 1.0); +} + } // namespace } // namespace webrtc
diff --git a/modules/congestion_controller/scream/scream_v2_parameters.cc b/modules/congestion_controller/scream/scream_v2_parameters.cc index edc4a43..f0dee98 100644 --- a/modules/congestion_controller/scream/scream_v2_parameters.cc +++ b/modules/congestion_controller/scream/scream_v2_parameters.cc
@@ -69,7 +69,11 @@ TimeDelta::Seconds(15)), enable_alr("EnableAlr", true), alr_threshold("AlrThreshold", 0.9), - received_rate_window("ReceivedRateWindow", TimeDelta::Millis(100)) { + received_rate_window("ReceivedRateWindow", TimeDelta::Millis(100)), + min_pacing_delay_for_pushback("MinPacingDelayForPushback", + TimeDelta::Millis(100)), + max_pacing_delay_for_pushback("MaxPacingDelayForPushback", + TimeDelta::Millis(500)) { ParseFieldTrial({&min_ref_window, &l4s_avg_g_up, &l4s_avg_g_down, @@ -108,7 +112,9 @@ &allow_large_pacing_bursts_after_congestion_time, &enable_alr, &alr_threshold, - &received_rate_window}, + &received_rate_window, + &min_pacing_delay_for_pushback, + &max_pacing_delay_for_pushback}, trials.Lookup("WebRTC-Bwe-ScreamV2")); }
diff --git a/modules/congestion_controller/scream/scream_v2_parameters.h b/modules/congestion_controller/scream/scream_v2_parameters.h index d21c444..9f6f99f 100644 --- a/modules/congestion_controller/scream/scream_v2_parameters.h +++ b/modules/congestion_controller/scream/scream_v2_parameters.h
@@ -159,6 +159,11 @@ // Window over which received rate is calculated. FieldTrialParameter<TimeDelta> received_rate_window; + + // Minimum pacing delay before starting cwnd pushback reduction. + FieldTrialParameter<TimeDelta> min_pacing_delay_for_pushback; + // Maximum pacing delay for full cwnd pushback reduction. + FieldTrialParameter<TimeDelta> max_pacing_delay_for_pushback; }; } // namespace webrtc
diff --git a/test/peer_scenario/bwe_integration_tests/scream_test.cc b/test/peer_scenario/bwe_integration_tests/scream_test.cc index bc1947f..57b5b09 100644 --- a/test/peer_scenario/bwe_integration_tests/scream_test.cc +++ b/test/peer_scenario/bwe_integration_tests/scream_test.cc
@@ -545,7 +545,7 @@ SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s); EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween( DataRate::KilobitsPerSec(1300), - DataRate::KilobitsPerSec(2300)))); + DataRate::KilobitsPerSec(2500)))); } TEST(ScreamTest, MaybeTest(LinkCapacity2MbpsRtt50msEcn)) {