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