xref: /aosp_15_r20/external/webrtc/modules/video_coding/timing/jitter_estimator.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "modules/video_coding/timing/jitter_estimator.h"
12 
13 #include <math.h>
14 #include <string.h>
15 
16 #include <algorithm>
17 #include <cstdint>
18 
19 #include "absl/types/optional.h"
20 #include "api/field_trials_view.h"
21 #include "api/units/data_size.h"
22 #include "api/units/frequency.h"
23 #include "api/units/time_delta.h"
24 #include "api/units/timestamp.h"
25 #include "modules/video_coding/timing/rtt_filter.h"
26 #include "rtc_base/checks.h"
27 #include "rtc_base/logging.h"
28 #include "rtc_base/numerics/safe_conversions.h"
29 #include "system_wrappers/include/clock.h"
30 
31 namespace webrtc {
32 namespace {
33 
34 // Number of frames to wait for before post processing estimate. Also used in
35 // the frame rate estimator ramp-up.
36 constexpr size_t kFrameProcessingStartupCount = 30;
37 
38 // Number of frames to wait for before enabling the frame size filters.
39 constexpr size_t kFramesUntilSizeFiltering = 5;
40 
41 // Initial value for frame size filters.
42 constexpr double kInitialAvgAndMaxFrameSizeBytes = 500.0;
43 
44 // Time constant for average frame size filter.
45 constexpr double kPhi = 0.97;
46 // Time constant for max frame size filter.
47 constexpr double kPsi = 0.9999;
48 // Default constants for percentile frame size filter.
49 constexpr double kDefaultMaxFrameSizePercentile = 0.95;
50 constexpr int kDefaultFrameSizeWindow = 30 * 10;
51 
52 // Outlier rejection constants.
53 constexpr double kNumStdDevDelayClamp = 3.5;
54 constexpr double kNumStdDevDelayOutlier = 15.0;
55 constexpr double kNumStdDevSizeOutlier = 3.0;
56 constexpr double kCongestionRejectionFactor = -0.25;
57 
58 // Rampup constant for deviation noise filters.
59 constexpr size_t kAlphaCountMax = 400;
60 
61 // Noise threshold constants.
62 // ~Less than 1% chance (look up in normal distribution table)...
63 constexpr double kNoiseStdDevs = 2.33;
64 // ...of getting 30 ms freezes
65 constexpr double kNoiseStdDevOffset = 30.0;
66 
67 // Jitter estimate clamping limits.
68 constexpr TimeDelta kMinJitterEstimate = TimeDelta::Millis(1);
69 constexpr TimeDelta kMaxJitterEstimate = TimeDelta::Seconds(10);
70 
71 // A constant describing the delay from the jitter buffer to the delay on the
72 // receiving side which is not accounted for by the jitter buffer nor the
73 // decoding delay estimate.
74 constexpr TimeDelta OPERATING_SYSTEM_JITTER = TimeDelta::Millis(10);
75 
76 // Time constant for reseting the NACK count.
77 constexpr TimeDelta kNackCountTimeout = TimeDelta::Seconds(60);
78 
79 // RTT mult activation.
80 constexpr size_t kNackLimit = 3;
81 
82 // Frame rate estimate clamping limit.
83 constexpr Frequency kMaxFramerateEstimate = Frequency::Hertz(200);
84 
85 }  // namespace
86 
87 constexpr char JitterEstimator::Config::kFieldTrialsKey[];
88 
ParseAndValidate(absl::string_view field_trial)89 JitterEstimator::Config JitterEstimator::Config::ParseAndValidate(
90     absl::string_view field_trial) {
91   Config config;
92   config.Parser()->Parse(field_trial);
93 
94   // The `MovingPercentileFilter` RTC_CHECKs on the validity of the
95   // percentile and window length, so we'd better validate the field trial
96   // provided values here.
97   if (config.max_frame_size_percentile) {
98     double original = *config.max_frame_size_percentile;
99     config.max_frame_size_percentile = std::min(std::max(0.0, original), 1.0);
100     if (config.max_frame_size_percentile != original) {
101       RTC_LOG(LS_ERROR) << "Skipping invalid max_frame_size_percentile="
102                         << original;
103     }
104   }
105   if (config.frame_size_window && config.frame_size_window < 1) {
106     RTC_LOG(LS_ERROR) << "Skipping invalid frame_size_window="
107                       << *config.frame_size_window;
108     config.frame_size_window = 1;
109   }
110 
111   // General sanity checks.
112   if (config.num_stddev_delay_clamp && config.num_stddev_delay_clamp < 0.0) {
113     RTC_LOG(LS_ERROR) << "Skipping invalid num_stddev_delay_clamp="
114                       << *config.num_stddev_delay_clamp;
115     config.num_stddev_delay_clamp = 0.0;
116   }
117   if (config.num_stddev_delay_outlier &&
118       config.num_stddev_delay_outlier < 0.0) {
119     RTC_LOG(LS_ERROR) << "Skipping invalid num_stddev_delay_outlier="
120                       << *config.num_stddev_delay_outlier;
121     config.num_stddev_delay_outlier = 0.0;
122   }
123   if (config.num_stddev_size_outlier && config.num_stddev_size_outlier < 0.0) {
124     RTC_LOG(LS_ERROR) << "Skipping invalid num_stddev_size_outlier="
125                       << *config.num_stddev_size_outlier;
126     config.num_stddev_size_outlier = 0.0;
127   }
128 
129   return config;
130 }
131 
JitterEstimator(Clock * clock,const FieldTrialsView & field_trials)132 JitterEstimator::JitterEstimator(Clock* clock,
133                                  const FieldTrialsView& field_trials)
134     : config_(Config::ParseAndValidate(
135           field_trials.Lookup(Config::kFieldTrialsKey))),
136       avg_frame_size_median_bytes_(static_cast<size_t>(
137           config_.frame_size_window.value_or(kDefaultFrameSizeWindow))),
138       max_frame_size_bytes_percentile_(
139           config_.max_frame_size_percentile.value_or(
140               kDefaultMaxFrameSizePercentile),
141           static_cast<size_t>(
142               config_.frame_size_window.value_or(kDefaultFrameSizeWindow))),
143       fps_counter_(30),  // TODO(sprang): Use an estimator with limit based
144                          // on time, rather than number of samples.
145       clock_(clock) {
146   Reset();
147 }
148 
149 JitterEstimator::~JitterEstimator() = default;
150 
151 // Resets the JitterEstimate.
Reset()152 void JitterEstimator::Reset() {
153   avg_frame_size_bytes_ = kInitialAvgAndMaxFrameSizeBytes;
154   max_frame_size_bytes_ = kInitialAvgAndMaxFrameSizeBytes;
155   var_frame_size_bytes2_ = 100;
156   avg_frame_size_median_bytes_.Reset();
157   max_frame_size_bytes_percentile_.Reset();
158   last_update_time_ = absl::nullopt;
159   prev_estimate_ = absl::nullopt;
160   prev_frame_size_ = absl::nullopt;
161   avg_noise_ms_ = 0.0;
162   var_noise_ms2_ = 4.0;
163   alpha_count_ = 1;
164   filter_jitter_estimate_ = TimeDelta::Zero();
165   latest_nack_ = Timestamp::Zero();
166   nack_count_ = 0;
167   startup_frame_size_sum_bytes_ = 0;
168   startup_frame_size_count_ = 0;
169   startup_count_ = 0;
170   rtt_filter_.Reset();
171   fps_counter_.Reset();
172 
173   kalman_filter_ = FrameDelayVariationKalmanFilter();
174 }
175 
176 // Updates the estimates with the new measurements.
UpdateEstimate(TimeDelta frame_delay,DataSize frame_size)177 void JitterEstimator::UpdateEstimate(TimeDelta frame_delay,
178                                      DataSize frame_size) {
179   if (frame_size.IsZero()) {
180     return;
181   }
182   // Can't use DataSize since this can be negative.
183   double delta_frame_bytes =
184       frame_size.bytes() - prev_frame_size_.value_or(DataSize::Zero()).bytes();
185   if (startup_frame_size_count_ < kFramesUntilSizeFiltering) {
186     startup_frame_size_sum_bytes_ += frame_size.bytes();
187     startup_frame_size_count_++;
188   } else if (startup_frame_size_count_ == kFramesUntilSizeFiltering) {
189     // Give the frame size filter.
190     avg_frame_size_bytes_ = startup_frame_size_sum_bytes_ /
191                             static_cast<double>(startup_frame_size_count_);
192     startup_frame_size_count_++;
193   }
194 
195   double avg_frame_size_bytes =
196       kPhi * avg_frame_size_bytes_ + (1 - kPhi) * frame_size.bytes();
197   double deviation_size_bytes = 2 * sqrt(var_frame_size_bytes2_);
198   if (frame_size.bytes() < avg_frame_size_bytes_ + deviation_size_bytes) {
199     // Only update the average frame size if this sample wasn't a key frame.
200     avg_frame_size_bytes_ = avg_frame_size_bytes;
201   }
202 
203   double delta_bytes = frame_size.bytes() - avg_frame_size_bytes;
204   var_frame_size_bytes2_ = std::max(
205       kPhi * var_frame_size_bytes2_ + (1 - kPhi) * (delta_bytes * delta_bytes),
206       1.0);
207 
208   // Update non-linear IIR estimate of max frame size.
209   max_frame_size_bytes_ =
210       std::max<double>(kPsi * max_frame_size_bytes_, frame_size.bytes());
211 
212   // Maybe update percentile estimates of frame sizes.
213   if (config_.avg_frame_size_median) {
214     avg_frame_size_median_bytes_.Insert(frame_size.bytes());
215   }
216   if (config_.MaxFrameSizePercentileEnabled()) {
217     max_frame_size_bytes_percentile_.Insert(frame_size.bytes());
218   }
219 
220   if (!prev_frame_size_) {
221     prev_frame_size_ = frame_size;
222     return;
223   }
224   prev_frame_size_ = frame_size;
225 
226   // Cap frame_delay based on the current time deviation noise.
227   double num_stddev_delay_clamp =
228       config_.num_stddev_delay_clamp.value_or(kNumStdDevDelayClamp);
229   TimeDelta max_time_deviation =
230       TimeDelta::Millis(num_stddev_delay_clamp * sqrt(var_noise_ms2_) + 0.5);
231   frame_delay.Clamp(-max_time_deviation, max_time_deviation);
232 
233   double delay_deviation_ms =
234       frame_delay.ms() -
235       kalman_filter_.GetFrameDelayVariationEstimateTotal(delta_frame_bytes);
236 
237   // Outlier rejection: these conditions depend on filtered versions of the
238   // delay and frame size _means_, respectively, together with a configurable
239   // number of standard deviations. If a sample is large with respect to the
240   // corresponding mean and dispersion (defined by the number of
241   // standard deviations and the sample standard deviation), it is deemed an
242   // outlier. This "empirical rule" is further described in
243   // https://en.wikipedia.org/wiki/68-95-99.7_rule. Note that neither of the
244   // estimated means are true sample means, which implies that they are possibly
245   // not normally distributed. Hence, this rejection method is just a heuristic.
246   double num_stddev_delay_outlier =
247       config_.num_stddev_delay_outlier.value_or(kNumStdDevDelayOutlier);
248   // Delay outlier rejection is two-sided.
249   bool abs_delay_is_not_outlier =
250       fabs(delay_deviation_ms) <
251       num_stddev_delay_outlier * sqrt(var_noise_ms2_);
252   // The reasoning above means, in particular, that we should use the sample
253   // mean-style `avg_frame_size_bytes_` estimate, as opposed to the
254   // median-filtered version, even if configured to use latter for the
255   // calculation in `CalculateEstimate()`.
256   // Size outlier rejection is one-sided.
257   double num_stddev_size_outlier =
258       config_.num_stddev_size_outlier.value_or(kNumStdDevSizeOutlier);
259   bool size_is_positive_outlier =
260       frame_size.bytes() >
261       avg_frame_size_bytes_ +
262           num_stddev_size_outlier * sqrt(var_frame_size_bytes2_);
263 
264   // Only update the Kalman filter if the sample is not considered an extreme
265   // outlier. Even if it is an extreme outlier from a delay point of view, if
266   // the frame size also is large the deviation is probably due to an incorrect
267   // line slope.
268   if (abs_delay_is_not_outlier || size_is_positive_outlier) {
269     // Prevent updating with frames which have been congested by a large frame,
270     // and therefore arrives almost at the same time as that frame.
271     // This can occur when we receive a large frame (key frame) which has been
272     // delayed. The next frame is of normal size (delta frame), and thus deltaFS
273     // will be << 0. This removes all frame samples which arrives after a key
274     // frame.
275     double congestion_rejection_factor =
276         config_.congestion_rejection_factor.value_or(
277             kCongestionRejectionFactor);
278     double filtered_max_frame_size_bytes =
279         config_.MaxFrameSizePercentileEnabled()
280             ? max_frame_size_bytes_percentile_.GetFilteredValue()
281             : max_frame_size_bytes_;
282     bool is_not_congested =
283         delta_frame_bytes >
284         congestion_rejection_factor * filtered_max_frame_size_bytes;
285 
286     if (is_not_congested || config_.estimate_noise_when_congested) {
287       // Update the variance of the deviation from the line given by the Kalman
288       // filter.
289       EstimateRandomJitter(delay_deviation_ms);
290     }
291     if (is_not_congested) {
292       // Neither a delay outlier nor a congested frame, so we can safely update
293       // the Kalman filter with the sample.
294       kalman_filter_.PredictAndUpdate(frame_delay.ms(), delta_frame_bytes,
295                                       filtered_max_frame_size_bytes,
296                                       var_noise_ms2_);
297     }
298   } else {
299     // Delay outliers affect the noise estimate through a value equal to the
300     // outlier rejection threshold.
301     double num_stddev = (delay_deviation_ms >= 0) ? num_stddev_delay_outlier
302                                                   : -num_stddev_delay_outlier;
303     EstimateRandomJitter(num_stddev * sqrt(var_noise_ms2_));
304   }
305   // Post process the total estimated jitter
306   if (startup_count_ >= kFrameProcessingStartupCount) {
307     PostProcessEstimate();
308   } else {
309     startup_count_++;
310   }
311 }
312 
313 // Updates the nack/packet ratio.
FrameNacked()314 void JitterEstimator::FrameNacked() {
315   if (nack_count_ < kNackLimit) {
316     nack_count_++;
317   }
318   latest_nack_ = clock_->CurrentTime();
319 }
320 
UpdateRtt(TimeDelta rtt)321 void JitterEstimator::UpdateRtt(TimeDelta rtt) {
322   rtt_filter_.Update(rtt);
323 }
324 
GetConfigForTest() const325 JitterEstimator::Config JitterEstimator::GetConfigForTest() const {
326   return config_;
327 }
328 
329 // Estimates the random jitter by calculating the variance of the sample
330 // distance from the line given by the Kalman filter.
EstimateRandomJitter(double d_dT)331 void JitterEstimator::EstimateRandomJitter(double d_dT) {
332   Timestamp now = clock_->CurrentTime();
333   if (last_update_time_.has_value()) {
334     fps_counter_.AddSample((now - *last_update_time_).us());
335   }
336   last_update_time_ = now;
337 
338   if (alpha_count_ == 0) {
339     RTC_DCHECK_NOTREACHED();
340     return;
341   }
342   double alpha =
343       static_cast<double>(alpha_count_ - 1) / static_cast<double>(alpha_count_);
344   alpha_count_++;
345   if (alpha_count_ > kAlphaCountMax)
346     alpha_count_ = kAlphaCountMax;
347 
348   // In order to avoid a low frame rate stream to react slower to changes,
349   // scale the alpha weight relative a 30 fps stream.
350   Frequency fps = GetFrameRate();
351   if (fps > Frequency::Zero()) {
352     constexpr Frequency k30Fps = Frequency::Hertz(30);
353     double rate_scale = k30Fps / fps;
354     // At startup, there can be a lot of noise in the fps estimate.
355     // Interpolate rate_scale linearly, from 1.0 at sample #1, to 30.0 / fps
356     // at sample #kFrameProcessingStartupCount.
357     if (alpha_count_ < kFrameProcessingStartupCount) {
358       rate_scale = (alpha_count_ * rate_scale +
359                     (kFrameProcessingStartupCount - alpha_count_)) /
360                    kFrameProcessingStartupCount;
361     }
362     alpha = pow(alpha, rate_scale);
363   }
364 
365   double avg_noise_ms = alpha * avg_noise_ms_ + (1 - alpha) * d_dT;
366   double var_noise_ms2 = alpha * var_noise_ms2_ + (1 - alpha) *
367                                                       (d_dT - avg_noise_ms_) *
368                                                       (d_dT - avg_noise_ms_);
369   avg_noise_ms_ = avg_noise_ms;
370   var_noise_ms2_ = var_noise_ms2;
371   if (var_noise_ms2_ < 1.0) {
372     // The variance should never be zero, since we might get stuck and consider
373     // all samples as outliers.
374     var_noise_ms2_ = 1.0;
375   }
376 }
377 
NoiseThreshold() const378 double JitterEstimator::NoiseThreshold() const {
379   double noise_threshold_ms =
380       kNoiseStdDevs * sqrt(var_noise_ms2_) - kNoiseStdDevOffset;
381   if (noise_threshold_ms < 1.0) {
382     noise_threshold_ms = 1.0;
383   }
384   return noise_threshold_ms;
385 }
386 
387 // Calculates the current jitter estimate from the filtered estimates.
CalculateEstimate()388 TimeDelta JitterEstimator::CalculateEstimate() {
389   // Using median- and percentile-filtered versions of the frame sizes may be
390   // more robust than using sample mean-style estimates.
391   double filtered_avg_frame_size_bytes =
392       config_.avg_frame_size_median
393           ? avg_frame_size_median_bytes_.GetFilteredValue()
394           : avg_frame_size_bytes_;
395   double filtered_max_frame_size_bytes =
396       config_.MaxFrameSizePercentileEnabled()
397           ? max_frame_size_bytes_percentile_.GetFilteredValue()
398           : max_frame_size_bytes_;
399   double worst_case_frame_size_deviation_bytes =
400       filtered_max_frame_size_bytes - filtered_avg_frame_size_bytes;
401   double ret_ms = kalman_filter_.GetFrameDelayVariationEstimateSizeBased(
402                       worst_case_frame_size_deviation_bytes) +
403                   NoiseThreshold();
404   TimeDelta ret = TimeDelta::Millis(ret_ms);
405 
406   // A very low estimate (or negative) is neglected.
407   if (ret < kMinJitterEstimate) {
408     ret = prev_estimate_.value_or(kMinJitterEstimate);
409     // Sanity check to make sure that no other method has set `prev_estimate_`
410     // to a value lower than `kMinJitterEstimate`.
411     RTC_DCHECK_GE(ret, kMinJitterEstimate);
412   } else if (ret > kMaxJitterEstimate) {  // Sanity
413     ret = kMaxJitterEstimate;
414   }
415   prev_estimate_ = ret;
416   return ret;
417 }
418 
PostProcessEstimate()419 void JitterEstimator::PostProcessEstimate() {
420   filter_jitter_estimate_ = CalculateEstimate();
421 }
422 
423 // Returns the current filtered estimate if available,
424 // otherwise tries to calculate an estimate.
GetJitterEstimate(double rtt_multiplier,absl::optional<TimeDelta> rtt_mult_add_cap)425 TimeDelta JitterEstimator::GetJitterEstimate(
426     double rtt_multiplier,
427     absl::optional<TimeDelta> rtt_mult_add_cap) {
428   TimeDelta jitter = CalculateEstimate() + OPERATING_SYSTEM_JITTER;
429   Timestamp now = clock_->CurrentTime();
430 
431   if (now - latest_nack_ > kNackCountTimeout)
432     nack_count_ = 0;
433 
434   if (filter_jitter_estimate_ > jitter)
435     jitter = filter_jitter_estimate_;
436   if (nack_count_ >= kNackLimit) {
437     if (rtt_mult_add_cap.has_value()) {
438       jitter += std::min(rtt_filter_.Rtt() * rtt_multiplier,
439                          rtt_mult_add_cap.value());
440     } else {
441       jitter += rtt_filter_.Rtt() * rtt_multiplier;
442     }
443   }
444 
445   static const Frequency kJitterScaleLowThreshold = Frequency::Hertz(5);
446   static const Frequency kJitterScaleHighThreshold = Frequency::Hertz(10);
447   Frequency fps = GetFrameRate();
448   // Ignore jitter for very low fps streams.
449   if (fps < kJitterScaleLowThreshold) {
450     if (fps.IsZero()) {
451       return std::max(TimeDelta::Zero(), jitter);
452     }
453     return TimeDelta::Zero();
454   }
455 
456   // Semi-low frame rate; scale by factor linearly interpolated from 0.0 at
457   // kJitterScaleLowThreshold to 1.0 at kJitterScaleHighThreshold.
458   if (fps < kJitterScaleHighThreshold) {
459     jitter = (1.0 / (kJitterScaleHighThreshold - kJitterScaleLowThreshold)) *
460              (fps - kJitterScaleLowThreshold) * jitter;
461   }
462 
463   return std::max(TimeDelta::Zero(), jitter);
464 }
465 
GetFrameRate() const466 Frequency JitterEstimator::GetFrameRate() const {
467   TimeDelta mean_frame_period = TimeDelta::Micros(fps_counter_.ComputeMean());
468   if (mean_frame_period <= TimeDelta::Zero())
469     return Frequency::Zero();
470 
471   Frequency fps = 1 / mean_frame_period;
472   // Sanity check.
473   RTC_DCHECK_GE(fps, Frequency::Zero());
474   return std::min(fps, kMaxFramerateEstimate);
475 }
476 }  // namespace webrtc
477