blob: 7f54aad8087fb08909a9ff9ff2f8a8d4c8bfdd98 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/video_coding/jitter_estimator.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
henrik.lundin@webrtc.org7d8c72e2011-12-21 15:24:01 +000013#include <assert.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000014#include <math.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000015#include <string.h>
Erik Språngb1e031a2018-11-01 11:20:49 +010016#include <algorithm>
Yves Gerey3e707812018-11-28 16:47:49 +010017#include <cstdint>
philipel9d3ab612015-12-21 04:12:39 -080018
Erik Språngb1e031a2018-11-01 11:20:49 +010019#include "absl/types/optional.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "modules/video_coding/internal_defines.h"
21#include "modules/video_coding/rtt_filter.h"
Erik Språngb1e031a2018-11-01 11:20:49 +010022#include "rtc_base/experiments/jitter_upper_bound_experiment.h"
Yves Gerey3e707812018-11-28 16:47:49 +010023#include "rtc_base/numerics/safe_conversions.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020024#include "system_wrappers/include/clock.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000025
26namespace webrtc {
Erik Språngb1e031a2018-11-01 11:20:49 +010027namespace {
28static constexpr uint32_t kStartupDelaySamples = 30;
29static constexpr int64_t kFsAccuStartupSamples = 5;
30static constexpr double kMaxFramerateEstimate = 200.0;
31static constexpr int64_t kNackCountTimeoutMs = 60000;
32static constexpr double kDefaultMaxTimestampDeviationInSigmas = 3.5;
33} // namespace
sprang@webrtc.org70e2d112014-09-24 14:06:56 +000034
Åsa Persson3fcc5be2019-04-04 09:40:27 +020035VCMJitterEstimator::VCMJitterEstimator(Clock* clock)
36 : _phi(0.97),
stefan@webrtc.org34c5da62014-04-11 14:08:35 +000037 _psi(0.9999),
38 _alphaCountMax(400),
39 _thetaLow(0.000001),
40 _nackLimit(3),
41 _numStdDevDelayOutlier(15),
42 _numStdDevFrameSizeOutlier(3),
43 _noiseStdDevs(2.33), // ~Less than 1% chance
44 // (look up in normal distribution table)...
45 _noiseStdDevOffset(30.0), // ...of getting 30 ms freezes
sprang@webrtc.org70e2d112014-09-24 14:06:56 +000046 _rttFilter(),
47 fps_counter_(30), // TODO(sprang): Use an estimator with limit based on
48 // time, rather than number of samples.
Erik Språngb1e031a2018-11-01 11:20:49 +010049 time_deviation_upper_bound_(
50 JitterUpperBoundExperiment::GetUpperBoundSigmas().value_or(
51 kDefaultMaxTimestampDeviationInSigmas)),
sprang@webrtc.org70e2d112014-09-24 14:06:56 +000052 clock_(clock) {
53 Reset();
54}
55
philipel9d3ab612015-12-21 04:12:39 -080056VCMJitterEstimator::~VCMJitterEstimator() {}
niklase@google.com470e71d2011-07-07 08:21:25 +000057
philipel9d3ab612015-12-21 04:12:39 -080058VCMJitterEstimator& VCMJitterEstimator::operator=(
59 const VCMJitterEstimator& rhs) {
60 if (this != &rhs) {
61 memcpy(_thetaCov, rhs._thetaCov, sizeof(_thetaCov));
62 memcpy(_Qcov, rhs._Qcov, sizeof(_Qcov));
niklase@google.com470e71d2011-07-07 08:21:25 +000063
philipel9d3ab612015-12-21 04:12:39 -080064 _avgFrameSize = rhs._avgFrameSize;
65 _varFrameSize = rhs._varFrameSize;
66 _maxFrameSize = rhs._maxFrameSize;
67 _fsSum = rhs._fsSum;
68 _fsCount = rhs._fsCount;
69 _lastUpdateT = rhs._lastUpdateT;
70 _prevEstimate = rhs._prevEstimate;
71 _prevFrameSize = rhs._prevFrameSize;
72 _avgNoise = rhs._avgNoise;
73 _alphaCount = rhs._alphaCount;
74 _filterJitterEstimate = rhs._filterJitterEstimate;
75 _startupCount = rhs._startupCount;
76 _latestNackTimestamp = rhs._latestNackTimestamp;
77 _nackCount = rhs._nackCount;
78 _rttFilter = rhs._rttFilter;
Erik Språngb1e031a2018-11-01 11:20:49 +010079 clock_ = rhs.clock_;
philipel9d3ab612015-12-21 04:12:39 -080080 }
81 return *this;
niklase@google.com470e71d2011-07-07 08:21:25 +000082}
83
Åsa Persson3fcc5be2019-04-04 09:40:27 +020084// Resets the JitterEstimate.
philipel9d3ab612015-12-21 04:12:39 -080085void VCMJitterEstimator::Reset() {
86 _theta[0] = 1 / (512e3 / 8);
87 _theta[1] = 0;
88 _varNoise = 4.0;
niklase@google.com470e71d2011-07-07 08:21:25 +000089
philipel9d3ab612015-12-21 04:12:39 -080090 _thetaCov[0][0] = 1e-4;
91 _thetaCov[1][1] = 1e2;
92 _thetaCov[0][1] = _thetaCov[1][0] = 0;
93 _Qcov[0][0] = 2.5e-10;
94 _Qcov[1][1] = 1e-10;
95 _Qcov[0][1] = _Qcov[1][0] = 0;
96 _avgFrameSize = 500;
97 _maxFrameSize = 500;
98 _varFrameSize = 100;
99 _lastUpdateT = -1;
100 _prevEstimate = -1.0;
101 _prevFrameSize = 0;
102 _avgNoise = 0.0;
103 _alphaCount = 1;
104 _filterJitterEstimate = 0.0;
105 _latestNackTimestamp = 0;
106 _nackCount = 0;
Gustavo Garciaf7a7c8a2018-01-08 15:23:38 +0100107 _latestNackTimestamp = 0;
philipel9d3ab612015-12-21 04:12:39 -0800108 _fsSum = 0;
109 _fsCount = 0;
110 _startupCount = 0;
111 _rttFilter.Reset();
112 fps_counter_.Reset();
niklase@google.com470e71d2011-07-07 08:21:25 +0000113}
114
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200115// Updates the estimates with the new measurements.
philipel9d3ab612015-12-21 04:12:39 -0800116void VCMJitterEstimator::UpdateEstimate(int64_t frameDelayMS,
117 uint32_t frameSizeBytes,
118 bool incompleteFrame /* = false */) {
119 if (frameSizeBytes == 0) {
120 return;
121 }
122 int deltaFS = frameSizeBytes - _prevFrameSize;
123 if (_fsCount < kFsAccuStartupSamples) {
124 _fsSum += frameSizeBytes;
125 _fsCount++;
126 } else if (_fsCount == kFsAccuStartupSamples) {
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200127 // Give the frame size filter.
philipel9d3ab612015-12-21 04:12:39 -0800128 _avgFrameSize = static_cast<double>(_fsSum) / static_cast<double>(_fsCount);
129 _fsCount++;
130 }
131 if (!incompleteFrame || frameSizeBytes > _avgFrameSize) {
132 double avgFrameSize = _phi * _avgFrameSize + (1 - _phi) * frameSizeBytes;
133 if (frameSizeBytes < _avgFrameSize + 2 * sqrt(_varFrameSize)) {
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200134 // Only update the average frame size if this sample wasn't a key frame.
philipel9d3ab612015-12-21 04:12:39 -0800135 _avgFrameSize = avgFrameSize;
niklase@google.com470e71d2011-07-07 08:21:25 +0000136 }
philipel9d3ab612015-12-21 04:12:39 -0800137 // Update the variance anyway since we want to capture cases where we only
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200138 // get key frames.
Yves Gerey665174f2018-06-19 15:03:05 +0200139 _varFrameSize = VCM_MAX(
140 _phi * _varFrameSize + (1 - _phi) * (frameSizeBytes - avgFrameSize) *
141 (frameSizeBytes - avgFrameSize),
142 1.0);
philipel9d3ab612015-12-21 04:12:39 -0800143 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000144
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200145 // Update max frameSize estimate.
philipel9d3ab612015-12-21 04:12:39 -0800146 _maxFrameSize =
147 VCM_MAX(_psi * _maxFrameSize, static_cast<double>(frameSizeBytes));
niklase@google.com470e71d2011-07-07 08:21:25 +0000148
philipel9d3ab612015-12-21 04:12:39 -0800149 if (_prevFrameSize == 0) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000150 _prevFrameSize = frameSizeBytes;
philipel9d3ab612015-12-21 04:12:39 -0800151 return;
152 }
153 _prevFrameSize = frameSizeBytes;
niklase@google.com470e71d2011-07-07 08:21:25 +0000154
Erik Språngb1e031a2018-11-01 11:20:49 +0100155 // Cap frameDelayMS based on the current time deviation noise.
156 int64_t max_time_deviation_ms =
157 static_cast<int64_t>(time_deviation_upper_bound_ * sqrt(_varNoise) + 0.5);
158 frameDelayMS = std::max(std::min(frameDelayMS, max_time_deviation_ms),
159 -max_time_deviation_ms);
160
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200161 // Only update the Kalman filter if the sample is not considered an extreme
162 // outlier. Even if it is an extreme outlier from a delay point of view, if
163 // the frame size also is large the deviation is probably due to an incorrect
164 // line slope.
philipel9d3ab612015-12-21 04:12:39 -0800165 double deviation = DeviationFromExpectedDelay(frameDelayMS, deltaFS);
niklase@google.com470e71d2011-07-07 08:21:25 +0000166
philipel9d3ab612015-12-21 04:12:39 -0800167 if (fabs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) ||
168 frameSizeBytes >
169 _avgFrameSize + _numStdDevFrameSizeOutlier * sqrt(_varFrameSize)) {
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200170 // Update the variance of the deviation from the line given by the Kalman
171 // filter.
philipel9d3ab612015-12-21 04:12:39 -0800172 EstimateRandomJitter(deviation, incompleteFrame);
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200173 // Prevent updating with frames which have been congested by a large frame,
174 // and therefore arrives almost at the same time as that frame.
175 // This can occur when we receive a large frame (key frame) which has been
176 // delayed. The next frame is of normal size (delta frame), and thus deltaFS
177 // will be << 0. This removes all frame samples which arrives after a key
178 // frame.
philipel9d3ab612015-12-21 04:12:39 -0800179 if ((!incompleteFrame || deviation >= 0.0) &&
180 static_cast<double>(deltaFS) > -0.25 * _maxFrameSize) {
181 // Update the Kalman filter with the new data
182 KalmanEstimateChannel(frameDelayMS, deltaFS);
niklase@google.com470e71d2011-07-07 08:21:25 +0000183 }
philipel9d3ab612015-12-21 04:12:39 -0800184 } else {
185 int nStdDev =
186 (deviation >= 0) ? _numStdDevDelayOutlier : -_numStdDevDelayOutlier;
187 EstimateRandomJitter(nStdDev * sqrt(_varNoise), incompleteFrame);
188 }
189 // Post process the total estimated jitter
190 if (_startupCount >= kStartupDelaySamples) {
191 PostProcessEstimate();
192 } else {
193 _startupCount++;
194 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000195}
196
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200197// Updates the nack/packet ratio.
philipel9d3ab612015-12-21 04:12:39 -0800198void VCMJitterEstimator::FrameNacked() {
philipel9d3ab612015-12-21 04:12:39 -0800199 if (_nackCount < _nackLimit) {
200 _nackCount++;
201 }
Gustavo Garciaf7a7c8a2018-01-08 15:23:38 +0100202 _latestNackTimestamp = clock_->TimeInMicroseconds();
niklase@google.com470e71d2011-07-07 08:21:25 +0000203}
204
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200205// Updates Kalman estimate of the channel.
niklase@google.com470e71d2011-07-07 08:21:25 +0000206// The caller is expected to sanity check the inputs.
philipel9d3ab612015-12-21 04:12:39 -0800207void VCMJitterEstimator::KalmanEstimateChannel(int64_t frameDelayMS,
208 int32_t deltaFSBytes) {
209 double Mh[2];
210 double hMh_sigma;
211 double kalmanGain[2];
212 double measureRes;
213 double t00, t01;
niklase@google.com470e71d2011-07-07 08:21:25 +0000214
philipel9d3ab612015-12-21 04:12:39 -0800215 // Kalman filtering
niklase@google.com470e71d2011-07-07 08:21:25 +0000216
philipel9d3ab612015-12-21 04:12:39 -0800217 // Prediction
218 // M = M + Q
219 _thetaCov[0][0] += _Qcov[0][0];
220 _thetaCov[0][1] += _Qcov[0][1];
221 _thetaCov[1][0] += _Qcov[1][0];
222 _thetaCov[1][1] += _Qcov[1][1];
niklase@google.com470e71d2011-07-07 08:21:25 +0000223
philipel9d3ab612015-12-21 04:12:39 -0800224 // Kalman gain
225 // K = M*h'/(sigma2n + h*M*h') = M*h'/(1 + h*M*h')
226 // h = [dFS 1]
227 // Mh = M*h'
228 // hMh_sigma = h*M*h' + R
229 Mh[0] = _thetaCov[0][0] * deltaFSBytes + _thetaCov[0][1];
230 Mh[1] = _thetaCov[1][0] * deltaFSBytes + _thetaCov[1][1];
231 // sigma weights measurements with a small deltaFS as noisy and
232 // measurements with large deltaFS as good
233 if (_maxFrameSize < 1.0) {
234 return;
235 }
236 double sigma = (300.0 * exp(-fabs(static_cast<double>(deltaFSBytes)) /
237 (1e0 * _maxFrameSize)) +
238 1) *
239 sqrt(_varNoise);
240 if (sigma < 1.0) {
241 sigma = 1.0;
242 }
243 hMh_sigma = deltaFSBytes * Mh[0] + Mh[1] + sigma;
244 if ((hMh_sigma < 1e-9 && hMh_sigma >= 0) ||
245 (hMh_sigma > -1e-9 && hMh_sigma <= 0)) {
246 assert(false);
247 return;
248 }
249 kalmanGain[0] = Mh[0] / hMh_sigma;
250 kalmanGain[1] = Mh[1] / hMh_sigma;
niklase@google.com470e71d2011-07-07 08:21:25 +0000251
philipel9d3ab612015-12-21 04:12:39 -0800252 // Correction
253 // theta = theta + K*(dT - h*theta)
254 measureRes = frameDelayMS - (deltaFSBytes * _theta[0] + _theta[1]);
255 _theta[0] += kalmanGain[0] * measureRes;
256 _theta[1] += kalmanGain[1] * measureRes;
niklase@google.com470e71d2011-07-07 08:21:25 +0000257
philipel9d3ab612015-12-21 04:12:39 -0800258 if (_theta[0] < _thetaLow) {
259 _theta[0] = _thetaLow;
260 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000261
philipel9d3ab612015-12-21 04:12:39 -0800262 // M = (I - K*h)*M
263 t00 = _thetaCov[0][0];
264 t01 = _thetaCov[0][1];
265 _thetaCov[0][0] = (1 - kalmanGain[0] * deltaFSBytes) * t00 -
266 kalmanGain[0] * _thetaCov[1][0];
267 _thetaCov[0][1] = (1 - kalmanGain[0] * deltaFSBytes) * t01 -
268 kalmanGain[0] * _thetaCov[1][1];
269 _thetaCov[1][0] = _thetaCov[1][0] * (1 - kalmanGain[1]) -
270 kalmanGain[1] * deltaFSBytes * t00;
271 _thetaCov[1][1] = _thetaCov[1][1] * (1 - kalmanGain[1]) -
272 kalmanGain[1] * deltaFSBytes * t01;
niklase@google.com470e71d2011-07-07 08:21:25 +0000273
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200274 // Covariance matrix, must be positive semi-definite.
philipel9d3ab612015-12-21 04:12:39 -0800275 assert(_thetaCov[0][0] + _thetaCov[1][1] >= 0 &&
276 _thetaCov[0][0] * _thetaCov[1][1] -
277 _thetaCov[0][1] * _thetaCov[1][0] >=
278 0 &&
279 _thetaCov[0][0] >= 0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000280}
281
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200282// Calculate difference in delay between a sample and the expected delay
283// estimated by the Kalman filter
philipel9d3ab612015-12-21 04:12:39 -0800284double VCMJitterEstimator::DeviationFromExpectedDelay(
285 int64_t frameDelayMS,
286 int32_t deltaFSBytes) const {
287 return frameDelayMS - (_theta[0] * deltaFSBytes + _theta[1]);
niklase@google.com470e71d2011-07-07 08:21:25 +0000288}
289
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200290// Estimates the random jitter by calculating the variance of the sample
291// distance from the line given by theta.
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000292void VCMJitterEstimator::EstimateRandomJitter(double d_dT,
293 bool incompleteFrame) {
294 uint64_t now = clock_->TimeInMicroseconds();
295 if (_lastUpdateT != -1) {
296 fps_counter_.AddSample(now - _lastUpdateT);
297 }
298 _lastUpdateT = now;
299
300 if (_alphaCount == 0) {
301 assert(false);
302 return;
303 }
304 double alpha =
305 static_cast<double>(_alphaCount - 1) / static_cast<double>(_alphaCount);
306 _alphaCount++;
307 if (_alphaCount > _alphaCountMax)
308 _alphaCount = _alphaCountMax;
309
Erik Språngb1e031a2018-11-01 11:20:49 +0100310 // In order to avoid a low frame rate stream to react slower to changes,
311 // scale the alpha weight relative a 30 fps stream.
312 double fps = GetFrameRate();
313 if (fps > 0.0) {
314 double rate_scale = 30.0 / fps;
315 // At startup, there can be a lot of noise in the fps estimate.
316 // Interpolate rate_scale linearly, from 1.0 at sample #1, to 30.0 / fps
317 // at sample #kStartupDelaySamples.
318 if (_alphaCount < kStartupDelaySamples) {
319 rate_scale =
320 (_alphaCount * rate_scale + (kStartupDelaySamples - _alphaCount)) /
321 kStartupDelaySamples;
niklase@google.com470e71d2011-07-07 08:21:25 +0000322 }
Erik Språngb1e031a2018-11-01 11:20:49 +0100323 alpha = pow(alpha, rate_scale);
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000324 }
325
326 double avgNoise = alpha * _avgNoise + (1 - alpha) * d_dT;
327 double varNoise =
328 alpha * _varNoise + (1 - alpha) * (d_dT - _avgNoise) * (d_dT - _avgNoise);
329 if (!incompleteFrame || varNoise > _varNoise) {
330 _avgNoise = avgNoise;
331 _varNoise = varNoise;
332 }
333 if (_varNoise < 1.0) {
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200334 // The variance should never be zero, since we might get stuck and consider
335 // all samples as outliers.
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000336 _varNoise = 1.0;
337 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000338}
339
philipel9d3ab612015-12-21 04:12:39 -0800340double VCMJitterEstimator::NoiseThreshold() const {
341 double noiseThreshold = _noiseStdDevs * sqrt(_varNoise) - _noiseStdDevOffset;
342 if (noiseThreshold < 1.0) {
343 noiseThreshold = 1.0;
344 }
345 return noiseThreshold;
niklase@google.com470e71d2011-07-07 08:21:25 +0000346}
347
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200348// Calculates the current jitter estimate from the filtered estimates.
philipel9d3ab612015-12-21 04:12:39 -0800349double VCMJitterEstimator::CalculateEstimate() {
350 double ret = _theta[0] * (_maxFrameSize - _avgFrameSize) + NoiseThreshold();
niklase@google.com470e71d2011-07-07 08:21:25 +0000351
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200352 // A very low estimate (or negative) is neglected.
philipel9d3ab612015-12-21 04:12:39 -0800353 if (ret < 1.0) {
354 if (_prevEstimate <= 0.01) {
355 ret = 1.0;
356 } else {
357 ret = _prevEstimate;
niklase@google.com470e71d2011-07-07 08:21:25 +0000358 }
philipel9d3ab612015-12-21 04:12:39 -0800359 }
360 if (ret > 10000.0) { // Sanity
361 ret = 10000.0;
362 }
363 _prevEstimate = ret;
364 return ret;
niklase@google.com470e71d2011-07-07 08:21:25 +0000365}
366
philipel9d3ab612015-12-21 04:12:39 -0800367void VCMJitterEstimator::PostProcessEstimate() {
368 _filterJitterEstimate = CalculateEstimate();
niklase@google.com470e71d2011-07-07 08:21:25 +0000369}
370
philipel9d3ab612015-12-21 04:12:39 -0800371void VCMJitterEstimator::UpdateRtt(int64_t rttMs) {
372 _rttFilter.Update(rttMs);
niklase@google.com470e71d2011-07-07 08:21:25 +0000373}
374
niklase@google.com470e71d2011-07-07 08:21:25 +0000375// Returns the current filtered estimate if available,
376// otherwise tries to calculate an estimate.
“Michaele0f37042019-06-04 10:04:12 -0500377int VCMJitterEstimator::GetJitterEstimate(
378 double rttMultiplier,
379 absl::optional<double> rttMultAddCapMs) {
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000380 double jitterMS = CalculateEstimate() + OPERATING_SYSTEM_JITTER;
Gustavo Garciaf7a7c8a2018-01-08 15:23:38 +0100381 uint64_t now = clock_->TimeInMicroseconds();
382
383 if (now - _latestNackTimestamp > kNackCountTimeoutMs * 1000)
384 _nackCount = 0;
385
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000386 if (_filterJitterEstimate > jitterMS)
387 jitterMS = _filterJitterEstimate;
“Michaele0f37042019-06-04 10:04:12 -0500388 if (_nackCount >= _nackLimit) {
389 if (rttMultAddCapMs.has_value()) {
390 jitterMS +=
391 std::min(_rttFilter.RttMs() * rttMultiplier, rttMultAddCapMs.value());
392 } else {
393 jitterMS += _rttFilter.RttMs() * rttMultiplier;
394 }
395 }
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000396
Erik Språngb1e031a2018-11-01 11:20:49 +0100397 static const double kJitterScaleLowThreshold = 5.0;
398 static const double kJitterScaleHighThreshold = 10.0;
399 double fps = GetFrameRate();
400 // Ignore jitter for very low fps streams.
401 if (fps < kJitterScaleLowThreshold) {
402 if (fps == 0.0) {
403 return rtc::checked_cast<int>(std::max(0.0, jitterMS) + 0.5);
niklase@google.com470e71d2011-07-07 08:21:25 +0000404 }
Erik Språngb1e031a2018-11-01 11:20:49 +0100405 return 0;
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000406 }
407
Erik Språngb1e031a2018-11-01 11:20:49 +0100408 // Semi-low frame rate; scale by factor linearly interpolated from 0.0 at
409 // kJitterScaleLowThreshold to 1.0 at kJitterScaleHighThreshold.
410 if (fps < kJitterScaleHighThreshold) {
411 jitterMS = (1.0 / (kJitterScaleHighThreshold - kJitterScaleLowThreshold)) *
412 (fps - kJitterScaleLowThreshold) * jitterMS;
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000413 }
Erik Språngb1e031a2018-11-01 11:20:49 +0100414
415 return rtc::checked_cast<int>(std::max(0.0, jitterMS) + 0.5);
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000416}
417
418double VCMJitterEstimator::GetFrameRate() const {
Sebastian Jansson7a994332019-06-11 10:28:20 +0200419 if (fps_counter_.ComputeMean() <= 0.0)
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000420 return 0;
421
422 double fps = 1000000.0 / fps_counter_.ComputeMean();
423 // Sanity check.
424 assert(fps >= 0.0);
425 if (fps > kMaxFramerateEstimate) {
426 fps = kMaxFramerateEstimate;
427 }
428 return fps;
niklase@google.com470e71d2011-07-07 08:21:25 +0000429}
philipel9d3ab612015-12-21 04:12:39 -0800430} // namespace webrtc