Cap feedback interval to max_time_between_feedback if send BWE is zero When send bandwidth estimate is zero and feedback is bandwidth limited, set the next possible feedback send time to last_feedback_sent_time + max_time_between_feedback. This avoids dividing by zero in debt pacing while still respecting the maximum time between feedbacks. Bug: webrtc:519655843 Change-Id: Id28188dd6e480636cbf15cea5ca150263791b8bc Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/506360 Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Commit-Queue: Per Kjellander <perkj@webrtc.org> Cr-Commit-Position: refs/heads/main@{#48759}
diff --git a/modules/remote_bitrate_estimator/congestion_control_feedback_generator.cc b/modules/remote_bitrate_estimator/congestion_control_feedback_generator.cc index f674d5a..a98f463 100644 --- a/modules/remote_bitrate_estimator/congestion_control_feedback_generator.cc +++ b/modules/remote_bitrate_estimator/congestion_control_feedback_generator.cc
@@ -129,12 +129,17 @@ TimeDelta time_since_last_sent = last_feedback_sent_time_.IsFinite() ? now - last_feedback_sent_time_ : TimeDelta::Zero(); + last_feedback_sent_time_ = now; DataRate max_feedback_rate = kMaxFeedbackRate; if (is_bandwidth_limited_ && max_feedback_fraction_.Get() > 0.0 && send_bandwidth_estimate_.has_value() && - send_bandwidth_estimate_->IsFinite() && - *send_bandwidth_estimate_ > DataRate::Zero()) { + send_bandwidth_estimate_->IsFinite()) { + if (send_bandwidth_estimate_->IsZero()) { + next_possible_feedback_send_time_ = + last_feedback_sent_time_ + max_time_between_feedback_.Get(); + return; + } max_feedback_rate = *send_bandwidth_estimate_ * max_feedback_fraction_.Get(); } @@ -142,7 +147,6 @@ send_rate_debt_ = debt_payed > send_rate_debt_ ? DataSize::Zero() : send_rate_debt_ - debt_payed; send_rate_debt_ += feedback_size; - last_feedback_sent_time_ = now; next_possible_feedback_send_time_ = now + std::clamp(send_rate_debt_ / max_feedback_rate, min_time_between_feedback_.Get(),
diff --git a/modules/remote_bitrate_estimator/congestion_control_feedback_generator_unittest.cc b/modules/remote_bitrate_estimator/congestion_control_feedback_generator_unittest.cc index 29fba5f..aa4b110 100644 --- a/modules/remote_bitrate_estimator/congestion_control_feedback_generator_unittest.cc +++ b/modules/remote_bitrate_estimator/congestion_control_feedback_generator_unittest.cc
@@ -511,5 +511,62 @@ EXPECT_EQ(number_of_feedback_packets, 40); } +TEST(CongestionControlFeedbackGeneratorTest, + FeedbackSentEveryMaxTimeBetweenFeedbackWhenSendBweIsZero) { + MockFunction<void(std::vector<std::unique_ptr<rtcp::RtcpPacket>>)> + rtcp_sender; + SimulatedClock clock(123456); + + // Enable 5% feedback fraction limit via field trial + CongestionControlFeedbackGenerator generator( + CreateTestEnvironment({.field_trials = + "WebRTC-RFC8888CongestionControlFeedback/" + "feedback_fraction:0.05/", + .time = &clock}), + rtcp_sender.AsStdFunction()); + + generator.OnSendBandwidthEstimateChanged( + DataRate::Zero(), + /*is_bandwidth_limited=*/true, + /*transport_overhead=*/DataSize::Bytes(42)); + + int number_of_feedback_packets = 0; + Timestamp last_feedback_time = Timestamp::MinusInfinity(); + EXPECT_CALL(rtcp_sender, Call) + .WillRepeatedly( + [&](std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets) { + ASSERT_THAT(rtcp_packets, SizeIs(1)); + number_of_feedback_packets++; + if (last_feedback_time.IsFinite()) { + EXPECT_EQ(clock.CurrentTime() - last_feedback_time, + TimeDelta::Millis(250)); + } + last_feedback_time = clock.CurrentTime(); + }); + + Timestamp start_time = clock.CurrentTime(); + Timestamp last_process_time = clock.CurrentTime(); + TimeDelta time_to_next_process = generator.Process(clock.CurrentTime()); + uint16_t rtp_sequence_number = 0; + + // Receive 1 packet every 10 ms for 1 second. + while (clock.CurrentTime() < start_time + TimeDelta::Seconds(1)) { + if ((clock.CurrentTime() - start_time).ms() % 10 == 0) { + generator.OnReceivedPacket(CreatePacket(clock.CurrentTime(), + /*marker=*/true, /*ssrc=*/1234, + rtp_sequence_number++)); + } + + if (clock.CurrentTime() >= last_process_time + time_to_next_process) { + last_process_time = clock.CurrentTime(); + time_to_next_process = generator.Process(clock.CurrentTime()); + } + clock.AdvanceTime(TimeDelta::Millis(1)); + } + + // 1000ms / 250ms = 4 feedback packets. + EXPECT_EQ(number_of_feedback_packets, 4); +} + } // namespace } // namespace webrtc