ScreamV2, add application limited state To enter application limited state: bool alr = max_allowed_ref_window() < ref_window_ In application limited state, smoothed RTT use a slower EWMA filter in order to reduce RTT variations. It make RTT decrease slower due to that link capacity is not utilized. It make RTT increase slower in order to not decrease target rate due to RTT spikes. RTT is still updated though to ensure that RTT eventually will decrease to avoid getting stuck at a low target rate if RTT was high due to a delay spike when entering application limited state. ref_window_ will not increase in application limited state. Bug: webrtc:447037083 Change-Id: I2c5767bfeefc822031ffaf9eaf74b19759cc511b Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/469940 Reviewed-by: Björn Terelius <terelius@webrtc.org> Commit-Queue: Per Kjellander <perkj@webrtc.org> Cr-Commit-Position: refs/heads/main@{#47639}
diff --git a/modules/congestion_controller/scream/delay_based_congestion_control.cc b/modules/congestion_controller/scream/delay_based_congestion_control.cc index 94be767..8980be8 100644 --- a/modules/congestion_controller/scream/delay_based_congestion_control.cc +++ b/modules/congestion_controller/scream/delay_based_congestion_control.cc
@@ -31,7 +31,8 @@ } void DelayBasedCongestionControl::OnTransportPacketsFeedback( - const TransportPacketsFeedback& msg) { + const TransportPacketsFeedback& msg, + bool alr) { if (msg.PacketsWithFeedback().empty()) { return; } @@ -57,7 +58,7 @@ received_packets.back().arrival_time_offset.value_or( TimeDelta::Zero()), TimeDelta::Zero()); - UpdateSmoothedRtt(rtt_sample); + UpdateSmoothedRtt(rtt_sample, alr); TimeDelta min_queue_delay = min_one_way_delay - min_base_delay(); if (min_queue_delay > params_.queue_delay_drain_threshold.Get()) { @@ -116,15 +117,14 @@ 2 * params_.latency_diff_threshold.Get()); } -void DelayBasedCongestionControl::UpdateSmoothedRtt(TimeDelta rtt_sample) { +void DelayBasedCongestionControl::UpdateSmoothedRtt(TimeDelta rtt_sample, + bool alr) { RTC_DCHECK(rtt_sample >= TimeDelta::Zero()); if (last_smoothed_rtt_.IsZero()) { last_smoothed_rtt_ = rtt_sample; } else { - double g = params_.smoothed_rtt_avg_g_up.Get(); - if (rtt_sample < last_smoothed_rtt_) { - g = params_.smoothed_rtt_avg_g_down.Get(); - } + double g = alr ? params_.smoothed_rtt_avg_in_alr_g.Get() + : params_.smoothed_rtt_avg_g.Get(); last_smoothed_rtt_ = rtt_sample * g + last_smoothed_rtt_ * (1.0 - g); } }
diff --git a/modules/congestion_controller/scream/delay_based_congestion_control.h b/modules/congestion_controller/scream/delay_based_congestion_control.h index 91d1483..287e5be 100644 --- a/modules/congestion_controller/scream/delay_based_congestion_control.h +++ b/modules/congestion_controller/scream/delay_based_congestion_control.h
@@ -31,7 +31,8 @@ public: explicit DelayBasedCongestionControl(ScreamV2Parameters params); - void OnTransportPacketsFeedback(const TransportPacketsFeedback& msg); + void OnTransportPacketsFeedback(const TransportPacketsFeedback& msg, + bool alr); // Set a limit on how much the reference window can be reduced due to // increased delay. @@ -87,7 +88,7 @@ TimeDelta min_base_delay() const { return std::min(next_base_delay_, base_delay_history_.GetMin()); } - void UpdateSmoothedRtt(TimeDelta rtt_sample); + void UpdateSmoothedRtt(TimeDelta rtt_sample, bool alr); void UpdateQueueDelayAverage(TimeDelta one_way_delay); void UpdateQueueDelayMinAverage(TimeDelta packet_qdelay); void UpdateLatencyDifferenceAverage(TimeDelta packet_latency_diff); @@ -104,6 +105,7 @@ Timestamp min_queue_delay_above_threshold_start_ = Timestamp::MinusInfinity(); TimeDelta last_smoothed_rtt_ = TimeDelta::Zero(); + Timestamp last_update_qdelay_avg_time_ = Timestamp::MinusInfinity(); TimeDelta last_queue_delay_sample_ = TimeDelta::PlusInfinity(); TimeDelta queue_delay_avg_ = TimeDelta::PlusInfinity();
diff --git a/modules/congestion_controller/scream/delay_based_congestion_control_unittest.cc b/modules/congestion_controller/scream/delay_based_congestion_control_unittest.cc index eea60d3..d9a641d 100644 --- a/modules/congestion_controller/scream/delay_based_congestion_control_unittest.cc +++ b/modules/congestion_controller/scream/delay_based_congestion_control_unittest.cc
@@ -53,7 +53,7 @@ TransportPacketsFeedback feedback = feedback_generator.ProcessUntilNextFeedback(send_rate, clock); - delay_controller.OnTransportPacketsFeedback(feedback); + delay_controller.OnTransportPacketsFeedback(feedback, /*alr=*/false); EXPECT_EQ(delay_controller.rtt(), TimeDelta::Millis(58)); EXPECT_EQ(delay_controller.queue_delay(), TimeDelta::Millis(0)); EXPECT_FALSE(delay_controller.IsQueueDelayDetected()); @@ -77,7 +77,7 @@ DataRate send_rate = DataRate::KilobitsPerSec(2000); TransportPacketsFeedback feedback = feedback_generator.ProcessUntilNextFeedback(send_rate, clock); - delay_controller.OnTransportPacketsFeedback(feedback); + delay_controller.OnTransportPacketsFeedback(feedback, /*alr=*/false); } EXPECT_GT(delay_controller.queue_delay(), TimeDelta::Millis(50)); EXPECT_TRUE(delay_controller.IsQueueDelayDetected()); @@ -100,7 +100,7 @@ DataSize ref_window = send_rate * TimeDelta::Millis(50); TransportPacketsFeedback feedback = feedback_generator.ProcessUntilNextFeedback(send_rate, clock); - delay_controller.OnTransportPacketsFeedback(feedback); + delay_controller.OnTransportPacketsFeedback(feedback, /*alr=*/false); ASSERT_EQ(delay_controller.queue_delay(), TimeDelta::Millis(0)); EXPECT_EQ(delay_controller.UpdateReferenceWindow( @@ -127,7 +127,7 @@ // Send faster than link capacity to build a queue. TransportPacketsFeedback feedback = feedback_generator.ProcessUntilNextFeedback(send_rate, clock); - delay_controller.OnTransportPacketsFeedback(feedback); + delay_controller.OnTransportPacketsFeedback(feedback, /*alr=*/false); smoothed_rtt = delay_controller.rtt(); } DataSize ref_window = send_rate * smoothed_rtt; @@ -155,7 +155,7 @@ // Send faster than link capacity to build a queue. TransportPacketsFeedback feedback = feedback_generator.ProcessUntilNextFeedback(send_rate, clock); - delay_controller.OnTransportPacketsFeedback(feedback); + delay_controller.OnTransportPacketsFeedback(feedback, /*alr=*/false); smoothed_rtt = delay_controller.rtt(); } DataSize ref_window = send_rate * smoothed_rtt; @@ -185,7 +185,7 @@ TransportPacketsFeedback feedback = feedback_generator.ProcessUntilNextFeedback( DataRate::KilobitsPerSec(150), clock); - delay_controller.OnTransportPacketsFeedback(feedback); + delay_controller.OnTransportPacketsFeedback(feedback, /*alr=*/false); last_smoothed_rtt = delay_controller.rtt(); } TimeDelta queue_delay_before_reset = delay_controller.queue_delay(); @@ -198,7 +198,7 @@ TransportPacketsFeedback feedback = feedback_generator.ProcessUntilNextFeedback(DataRate::KilobitsPerSec(150), clock); - delay_controller.OnTransportPacketsFeedback(feedback); + delay_controller.OnTransportPacketsFeedback(feedback, /*alr=*/false); // RTT is still increasing or equal to the last feedback. EXPECT_GE(delay_controller.rtt(), last_smoothed_rtt); // But queue delay should be lower. @@ -222,7 +222,7 @@ TransportPacketsFeedback feedback = feedback_generator.ProcessUntilNextFeedback( DataRate::KilobitsPerSec(150), clock); - delay_controller.OnTransportPacketsFeedback(feedback); + delay_controller.OnTransportPacketsFeedback(feedback, /*alr=*/false); } EXPECT_LT(clock.CurrentTime(), start_time + TimeDelta::Seconds(30)); EXPECT_GT(clock.CurrentTime(), start_time + TimeDelta::Seconds(10)); @@ -252,7 +252,7 @@ clock.CurrentTime() - TimeDelta::Millis(100) - qdelay; packet.sent_packet.sequence_number = i; msg.packet_feedbacks.push_back(packet); - delay_controller.OnTransportPacketsFeedback(msg); + delay_controller.OnTransportPacketsFeedback(msg, /*alr=*/false); } }; @@ -310,7 +310,7 @@ msg.packet_feedbacks.push_back(packet1); msg.packet_feedbacks.push_back(packet2); - delay_controller.OnTransportPacketsFeedback(msg); + delay_controller.OnTransportPacketsFeedback(msg, /*alr=*/false); } }; @@ -341,6 +341,94 @@ 0.01); } +TEST(DelayBasedCongestionControlTest, RttDecaysSlowerInAlr) { + SimulatedClock clock(Timestamp::Seconds(1234)); + Environment env = CreateTestEnvironment({.time = &clock}); + DelayBasedCongestionControl delay_controller_alr( + ScreamV2Parameters(env.field_trials())); + DelayBasedCongestionControl delay_controller_no_alr( + ScreamV2Parameters(env.field_trials())); + + auto feed_feedback = [&](DelayBasedCongestionControl& controller, + TimeDelta rtt, bool alr) { + TransportPacketsFeedback msg; + msg.feedback_time = clock.CurrentTime(); + PacketResult packet; + packet.receive_time = clock.CurrentTime(); + packet.sent_packet.send_time = clock.CurrentTime() - rtt; + packet.sent_packet.sequence_number = 0; + msg.packet_feedbacks.push_back(packet); + controller.OnTransportPacketsFeedback(msg, alr); + }; + + // Establish initial smoothed RTT of 200ms. + feed_feedback(delay_controller_alr, TimeDelta::Millis(200), /*alr=*/false); + feed_feedback(delay_controller_no_alr, TimeDelta::Millis(200), /*alr=*/false); + + EXPECT_EQ(delay_controller_alr.rtt(), TimeDelta::Millis(200)); + EXPECT_EQ(delay_controller_no_alr.rtt(), TimeDelta::Millis(200)); + + // Advance time. + clock.AdvanceTime(TimeDelta::Millis(100)); + + // Send feedback with a lower RTT sample of 100ms. + feed_feedback(delay_controller_alr, TimeDelta::Millis(100), /*alr=*/true); + feed_feedback(delay_controller_no_alr, TimeDelta::Millis(100), /*alr=*/false); + + // With ALR, RTT decay is slower, so the resulting smoothed RTT is higher. + EXPECT_GT(delay_controller_alr.rtt(), delay_controller_no_alr.rtt()); + + // Verify actual values. + // no_alr: 100 * 0.125 + 200 * 0.875 = 187.5ms + // alr: 100 * (1/128) + 200 * (127/128) = 199.21875ms + EXPECT_NEAR(delay_controller_no_alr.rtt().ms<double>(), 187.5, 0.1); + EXPECT_NEAR(delay_controller_alr.rtt().ms<double>(), 199.2, 0.1); +} + +TEST(DelayBasedCongestionControlTest, RttIncreasesSlowerInAlr) { + SimulatedClock clock(Timestamp::Seconds(1234)); + Environment env = CreateTestEnvironment({.time = &clock}); + DelayBasedCongestionControl delay_controller_alr( + ScreamV2Parameters(env.field_trials())); + DelayBasedCongestionControl delay_controller_no_alr( + ScreamV2Parameters(env.field_trials())); + + auto feed_feedback = [&](DelayBasedCongestionControl& controller, + TimeDelta rtt, bool alr) { + TransportPacketsFeedback msg; + msg.feedback_time = clock.CurrentTime(); + PacketResult packet; + packet.receive_time = clock.CurrentTime(); + packet.sent_packet.send_time = clock.CurrentTime() - rtt; + packet.sent_packet.sequence_number = 0; + msg.packet_feedbacks.push_back(packet); + controller.OnTransportPacketsFeedback(msg, alr); + }; + + // Establish initial smoothed RTT of 100ms. + feed_feedback(delay_controller_alr, TimeDelta::Millis(100), /*alr=*/false); + feed_feedback(delay_controller_no_alr, TimeDelta::Millis(100), /*alr=*/false); + + EXPECT_EQ(delay_controller_alr.rtt(), TimeDelta::Millis(100)); + EXPECT_EQ(delay_controller_no_alr.rtt(), TimeDelta::Millis(100)); + + // Advance time. + clock.AdvanceTime(TimeDelta::Millis(100)); + + // Send feedback with a higher RTT sample of 200ms. + feed_feedback(delay_controller_alr, TimeDelta::Millis(200), /*alr=*/true); + feed_feedback(delay_controller_no_alr, TimeDelta::Millis(200), /*alr=*/false); + + // With ALR, RTT increase is slower, so the resulting smoothed RTT is lower. + EXPECT_LT(delay_controller_alr.rtt(), delay_controller_no_alr.rtt()); + + // Verify actual values. + // no_alr: 200 * 0.125 + 100 * 0.875 = 112.5ms + // alr: 200 * (1/128) + 100 * (127/128) = 100.78125ms + EXPECT_NEAR(delay_controller_no_alr.rtt().ms<double>(), 112.5, 0.1); + EXPECT_NEAR(delay_controller_alr.rtt().ms<double>(), 100.8, 0.1); +} + // TODO: bugs.webrtc.org/447037083 - add tests for clock drift in feedback NTP // time.
diff --git a/modules/congestion_controller/scream/scream_v2.cc b/modules/congestion_controller/scream/scream_v2.cc index db74e55..5f73d2f 100644 --- a/modules/congestion_controller/scream/scream_v2.cc +++ b/modules/congestion_controller/scream/scream_v2.cc
@@ -94,9 +94,15 @@ max_data_in_flight_this_rtt_ = std::max(max_data_in_flight_this_rtt_, msg.data_in_flight); - delay_based_congestion_control_.OnTransportPacketsFeedback(msg); + if (params_.enable_alr.Get()) { + is_application_limited_ = max_allowed_ref_window() < ref_window_; + } - UpdateFeedbackHoldTime(msg); + delay_based_congestion_control_.OnTransportPacketsFeedback( + msg, is_application_limited_); + if (!is_application_limited_) { + UpdateFeedbackHoldTime(msg); + } if (!first_feedback_processed_) { ref_window_ =
diff --git a/modules/congestion_controller/scream/scream_v2.h b/modules/congestion_controller/scream/scream_v2.h index 28eb672..5582917 100644 --- a/modules/congestion_controller/scream/scream_v2.h +++ b/modules/congestion_controller/scream/scream_v2.h
@@ -113,6 +113,8 @@ params_.max_segment_size.Get(); } + bool is_application_limited() const { return is_application_limited_; } + private: void UpdateL4SAlpha(const TransportPacketsFeedback& msg); void UpdateRefWindow(const TransportPacketsFeedback& msg); @@ -165,6 +167,11 @@ DelayBasedCongestionControl delay_based_congestion_control_; bool first_feedback_processed_ = false; + + // Tracks if the send rate is less than the network path can currently + // support. This is done by checking if max_allowed_ref_window() < + // ref_window_. + bool is_application_limited_ = false; }; } // namespace webrtc
diff --git a/modules/congestion_controller/scream/scream_v2_parameters.cc b/modules/congestion_controller/scream/scream_v2_parameters.cc index 68f6fe3..7b33fbdc 100644 --- a/modules/congestion_controller/scream/scream_v2_parameters.cc +++ b/modules/congestion_controller/scream/scream_v2_parameters.cc
@@ -21,8 +21,8 @@ : min_ref_window("MinRefWindow", DataSize::Bytes(1000)), l4s_avg_g_up("L4sAvgGUp", 1.0 / 8.0), l4s_avg_g_down("L4sAvgGDown", 1.0 / 128.0), - smoothed_rtt_avg_g_up("SmoothedRttAvgGUp", 1.0 / 8.0), - smoothed_rtt_avg_g_down("SmoothedRttAvgGDown", 1.0 / 8.0), + smoothed_rtt_avg_g("SmoothedRttAvgG", 1.0 / 8.0), + smoothed_rtt_avg_in_alr_g("SmoothedRttAvgInAlrG", 1.0 / 128.0), max_segment_size("MaxSegmentSize", DataSize::Bytes(1280)), bytes_in_flight_head_room("BytesInFlightHeadRoom", 1.1), beta_loss("BetaLoss", 0.7), @@ -62,12 +62,13 @@ feedback_hold_time_avg_g("FeedbackHoldTimeAvgG", 1.0 / 8.0), allow_large_pacing_bursts_after_congestion_time( "AllowLargePacingBurstsAfterCongestionTime", - TimeDelta::Seconds(15)) { + TimeDelta::Seconds(15)), + enable_alr("EnableAlr", true) { ParseFieldTrial({&min_ref_window, &l4s_avg_g_up, &l4s_avg_g_down, - &smoothed_rtt_avg_g_up, - &smoothed_rtt_avg_g_down, + &smoothed_rtt_avg_g, + &smoothed_rtt_avg_in_alr_g, &max_segment_size, &bytes_in_flight_head_room, &beta_loss, @@ -94,7 +95,8 @@ &initial_probing_duration, &pacing_factor, &feedback_hold_time_avg_g, - &allow_large_pacing_bursts_after_congestion_time}, + &allow_large_pacing_bursts_after_congestion_time, + &enable_alr}, 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 bd4c129..e24d7ad 100644 --- a/modules/congestion_controller/scream/scream_v2_parameters.h +++ b/modules/congestion_controller/scream/scream_v2_parameters.h
@@ -29,8 +29,8 @@ FieldTrialParameter<double> l4s_avg_g_down; // Exponentially Weighted Moving Average (EWMA) factor for smoothed rtt. - FieldTrialParameter<double> smoothed_rtt_avg_g_up; - FieldTrialParameter<double> smoothed_rtt_avg_g_down; + FieldTrialParameter<double> smoothed_rtt_avg_g; + FieldTrialParameter<double> smoothed_rtt_avg_in_alr_g; // Maximum Segment Size (MSS) // Size of the largest data segment that a sender is able to transmit. I.e @@ -135,6 +135,10 @@ // bursts. FieldTrialParameter<TimeDelta> allow_large_pacing_bursts_after_congestion_time; + + // Enable application-limited (ALR) state tracking. + // In ALR, reference window can not increase, and RTT is updated slower. + FieldTrialParameter<bool> enable_alr; }; } // namespace webrtc
diff --git a/modules/congestion_controller/scream/scream_v2_unittest.cc b/modules/congestion_controller/scream/scream_v2_unittest.cc index 39d8936..c4a1f6d 100644 --- a/modules/congestion_controller/scream/scream_v2_unittest.cc +++ b/modules/congestion_controller/scream/scream_v2_unittest.cc
@@ -383,5 +383,107 @@ TimeDelta::Millis(10 * 2 + 60)); } +TEST(ScreamV2Test, EntersAndExitsAlrState) { + SimulatedClock clock(Timestamp::Seconds(1'234)); + Environment env = CreateTestEnvironment({.time = &clock}); + ScreamV2 scream(env); + const DataRate kMaxDataRate = DataRate::KilobitsPerSec(2000); + scream.SetTargetBitrateConstraints(DataRate::Zero(), kMaxDataRate, + DataRate::KilobitsPerSec(300)); + + // Configure a feedback generator simulating a network with infinite + // capacity but 25ms one way delay. + CcFeedbackGenerator feedback_generator( + {.network_config = {.queue_delay_ms = 25}}); + + DataRate send_rate = DataRate::KilobitsPerSec(300); + + // 1. Ramp up send rate to establish a high target rate / ref_window. + for (int i = 0; i < 50; ++i) { + TransportPacketsFeedback feedback = + feedback_generator.ProcessUntilNextFeedback( + send_rate, clock, [&](const SentPacket& packet) { + scream.OnPacketSent(packet.data_in_flight); + }); + scream.OnTransportPacketsFeedback(feedback); + send_rate = scream.target_rate(); + } + + EXPECT_GT(send_rate, DataRate::KilobitsPerSec(1500)); + EXPECT_FALSE(scream.is_application_limited()); + + // 2. Drop send rate to 1000kbps to simulate application-limited state. + send_rate = DataRate::KilobitsPerSec(1000); + for (int i = 0; i < 10; ++i) { + TransportPacketsFeedback feedback = + feedback_generator.ProcessUntilNextFeedback( + send_rate, clock, [&](const SentPacket& packet) { + scream.OnPacketSent(packet.data_in_flight); + }); + scream.OnTransportPacketsFeedback(feedback); + } + + EXPECT_TRUE(scream.is_application_limited()); + + // 3. Increase send rate back to the target rate to exit ALR. + send_rate = scream.target_rate(); + for (int i = 0; i < 15; ++i) { + TransportPacketsFeedback feedback = + feedback_generator.ProcessUntilNextFeedback( + send_rate, clock, [&](const SentPacket& packet) { + scream.OnPacketSent(packet.data_in_flight); + }); + scream.OnTransportPacketsFeedback(feedback); + send_rate = scream.target_rate(); + } + + EXPECT_FALSE(scream.is_application_limited()); +} + +TEST(ScreamV2Test, DisableAlrViaFieldTrial) { + SimulatedClock clock(Timestamp::Seconds(1'234)); + Environment env = CreateTestEnvironment( + {.field_trials = "WebRTC-Bwe-ScreamV2/EnableAlr:false/", .time = &clock}); + ScreamV2 scream(env); + const DataRate kMaxDataRate = DataRate::KilobitsPerSec(2000); + scream.SetTargetBitrateConstraints(DataRate::Zero(), kMaxDataRate, + DataRate::KilobitsPerSec(300)); + + // Configure a feedback generator simulating a network with infinite + // capacity but 25ms one way delay. + CcFeedbackGenerator feedback_generator( + {.network_config = {.queue_delay_ms = 25}}); + + DataRate send_rate = DataRate::KilobitsPerSec(300); + + // 1. Ramp up send rate. + for (int i = 0; i < 50; ++i) { + TransportPacketsFeedback feedback = + feedback_generator.ProcessUntilNextFeedback( + send_rate, clock, [&](const SentPacket& packet) { + scream.OnPacketSent(packet.data_in_flight); + }); + scream.OnTransportPacketsFeedback(feedback); + send_rate = scream.target_rate(); + } + + EXPECT_GT(send_rate, DataRate::KilobitsPerSec(1500)); + EXPECT_FALSE(scream.is_application_limited()); + + // 2. Drop send rate to 100kbps. + send_rate = DataRate::KilobitsPerSec(100); + for (int i = 0; i < 10; ++i) { + TransportPacketsFeedback feedback = + feedback_generator.ProcessUntilNextFeedback( + send_rate, clock, [&](const SentPacket& packet) { + scream.OnPacketSent(packet.data_in_flight); + }); + scream.OnTransportPacketsFeedback(feedback); + } + + // ALR is disabled, so it should still not be in application-limited state. + EXPECT_FALSE(scream.is_application_limited()); +} + } // namespace } // namespace webrtc
diff --git a/rtc_tools/rtc_event_log_visualizer/analyze_bwe.cc b/rtc_tools/rtc_event_log_visualizer/analyze_bwe.cc index 30682e4..c61c97d 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyze_bwe.cc +++ b/rtc_tools/rtc_event_log_visualizer/analyze_bwe.cc
@@ -685,11 +685,16 @@ TimeSeries target_rate_series("Target rate", LineStyle::kStep); TimeSeries pacing_rate_series("Pacing rate", LineStyle::kStep); TimeSeries send_rate_series("Send rate", LineStyle::kStep); + IntervalSeries app_limited_series("Application limited", "#5092fc", + IntervalSeries::kHorizontal); LogScreamSimulation simulation({.rate_window = config.window_duration_}, config.env_); simulation.ProcessEventsInLog(parsed_log); + bool previously_app_limited = false; + float app_limited_start_time = 0; + for (const LogScreamSimulation::State& state : simulation.updates()) { target_rate_series.points.emplace_back(config.GetCallTimeSec(state.time), state.target_rate.bps() / 1000); @@ -697,10 +702,25 @@ state.pacing_rate.bps() / 1000); send_rate_series.points.emplace_back(config.GetCallTimeSec(state.time), state.send_rate.bps() / 1000); + if (state.is_application_limited && !previously_app_limited) { + app_limited_start_time = config.GetCallTimeSec(state.time); + previously_app_limited = true; + } else if (!state.is_application_limited && previously_app_limited) { + app_limited_series.intervals.emplace_back( + app_limited_start_time, config.GetCallTimeSec(state.time)); + previously_app_limited = false; + } } + + if (previously_app_limited) { + app_limited_series.intervals.emplace_back(app_limited_start_time, + config.CallEndTimeSec()); + } + plot->AppendTimeSeries(std::move(target_rate_series)); plot->AppendTimeSeries(std::move(pacing_rate_series)); plot->AppendTimeSeries(std::move(send_rate_series)); + plot->AppendIntervalSeries(std::move(app_limited_series)); plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", kLeftMargin, kRightMargin);
diff --git a/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.cc b/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.cc index e52e131..76bfb0f 100644 --- a/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.cc +++ b/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.cc
@@ -170,6 +170,7 @@ .ref_window_i = scream_->ref_window_i(), .max_allowed_ref_window = scream_->max_allowed_ref_window(), .max_data_in_flight = scream_->max_data_in_flight(), + .is_application_limited = scream_->is_application_limited(), .data_in_flight = data_in_flight_, .send_window_usage = send_window_usage_, .smoothed_rtt = scream_->delay_based_congestion_control().rtt(),
diff --git a/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.h b/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.h index 252880a..0e7e854 100644 --- a/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.h +++ b/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.h
@@ -49,6 +49,7 @@ DataSize ref_window_i = DataSize::Zero(); DataSize max_allowed_ref_window = DataSize::Zero(); DataSize max_data_in_flight = DataSize::Zero(); + bool is_application_limited = false; // Data in flight after last packet was sent before the state was captured. DataSize data_in_flight = DataSize::Zero(); // How the send window have been utilized. Based on data in flight when the
diff --git a/test/peer_scenario/bwe_integration_tests/scream_test.cc b/test/peer_scenario/bwe_integration_tests/scream_test.cc index 538b91a..295a7b2 100644 --- a/test/peer_scenario/bwe_integration_tests/scream_test.cc +++ b/test/peer_scenario/bwe_integration_tests/scream_test.cc
@@ -491,7 +491,7 @@ SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s); EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween( DataRate::KilobitsPerSec(800), - DataRate::KilobitsPerSec(2200)))); + DataRate::KilobitsPerSec(2100)))); } TEST(ScreamTest, MaybeTest(LinkCapacity2MbpsRtt50msNoEcn)) { @@ -508,7 +508,7 @@ SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s); EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween( DataRate::KilobitsPerSec(1300), - DataRate::KilobitsPerSec(2600)))); + DataRate::KilobitsPerSec(2300)))); } TEST(ScreamTest, MaybeTest(LinkCapacity2MbpsRtt50msEcn)) { @@ -670,7 +670,29 @@ // Ignore estimate during rampup. EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween( DataRate::KilobitsPerSec(1200), - DataRate::KilobitsPerSec(2800)))); + DataRate::KilobitsPerSec(2500)))); +} + +TEST(ScreamTest, MaybeTest(ScreencastSlideChange500Kbit30msRttNoEcn)) { + PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info()); + SendMediaTestParams params{.test_duration = TimeDelta::Seconds(20)}; + params.caller_to_callee_path = + CreateNetworkPath(s, /*use_dual_pi= */ false, + DataRate::KilobitsPerSec(500), TimeDelta::Millis(15)); + params.caller_to_callee_path = params.callee_to_caller_path = + CreateNetworkPath(s, /*use_dual_pi= */ false, + DataRate::KilobitsPerSec(500), TimeDelta::Millis(15)); + params.caller_video_conf = { + .generator = {.image_slides = + test::FrameGeneratorCapturerConfig::ImageSlides{ + .change_interval = TimeDelta::Seconds(3)}}}; + + SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s); + + // Ignore estimate during rampup. + EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween( + DataRate::KilobitsPerSec(200), + DataRate::KilobitsPerSec(700)))); } TEST(ScreamTest, MaybeTest(ScreencastSlideChangeRepeatedDelaySpikes)) { @@ -708,6 +730,21 @@ DataRate::KilobitsPerSec(5000)))); } +TEST(ScreamTest, MaybeTest(LinkCapacity2MbitRepeatedDelaySpikesNoEcn)) { + PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info()); + SendMediaTestParams params{.test_duration = TimeDelta::Seconds(20)}; + params.caller_to_callee_path = CreateNetworkPathWithRepeatedPause( + s, DataRate::KilobitsPerSec(2000), TimeDelta::Millis(100), + TimeDelta::Millis(200)); + params.callee_to_caller_path = CreateNetworkPathWithRepeatedPause( + s, DataRate::KilobitsPerSec(2000), TimeDelta::Millis(100), + TimeDelta::Millis(200)); + SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s); + EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween( + DataRate::KilobitsPerSec(700), + DataRate::KilobitsPerSec(2000)))); +} + TEST(ScreamTest, MaybeTest(RampupFastOnLinkCapacity50Mbit20MsRttNoEcn)) { PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info()); SendMediaTestParams params{.test_duration = TimeDelta::Seconds(1),