BitrateProber: Support higher probing bitrates

Currently, BitrateProber does not scale higher than 2 Mbps to 6 Mbps. The actual
number is dependent on the size of the last packet. If a packet of around 250
bytes is used for probing, it fails above 2 Mbps.

BitrateProber now provides a recommendation on probe size instead of a
packet size. PacedSender utilizes this to decide on the number of packets
per probe. This enables BitrateProber to scale up-to higher bitrates.

Tests with chromoting show it stalls at about 10 Mbps (perhaps due to the
limitation on the simulation pipeline to deliver packets).

BUG=webrtc:6332

Review-Url: https://codereview.webrtc.org/2347023002
Cr-Commit-Position: refs/heads/master@{#14503}
diff --git a/webrtc/modules/pacing/bitrate_prober.cc b/webrtc/modules/pacing/bitrate_prober.cc
index 936be39..fde2f65 100644
--- a/webrtc/modules/pacing/bitrate_prober.cc
+++ b/webrtc/modules/pacing/bitrate_prober.cc
@@ -23,17 +23,20 @@
 // Inactivity threshold above which probing is restarted.
 constexpr int kInactivityThresholdMs = 5000;
 
-int ComputeDeltaFromBitrate(size_t packet_size, uint32_t bitrate_bps) {
+// A minimum interval between probes to allow scheduling to be feasible.
+constexpr int kMinProbeDeltaMs = 1;
+
+int ComputeDeltaFromBitrate(size_t probe_size, uint32_t bitrate_bps) {
   RTC_CHECK_GT(bitrate_bps, 0u);
-  // Compute the time delta needed to send packet_size bytes at bitrate_bps
+  // Compute the time delta needed to send probe_size bytes at bitrate_bps
   // bps. Result is in milliseconds.
-  return static_cast<int>((1000ll * packet_size * 8) / bitrate_bps);
+  return static_cast<int>((1000ll * probe_size * 8) / bitrate_bps);
 }
 }  // namespace
 
 BitrateProber::BitrateProber()
     : probing_state_(ProbingState::kDisabled),
-      packet_size_last_sent_(0),
+      probe_size_last_sent_(0),
       time_last_probe_sent_ms_(-1),
       next_cluster_id_(0) {
   SetEnabled(true);
@@ -65,15 +68,15 @@
   }
 }
 
-void BitrateProber::CreateProbeCluster(int bitrate_bps, int num_packets) {
+void BitrateProber::CreateProbeCluster(int bitrate_bps, int num_probes) {
   RTC_DCHECK(probing_state_ != ProbingState::kDisabled);
   ProbeCluster cluster;
-  cluster.max_probe_packets = num_packets;
+  cluster.max_probes = num_probes;
   cluster.probe_bitrate_bps = bitrate_bps;
   cluster.id = next_cluster_id_++;
   clusters_.push(cluster);
-  LOG(LS_INFO) << "Probe cluster (bitrate:packets): ("
-               << cluster.probe_bitrate_bps << ":" << cluster.max_probe_packets
+  LOG(LS_INFO) << "Probe cluster (bitrate:probes): ("
+               << cluster.probe_bitrate_bps << ":" << cluster.max_probes
                << ") ";
   if (probing_state_ != ProbingState::kActive)
     probing_state_ = ProbingState::kInactive;
@@ -81,14 +84,14 @@
 
 void BitrateProber::ResetState() {
   time_last_probe_sent_ms_ = -1;
-  packet_size_last_sent_ = 0;
+  probe_size_last_sent_ = 0;
 
   // Recreate all probing clusters.
   std::queue<ProbeCluster> clusters;
   clusters.swap(clusters_);
   while (!clusters.empty()) {
     CreateProbeCluster(clusters.front().probe_bitrate_bps,
-                       clusters.front().max_probe_packets);
+                       clusters.front().max_probes);
     clusters.pop();
   }
   // If its enabled, reset to inactive.
@@ -116,13 +119,10 @@
   // We will send the first probe packet immediately if no packet has been
   // sent before.
   int time_until_probe_ms = 0;
-  if (packet_size_last_sent_ != 0 && probing_state_ == ProbingState::kActive) {
+  if (probe_size_last_sent_ != 0 && probing_state_ == ProbingState::kActive) {
     int next_delta_ms = ComputeDeltaFromBitrate(
-        packet_size_last_sent_, clusters_.front().probe_bitrate_bps);
+        probe_size_last_sent_, clusters_.front().probe_bitrate_bps);
     time_until_probe_ms = next_delta_ms - elapsed_time_ms;
-    // There is no point in trying to probe with less than 1 ms between packets
-    // as it essentially means trying to probe at infinite bandwidth.
-    const int kMinProbeDeltaMs = 1;
     // If we have waited more than 3 ms for a new packet to probe with we will
     // consider this probing session over.
     const int kMaxProbeDelayMs = 3;
@@ -142,22 +142,24 @@
   return clusters_.front().id;
 }
 
-size_t BitrateProber::RecommendedPacketSize() const {
-  return packet_size_last_sent_;
+// Probe size is recommended based on the probe bitrate required. We choose
+// a minimum of twice |kMinProbeDeltaMs| interval to allow scheduling to be
+// feasible.
+size_t BitrateProber::RecommendedMinProbeSize() const {
+  RTC_DCHECK(!clusters_.empty());
+  return clusters_.front().probe_bitrate_bps * 2 * kMinProbeDeltaMs /
+         (8 * 1000);
 }
 
-void BitrateProber::PacketSent(int64_t now_ms, size_t packet_size) {
-  assert(packet_size > 0);
-  if (packet_size < PacedSender::kMinProbePacketSize)
-    return;
-  packet_size_last_sent_ = packet_size;
-  if (probing_state_ != ProbingState::kActive)
-    return;
+void BitrateProber::ProbeSent(int64_t now_ms, size_t bytes) {
+  RTC_DCHECK(probing_state_ == ProbingState::kActive);
+  RTC_DCHECK_GT(bytes, 0u);
+  probe_size_last_sent_ = bytes;
   time_last_probe_sent_ms_ = now_ms;
   if (!clusters_.empty()) {
     ProbeCluster* cluster = &clusters_.front();
-    ++cluster->sent_probe_packets;
-    if (cluster->sent_probe_packets == cluster->max_probe_packets)
+    ++cluster->sent_probes;
+    if (cluster->sent_probes == cluster->max_probes)
       clusters_.pop();
     if (clusters_.empty())
       probing_state_ = ProbingState::kSuspended;
diff --git a/webrtc/modules/pacing/bitrate_prober.h b/webrtc/modules/pacing/bitrate_prober.h
index cfeb497..81eea64 100644
--- a/webrtc/modules/pacing/bitrate_prober.h
+++ b/webrtc/modules/pacing/bitrate_prober.h
@@ -36,24 +36,26 @@
   // with.
   void OnIncomingPacket(size_t packet_size);
 
-  // Create a cluster used to probe for |bitrate_bps| with |num_packets| number
-  // of packets.
-  void CreateProbeCluster(int bitrate_bps, int num_packets);
+  // Create a cluster used to probe for |bitrate_bps| with |num_probes| number
+  // of probes.
+  void CreateProbeCluster(int bitrate_bps, int num_probes);
 
-  // Returns the number of milliseconds until the next packet should be sent to
+  // Returns the number of milliseconds until the next probe should be sent to
   // get accurate probing.
   int TimeUntilNextProbe(int64_t now_ms);
 
   // Which cluster that is currently being used for probing.
   int CurrentClusterId() const;
 
-  // Returns the number of bytes that the prober recommends for the next probe
-  // packet.
-  size_t RecommendedPacketSize() const;
+  // Returns the minimum number of bytes that the prober recommends for
+  // the next probe.
+  size_t RecommendedMinProbeSize() const;
 
-  // Called to report to the prober that a packet has been sent, which helps the
-  // prober know when to move to the next packet in a probe.
-  void PacketSent(int64_t now_ms, size_t packet_size);
+  // Called to report to the prober that a probe has been sent. In case of
+  // multiple packets per probe, this call would be made at the end of sending
+  // the last packet in probe. |probe_size| is the total size of all packets
+  // in probe.
+  void ProbeSent(int64_t now_ms, size_t probe_size);
 
  private:
   enum class ProbingState {
@@ -69,9 +71,11 @@
     kSuspended,
   };
 
+  // A probe cluster consists of a set of probes. Each probe in turn can be
+  // divided into a number of packets to accomodate the MTU on the network.
   struct ProbeCluster {
-    int max_probe_packets = 0;
-    int sent_probe_packets = 0;
+    int max_probes = 0;
+    int sent_probes = 0;
     int probe_bitrate_bps = 0;
     int id = -1;
   };
@@ -84,7 +88,8 @@
   // the previous probe packet based on the size and time when that packet was
   // sent.
   std::queue<ProbeCluster> clusters_;
-  size_t packet_size_last_sent_;
+  // A probe can include one or more packets.
+  size_t probe_size_last_sent_;
   // The last time a probe was sent.
   int64_t time_last_probe_sent_ms_;
   int next_cluster_id_;
diff --git a/webrtc/modules/pacing/bitrate_prober_unittest.cc b/webrtc/modules/pacing/bitrate_prober_unittest.cc
index c55ac68..fb353aa 100644
--- a/webrtc/modules/pacing/bitrate_prober_unittest.cc
+++ b/webrtc/modules/pacing/bitrate_prober_unittest.cc
@@ -31,7 +31,7 @@
 
   // First packet should probe as soon as possible.
   EXPECT_EQ(0, prober.TimeUntilNextProbe(now_ms));
-  prober.PacketSent(now_ms, 1000);
+  prober.ProbeSent(now_ms, 1000);
 
   for (int i = 0; i < 5; ++i) {
     EXPECT_EQ(8, prober.TimeUntilNextProbe(now_ms));
@@ -40,14 +40,14 @@
     now_ms += 4;
     EXPECT_EQ(0, prober.TimeUntilNextProbe(now_ms));
     EXPECT_EQ(0, prober.CurrentClusterId());
-    prober.PacketSent(now_ms, 1000);
+    prober.ProbeSent(now_ms, 1000);
   }
   for (int i = 0; i < 5; ++i) {
     EXPECT_EQ(4, prober.TimeUntilNextProbe(now_ms));
     now_ms += 4;
     EXPECT_EQ(0, prober.TimeUntilNextProbe(now_ms));
     EXPECT_EQ(1, prober.CurrentClusterId());
-    prober.PacketSent(now_ms, 1000);
+    prober.ProbeSent(now_ms, 1000);
   }
 
   EXPECT_EQ(-1, prober.TimeUntilNextProbe(now_ms));
@@ -66,19 +66,19 @@
   prober.OnIncomingPacket(1000);
   EXPECT_TRUE(prober.IsProbing());
   EXPECT_EQ(0, prober.TimeUntilNextProbe(now_ms));
-  prober.PacketSent(now_ms, 1000);
+  prober.ProbeSent(now_ms, 1000);
   // Let time pass, no large enough packets put into prober.
   now_ms += 6000;
   EXPECT_EQ(-1, prober.TimeUntilNextProbe(now_ms));
   // Insert a small packet, not a candidate for probing.
   prober.OnIncomingPacket(100);
-  prober.PacketSent(now_ms, 100);
+  EXPECT_FALSE(prober.IsProbing());
   EXPECT_EQ(-1, prober.TimeUntilNextProbe(now_ms));
   // Insert a large-enough packet after downtime while probing should reset to
   // perform a new probe since the requested one didn't finish.
   prober.OnIncomingPacket(1000);
   EXPECT_EQ(0, prober.TimeUntilNextProbe(now_ms));
-  prober.PacketSent(now_ms, 1000);
+  prober.ProbeSent(now_ms, 1000);
   // Next packet should be part of new probe and be sent with non-zero delay.
   prober.OnIncomingPacket(1000);
   EXPECT_GT(prober.TimeUntilNextProbe(now_ms), 0);
@@ -93,4 +93,13 @@
   EXPECT_FALSE(prober.IsProbing());
 }
 
+TEST(BitrateProberTest, VerifyProbeSizeOnHighBitrate) {
+  BitrateProber prober;
+  constexpr unsigned kHighBitrateBps = 10000000;  // 10 Mbps
+
+  prober.CreateProbeCluster(kHighBitrateBps, 6);
+  // Probe size should ensure a minimum of 1 ms interval.
+  EXPECT_GT(prober.RecommendedMinProbeSize(), kHighBitrateBps / 8000);
+}
+
 }  // namespace webrtc
diff --git a/webrtc/modules/pacing/paced_sender.cc b/webrtc/modules/pacing/paced_sender.cc
index b0b86ee..fe918bb 100644
--- a/webrtc/modules/pacing/paced_sender.cc
+++ b/webrtc/modules/pacing/paced_sender.cc
@@ -402,8 +402,13 @@
   }
 
   bool is_probing = prober_->IsProbing();
-  int probe_cluster_id = is_probing ? prober_->CurrentClusterId()
-                                    : PacketInfo::kNotAProbe;
+  int probe_cluster_id = PacketInfo::kNotAProbe;
+  size_t bytes_sent = 0;
+  size_t recommended_probe_size = 0;
+  if (is_probing) {
+    probe_cluster_id = prober_->CurrentClusterId();
+    recommended_probe_size = prober_->RecommendedMinProbeSize();
+  }
   while (!packets_->Empty()) {
     // Since we need to release the lock in order to send, we first pop the
     // element from the priority queue but keep it in storage, so that we can
@@ -412,30 +417,32 @@
 
     if (SendPacket(packet, probe_cluster_id)) {
       // Send succeeded, remove it from the queue.
+      bytes_sent += packet.bytes;
       packets_->FinalizePop(packet);
-      if (is_probing)
-        return;
+      if (is_probing && bytes_sent > recommended_probe_size)
+        break;
     } else {
       // Send failed, put it back into the queue.
       packets_->CancelPop(packet);
-      return;
+      break;
     }
   }
 
-  RTC_DCHECK(packets_->Empty());
   // TODO(holmer): Remove the paused_ check when issue 5307 has been fixed.
-  if (paused_)
-    return;
+  if (packets_->Empty() && !paused_) {
+    // We can not send padding unless a normal packet has first been sent. If we
+    // do, timestamps get messed up.
+    if (packet_counter_ > 0) {
+      int padding_needed =
+          static_cast<int>(is_probing ? (recommended_probe_size - bytes_sent)
+                                      : padding_budget_->bytes_remaining());
 
-  // We can not send padding unless a normal packet has first been sent. If we
-  // do, timestamps get messed up.
-  if (packet_counter_ > 0) {
-    size_t padding_needed = is_probing ? prober_->RecommendedPacketSize()
-                                       : padding_budget_->bytes_remaining();
-
-    if (padding_needed > 0)
-      SendPadding(padding_needed, probe_cluster_id);
+      if (padding_needed > 0)
+        bytes_sent += SendPadding(padding_needed, probe_cluster_id);
+    }
   }
+  if (is_probing && bytes_sent > 0)
+    prober_->ProbeSent(clock_->TimeInMilliseconds(), bytes_sent);
 }
 
 bool PacedSender::SendPacket(const paced_sender::Packet& packet,
@@ -458,7 +465,6 @@
   critsect_->Enter();
 
   if (success) {
-    prober_->PacketSent(clock_->TimeInMilliseconds(), packet.bytes);
     // TODO(holmer): High priority packets should only be accounted for if we
     // are allocating bandwidth for audio.
     if (packet.priority != kHighPriority) {
@@ -471,17 +477,17 @@
   return success;
 }
 
-void PacedSender::SendPadding(size_t padding_needed, int probe_cluster_id) {
+size_t PacedSender::SendPadding(size_t padding_needed, int probe_cluster_id) {
   critsect_->Leave();
   size_t bytes_sent =
       packet_sender_->TimeToSendPadding(padding_needed, probe_cluster_id);
   critsect_->Enter();
 
   if (bytes_sent > 0) {
-    prober_->PacketSent(clock_->TimeInMilliseconds(), bytes_sent);
     media_budget_->UseBudget(bytes_sent);
     padding_budget_->UseBudget(bytes_sent);
   }
+  return bytes_sent;
 }
 
 void PacedSender::UpdateBytesPerInterval(int64_t delta_time_ms) {
diff --git a/webrtc/modules/pacing/paced_sender.h b/webrtc/modules/pacing/paced_sender.h
index f74a97a..02d1dbd 100644
--- a/webrtc/modules/pacing/paced_sender.h
+++ b/webrtc/modules/pacing/paced_sender.h
@@ -137,7 +137,7 @@
 
   bool SendPacket(const paced_sender::Packet& packet, int probe_cluster_id)
       EXCLUSIVE_LOCKS_REQUIRED(critsect_);
-  void SendPadding(size_t padding_needed, int probe_cluster_id)
+  size_t SendPadding(size_t padding_needed, int probe_cluster_id)
       EXCLUSIVE_LOCKS_REQUIRED(critsect_);
 
   Clock* const clock_;
diff --git a/webrtc/modules/pacing/paced_sender_unittest.cc b/webrtc/modules/pacing/paced_sender_unittest.cc
index a0bb92f..183d339 100644
--- a/webrtc/modules/pacing/paced_sender_unittest.cc
+++ b/webrtc/modules/pacing/paced_sender_unittest.cc
@@ -19,6 +19,18 @@
 using testing::_;
 using testing::Return;
 
+namespace {
+constexpr unsigned kFirstClusterBps = 900000;
+constexpr int kFirstClusterCount = 6;
+constexpr unsigned kSecondClusterBps = 1800000;
+constexpr int kSecondClusterCount = 5;
+
+// The error stems from truncating the time interval of probe packets to integer
+// values. This results in probing slightly higher than the target bitrate.
+// For 1.8 Mbps, this comes to be about 120 kbps with 1200 probe packets.
+constexpr int kBitrateProbingError = 150000;
+}  // namespace
+
 namespace webrtc {
 namespace test {
 
@@ -62,46 +74,29 @@
 
 class PacedSenderProbing : public PacedSender::PacketSender {
  public:
-  PacedSenderProbing(const std::list<int>& expected_deltas, Clock* clock)
-      : prev_packet_time_ms_(-1),
-        expected_deltas_(expected_deltas),
-        packets_sent_(0),
-        clock_(clock) {}
+  PacedSenderProbing() : packets_sent_(0), padding_sent_(0) {}
 
   bool TimeToSendPacket(uint32_t ssrc,
                         uint16_t sequence_number,
                         int64_t capture_time_ms,
                         bool retransmission,
                         int probe_cluster_id) override {
-    ExpectAndCountPacket();
+    packets_sent_++;
     return true;
   }
 
   size_t TimeToSendPadding(size_t bytes, int probe_cluster_id) override {
-    ExpectAndCountPacket();
-    return bytes;
-  }
-
-  void ExpectAndCountPacket() {
-    ++packets_sent_;
-    EXPECT_FALSE(expected_deltas_.empty());
-    if (expected_deltas_.empty())
-      return;
-    int64_t now_ms = clock_->TimeInMilliseconds();
-    if (prev_packet_time_ms_ >= 0) {
-      EXPECT_EQ(expected_deltas_.front(), now_ms - prev_packet_time_ms_);
-      expected_deltas_.pop_front();
-    }
-    prev_packet_time_ms_ = now_ms;
+    padding_sent_ += bytes;
+    return padding_sent_;
   }
 
   int packets_sent() const { return packets_sent_; }
 
+  int padding_sent() const { return padding_sent_; }
+
  private:
-  int64_t prev_packet_time_ms_;
-  std::list<int> expected_deltas_;
   int packets_sent_;
-  Clock* clock_;
+  int padding_sent_;
 };
 
 class PacedSenderTest : public ::testing::Test {
@@ -110,8 +105,8 @@
     srand(0);
     // Need to initialize PacedSender after we initialize clock.
     send_bucket_.reset(new PacedSender(&clock_, &callback_));
-    send_bucket_->CreateProbeCluster(900000, 6);
-    send_bucket_->CreateProbeCluster(1800000, 5);
+    send_bucket_->CreateProbeCluster(kFirstClusterBps, kFirstClusterCount);
+    send_bucket_->CreateProbeCluster(kSecondClusterBps, kSecondClusterCount);
     // Default to bitrate probing disabled for testing purposes. Probing tests
     // have to enable probing, either by creating a new PacedSender instance or
     // by calling SetProbingEnabled(true).
@@ -803,74 +798,86 @@
   EXPECT_EQ(0, send_bucket_->QueueInMs());
 }
 
-TEST_F(PacedSenderTest, ProbingWithInitialFrame) {
-  const int kNumPackets = 11;
-  const int kNumDeltas = kNumPackets - 1;
+TEST_F(PacedSenderTest, ProbingWithInsertedPackets) {
   const size_t kPacketSize = 1200;
   const int kInitialBitrateBps = 300000;
   uint32_t ssrc = 12346;
   uint16_t sequence_number = 1234;
 
-  const int expected_deltas[kNumDeltas] = {10, 10, 10, 10, 10, 5, 5, 5, 5, 5};
-  std::list<int> expected_deltas_list(expected_deltas,
-                                      expected_deltas + kNumDeltas);
-  PacedSenderProbing callback(expected_deltas_list, &clock_);
-  send_bucket_.reset(new PacedSender(&clock_, &callback));
-  send_bucket_->CreateProbeCluster(900000, 6);
-  send_bucket_->CreateProbeCluster(1800000, 5);
+  PacedSenderProbing packet_sender;
+  send_bucket_.reset(new PacedSender(&clock_, &packet_sender));
+  send_bucket_->CreateProbeCluster(kFirstClusterBps, kFirstClusterCount);
+  send_bucket_->CreateProbeCluster(kSecondClusterBps, kSecondClusterCount);
   send_bucket_->SetEstimatedBitrate(kInitialBitrateBps);
 
-  for (int i = 0; i < kNumPackets; ++i) {
+  for (int i = 0; i < kFirstClusterCount + kSecondClusterCount; ++i) {
     send_bucket_->InsertPacket(PacedSender::kNormalPriority, ssrc,
                                sequence_number++, clock_.TimeInMilliseconds(),
                                kPacketSize, false);
   }
 
-  while (callback.packets_sent() < kNumPackets) {
+  int64_t start = clock_.TimeInMilliseconds();
+  while (packet_sender.packets_sent() < kFirstClusterCount) {
     int time_until_process = send_bucket_->TimeUntilNextProcess();
-    if (time_until_process <= 0) {
-      send_bucket_->Process();
-    } else {
-      clock_.AdvanceTimeMilliseconds(time_until_process);
-    }
+    clock_.AdvanceTimeMilliseconds(time_until_process);
+    send_bucket_->Process();
   }
+  int packets_sent = packet_sender.packets_sent();
+  // Validate first cluster bitrate. Note that we have to account for number
+  // of intervals and hence (packets_sent - 1) on the first cluster.
+  EXPECT_NEAR((packets_sent - 1) * kPacketSize * 8000 /
+                  (clock_.TimeInMilliseconds() - start),
+              kFirstClusterBps, kBitrateProbingError);
+  EXPECT_EQ(0, packet_sender.padding_sent());
+
+  start = clock_.TimeInMilliseconds();
+  while (packet_sender.packets_sent() <
+         kFirstClusterCount + kSecondClusterCount) {
+    int time_until_process = send_bucket_->TimeUntilNextProcess();
+    clock_.AdvanceTimeMilliseconds(time_until_process);
+    send_bucket_->Process();
+  }
+  packets_sent = packet_sender.packets_sent() - packets_sent;
+  // Validate second cluster bitrate.
+  EXPECT_NEAR(
+      packets_sent * kPacketSize * 8000 / (clock_.TimeInMilliseconds() - start),
+      kSecondClusterBps, kBitrateProbingError);
 }
 
-TEST_F(PacedSenderTest, ProbingWithTooSmallInitialFrame) {
-  const int kNumPackets = 11;
-  const int kNumDeltas = kNumPackets - 1;
+TEST_F(PacedSenderTest, ProbingWithPaddingSupport) {
   const size_t kPacketSize = 1200;
   const int kInitialBitrateBps = 300000;
   uint32_t ssrc = 12346;
   uint16_t sequence_number = 1234;
-  const int expected_deltas[kNumDeltas] = {10, 10, 10, 10, 10, 5, 5, 5, 5, 5};
-  std::list<int> expected_deltas_list(expected_deltas,
-                                      expected_deltas + kNumDeltas);
-  PacedSenderProbing callback(expected_deltas_list, &clock_);
-  send_bucket_.reset(new PacedSender(&clock_, &callback));
-  send_bucket_->CreateProbeCluster(900000, 6);
-  send_bucket_->CreateProbeCluster(1800000, 5);
+
+  PacedSenderProbing packet_sender;
+  send_bucket_.reset(new PacedSender(&clock_, &packet_sender));
+  send_bucket_->CreateProbeCluster(kFirstClusterBps, kFirstClusterCount);
   send_bucket_->SetEstimatedBitrate(kInitialBitrateBps);
 
-  for (int i = 0; i < kNumPackets - 5; ++i) {
+  for (int i = 0; i < kFirstClusterCount - 2; ++i) {
     send_bucket_->InsertPacket(PacedSender::kNormalPriority, ssrc,
                                sequence_number++, clock_.TimeInMilliseconds(),
                                kPacketSize, false);
   }
-  while (callback.packets_sent() < kNumPackets) {
-    int time_until_process = send_bucket_->TimeUntilNextProcess();
-    if (time_until_process <= 0) {
-      send_bucket_->Process();
-    } else {
-      clock_.AdvanceTimeMilliseconds(time_until_process);
-    }
-  }
 
-  // Process one more time and make sure we don't send any more probes.
-  int time_until_process = send_bucket_->TimeUntilNextProcess();
-  clock_.AdvanceTimeMilliseconds(time_until_process);
-  send_bucket_->Process();
-  EXPECT_EQ(kNumPackets, callback.packets_sent());
+  int64_t start = clock_.TimeInMilliseconds();
+  int process_count = 0;
+  while (process_count < kFirstClusterCount) {
+    int time_until_process = send_bucket_->TimeUntilNextProcess();
+    clock_.AdvanceTimeMilliseconds(time_until_process);
+    send_bucket_->Process();
+    ++process_count;
+  }
+  int packets_sent = packet_sender.packets_sent();
+  int padding_sent = packet_sender.padding_sent();
+  EXPECT_GT(packets_sent, 0);
+  EXPECT_GT(padding_sent, 0);
+  // Note that the number of intervals here for kPacketSize is
+  // packets_sent due to padding in the same cluster.
+  EXPECT_NEAR((packets_sent * kPacketSize * 8000 + padding_sent) /
+                  (clock_.TimeInMilliseconds() - start),
+              kFirstClusterBps, kBitrateProbingError);
 }
 
 TEST_F(PacedSenderTest, PriorityInversion) {
diff --git a/webrtc/video/video_send_stream_tests.cc b/webrtc/video/video_send_stream_tests.cc
index 0276dbc..e6aabb9 100644
--- a/webrtc/video/video_send_stream_tests.cc
+++ b/webrtc/video/video_send_stream_tests.cc
@@ -1046,6 +1046,83 @@
   RunBaseTest(&test);
 }
 
+TEST_F(VideoSendStreamTest, PaddingIsPrimarilyRetransmissions) {
+  const int kCapacityKbps = 10000;  // 10 Mbps
+  class PaddingIsPrimarilyRetransmissions : public test::EndToEndTest {
+   public:
+    PaddingIsPrimarilyRetransmissions()
+        : EndToEndTest(kDefaultTimeoutMs),
+          clock_(Clock::GetRealTimeClock()),
+          padding_length_(0),
+          total_length_(0),
+          call_(nullptr) {}
+
+   private:
+    void OnCallsCreated(Call* sender_call, Call* receiver_call) override {
+      call_ = sender_call;
+    }
+
+    Action OnSendRtp(const uint8_t* packet, size_t length) override {
+      rtc::CritScope lock(&crit_);
+
+      RTPHeader header;
+      parser_->Parse(packet, length, &header);
+      padding_length_ += header.paddingLength;
+      total_length_ += length;
+      return SEND_PACKET;
+    }
+
+    test::PacketTransport* CreateSendTransport(Call* sender_call) override {
+      const int kNetworkDelayMs = 50;
+      FakeNetworkPipe::Config config;
+      config.loss_percent = 10;
+      config.link_capacity_kbps = kCapacityKbps;
+      config.queue_delay_ms = kNetworkDelayMs;
+      return new test::PacketTransport(sender_call, this,
+                                       test::PacketTransport::kSender, config);
+    }
+
+    void ModifyVideoConfigs(
+        VideoSendStream::Config* send_config,
+        std::vector<VideoReceiveStream::Config>* receive_configs,
+        VideoEncoderConfig* encoder_config) override {
+      send_config->rtp.extensions.clear();
+      send_config->rtp.extensions.push_back(
+          RtpExtension(RtpExtension::kTransportSequenceNumberUri,
+                       test::kTransportSequenceNumberExtensionId));
+      // Turn on RTX.
+      send_config->rtp.rtx.payload_type = kFakeVideoSendPayloadType;
+      send_config->rtp.rtx.ssrcs.push_back(kVideoSendSsrcs[0]);
+
+      (*receive_configs)[0].rtp.extensions.clear();
+      (*receive_configs)[0].rtp.extensions.push_back(
+          RtpExtension(RtpExtension::kTransportSequenceNumberUri,
+                       test::kTransportSequenceNumberExtensionId));
+      (*receive_configs)[0].rtp.transport_cc = true;
+    }
+
+    void PerformTest() override {
+      // TODO(isheriff): Some platforms do not ramp up as expected to full
+      // capacity due to packet scheduling delays. Fix that before getting
+      // rid of this.
+      SleepMs(5000);
+      {
+        rtc::CritScope lock(&crit_);
+        // Expect padding to be a small percentage of total bytes sent.
+        EXPECT_LT(padding_length_, .1 * total_length_);
+      }
+    }
+
+    rtc::CriticalSection crit_;
+    Clock* const clock_;
+    size_t padding_length_ GUARDED_BY(crit_);
+    size_t total_length_ GUARDED_BY(crit_);
+    Call* call_;
+  } test;
+
+  RunBaseTest(&test);
+}
+
 // This test first observes "high" bitrate use at which point it sends a REMB to
 // indicate that it should be lowered significantly. The test then observes that
 // the bitrate observed is sinking well below the min-transmit-bitrate threshold