Make scream more resilient to delay spikes Use the minimum queue_delay from the last two feedback when calculating average queue_delay. This decrease the chance of reducing reference window due to increased delay that is only seen in one feedback. Also replace usage of queue_delay_dev_norm with DelayBasedCongestionControl::ref_window_scale_factor_due_to_avg_min_delay and DelayBasedCongestionControl::ref_window_scale_factor_due_to_latency_difference Both are used similarly to the old queue_delay_dev_norm, but are calculated based on the minimum average min queue delay and the average latency difference per feeedback. This replace previous: if (l4s_alpha < 0.0001) increment_t *= max(0.1, 1.0 - qdelay_avg / (qdelay_target / 4)) end Bug: webrtc:447037083 Change-Id: Ic713258b2d0a50b486dc93a89f7c947156d86907 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/453300 Commit-Queue: Per Kjellander <perkj@webrtc.org> Reviewed-by: Björn Terelius <terelius@webrtc.org> Cr-Commit-Position: refs/heads/main@{#47129}
diff --git a/modules/congestion_controller/scream/delay_based_congestion_control.cc b/modules/congestion_controller/scream/delay_based_congestion_control.cc index 4cc5e0d..94be767 100644 --- a/modules/congestion_controller/scream/delay_based_congestion_control.cc +++ b/modules/congestion_controller/scream/delay_based_congestion_control.cc
@@ -39,15 +39,16 @@ if (received_packets.empty()) { return; } - TimeDelta one_way_delay_sum; + TimeDelta min_one_way_delay = TimeDelta::PlusInfinity(); + TimeDelta max_one_way_delay = TimeDelta::Zero(); for (const PacketResult& packet : received_packets) { TimeDelta one_way_delay = packet.receive_time - packet.sent_packet.send_time; next_base_delay_ = std::min(next_base_delay_, one_way_delay); - one_way_delay_sum += one_way_delay; min_one_way_delay = std::min(min_one_way_delay, one_way_delay); + max_one_way_delay = std::max(max_one_way_delay, one_way_delay); } // `arrival_time_offset` is null if TWCC is used. We assume feedback was sent // when the last sent packet was received. @@ -66,8 +67,12 @@ } else { min_queue_delay_above_threshold_start_ = Timestamp::MinusInfinity(); } - UpdateQueueDelayAverage(one_way_delay_sum / - static_cast<int>(received_packets.size())); + UpdateQueueDelayAverage(std::min(min_queue_delay, last_queue_delay_sample_)); + last_queue_delay_sample_ = min_queue_delay; + UpdateQueueDelayMinAverage(min_queue_delay); + UpdateLatencyDifferenceAverage(min_one_way_delay.IsFinite() + ? max_one_way_delay - min_one_way_delay + : TimeDelta::Zero()); if (msg.feedback_time - last_base_delay_update_ >= params_.base_delay_history_update_interval.Get()) { @@ -78,26 +83,37 @@ } void DelayBasedCongestionControl::UpdateQueueDelayAverage( - TimeDelta one_way_delay) { - TimeDelta current_qdelay = one_way_delay - min_base_delay(); - + TimeDelta min_qdelay_in_feedback) { // `queue_delay_avg_` is updated with a slow attack,fast decay EWMA // filter. - if (current_qdelay < queue_delay_avg_) { - queue_delay_avg_ = current_qdelay; + if (min_qdelay_in_feedback < queue_delay_avg_) { + queue_delay_avg_ = min_qdelay_in_feedback; } else { queue_delay_avg_ = - params_.queue_delay_avg_g.Get() * current_qdelay + + params_.queue_delay_avg_g.Get() * min_qdelay_in_feedback + (1.0 - params_.queue_delay_avg_g.Get()) * queue_delay_avg_; } +} - queue_delay_dev_norm_ = - (1.0 - params_.queue_delay_dev_avg_g.Get()) * queue_delay_dev_norm_ + - params_.queue_delay_dev_avg_g.Get() * - std::clamp((current_qdelay - - params_.queue_delay_dev_normalization.Get() / 4) / - params_.queue_delay_dev_normalization.Get(), - 0.0, 0.2); +void DelayBasedCongestionControl::UpdateQueueDelayMinAverage( + TimeDelta packet_qdelay) { + RTC_DCHECK(packet_qdelay >= TimeDelta::Zero()); + queue_delay_min_avg_ = + (1.0 - params_.delay_min_and_latency_diff_avg_g.Get()) * + queue_delay_min_avg_ + + params_.delay_min_and_latency_diff_avg_g.Get() * + std::min(packet_qdelay, 2 * params_.queue_delay_min_threshold.Get()); +} + +void DelayBasedCongestionControl::UpdateLatencyDifferenceAverage( + TimeDelta packet_latency_diff) { + RTC_DCHECK(packet_latency_diff >= TimeDelta::Zero()); + latency_difference_avg_ = + (1.0 - params_.delay_min_and_latency_diff_avg_g.Get()) * + latency_difference_avg_ + + params_.delay_min_and_latency_diff_avg_g.Get() * + std::min(packet_latency_diff, + 2 * params_.latency_diff_threshold.Get()); } void DelayBasedCongestionControl::UpdateSmoothedRtt(TimeDelta rtt_sample) { @@ -123,7 +139,26 @@ min_queue_delay_above_threshold_start_ = Timestamp::MinusInfinity(); last_update_qdelay_avg_time_ = Timestamp::MinusInfinity(); queue_delay_avg_ = TimeDelta::PlusInfinity(); - queue_delay_dev_norm_ = 0.0; + queue_delay_min_avg_ = TimeDelta::Zero(); + latency_difference_avg_ = TimeDelta::Zero(); +} + +double +DelayBasedCongestionControl::ref_window_scale_factor_due_to_avg_min_delay( + bool allow_zero) const { + TimeDelta norm = params_.queue_delay_min_threshold.Get(); + // Reaches 0.1 at norm, and 1.0 at norm / 4 + return std::clamp(0.1 + 1.2 * (norm - queue_delay_min_avg_) / norm, + allow_zero ? 0.0 : 0.1, 1.0); +} + +double +DelayBasedCongestionControl::ref_window_scale_factor_due_to_latency_difference() + const { + TimeDelta norm = params_.latency_diff_threshold.Get(); + // Reaches 0.1 at norm, and 1.0 at norm / 4 + return std::clamp(0.1 + 1.2 * (norm - latency_difference_avg_) / norm, 0.1, + 1.0); } DataSize DelayBasedCongestionControl::UpdateReferenceWindow(
diff --git a/modules/congestion_controller/scream/delay_based_congestion_control.h b/modules/congestion_controller/scream/delay_based_congestion_control.h index d8041b4..91d1483 100644 --- a/modules/congestion_controller/scream/delay_based_congestion_control.h +++ b/modules/congestion_controller/scream/delay_based_congestion_control.h
@@ -63,29 +63,21 @@ TimeDelta queue_delay() const { return queue_delay_avg_; } - double queue_delay_dev_norm() const { return queue_delay_dev_norm_; } + TimeDelta queue_delay_min_avg() const { return queue_delay_min_avg_; } + TimeDelta latency_difference_avg() const { return latency_difference_avg_; } - // Scales the reference window increase/decrease due to increased delay if L4S - // does not appear to be used. See 4.2.2.1. - double ref_window_scale_factor_due_to_increased_delay() const { - return std::clamp( - 1 - queue_delay_avg_ / (params_.queue_delay_target.Get() / 4), 0.1, - 1.0); - } + // Scale factor used for scaling reference window increase/decrease due to + // minimum average queue delay per feedback. The scale factor is 0.1 + // if the delay is higher than the threshold, and increases linearly to 1.0 if + // the delay is lower than the threshold / 4. + double ref_window_scale_factor_due_to_avg_min_delay( + bool allow_zero = false) const; - // Scales the reference window increase/decrease due to delay variation. - // See 4.2.2.1 and 4.2.2.2. The scale factor is 0.0 if the delay variation - // is higher than the threshold, and decreases linearly to 1.0 if the delay - // variation is zero. - double ref_window_scale_factor_due_to_delay_variation( - double ref_window_mss_ratio) const { - // 4.2.1.4. - double queue_delay_dev_norm_threshold = - std::max(0.05, 0.1 * (1.0 - ref_window_mss_ratio / 0.1)); - return std::clamp((queue_delay_dev_norm_threshold - queue_delay_dev_norm_) / - queue_delay_dev_norm_threshold, - 0.0, 1.0); - } + // Scale factor used for scaling reference window increase if + // latency differences per feedback is larger than the threshold. If the send + // rate is close to link capacity, the difference between min and max latency + // per feedback increases and the scale factor decreases. + double ref_window_scale_factor_due_to_latency_difference() const; TimeDelta rtt() const { return last_smoothed_rtt_; } @@ -97,6 +89,8 @@ } void UpdateSmoothedRtt(TimeDelta rtt_sample); void UpdateQueueDelayAverage(TimeDelta one_way_delay); + void UpdateQueueDelayMinAverage(TimeDelta packet_qdelay); + void UpdateLatencyDifferenceAverage(TimeDelta packet_latency_diff); const ScreamV2Parameters params_; @@ -111,8 +105,10 @@ 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(); - double queue_delay_dev_norm_ = 0.0; + TimeDelta queue_delay_min_avg_ = TimeDelta::Zero(); + TimeDelta latency_difference_avg_ = TimeDelta::Zero(); }; } // namespace webrtc
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 a2940ae..eea60d3 100644 --- a/modules/congestion_controller/scream/delay_based_congestion_control_unittest.cc +++ b/modules/congestion_controller/scream/delay_based_congestion_control_unittest.cc
@@ -229,6 +229,118 @@ EXPECT_FALSE(delay_controller.IsQueueDrainedInTime(clock.CurrentTime())); } +TEST(DelayBasedCongestionControlTest, + RefWindowScaleFactorDueToMinAverageQueueDelay) { + SimulatedClock clock(Timestamp::Seconds(1234)); + Environment env = CreateTestEnvironment({.time = &clock}); + ScreamV2Parameters params(env.field_trials()); + DelayBasedCongestionControl delay_controller(params); + + const TimeDelta kQueueDelyMinThreshold = + params.queue_delay_min_threshold.Get(); + + // Helper to establish queue delay. + // Assumes base delay is 100ms. + auto feed_packets = [&](TimeDelta qdelay, int count) { + for (int i = 0; i < count; ++i) { + clock.AdvanceTime(TimeDelta::Millis(10)); + TransportPacketsFeedback msg; + msg.feedback_time = clock.CurrentTime(); + PacketResult packet; + packet.receive_time = clock.CurrentTime(); + packet.sent_packet.send_time = + clock.CurrentTime() - TimeDelta::Millis(100) - qdelay; + packet.sent_packet.sequence_number = i; + msg.packet_feedbacks.push_back(packet); + delay_controller.OnTransportPacketsFeedback(msg); + } + }; + + // Establish base delay (100ms) with qdelay=0. + feed_packets(TimeDelta::Zero(), 100); + EXPECT_DOUBLE_EQ( + delay_controller.ref_window_scale_factor_due_to_avg_min_delay(), 1.0); + + // Drive queue delay to kQueueDelyMinThreshold. + feed_packets(kQueueDelyMinThreshold + TimeDelta::Millis(10), 250); + EXPECT_NEAR(delay_controller.ref_window_scale_factor_due_to_avg_min_delay(), + 0.1, 0.01); + + // Drive queue delay to `0.5 * kQueueDelyMinThreshold`. + // Expected: 0.1 + 1.2 * 0.5 = 0.7 + feed_packets(kQueueDelyMinThreshold / 2, 250); + EXPECT_NEAR(delay_controller.ref_window_scale_factor_due_to_avg_min_delay(), + 0.7, 0.01); + + // Drive queue delay to `0.25 * kQueueDelyMinThreshold`. + // Expected: 0.1 + 1.2 * 0.75 = 1.0. + feed_packets(kQueueDelyMinThreshold / 4, 250); + EXPECT_NEAR(delay_controller.ref_window_scale_factor_due_to_avg_min_delay(), + 1.0, 0.01); +} + +TEST(DelayBasedCongestionControlTest, + RefWindowScaleFactorDueToLatencyDifference) { + SimulatedClock clock(Timestamp::Seconds(1234)); + Environment env = CreateTestEnvironment({.time = &clock}); + ScreamV2Parameters params(env.field_trials()); + DelayBasedCongestionControl delay_controller(params); + + const TimeDelta kLatencyThreshold = params.latency_diff_threshold.Get(); + + // Helper to establish latency differences within feedbacks. + // Assumes a base one-way delay of 100ms. + auto feed_packets = [&](TimeDelta latency_diff, int count) { + for (int i = 0; i < count; ++i) { + clock.AdvanceTime(TimeDelta::Millis(10)); + TransportPacketsFeedback msg; + msg.feedback_time = clock.CurrentTime(); + + PacketResult packet1; + packet1.receive_time = clock.CurrentTime(); + packet1.sent_packet.send_time = + clock.CurrentTime() - TimeDelta::Millis(100); + packet1.sent_packet.sequence_number = i * 2; + + PacketResult packet2; + packet2.receive_time = clock.CurrentTime(); + packet2.sent_packet.send_time = + clock.CurrentTime() - TimeDelta::Millis(100) - latency_diff; + packet2.sent_packet.sequence_number = i * 2 + 1; + + msg.packet_feedbacks.push_back(packet1); + msg.packet_feedbacks.push_back(packet2); + delay_controller.OnTransportPacketsFeedback(msg); + } + }; + + // Establish 0 latency difference. + feed_packets(TimeDelta::Zero(), 100); + EXPECT_DOUBLE_EQ( + delay_controller.ref_window_scale_factor_due_to_latency_difference(), + 1.0); + + // Drive to `kLatencyThreshold`. + feed_packets(kLatencyThreshold + TimeDelta::Millis(10), 250); + EXPECT_NEAR( + delay_controller.ref_window_scale_factor_due_to_latency_difference(), 0.1, + 0.01); + + // Drive to `0.5 * kLatencyThreshold`. + // Expected: 0.1 + 1.2 * 0.5 = 0.7 + feed_packets(kLatencyThreshold / 2, 250); + EXPECT_NEAR( + delay_controller.ref_window_scale_factor_due_to_latency_difference(), 0.7, + 0.01); + + // Drive to `0.25 * kLatencyThreshold`. + // Expected: 0.1 + 1.2 * 0.75 = 1.0. + feed_packets(kLatencyThreshold / 4, 250); + EXPECT_NEAR( + delay_controller.ref_window_scale_factor_due_to_latency_difference(), 1.0, + 0.01); +} + // TODO: bugs.webrtc.org/447037083 - add tests for clock drift in feedback NTP // time.
diff --git a/modules/congestion_controller/scream/scream_network_controller_unittest.cc b/modules/congestion_controller/scream/scream_network_controller_unittest.cc index b3a0d90..745f70d 100644 --- a/modules/congestion_controller/scream/scream_network_controller_unittest.cc +++ b/modules/congestion_controller/scream/scream_network_controller_unittest.cc
@@ -420,7 +420,7 @@ PaddingTestResult result = ProcessUntilPaddingStartAndStop( clock, scream_controller, feedback_generator); - EXPECT_LT(result.target_rate, DataRate::KilobitsPerSec(700)); + EXPECT_LT(result.target_rate, DataRate::KilobitsPerSec(720)); // Padding should stop when congestion is detected. EXPECT_LT(result.padding_stop - result.padding_start, TimeDelta::Seconds(1)); }
diff --git a/modules/congestion_controller/scream/scream_v2.cc b/modules/congestion_controller/scream/scream_v2.cc index 3836c93..fc5d533 100644 --- a/modules/congestion_controller/scream/scream_v2.cc +++ b/modules/congestion_controller/scream/scream_v2.cc
@@ -180,8 +180,7 @@ // of competing TCP Prague flows. backoff *= std::max(0.1, delay_based_congestion_control_ - .ref_window_scale_factor_due_to_delay_variation( - ref_window_mss_ratio())); + .ref_window_scale_factor_due_to_avg_min_delay()); } if (msg.feedback_time - last_reaction_to_congestion_time_ > @@ -233,23 +232,17 @@ increase_scale_factor * std::max(0.25, ref_window_scale_factor_close_to_ref_window_i()); - // Limit increase if L4S not enabled and queue delay is increased. - if (l4s_alpha_ < 0.0001) { - increase_scale_factor = - increase_scale_factor * - delay_based_congestion_control_ - .ref_window_scale_factor_due_to_increased_delay(); - } - // Put a additional restriction on reference window growth if rtt varies a // lot. // Better to enforce a slow increase in reference window and get // a more stable bitrate. + increase_scale_factor = increase_scale_factor * + delay_based_congestion_control_ + .ref_window_scale_factor_due_to_avg_min_delay(); increase_scale_factor = increase_scale_factor * - std::max(0.1, delay_based_congestion_control_ - .ref_window_scale_factor_due_to_delay_variation( - ref_window_mss_ratio())); + delay_based_congestion_control_ + .ref_window_scale_factor_due_to_latency_difference(); // Use lower multiplicative scale factor if congestion was detected // recently. @@ -299,8 +292,6 @@ << " is_virtual_ce=" << is_virtual_ce << " is_loss=" << is_loss << " smoothed_rtt=" << delay_based_congestion_control_.rtt().ms() << ", queue_delay=" << delay_based_congestion_control_.queue_delay().ms() - << ", queue_delay_dev_norm=" - << delay_based_congestion_control_.queue_delay_dev_norm() << " feedback_hold" << feedback_hold_time_.ms() << ", target_rate =" << target_rate_.kbps(); } @@ -312,8 +303,8 @@ (params_.ref_window_overhead_max.Get() - params_.ref_window_overhead_min.Get()) * delay_based_congestion_control_ - .ref_window_scale_factor_due_to_delay_variation( - ref_window_mss_ratio()); + .ref_window_scale_factor_due_to_avg_min_delay( + /*allow_zero=*/true); return ref_window_ * ref_window_overhead; }
diff --git a/modules/congestion_controller/scream/scream_v2_parameters.cc b/modules/congestion_controller/scream/scream_v2_parameters.cc index 16c91b2..63c01c4 100644 --- a/modules/congestion_controller/scream/scream_v2_parameters.cc +++ b/modules/congestion_controller/scream/scream_v2_parameters.cc
@@ -41,8 +41,10 @@ ref_window_overhead_min("RefWinMin", 1.5), ref_window_overhead_max("RefWinMax", 3.0), queue_delay_avg_g("QDelayAvgG", 1.0 / 4.0), - queue_delay_dev_avg_g("QDelayDevAvgG", 1.0 / 64.0), - queue_delay_dev_normalization("QDelayDevNorm", TimeDelta::Millis(25)), + delay_min_and_latency_diff_avg_g("DelayMinAndLatencyDiffAvgG", + 1.0 / 32.0), + queue_delay_min_threshold("QDelayMinThreshold", TimeDelta::Millis(10)), + latency_diff_threshold("LatencyDiffThreshold", TimeDelta::Millis(10)), base_delay_window_length("BaseDelayWindowLength", 10), base_delay_history_update_interval("BaseDelayHistoryUpdateInterval", TimeDelta::Minutes(1)), @@ -76,8 +78,9 @@ &ref_window_overhead_min, &ref_window_overhead_max, &queue_delay_avg_g, - &queue_delay_dev_avg_g, - &queue_delay_dev_normalization, + &delay_min_and_latency_diff_avg_g, + &queue_delay_min_threshold, + &latency_diff_threshold, &base_delay_window_length, &base_delay_history_update_interval, &queue_delay_target,
diff --git a/modules/congestion_controller/scream/scream_v2_parameters.h b/modules/congestion_controller/scream/scream_v2_parameters.h index 53c8296..9dafdba 100644 --- a/modules/congestion_controller/scream/scream_v2_parameters.h +++ b/modules/congestion_controller/scream/scream_v2_parameters.h
@@ -74,10 +74,16 @@ // Exponentially Weighted Moving Average (EWMA) factor for updating queue // delay. FieldTrialParameter<double> queue_delay_avg_g; - FieldTrialParameter<double> queue_delay_dev_avg_g; + // Exponentially Weighted Moving Average (EWMA) factor for updating average + // min queue delay and average latency difference. + FieldTrialParameter<double> delay_min_and_latency_diff_avg_g; - // Normalization factor for queue delay variation. - FieldTrialParameter<TimeDelta> queue_delay_dev_normalization; + // Thresholds for average min queue delay. Used for limiting ref_window + // increase. + FieldTrialParameter<TimeDelta> queue_delay_min_threshold; + // Thresholds for average latency difference. Used for limiting ref_window + // increase. + FieldTrialParameter<TimeDelta> latency_diff_threshold; // Determines the length of the base delay history when estimating one way // delay (owd)
diff --git a/rtc_tools/rtc_event_log_visualizer/analyze_bwe.cc b/rtc_tools/rtc_event_log_visualizer/analyze_bwe.cc index d0cf941..30682e4 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyze_bwe.cc +++ b/rtc_tools/rtc_event_log_visualizer/analyze_bwe.cc
@@ -644,6 +644,41 @@ plot->SetTitle("Simulated BWE behavior"); } +void CreateScreamSimulationDelayGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + TimeSeries smoothed_rtt_series("Smoothed RTT", LineStyle::kStep); + TimeSeries queue_delay_series("Queue delay", LineStyle::kStep); + TimeSeries queue_delay_min_avg_series("Queue delay min avg", + LineStyle::kStep); + TimeSeries latency_difference_avg_series("Latency difference avg", + LineStyle::kStep); + + LogScreamSimulation simulation({.rate_window = config.window_duration_}, + config.env_); + simulation.ProcessEventsInLog(parsed_log); + + for (const LogScreamSimulation::State& state : simulation.updates()) { + smoothed_rtt_series.points.emplace_back(config.GetCallTimeSec(state.time), + state.smoothed_rtt.ms()); + queue_delay_series.points.emplace_back(config.GetCallTimeSec(state.time), + state.queue_delay.ms()); + queue_delay_min_avg_series.points.emplace_back( + config.GetCallTimeSec(state.time), state.queue_delay_min_avg.ms()); + latency_difference_avg_series.points.emplace_back( + config.GetCallTimeSec(state.time), state.latency_difference_avg.ms()); + } + plot->AppendTimeSeries(std::move(smoothed_rtt_series)); + plot->AppendTimeSeries(std::move(queue_delay_series)); + plot->AppendTimeSeries(std::move(queue_delay_min_avg_series)); + plot->AppendTimeSeries(std::move(latency_difference_avg_series)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 50, "Delay (ms)", kBottomMargin, kTopMargin); + plot->SetTitle("Simulated Scream delays"); +} + void CreateScreamSimulationBitrateGraph(const ParsedRtcEventLog& parsed_log, const AnalyzerConfig& config, Plot* plot) { @@ -757,10 +792,10 @@ TimeSeries queue_delay_dev_norm_series("QueueDelayDevNorm", LineStyle::kStep); TimeSeries l4s_alpha_series("L4sAlpha", LineStyle::kStep); TimeSeries l4s_alpha_v_series("L4sAlphaV", LineStyle::kStep); - TimeSeries ref_window_scale_factor_due_to_increased_delay( - "RefWindowScaleFactorDueToIncreasedDelay", LineStyle::kStep); - TimeSeries ref_window_scale_factor_due_to_delay_variation( - "RefWindowScaleFactorDueToDelayVariation", LineStyle::kStep); + TimeSeries ref_window_scale_factor_due_to_min_delay_variation( + "RefWindowScaleFactorDueToAvgMinQueueDelay", LineStyle::kStep); + TimeSeries ref_window_scale_factor_due_to_latency_difference( + "RefWindowScaleFactorDueToAvgLatencyDifference", LineStyle::kStep); TimeSeries ref_window_scale_factor_close_to_ref_window_i( "RefWindowScaleFactorCloseToRefWindowI", LineStyle::kStep); TimeSeries ref_window_combined_increase_scale_factor( @@ -777,12 +812,12 @@ state.l4s_alpha); l4s_alpha_v_series.points.emplace_back(config.GetCallTimeSec(state.time), state.l4s_alpha_v); - ref_window_scale_factor_due_to_increased_delay.points.emplace_back( + ref_window_scale_factor_due_to_min_delay_variation.points.emplace_back( config.GetCallTimeSec(state.time), - state.ref_window_scale_factor_due_to_increased_delay); - ref_window_scale_factor_due_to_delay_variation.points.emplace_back( + state.ref_window_scale_factor_due_to_avg_min_delay); + ref_window_scale_factor_due_to_latency_difference.points.emplace_back( config.GetCallTimeSec(state.time), - state.ref_window_scale_factor_due_to_delay_variation); + state.ref_window_scale_factor_due_to_latency_difference); ref_window_scale_factor_close_to_ref_window_i.points.emplace_back( config.GetCallTimeSec(state.time), state.ref_window_scale_factor_close_to_ref_window_i); @@ -794,9 +829,9 @@ plot->AppendTimeSeries(std::move(l4s_alpha_series)); plot->AppendTimeSeries(std::move(l4s_alpha_v_series)); plot->AppendTimeSeries( - std::move(ref_window_scale_factor_due_to_increased_delay)); + std::move(ref_window_scale_factor_due_to_min_delay_variation)); plot->AppendTimeSeries( - std::move(ref_window_scale_factor_due_to_delay_variation)); + std::move(ref_window_scale_factor_due_to_latency_difference)); plot->AppendTimeSeries( std::move(ref_window_scale_factor_close_to_ref_window_i)); plot->AppendTimeSeries(std::move(ref_window_combined_increase_scale_factor));
diff --git a/rtc_tools/rtc_event_log_visualizer/analyze_bwe.h b/rtc_tools/rtc_event_log_visualizer/analyze_bwe.h index fd4b041..5de8598 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyze_bwe.h +++ b/rtc_tools/rtc_event_log_visualizer/analyze_bwe.h
@@ -40,10 +40,13 @@ const AnalyzerConfig& config, Plot* plot); +void CreateScreamSimulationDelayGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + void CreateScreamSimulationBitrateGraph(const ParsedRtcEventLog& parsed_log, const AnalyzerConfig& config, Plot* plot); - void CreateScreamSimulationRefWindowGraph(const ParsedRtcEventLog& parsed_log, const AnalyzerConfig& config, Plot* plot);
diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer.cc b/rtc_tools/rtc_event_log_visualizer/analyzer.cc index 72ef110..a44d683 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyzer.cc +++ b/rtc_tools/rtc_event_log_visualizer/analyzer.cc
@@ -172,6 +172,9 @@ plots_.RegisterPlot("simulated_goog_cc", [this](Plot* plot) { this->CreateGoogCcSimulationGraph(plot); }); + plots_.RegisterPlot("simulated_scream_delay", [this](Plot* plot) { + this->CreateScreamSimulationDelayGraph(plot); + }); plots_.RegisterPlot("simulated_scream_bitrates", [this](Plot* plot) { this->CreateScreamSimulationBitrateGraph(plot); }); @@ -403,6 +406,10 @@ webrtc::CreateGoogCcSimulationGraph(parsed_log_, config_, plot); } +void EventLogAnalyzer::CreateScreamSimulationDelayGraph(Plot* plot) const { + webrtc::CreateScreamSimulationDelayGraph(parsed_log_, config_, plot); +} + void EventLogAnalyzer::CreateScreamSimulationBitrateGraph(Plot* plot) const { webrtc::CreateScreamSimulationBitrateGraph(parsed_log_, config_, plot); }
diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer.h b/rtc_tools/rtc_event_log_visualizer/analyzer.h index 511d002..60d2244 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyzer.h +++ b/rtc_tools/rtc_event_log_visualizer/analyzer.h
@@ -131,6 +131,7 @@ void CreateScreamRefWindowGraph(Plot* plot) const; void CreateScreamDelayEstimateGraph(Plot* plot) const; void CreateGoogCcSimulationGraph(Plot* plot) const; + void CreateScreamSimulationDelayGraph(Plot* plot) const; void CreateScreamSimulationBitrateGraph(Plot* plot) const; void CreateScreamSimulationRefWindowGraph(Plot* plot) const; void CreateScreamSimulationRatiosGraph(Plot* plot) const;
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 5e97896..e52e131 100644 --- a/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.cc +++ b/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.cc
@@ -172,17 +172,18 @@ .max_data_in_flight = scream_->max_data_in_flight(), .data_in_flight = data_in_flight_, .send_window_usage = send_window_usage_, - .queue_delay_dev_norm = - scream_->delay_based_congestion_control().queue_delay_dev_norm(), - .ref_window_scale_factor_due_to_increased_delay = - scream_->delay_based_congestion_control().IsQueueDelayDetected() - ? 0.0 - : scream_->delay_based_congestion_control() - .ref_window_scale_factor_due_to_increased_delay(), - .ref_window_scale_factor_due_to_delay_variation = + .smoothed_rtt = scream_->delay_based_congestion_control().rtt(), + .queue_delay = scream_->delay_based_congestion_control().queue_delay(), + .queue_delay_min_avg = + scream_->delay_based_congestion_control().queue_delay_min_avg(), + .latency_difference_avg = + scream_->delay_based_congestion_control().latency_difference_avg(), + .ref_window_scale_factor_due_to_avg_min_delay = scream_->delay_based_congestion_control() - .ref_window_scale_factor_due_to_delay_variation( - scream_->ref_window_mss_ratio()), + .ref_window_scale_factor_due_to_avg_min_delay(), + .ref_window_scale_factor_due_to_latency_difference = + scream_->delay_based_congestion_control() + .ref_window_scale_factor_due_to_latency_difference(), .ref_window_scale_factor_close_to_ref_window_i = scream_->ref_window_scale_factor_close_to_ref_window_i(), .ref_window_combined_increase_scale_factor =
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 9641f77..252880a 100644 --- a/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.h +++ b/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.h
@@ -55,9 +55,14 @@ // last packet was sent before the state was captured. SendWindowUsage send_window_usage = kBelowRefWindow; + TimeDelta smoothed_rtt = TimeDelta::Zero(); + TimeDelta queue_delay = TimeDelta::Zero(); + TimeDelta queue_delay_min_avg = TimeDelta::Zero(); + TimeDelta latency_difference_avg = TimeDelta::Zero(); + double queue_delay_dev_norm = 0; - double ref_window_scale_factor_due_to_increased_delay = 0.0; - double ref_window_scale_factor_due_to_delay_variation = 0.0; + double ref_window_scale_factor_due_to_avg_min_delay = 0.0; + double ref_window_scale_factor_due_to_latency_difference = 0.0; double ref_window_scale_factor_close_to_ref_window_i = 0.0; double ref_window_combined_increase_scale_factor = 0.0; double l4s_alpha = 0.0;
diff --git a/rtc_tools/rtc_event_log_visualizer/main.cc b/rtc_tools/rtc_event_log_visualizer/main.cc index 67ac267..f299d9a 100644 --- a/rtc_tools/rtc_event_log_visualizer/main.cc +++ b/rtc_tools/rtc_event_log_visualizer/main.cc
@@ -251,7 +251,7 @@ {"incoming_bitrate", "outgoing_bitrate", "incoming_ecn_feedback", "outgoing_ecn_feedback"}}, {"scream", - {"scream_delay_estimates", "scream_ref_window", + {"scream_ref_window", "simulated_scream_delay", "simulated_scream_bitrates", "simulated_scream_ref_window", "simulated_scream_ratios", "network_delay_feedback", "pacer_delay"}}};
diff --git a/test/peer_scenario/bwe_integration_tests/scream_test.cc b/test/peer_scenario/bwe_integration_tests/scream_test.cc index 33b5901..8ed6a43 100644 --- a/test/peer_scenario/bwe_integration_tests/scream_test.cc +++ b/test/peer_scenario/bwe_integration_tests/scream_test.cc
@@ -337,7 +337,7 @@ EXPECT_THAT( result.caller().subspan(1, 3), Each(AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(10), - DataRate::KilobitsPerSec(100)))); + DataRate::KilobitsPerSec(120)))); EXPECT_THAT(result.caller()[3], CurrentRoundTripTimeIsBetween(TimeDelta::Millis(40), TimeDelta::Millis(200))); @@ -347,7 +347,7 @@ // is disabled. EXPECT_THAT(result.caller_stats.back(), AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(100), - DataRate::KilobitsPerSec(5000))); + DataRate::KilobitsPerSec(6500))); } TEST(ScreamTest, MaybeTest(LinkCapacity600KbpsRtt20msNoEcn)) { @@ -363,8 +363,8 @@ SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s); EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween( - DataRate::KilobitsPerSec(300), - DataRate::KilobitsPerSec(700)))); + DataRate::KilobitsPerSec(200), + DataRate::KilobitsPerSec(800)))); } TEST(ScreamTest, MaybeTest(LinkCapacity600KbpsRtt100msEcn)) { @@ -467,7 +467,7 @@ SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s); EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween( DataRate::KilobitsPerSec(800), - DataRate::KilobitsPerSec(1900)))); + DataRate::KilobitsPerSec(2000)))); } TEST(ScreamTest, MaybeTest(LinkCapacity2MbpsRtt50msNoEcn)) { @@ -484,7 +484,7 @@ SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s); EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween( DataRate::KilobitsPerSec(1300), - DataRate::KilobitsPerSec(2300)))); + DataRate::KilobitsPerSec(2400)))); } TEST(ScreamTest, MaybeTest(LinkCapacity2MbpsRtt50msEcn)) { @@ -525,6 +525,26 @@ DataRate::KilobitsPerSec(2600))); } +TEST(ScreamTest, MaybeTest(LinkCapacity4MbpsRtt50ms10MsDelayStdDev)) { + PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info()); + NetworkEmulationManager::SimulatedNetworkNode::Builder network_builder( + s.net()); + network_builder.delay_ms(25); + network_builder.delay_standard_deviation_ms(10); + network_builder.capacity_Mbps(4); + SendMediaTestParams params; + params.test_duration = TimeDelta::Seconds(30); + params.callee_to_caller_path = + CreateNetworkPath(network_builder, /*use_dual_pi= */ false); + params.caller_to_callee_path = + CreateNetworkPath(network_builder, /*use_dual_pi= */ false); + + SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s); + EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween( + DataRate::KilobitsPerSec(700), + DataRate::KilobitsPerSec(4100)))); +} + TEST(ScreamTest, MaybeTest(LinkCapacity2MbpsRtt50msNoEcnWithTwcc)) { PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info()); SendMediaTestParams params; @@ -545,7 +565,7 @@ // approximately. EXPECT_THAT(result.caller().subspan(5), Each(AvailableSendBitrateIsBetween( DataRate::KilobitsPerSec(1200), - DataRate::KilobitsPerSec(2300)))); + DataRate::KilobitsPerSec(2400)))); } TEST(ScreamTest, MaybeTest(CallerPauseSendingVideoIfFeedbackNotReceived)) { @@ -627,7 +647,7 @@ // Ignore estimate during rampup. EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween( DataRate::KilobitsPerSec(1200), - DataRate::KilobitsPerSec(2600)))); + DataRate::KilobitsPerSec(2800)))); } TEST(ScreamTest, MaybeTest(ScreencastSlideChangeRepeatedDelaySpikes)) { @@ -645,12 +665,9 @@ .change_interval = TimeDelta::Seconds(5)}}}; SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s); - // Ignore estimate during rampup. - // TODO: bugs.webrtc.org/447037083 - consider improving resilience by limiting - // queue delay from one feedback. - EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween( - DataRate::KilobitsPerSec(100), - DataRate::KilobitsPerSec(4000)))); + EXPECT_THAT(result.caller(), Each(AvailableSendBitrateIsBetween( + DataRate::KilobitsPerSec(500), + DataRate::KilobitsPerSec(4300)))); } TEST(ScreamTest, MaybeTest(LinkCapacity5MbitRepeatedDelaySpikesNoEcn)) { @@ -664,16 +681,14 @@ TimeDelta::Millis(200)); SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s); - // TODO: bugs.webrtc.org/447037083 - consider improving resilience by limiting - // queue delay from one feedback. EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween( - DataRate::KilobitsPerSec(500), + DataRate::KilobitsPerSec(1200), DataRate::KilobitsPerSec(5000)))); } TEST(ScreamTest, MaybeTest(RampupFastOnLinkCapacity50Mbit20MsRttNoEcn)) { PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info()); - SendMediaTestParams params{.test_duration = TimeDelta::Millis(300), + SendMediaTestParams params{.test_duration = TimeDelta::Seconds(1), .stats_interval = TimeDelta::Millis(100)}; params.caller_to_callee_path = CreateNetworkPath(s, /*use_dual_pi= */ false, @@ -685,9 +700,14 @@ // TODO: bugs.webrtc.org/447037083 - On a good network, we should be able to // let the bitrate increase by 50% every RTT. - EXPECT_THAT(result.caller_stats.back(), + // After 300ms. + EXPECT_THAT(result.caller_stats[2], AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(500), DataRate::KilobitsPerSec(5000))); + // After 1s. + EXPECT_THAT(result.caller_stats.back(), + AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(1500), + DataRate::KilobitsPerSec(5000))); } TEST(ScreamTest, MaybeTest(LinkCapacity100Kbit50msRttNoEcn)) { @@ -767,7 +787,7 @@ GetPacketsSent(result.caller_stats[5])); EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween( DataRate::KilobitsPerSec(300), - DataRate::KilobitsPerSec(1100)))); + DataRate::KilobitsPerSec(1300)))); } TEST(ScreamTest, MaybeTest(SendVideoOnlyReturnLinkWithBurstLoss)) {