• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/jitter_estimator.h"
12 
13 #include <assert.h>
14 #include <math.h>
15 #include <string.h>
16 
17 #include <algorithm>
18 #include <cstdint>
19 
20 #include "absl/types/optional.h"
21 #include "modules/video_coding/internal_defines.h"
22 #include "modules/video_coding/rtt_filter.h"
23 #include "rtc_base/experiments/jitter_upper_bound_experiment.h"
24 #include "rtc_base/numerics/safe_conversions.h"
25 #include "system_wrappers/include/clock.h"
26 #include "system_wrappers/include/field_trial.h"
27 
28 namespace webrtc {
29 namespace {
30 static constexpr uint32_t kStartupDelaySamples = 30;
31 static constexpr int64_t kFsAccuStartupSamples = 5;
32 static constexpr double kMaxFramerateEstimate = 200.0;
33 static constexpr int64_t kNackCountTimeoutMs = 60000;
34 static constexpr double kDefaultMaxTimestampDeviationInSigmas = 3.5;
35 }  // namespace
36 
VCMJitterEstimator(Clock * clock)37 VCMJitterEstimator::VCMJitterEstimator(Clock* clock)
38     : _phi(0.97),
39       _psi(0.9999),
40       _alphaCountMax(400),
41       _thetaLow(0.000001),
42       _nackLimit(3),
43       _numStdDevDelayOutlier(15),
44       _numStdDevFrameSizeOutlier(3),
45       _noiseStdDevs(2.33),       // ~Less than 1% chance
46                                  // (look up in normal distribution table)...
47       _noiseStdDevOffset(30.0),  // ...of getting 30 ms freezes
48       _rttFilter(),
49       fps_counter_(30),  // TODO(sprang): Use an estimator with limit based on
50                          // time, rather than number of samples.
51       time_deviation_upper_bound_(
52           JitterUpperBoundExperiment::GetUpperBoundSigmas().value_or(
53               kDefaultMaxTimestampDeviationInSigmas)),
54       enable_reduced_delay_(
55           !field_trial::IsEnabled("WebRTC-ReducedJitterDelayKillSwitch")),
56       clock_(clock) {
57   Reset();
58 }
59 
~VCMJitterEstimator()60 VCMJitterEstimator::~VCMJitterEstimator() {}
61 
operator =(const VCMJitterEstimator & rhs)62 VCMJitterEstimator& VCMJitterEstimator::operator=(
63     const VCMJitterEstimator& rhs) {
64   if (this != &rhs) {
65     memcpy(_thetaCov, rhs._thetaCov, sizeof(_thetaCov));
66     memcpy(_Qcov, rhs._Qcov, sizeof(_Qcov));
67 
68     _avgFrameSize = rhs._avgFrameSize;
69     _varFrameSize = rhs._varFrameSize;
70     _maxFrameSize = rhs._maxFrameSize;
71     _fsSum = rhs._fsSum;
72     _fsCount = rhs._fsCount;
73     _lastUpdateT = rhs._lastUpdateT;
74     _prevEstimate = rhs._prevEstimate;
75     _prevFrameSize = rhs._prevFrameSize;
76     _avgNoise = rhs._avgNoise;
77     _alphaCount = rhs._alphaCount;
78     _filterJitterEstimate = rhs._filterJitterEstimate;
79     _startupCount = rhs._startupCount;
80     _latestNackTimestamp = rhs._latestNackTimestamp;
81     _nackCount = rhs._nackCount;
82     _rttFilter = rhs._rttFilter;
83     clock_ = rhs.clock_;
84   }
85   return *this;
86 }
87 
88 // Resets the JitterEstimate.
Reset()89 void VCMJitterEstimator::Reset() {
90   _theta[0] = 1 / (512e3 / 8);
91   _theta[1] = 0;
92   _varNoise = 4.0;
93 
94   _thetaCov[0][0] = 1e-4;
95   _thetaCov[1][1] = 1e2;
96   _thetaCov[0][1] = _thetaCov[1][0] = 0;
97   _Qcov[0][0] = 2.5e-10;
98   _Qcov[1][1] = 1e-10;
99   _Qcov[0][1] = _Qcov[1][0] = 0;
100   _avgFrameSize = 500;
101   _maxFrameSize = 500;
102   _varFrameSize = 100;
103   _lastUpdateT = -1;
104   _prevEstimate = -1.0;
105   _prevFrameSize = 0;
106   _avgNoise = 0.0;
107   _alphaCount = 1;
108   _filterJitterEstimate = 0.0;
109   _latestNackTimestamp = 0;
110   _nackCount = 0;
111   _latestNackTimestamp = 0;
112   _fsSum = 0;
113   _fsCount = 0;
114   _startupCount = 0;
115   _rttFilter.Reset();
116   fps_counter_.Reset();
117 }
118 
119 // Updates the estimates with the new measurements.
UpdateEstimate(int64_t frameDelayMS,uint32_t frameSizeBytes,bool incompleteFrame)120 void VCMJitterEstimator::UpdateEstimate(int64_t frameDelayMS,
121                                         uint32_t frameSizeBytes,
122                                         bool incompleteFrame /* = false */) {
123   if (frameSizeBytes == 0) {
124     return;
125   }
126   int deltaFS = frameSizeBytes - _prevFrameSize;
127   if (_fsCount < kFsAccuStartupSamples) {
128     _fsSum += frameSizeBytes;
129     _fsCount++;
130   } else if (_fsCount == kFsAccuStartupSamples) {
131     // Give the frame size filter.
132     _avgFrameSize = static_cast<double>(_fsSum) / static_cast<double>(_fsCount);
133     _fsCount++;
134   }
135   if (!incompleteFrame || frameSizeBytes > _avgFrameSize) {
136     double avgFrameSize = _phi * _avgFrameSize + (1 - _phi) * frameSizeBytes;
137     if (frameSizeBytes < _avgFrameSize + 2 * sqrt(_varFrameSize)) {
138       // Only update the average frame size if this sample wasn't a key frame.
139       _avgFrameSize = avgFrameSize;
140     }
141     // Update the variance anyway since we want to capture cases where we only
142     // get key frames.
143     _varFrameSize = VCM_MAX(
144         _phi * _varFrameSize + (1 - _phi) * (frameSizeBytes - avgFrameSize) *
145                                    (frameSizeBytes - avgFrameSize),
146         1.0);
147   }
148 
149   // Update max frameSize estimate.
150   _maxFrameSize =
151       VCM_MAX(_psi * _maxFrameSize, static_cast<double>(frameSizeBytes));
152 
153   if (_prevFrameSize == 0) {
154     _prevFrameSize = frameSizeBytes;
155     return;
156   }
157   _prevFrameSize = frameSizeBytes;
158 
159   // Cap frameDelayMS based on the current time deviation noise.
160   int64_t max_time_deviation_ms =
161       static_cast<int64_t>(time_deviation_upper_bound_ * sqrt(_varNoise) + 0.5);
162   frameDelayMS = std::max(std::min(frameDelayMS, max_time_deviation_ms),
163                           -max_time_deviation_ms);
164 
165   // Only update the Kalman filter if the sample is not considered an extreme
166   // outlier. Even if it is an extreme outlier from a delay point of view, if
167   // the frame size also is large the deviation is probably due to an incorrect
168   // line slope.
169   double deviation = DeviationFromExpectedDelay(frameDelayMS, deltaFS);
170 
171   if (fabs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) ||
172       frameSizeBytes >
173           _avgFrameSize + _numStdDevFrameSizeOutlier * sqrt(_varFrameSize)) {
174     // Update the variance of the deviation from the line given by the Kalman
175     // filter.
176     EstimateRandomJitter(deviation, incompleteFrame);
177     // Prevent updating with frames which have been congested by a large frame,
178     // and therefore arrives almost at the same time as that frame.
179     // This can occur when we receive a large frame (key frame) which has been
180     // delayed. The next frame is of normal size (delta frame), and thus deltaFS
181     // will be << 0. This removes all frame samples which arrives after a key
182     // frame.
183     if ((!incompleteFrame || deviation >= 0.0) &&
184         static_cast<double>(deltaFS) > -0.25 * _maxFrameSize) {
185       // Update the Kalman filter with the new data
186       KalmanEstimateChannel(frameDelayMS, deltaFS);
187     }
188   } else {
189     int nStdDev =
190         (deviation >= 0) ? _numStdDevDelayOutlier : -_numStdDevDelayOutlier;
191     EstimateRandomJitter(nStdDev * sqrt(_varNoise), incompleteFrame);
192   }
193   // Post process the total estimated jitter
194   if (_startupCount >= kStartupDelaySamples) {
195     PostProcessEstimate();
196   } else {
197     _startupCount++;
198   }
199 }
200 
201 // Updates the nack/packet ratio.
FrameNacked()202 void VCMJitterEstimator::FrameNacked() {
203   if (_nackCount < _nackLimit) {
204     _nackCount++;
205   }
206   _latestNackTimestamp = clock_->TimeInMicroseconds();
207 }
208 
209 // Updates Kalman estimate of the channel.
210 // The caller is expected to sanity check the inputs.
KalmanEstimateChannel(int64_t frameDelayMS,int32_t deltaFSBytes)211 void VCMJitterEstimator::KalmanEstimateChannel(int64_t frameDelayMS,
212                                                int32_t deltaFSBytes) {
213   double Mh[2];
214   double hMh_sigma;
215   double kalmanGain[2];
216   double measureRes;
217   double t00, t01;
218 
219   // Kalman filtering
220 
221   // Prediction
222   // M = M + Q
223   _thetaCov[0][0] += _Qcov[0][0];
224   _thetaCov[0][1] += _Qcov[0][1];
225   _thetaCov[1][0] += _Qcov[1][0];
226   _thetaCov[1][1] += _Qcov[1][1];
227 
228   // Kalman gain
229   // K = M*h'/(sigma2n + h*M*h') = M*h'/(1 + h*M*h')
230   // h = [dFS 1]
231   // Mh = M*h'
232   // hMh_sigma = h*M*h' + R
233   Mh[0] = _thetaCov[0][0] * deltaFSBytes + _thetaCov[0][1];
234   Mh[1] = _thetaCov[1][0] * deltaFSBytes + _thetaCov[1][1];
235   // sigma weights measurements with a small deltaFS as noisy and
236   // measurements with large deltaFS as good
237   if (_maxFrameSize < 1.0) {
238     return;
239   }
240   double sigma = (300.0 * exp(-fabs(static_cast<double>(deltaFSBytes)) /
241                               (1e0 * _maxFrameSize)) +
242                   1) *
243                  sqrt(_varNoise);
244   if (sigma < 1.0) {
245     sigma = 1.0;
246   }
247   hMh_sigma = deltaFSBytes * Mh[0] + Mh[1] + sigma;
248   if ((hMh_sigma < 1e-9 && hMh_sigma >= 0) ||
249       (hMh_sigma > -1e-9 && hMh_sigma <= 0)) {
250     assert(false);
251     return;
252   }
253   kalmanGain[0] = Mh[0] / hMh_sigma;
254   kalmanGain[1] = Mh[1] / hMh_sigma;
255 
256   // Correction
257   // theta = theta + K*(dT - h*theta)
258   measureRes = frameDelayMS - (deltaFSBytes * _theta[0] + _theta[1]);
259   _theta[0] += kalmanGain[0] * measureRes;
260   _theta[1] += kalmanGain[1] * measureRes;
261 
262   if (_theta[0] < _thetaLow) {
263     _theta[0] = _thetaLow;
264   }
265 
266   // M = (I - K*h)*M
267   t00 = _thetaCov[0][0];
268   t01 = _thetaCov[0][1];
269   _thetaCov[0][0] = (1 - kalmanGain[0] * deltaFSBytes) * t00 -
270                     kalmanGain[0] * _thetaCov[1][0];
271   _thetaCov[0][1] = (1 - kalmanGain[0] * deltaFSBytes) * t01 -
272                     kalmanGain[0] * _thetaCov[1][1];
273   _thetaCov[1][0] = _thetaCov[1][0] * (1 - kalmanGain[1]) -
274                     kalmanGain[1] * deltaFSBytes * t00;
275   _thetaCov[1][1] = _thetaCov[1][1] * (1 - kalmanGain[1]) -
276                     kalmanGain[1] * deltaFSBytes * t01;
277 
278   // Covariance matrix, must be positive semi-definite.
279   assert(_thetaCov[0][0] + _thetaCov[1][1] >= 0 &&
280          _thetaCov[0][0] * _thetaCov[1][1] -
281                  _thetaCov[0][1] * _thetaCov[1][0] >=
282              0 &&
283          _thetaCov[0][0] >= 0);
284 }
285 
286 // Calculate difference in delay between a sample and the expected delay
287 // estimated by the Kalman filter
DeviationFromExpectedDelay(int64_t frameDelayMS,int32_t deltaFSBytes) const288 double VCMJitterEstimator::DeviationFromExpectedDelay(
289     int64_t frameDelayMS,
290     int32_t deltaFSBytes) const {
291   return frameDelayMS - (_theta[0] * deltaFSBytes + _theta[1]);
292 }
293 
294 // Estimates the random jitter by calculating the variance of the sample
295 // distance from the line given by theta.
EstimateRandomJitter(double d_dT,bool incompleteFrame)296 void VCMJitterEstimator::EstimateRandomJitter(double d_dT,
297                                               bool incompleteFrame) {
298   uint64_t now = clock_->TimeInMicroseconds();
299   if (_lastUpdateT != -1) {
300     fps_counter_.AddSample(now - _lastUpdateT);
301   }
302   _lastUpdateT = now;
303 
304   if (_alphaCount == 0) {
305     assert(false);
306     return;
307   }
308   double alpha =
309       static_cast<double>(_alphaCount - 1) / static_cast<double>(_alphaCount);
310   _alphaCount++;
311   if (_alphaCount > _alphaCountMax)
312     _alphaCount = _alphaCountMax;
313 
314   // In order to avoid a low frame rate stream to react slower to changes,
315   // scale the alpha weight relative a 30 fps stream.
316   double fps = GetFrameRate();
317   if (fps > 0.0) {
318     double rate_scale = 30.0 / fps;
319     // At startup, there can be a lot of noise in the fps estimate.
320     // Interpolate rate_scale linearly, from 1.0 at sample #1, to 30.0 / fps
321     // at sample #kStartupDelaySamples.
322     if (_alphaCount < kStartupDelaySamples) {
323       rate_scale =
324           (_alphaCount * rate_scale + (kStartupDelaySamples - _alphaCount)) /
325           kStartupDelaySamples;
326     }
327     alpha = pow(alpha, rate_scale);
328   }
329 
330   double avgNoise = alpha * _avgNoise + (1 - alpha) * d_dT;
331   double varNoise =
332       alpha * _varNoise + (1 - alpha) * (d_dT - _avgNoise) * (d_dT - _avgNoise);
333   if (!incompleteFrame || varNoise > _varNoise) {
334     _avgNoise = avgNoise;
335     _varNoise = varNoise;
336   }
337   if (_varNoise < 1.0) {
338     // The variance should never be zero, since we might get stuck and consider
339     // all samples as outliers.
340     _varNoise = 1.0;
341   }
342 }
343 
NoiseThreshold() const344 double VCMJitterEstimator::NoiseThreshold() const {
345   double noiseThreshold = _noiseStdDevs * sqrt(_varNoise) - _noiseStdDevOffset;
346   if (noiseThreshold < 1.0) {
347     noiseThreshold = 1.0;
348   }
349   return noiseThreshold;
350 }
351 
352 // Calculates the current jitter estimate from the filtered estimates.
CalculateEstimate()353 double VCMJitterEstimator::CalculateEstimate() {
354   double ret = _theta[0] * (_maxFrameSize - _avgFrameSize) + NoiseThreshold();
355 
356   // A very low estimate (or negative) is neglected.
357   if (ret < 1.0) {
358     if (_prevEstimate <= 0.01) {
359       ret = 1.0;
360     } else {
361       ret = _prevEstimate;
362     }
363   }
364   if (ret > 10000.0) {  // Sanity
365     ret = 10000.0;
366   }
367   _prevEstimate = ret;
368   return ret;
369 }
370 
PostProcessEstimate()371 void VCMJitterEstimator::PostProcessEstimate() {
372   _filterJitterEstimate = CalculateEstimate();
373 }
374 
UpdateRtt(int64_t rttMs)375 void VCMJitterEstimator::UpdateRtt(int64_t rttMs) {
376   _rttFilter.Update(rttMs);
377 }
378 
379 // Returns the current filtered estimate if available,
380 // otherwise tries to calculate an estimate.
GetJitterEstimate(double rttMultiplier,absl::optional<double> rttMultAddCapMs)381 int VCMJitterEstimator::GetJitterEstimate(
382     double rttMultiplier,
383     absl::optional<double> rttMultAddCapMs) {
384   double jitterMS = CalculateEstimate() + OPERATING_SYSTEM_JITTER;
385   uint64_t now = clock_->TimeInMicroseconds();
386 
387   if (now - _latestNackTimestamp > kNackCountTimeoutMs * 1000)
388     _nackCount = 0;
389 
390   if (_filterJitterEstimate > jitterMS)
391     jitterMS = _filterJitterEstimate;
392   if (_nackCount >= _nackLimit) {
393     if (rttMultAddCapMs.has_value()) {
394       jitterMS +=
395           std::min(_rttFilter.RttMs() * rttMultiplier, rttMultAddCapMs.value());
396     } else {
397       jitterMS += _rttFilter.RttMs() * rttMultiplier;
398     }
399   }
400 
401   if (enable_reduced_delay_) {
402     static const double kJitterScaleLowThreshold = 5.0;
403     static const double kJitterScaleHighThreshold = 10.0;
404     double fps = GetFrameRate();
405     // Ignore jitter for very low fps streams.
406     if (fps < kJitterScaleLowThreshold) {
407       if (fps == 0.0) {
408         return rtc::checked_cast<int>(std::max(0.0, jitterMS) + 0.5);
409       }
410       return 0;
411     }
412 
413     // Semi-low frame rate; scale by factor linearly interpolated from 0.0 at
414     // kJitterScaleLowThreshold to 1.0 at kJitterScaleHighThreshold.
415     if (fps < kJitterScaleHighThreshold) {
416       jitterMS =
417           (1.0 / (kJitterScaleHighThreshold - kJitterScaleLowThreshold)) *
418           (fps - kJitterScaleLowThreshold) * jitterMS;
419     }
420   }
421 
422   return rtc::checked_cast<int>(std::max(0.0, jitterMS) + 0.5);
423 }
424 
GetFrameRate() const425 double VCMJitterEstimator::GetFrameRate() const {
426   if (fps_counter_.ComputeMean() <= 0.0)
427     return 0;
428 
429   double fps = 1000000.0 / fps_counter_.ComputeMean();
430   // Sanity check.
431   assert(fps >= 0.0);
432   if (fps > kMaxFramerateEstimate) {
433     fps = kMaxFramerateEstimate;
434   }
435   return fps;
436 }
437 }  // namespace webrtc
438