Adds trial to always start probes with a small padding packet.

This will reduce bias caused by uncertainty in averaging window.

Bug: None
Change-Id: I5c4fe39ffe69fb4af87d86995196a54115d3e0b2
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/144720
Commit-Queue: Erik Språng <sprang@webrtc.org>
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29374}
diff --git a/api/transport/network_types.h b/api/transport/network_types.h
index 320a7c0..f45a35d 100644
--- a/api/transport/network_types.h
+++ b/api/transport/network_types.h
@@ -98,6 +98,7 @@
   int probe_cluster_id = kNotAProbe;
   int probe_cluster_min_probes = -1;
   int probe_cluster_min_bytes = -1;
+  int probe_cluster_bytes_sent = 0;
 };
 
 struct SentPacket {
diff --git a/modules/pacing/bitrate_prober.cc b/modules/pacing/bitrate_prober.cc
index 99041da..4192df9 100644
--- a/modules/pacing/bitrate_prober.cc
+++ b/modules/pacing/bitrate_prober.cc
@@ -146,7 +146,9 @@
 PacedPacketInfo BitrateProber::CurrentCluster() const {
   RTC_DCHECK(!clusters_.empty());
   RTC_DCHECK(probing_state_ == ProbingState::kActive);
-  return clusters_.front().pace_info;
+  PacedPacketInfo info = clusters_.front().pace_info;
+  info.probe_cluster_bytes_sent = clusters_.front().sent_bytes;
+  return info;
 }
 
 // Probe size is recommended based on the probe bitrate required. We choose
diff --git a/modules/pacing/pacing_controller.cc b/modules/pacing/pacing_controller.cc
index 1633de9..85b9e05 100644
--- a/modules/pacing/pacing_controller.cc
+++ b/modules/pacing/pacing_controller.cc
@@ -34,6 +34,8 @@
 // time.
 constexpr TimeDelta kMaxProcessingInterval = TimeDelta::Millis<30>();
 
+constexpr int kFirstPriority = 0;
+
 bool IsDisabled(const WebRtcKeyValueConfig& field_trials,
                 absl::string_view key) {
   return field_trials.Lookup(key).find("Disabled") == 0;
@@ -45,24 +47,24 @@
 }
 
 int GetPriorityForType(RtpPacketToSend::Type type) {
+  // Lower number takes priority over higher.
   switch (type) {
     case RtpPacketToSend::Type::kAudio:
       // Audio is always prioritized over other packet types.
-      return 0;
+      return kFirstPriority + 1;
     case RtpPacketToSend::Type::kRetransmission:
       // Send retransmissions before new media.
-      return 1;
+      return kFirstPriority + 2;
     case RtpPacketToSend::Type::kVideo:
-      // Video has "normal" priority, in the old speak.
-      return 2;
     case RtpPacketToSend::Type::kForwardErrorCorrection:
+      // Video has "normal" priority, in the old speak.
       // Send redundancy concurrently to video. If it is delayed it might have a
       // lower chance of being useful.
-      return 2;
+      return kFirstPriority + 3;
     case RtpPacketToSend::Type::kPadding:
       // Packets that are in themselves likely useless, only sent to keep the
       // BWE high.
-      return 3;
+      return kFirstPriority + 4;
   }
 }
 
@@ -88,6 +90,8 @@
       send_padding_if_silent_(
           IsEnabled(*field_trials_, "WebRTC-Pacer-PadInSilence")),
       pace_audio_(!IsDisabled(*field_trials_, "WebRTC-Pacer-BlockAudio")),
+      small_first_probe_packet_(
+          IsEnabled(*field_trials_, "WebRTC-Pacer-SmallFirstProbePacket")),
       min_packet_limit_(kDefaultMinPacketLimit),
       last_timestamp_(clock_->CurrentTime()),
       paused_(false),
@@ -187,17 +191,11 @@
 void PacingController::EnqueuePacket(std::unique_ptr<RtpPacketToSend> packet) {
   RTC_DCHECK(pacing_bitrate_ > DataRate::Zero())
       << "SetPacingRate must be called before InsertPacket.";
-
-  Timestamp now = CurrentTime();
-  prober_.OnIncomingPacket(packet->payload_size());
-
-  if (packet->capture_time_ms() < 0) {
-    packet->set_capture_time_ms(now.ms());
-  }
-
   RTC_CHECK(packet->packet_type());
-  int priority = GetPriorityForType(*packet->packet_type());
-  packet_queue_.Push(priority, now, packet_counter_++, std::move(packet));
+  // Get priority first and store in temporary, to avoid chance of object being
+  // moved before GetPriorityForType() being called.
+  const int priority = GetPriorityForType(*packet->packet_type());
+  EnqueuePacketInternal(std::move(packet), priority);
 }
 
 void PacingController::SetAccountForAudioPackets(bool account_for_audio) {
@@ -232,6 +230,22 @@
   return CurrentTime() - oldest_packet;
 }
 
+void PacingController::EnqueuePacketInternal(
+    std::unique_ptr<RtpPacketToSend> packet,
+    int priority) {
+  prober_.OnIncomingPacket(packet->payload_size());
+
+  Timestamp now = CurrentTime();
+  prober_.OnIncomingPacket(packet->payload_size());
+
+  // TODO(sprang): Make sure tests respect this, replace with DCHECK.
+  if (packet->capture_time_ms() < 0) {
+    packet->set_capture_time_ms(now.ms());
+  }
+
+  packet_queue_.Push(priority, now, packet_counter_++, std::move(packet));
+}
+
 TimeDelta PacingController::UpdateTimeAndGetElapsed(Timestamp now) {
   TimeDelta elapsed_time = now - time_last_process_;
   time_last_process_ = now;
@@ -322,11 +336,13 @@
     UpdateBudgetWithElapsedTime(elapsed_time);
   }
 
+  bool first_packet_in_probe = false;
   bool is_probing = prober_.IsProbing();
   PacedPacketInfo pacing_info;
   absl::optional<DataSize> recommended_probe_size;
   if (is_probing) {
     pacing_info = prober_.CurrentCluster();
+    first_packet_in_probe = pacing_info.probe_cluster_bytes_sent == 0;
     recommended_probe_size = DataSize::bytes(prober_.RecommendedMinProbeSize());
   }
 
@@ -334,6 +350,22 @@
   // The paused state is checked in the loop since it leaves the critical
   // section allowing the paused state to be changed from other code.
   while (!paused_) {
+    if (small_first_probe_packet_ && first_packet_in_probe) {
+      // If first packet in probe, insert a small padding packet so we have a
+      // more reliable start window for the rate estimation.
+      auto padding = packet_sender_->GeneratePadding(DataSize::bytes(1));
+      // If no RTP modules sending media are registered, we may not get a
+      // padding packet back.
+      if (!padding.empty()) {
+        // Insert with high priority so larger media packets don't preempt it.
+        EnqueuePacketInternal(std::move(padding[0]), kFirstPriority);
+        // We should never get more than one padding packets with a requested
+        // size of 1 byte.
+        RTC_DCHECK_EQ(padding.size(), 1u);
+      }
+      first_packet_in_probe = false;
+    }
+
     auto* packet = GetPendingPacket(pacing_info);
     if (packet == nullptr) {
       // No packet available to send, check if we should send padding.
diff --git a/modules/pacing/pacing_controller.h b/modules/pacing/pacing_controller.h
index 1b05444..d0e68a9 100644
--- a/modules/pacing/pacing_controller.h
+++ b/modules/pacing/pacing_controller.h
@@ -135,6 +135,8 @@
   bool Congested() const;
 
  private:
+  void EnqueuePacketInternal(std::unique_ptr<RtpPacketToSend> packet,
+                             int priority);
   TimeDelta UpdateTimeAndGetElapsed(Timestamp now);
   bool ShouldSendKeepalive(Timestamp now) const;
 
@@ -160,6 +162,7 @@
   const bool drain_large_queues_;
   const bool send_padding_if_silent_;
   const bool pace_audio_;
+  const bool small_first_probe_packet_;
   TimeDelta min_packet_limit_;
 
   // TODO(webrtc:9716): Remove this when we are certain clocks are monotonic.
diff --git a/modules/pacing/pacing_controller_unittest.cc b/modules/pacing/pacing_controller_unittest.cc
index bcd4384..caec575 100644
--- a/modules/pacing/pacing_controller_unittest.cc
+++ b/modules/pacing/pacing_controller_unittest.cc
@@ -1265,5 +1265,47 @@
   clock_.AdvanceTimeMilliseconds(200);
   pacer_->ProcessPackets();
 }
+
+TEST_F(PacingControllerTest, SmallFirstProbePacket) {
+  ScopedFieldTrials trial("WebRTC-Pacer-SmallFirstProbePacket/Enabled/");
+  MockPacketSender callback;
+  pacer_ =
+      std::make_unique<PacingController>(&clock_, &callback, nullptr, nullptr);
+  pacer_->CreateProbeCluster(kFirstClusterRate, /*cluster_id=*/0);
+  pacer_->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
+
+  // Add high prio media.
+  pacer_->EnqueuePacket(BuildRtpPacket(RtpPacketToSend::Type::kAudio));
+
+  // Expect small padding packet to be requested.
+  EXPECT_CALL(callback, GeneratePadding(DataSize::bytes(1)))
+      .WillOnce([&](DataSize padding_size) {
+        std::vector<std::unique_ptr<RtpPacketToSend>> padding_packets;
+        padding_packets.emplace_back(
+            BuildPacket(RtpPacketToSend::Type::kPadding, kAudioSsrc, 1,
+                        clock_.TimeInMilliseconds(), 1));
+        return padding_packets;
+      });
+
+  size_t packets_sent = 0;
+  bool media_seen = false;
+  EXPECT_CALL(callback, SendRtpPacket)
+      .Times(::testing::AnyNumber())
+      .WillRepeatedly([&](std::unique_ptr<RtpPacketToSend> packet,
+                          const PacedPacketInfo& cluster_info) {
+        if (packets_sent == 0) {
+          EXPECT_EQ(packet->packet_type(), RtpPacketToSend::Type::kPadding);
+        } else {
+          if (packet->packet_type() == RtpPacketToSend::Type::kAudio) {
+            media_seen = true;
+          }
+        }
+        packets_sent++;
+      });
+  while (!media_seen) {
+    pacer_->ProcessPackets();
+    clock_.AdvanceTimeMilliseconds(5);
+  }
+}
 }  // namespace test
 }  // namespace webrtc