blob: e380f9ea9661ba9e42b919f0916869e6d7c479a2 [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
philipel9d3ab612015-12-21 04:12:39 -0800115void VCMJitterEstimator::ResetNackCount() {
116 _nackCount = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000117}
118
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200119// Updates the estimates with the new measurements.
philipel9d3ab612015-12-21 04:12:39 -0800120void 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) {
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200131 // Give the frame size filter.
philipel9d3ab612015-12-21 04:12:39 -0800132 _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)) {
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200138 // Only update the average frame size if this sample wasn't a key frame.
philipel9d3ab612015-12-21 04:12:39 -0800139 _avgFrameSize = avgFrameSize;
niklase@google.com470e71d2011-07-07 08:21:25 +0000140 }
philipel9d3ab612015-12-21 04:12:39 -0800141 // Update the variance anyway since we want to capture cases where we only
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200142 // get key frames.
Yves Gerey665174f2018-06-19 15:03:05 +0200143 _varFrameSize = VCM_MAX(
144 _phi * _varFrameSize + (1 - _phi) * (frameSizeBytes - avgFrameSize) *
145 (frameSizeBytes - avgFrameSize),
146 1.0);
philipel9d3ab612015-12-21 04:12:39 -0800147 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000148
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200149 // Update max frameSize estimate.
philipel9d3ab612015-12-21 04:12:39 -0800150 _maxFrameSize =
151 VCM_MAX(_psi * _maxFrameSize, static_cast<double>(frameSizeBytes));
niklase@google.com470e71d2011-07-07 08:21:25 +0000152
philipel9d3ab612015-12-21 04:12:39 -0800153 if (_prevFrameSize == 0) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000154 _prevFrameSize = frameSizeBytes;
philipel9d3ab612015-12-21 04:12:39 -0800155 return;
156 }
157 _prevFrameSize = frameSizeBytes;
niklase@google.com470e71d2011-07-07 08:21:25 +0000158
Erik Språngb1e031a2018-11-01 11:20:49 +0100159 // 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
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200165 // 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.
philipel9d3ab612015-12-21 04:12:39 -0800169 double deviation = DeviationFromExpectedDelay(frameDelayMS, deltaFS);
niklase@google.com470e71d2011-07-07 08:21:25 +0000170
philipel9d3ab612015-12-21 04:12:39 -0800171 if (fabs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) ||
172 frameSizeBytes >
173 _avgFrameSize + _numStdDevFrameSizeOutlier * sqrt(_varFrameSize)) {
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200174 // Update the variance of the deviation from the line given by the Kalman
175 // filter.
philipel9d3ab612015-12-21 04:12:39 -0800176 EstimateRandomJitter(deviation, incompleteFrame);
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200177 // 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.
philipel9d3ab612015-12-21 04:12:39 -0800183 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);
niklase@google.com470e71d2011-07-07 08:21:25 +0000187 }
philipel9d3ab612015-12-21 04:12:39 -0800188 } 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 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000199}
200
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200201// Updates the nack/packet ratio.
philipel9d3ab612015-12-21 04:12:39 -0800202void VCMJitterEstimator::FrameNacked() {
philipel9d3ab612015-12-21 04:12:39 -0800203 if (_nackCount < _nackLimit) {
204 _nackCount++;
205 }
Gustavo Garciaf7a7c8a2018-01-08 15:23:38 +0100206 _latestNackTimestamp = clock_->TimeInMicroseconds();
niklase@google.com470e71d2011-07-07 08:21:25 +0000207}
208
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200209// Updates Kalman estimate of the channel.
niklase@google.com470e71d2011-07-07 08:21:25 +0000210// The caller is expected to sanity check the inputs.
philipel9d3ab612015-12-21 04:12:39 -0800211void 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;
niklase@google.com470e71d2011-07-07 08:21:25 +0000218
philipel9d3ab612015-12-21 04:12:39 -0800219 // Kalman filtering
niklase@google.com470e71d2011-07-07 08:21:25 +0000220
philipel9d3ab612015-12-21 04:12:39 -0800221 // 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];
niklase@google.com470e71d2011-07-07 08:21:25 +0000227
philipel9d3ab612015-12-21 04:12:39 -0800228 // 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;
niklase@google.com470e71d2011-07-07 08:21:25 +0000255
philipel9d3ab612015-12-21 04:12:39 -0800256 // 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;
niklase@google.com470e71d2011-07-07 08:21:25 +0000261
philipel9d3ab612015-12-21 04:12:39 -0800262 if (_theta[0] < _thetaLow) {
263 _theta[0] = _thetaLow;
264 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000265
philipel9d3ab612015-12-21 04:12:39 -0800266 // 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;
niklase@google.com470e71d2011-07-07 08:21:25 +0000277
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200278 // Covariance matrix, must be positive semi-definite.
philipel9d3ab612015-12-21 04:12:39 -0800279 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);
niklase@google.com470e71d2011-07-07 08:21:25 +0000284}
285
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200286// Calculate difference in delay between a sample and the expected delay
287// estimated by the Kalman filter
philipel9d3ab612015-12-21 04:12:39 -0800288double VCMJitterEstimator::DeviationFromExpectedDelay(
289 int64_t frameDelayMS,
290 int32_t deltaFSBytes) const {
291 return frameDelayMS - (_theta[0] * deltaFSBytes + _theta[1]);
niklase@google.com470e71d2011-07-07 08:21:25 +0000292}
293
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200294// Estimates the random jitter by calculating the variance of the sample
295// distance from the line given by theta.
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000296void 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
Erik Språngb1e031a2018-11-01 11:20:49 +0100314 // 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;
niklase@google.com470e71d2011-07-07 08:21:25 +0000326 }
Erik Språngb1e031a2018-11-01 11:20:49 +0100327 alpha = pow(alpha, rate_scale);
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000328 }
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) {
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200338 // The variance should never be zero, since we might get stuck and consider
339 // all samples as outliers.
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000340 _varNoise = 1.0;
341 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000342}
343
philipel9d3ab612015-12-21 04:12:39 -0800344double VCMJitterEstimator::NoiseThreshold() const {
345 double noiseThreshold = _noiseStdDevs * sqrt(_varNoise) - _noiseStdDevOffset;
346 if (noiseThreshold < 1.0) {
347 noiseThreshold = 1.0;
348 }
349 return noiseThreshold;
niklase@google.com470e71d2011-07-07 08:21:25 +0000350}
351
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200352// Calculates the current jitter estimate from the filtered estimates.
philipel9d3ab612015-12-21 04:12:39 -0800353double VCMJitterEstimator::CalculateEstimate() {
354 double ret = _theta[0] * (_maxFrameSize - _avgFrameSize) + NoiseThreshold();
niklase@google.com470e71d2011-07-07 08:21:25 +0000355
Åsa Persson3fcc5be2019-04-04 09:40:27 +0200356 // A very low estimate (or negative) is neglected.
philipel9d3ab612015-12-21 04:12:39 -0800357 if (ret < 1.0) {
358 if (_prevEstimate <= 0.01) {
359 ret = 1.0;
360 } else {
361 ret = _prevEstimate;
niklase@google.com470e71d2011-07-07 08:21:25 +0000362 }
philipel9d3ab612015-12-21 04:12:39 -0800363 }
364 if (ret > 10000.0) { // Sanity
365 ret = 10000.0;
366 }
367 _prevEstimate = ret;
368 return ret;
niklase@google.com470e71d2011-07-07 08:21:25 +0000369}
370
philipel9d3ab612015-12-21 04:12:39 -0800371void VCMJitterEstimator::PostProcessEstimate() {
372 _filterJitterEstimate = CalculateEstimate();
niklase@google.com470e71d2011-07-07 08:21:25 +0000373}
374
philipel9d3ab612015-12-21 04:12:39 -0800375void VCMJitterEstimator::UpdateRtt(int64_t rttMs) {
376 _rttFilter.Update(rttMs);
niklase@google.com470e71d2011-07-07 08:21:25 +0000377}
378
philipel9d3ab612015-12-21 04:12:39 -0800379void VCMJitterEstimator::UpdateMaxFrameSize(uint32_t frameSizeBytes) {
380 if (_maxFrameSize < frameSizeBytes) {
381 _maxFrameSize = frameSizeBytes;
382 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000383}
384
385// Returns the current filtered estimate if available,
386// otherwise tries to calculate an estimate.
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000387int VCMJitterEstimator::GetJitterEstimate(double rttMultiplier) {
388 double jitterMS = CalculateEstimate() + OPERATING_SYSTEM_JITTER;
Gustavo Garciaf7a7c8a2018-01-08 15:23:38 +0100389 uint64_t now = clock_->TimeInMicroseconds();
390
391 if (now - _latestNackTimestamp > kNackCountTimeoutMs * 1000)
392 _nackCount = 0;
393
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000394 if (_filterJitterEstimate > jitterMS)
395 jitterMS = _filterJitterEstimate;
396 if (_nackCount >= _nackLimit)
397 jitterMS += _rttFilter.RttMs() * rttMultiplier;
398
Erik Språngb1e031a2018-11-01 11:20:49 +0100399 static const double kJitterScaleLowThreshold = 5.0;
400 static const double kJitterScaleHighThreshold = 10.0;
401 double fps = GetFrameRate();
402 // Ignore jitter for very low fps streams.
403 if (fps < kJitterScaleLowThreshold) {
404 if (fps == 0.0) {
405 return rtc::checked_cast<int>(std::max(0.0, jitterMS) + 0.5);
niklase@google.com470e71d2011-07-07 08:21:25 +0000406 }
Erik Språngb1e031a2018-11-01 11:20:49 +0100407 return 0;
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000408 }
409
Erik Språngb1e031a2018-11-01 11:20:49 +0100410 // Semi-low frame rate; scale by factor linearly interpolated from 0.0 at
411 // kJitterScaleLowThreshold to 1.0 at kJitterScaleHighThreshold.
412 if (fps < kJitterScaleHighThreshold) {
413 jitterMS = (1.0 / (kJitterScaleHighThreshold - kJitterScaleLowThreshold)) *
414 (fps - kJitterScaleLowThreshold) * jitterMS;
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000415 }
Erik Språngb1e031a2018-11-01 11:20:49 +0100416
417 return rtc::checked_cast<int>(std::max(0.0, jitterMS) + 0.5);
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000418}
419
420double VCMJitterEstimator::GetFrameRate() const {
Peter Boström74451a52016-02-01 16:31:10 +0100421 if (fps_counter_.ComputeMean() == 0.0)
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000422 return 0;
423
424 double fps = 1000000.0 / fps_counter_.ComputeMean();
425 // Sanity check.
426 assert(fps >= 0.0);
427 if (fps > kMaxFramerateEstimate) {
428 fps = kMaxFramerateEstimate;
429 }
430 return fps;
niklase@google.com470e71d2011-07-07 08:21:25 +0000431}
philipel9d3ab612015-12-21 04:12:39 -0800432} // namespace webrtc