[SlackedPacer] Add experiment arm for high precision if long queue time.

This CL adds flag "max_queue_time", e.g:
WebRTC-SlackedTaskQueuePacedSender/Enabled:true,max_queue_time:75ms

If the PacingController's ExpectedQueueTime() is greater than or equal
to "max_queue_time" then we will use high precision delayed tasks to
ensure a smoother (less bursty) pacing of packets.

This should only get triggered in ultra high definition scenarios or
temporarily during key frames being sent. I have confirmed manually that
with the flag set to 75 ms I get low precision when not sending key
frames and temporarily get high precision for 5-15 delayed tasks during
the sending of a key frame.

Bug: webrtc:13957
Change-Id: I3c8aeba5fe18812ed0187610cd3f92a375bc6f18
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/258623
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#36538}
diff --git a/modules/pacing/task_queue_paced_sender.cc b/modules/pacing/task_queue_paced_sender.cc
index 1ae182c..a597f07 100644
--- a/modules/pacing/task_queue_paced_sender.cc
+++ b/modules/pacing/task_queue_paced_sender.cc
@@ -15,6 +15,8 @@
 
 #include "absl/memory/memory.h"
 #include "rtc_base/checks.h"
+#include "rtc_base/experiments/field_trial_parser.h"
+#include "rtc_base/experiments/field_trial_units.h"
 #include "rtc_base/trace_event.h"
 
 namespace webrtc {
@@ -28,6 +30,15 @@
 
 const int TaskQueuePacedSender::kNoPacketHoldback = -1;
 
+TaskQueuePacedSender::SlackedPacerFlags::SlackedPacerFlags(
+    const FieldTrialsView& field_trials)
+    : allow_low_precision("Enabled"),
+      max_low_precision_expected_queue_time("max_queue_time") {
+  ParseFieldTrial(
+      {&allow_low_precision, &max_low_precision_expected_queue_time},
+      field_trials.Lookup(kSlackedTaskQueuePacedSenderFieldTrial));
+}
+
 TaskQueuePacedSender::TaskQueuePacedSender(
     Clock* clock,
     PacingController::PacketSender* packet_sender,
@@ -36,13 +47,13 @@
     TimeDelta max_hold_back_window,
     int max_hold_back_window_in_packets)
     : clock_(clock),
-      allow_low_precision_(
-          field_trials.IsEnabled(kSlackedTaskQueuePacedSenderFieldTrial)),
-      max_hold_back_window_(allow_low_precision_
+      slacked_pacer_flags_(field_trials),
+      max_hold_back_window_(slacked_pacer_flags_.allow_low_precision
                                 ? PacingController::kMinSleepTime
                                 : max_hold_back_window),
-      max_hold_back_window_in_packets_(
-          allow_low_precision_ ? 0 : max_hold_back_window_in_packets),
+      max_hold_back_window_in_packets_(slacked_pacer_flags_.allow_low_precision
+                                           ? 0
+                                           : max_hold_back_window_in_packets),
       pacing_controller_(clock,
                          packet_sender,
                          field_trials,
@@ -279,9 +290,19 @@
       next_process_time_ > next_send_time) {
     // Prefer low precision if allowed and not probing.
     TaskQueueBase::DelayPrecision precision =
-        allow_low_precision_ && !pacing_controller_.IsProbing()
+        slacked_pacer_flags_.allow_low_precision &&
+                !pacing_controller_.IsProbing()
             ? TaskQueueBase::DelayPrecision::kLow
             : TaskQueueBase::DelayPrecision::kHigh;
+    // Optionally disable low precision if the expected queue time is greater
+    // than `max_low_precision_expected_queue_time`.
+    if (precision == TaskQueueBase::DelayPrecision::kLow &&
+        slacked_pacer_flags_.max_low_precision_expected_queue_time &&
+        pacing_controller_.ExpectedQueueTime() >=
+            slacked_pacer_flags_.max_low_precision_expected_queue_time
+                .Value()) {
+      precision = TaskQueueBase::DelayPrecision::kHigh;
+    }
 
     task_queue_.PostDelayedTaskWithPrecision(
         precision,
diff --git a/modules/pacing/task_queue_paced_sender.h b/modules/pacing/task_queue_paced_sender.h
index d72982f..32c5c1f 100644
--- a/modules/pacing/task_queue_paced_sender.h
+++ b/modules/pacing/task_queue_paced_sender.h
@@ -28,6 +28,7 @@
 #include "modules/pacing/pacing_controller.h"
 #include "modules/pacing/rtp_packet_pacer.h"
 #include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
+#include "rtc_base/experiments/field_trial_parser.h"
 #include "rtc_base/numerics/exp_filter.h"
 #include "rtc_base/task_queue.h"
 #include "rtc_base/thread_annotations.h"
@@ -129,16 +130,24 @@
   Stats GetStats() const;
 
   Clock* const clock_;
-  // If `kSlackedTaskQueuePacedSenderFieldTrial` is enabled, delayed tasks
-  // invoking MaybeProcessPackets() are scheduled using low precision instead of
-  // high precision, resulting in less idle wake ups and packets being sent in
-  // bursts if the `task_queue_` implementation supports slack.
-  //
-  // When probing, high precision is used regardless of `allow_low_precision_`
-  // to ensure good bandwidth estimation.
-  const bool allow_low_precision_;
+  struct SlackedPacerFlags {
+    // Parses `kSlackedTaskQueuePacedSenderFieldTrial`. Example:
+    // --force-fieldtrials=WebRTC-SlackedTaskQueuePacedSender/Enabled,max_queue_time:75ms/
+    explicit SlackedPacerFlags(const FieldTrialsView& field_trials);
+    // When "Enabled", delayed tasks invoking MaybeProcessPackets() are
+    // scheduled using low precision instead of high precision, resulting in
+    // less idle wake ups and packets being sent in bursts if the `task_queue_`
+    // implementation supports slack. When probing, high precision is used
+    // regardless to ensure good bandwidth estimation.
+    FieldTrialFlag allow_low_precision;
+    // Controlled via the "max_queue_time" experiment arm. If set, uses high
+    // precision scheduling of MaybeProcessPackets() whenever the expected queue
+    // time is greater than or equal to this value.
+    FieldTrialOptional<TimeDelta> max_low_precision_expected_queue_time;
+  };
+  const SlackedPacerFlags slacked_pacer_flags_;
   // The holdback window prevents too frequent delayed MaybeProcessPackets()
-  // calls. These are only applicable if `allow_low_precision_` is false.
+  // calls. These are only applicable if `allow_low_precision` is false.
   const TimeDelta max_hold_back_window_;
   const int max_hold_back_window_in_packets_;