Upper limit pacer send bursts to about 63Kbyte

The purpose is to ensure send socket buffers are not overfilled at high
pacing rates.

Bug: chromium:1354491
Change-Id: Ic6f473080292f84a2a099b85fb5817f7e14e7355
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/323000
Commit-Queue: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40911}
diff --git a/modules/pacing/pacing_controller.cc b/modules/pacing/pacing_controller.cc
index 74def9c..54a2f7a 100644
--- a/modules/pacing/pacing_controller.cc
+++ b/modules/pacing/pacing_controller.cc
@@ -17,6 +17,8 @@
 
 #include "absl/cleanup/cleanup.h"
 #include "absl/strings/match.h"
+#include "api/units/data_size.h"
+#include "api/units/time_delta.h"
 #include "modules/pacing/bitrate_prober.h"
 #include "modules/pacing/interval_budget.h"
 #include "rtc_base/checks.h"
@@ -343,9 +345,13 @@
     // debt is allowed to grow up to one packet more than what can be sent
     // during 'send_burst_period_'.
     TimeDelta drain_time = media_debt_ / adjusted_media_rate_;
+    // Ensure that a burst of sent packet is not larger than kMaxBurstSize in
+    // order to not risk overfilling socket buffers at high bitrate.
+    TimeDelta send_burst_interval =
+        std::min(send_burst_interval_, kMaxBurstSize / adjusted_media_rate_);
     next_send_time =
         last_process_time_ +
-        ((send_burst_interval_ > drain_time) ? TimeDelta::Zero() : drain_time);
+        ((send_burst_interval > drain_time) ? TimeDelta::Zero() : drain_time);
   } else if (padding_rate_ > DataRate::Zero() && packet_queue_.Empty()) {
     // If we _don't_ have pending packets, check how long until we have
     // bandwidth for padding packets. Both media and padding debts must
diff --git a/modules/pacing/pacing_controller.h b/modules/pacing/pacing_controller.h
index 2145868..0a138c1 100644
--- a/modules/pacing/pacing_controller.h
+++ b/modules/pacing/pacing_controller.h
@@ -24,6 +24,7 @@
 #include "api/function_view.h"
 #include "api/transport/field_trial_based_config.h"
 #include "api/transport/network_types.h"
+#include "api/units/data_size.h"
 #include "modules/pacing/bitrate_prober.h"
 #include "modules/pacing/interval_budget.h"
 #include "modules/pacing/prioritized_packet_queue.h"
@@ -86,6 +87,11 @@
   // set to 1ms as this is intended to allow times be rounded down to the
   // nearest millisecond.
   static const TimeDelta kMaxEarlyProbeProcessing;
+  // Max total size of packets expected to be sent in a burst in order to not
+  // risk loosing packets due to too small send socket buffers. It upper limits
+  // the send burst interval.
+  // Ex: max send burst interval = 63Kb / 10Mbit/s = 50ms.
+  static constexpr DataSize kMaxBurstSize = DataSize::Bytes(63 * 1000);
 
   PacingController(Clock* clock,
                    PacketSender* packet_sender,
diff --git a/modules/pacing/pacing_controller_unittest.cc b/modules/pacing/pacing_controller_unittest.cc
index ade71cd..ba93d05 100644
--- a/modules/pacing/pacing_controller_unittest.cc
+++ b/modules/pacing/pacing_controller_unittest.cc
@@ -11,17 +11,16 @@
 #include "modules/pacing/pacing_controller.h"
 
 #include <algorithm>
-#include <list>
+#include <cstddef>
 #include <memory>
-#include <string>
 #include <utility>
 #include <vector>
 
 #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/pacing/packet_router.h"
 #include "system_wrappers/include/clock.h"
 #include "test/explicit_key_value_config.h"
 #include "test/gmock.h"
@@ -30,6 +29,7 @@
 using ::testing::_;
 using ::testing::AnyNumber;
 using ::testing::Field;
+using ::testing::NiceMock;
 using ::testing::Pointee;
 using ::testing::Property;
 using ::testing::Return;
@@ -2146,6 +2146,36 @@
   EXPECT_EQ(number_of_bursts, 4);
 }
 
+TEST_F(PacingControllerTest,
+       MaxBurstSizeLimitedAtHighPacingRateWhenSendingPacketsInBursts) {
+  NiceMock<MockPacketSender> callback;
+  PacingController pacer(&clock_, &callback, trials_);
+  pacer.SetSendBurstInterval(TimeDelta::Millis(100));
+  pacer.SetPacingRates(DataRate::KilobitsPerSec(10'000), DataRate::Zero());
+
+  size_t sent_size_in_burst = 0;
+  EXPECT_CALL(callback, SendPacket)
+      .WillRepeatedly([&](std::unique_ptr<RtpPacketToSend> packet,
+                          const PacedPacketInfo& cluster_info) {
+        sent_size_in_burst += packet->size();
+      });
+
+  // Enqueue 200 packets from a 200Kb encoded frame.
+  for (int i = 0; i < 200; ++i) {
+    pacer.EnqueuePacket(video_.BuildNextPacket(1000));
+  }
+
+  while (pacer.QueueSizePackets() > 70) {
+    pacer.ProcessPackets();
+    EXPECT_NEAR(sent_size_in_burst, PacingController::kMaxBurstSize.bytes(),
+                1000);
+    sent_size_in_burst = 0;
+    TimeDelta time_to_next = pacer.NextSendTime() - clock_.CurrentTime();
+    EXPECT_NEAR(time_to_next.ms(), 50, 2);
+    clock_.AdvanceTime(time_to_next);
+  }
+}
+
 TEST_F(PacingControllerTest, RespectsQueueTimeLimit) {
   static constexpr DataSize kPacketSize = DataSize::Bytes(100);
   static constexpr DataRate kNominalPacingRate = DataRate::KilobitsPerSec(200);