blob: 00d75da812f740f4fc0b39a6fb7bba36d98048f9 [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
35VCMJitterEstimator::VCMJitterEstimator(const Clock* clock,
36 int32_t vcmId,
37 int32_t receiverId)
stefan@webrtc.org34c5da62014-04-11 14:08:35 +000038 : _vcmId(vcmId),
39 _receiverId(receiverId),
40 _phi(0.97),
41 _psi(0.9999),
42 _alphaCountMax(400),
43 _thetaLow(0.000001),
44 _nackLimit(3),
45 _numStdDevDelayOutlier(15),
46 _numStdDevFrameSizeOutlier(3),
47 _noiseStdDevs(2.33), // ~Less than 1% chance
48 // (look up in normal distribution table)...
49 _noiseStdDevOffset(30.0), // ...of getting 30 ms freezes
sprang@webrtc.org70e2d112014-09-24 14:06:56 +000050 _rttFilter(),
51 fps_counter_(30), // TODO(sprang): Use an estimator with limit based on
52 // time, rather than number of samples.
Erik Språngb1e031a2018-11-01 11:20:49 +010053 time_deviation_upper_bound_(
54 JitterUpperBoundExperiment::GetUpperBoundSigmas().value_or(
55 kDefaultMaxTimestampDeviationInSigmas)),
sprang@webrtc.org70e2d112014-09-24 14:06:56 +000056 clock_(clock) {
57 Reset();
58}
59
philipel9d3ab612015-12-21 04:12:39 -080060VCMJitterEstimator::~VCMJitterEstimator() {}
niklase@google.com470e71d2011-07-07 08:21:25 +000061
philipel9d3ab612015-12-21 04:12:39 -080062VCMJitterEstimator& VCMJitterEstimator::operator=(
63 const VCMJitterEstimator& rhs) {
64 if (this != &rhs) {
65 memcpy(_thetaCov, rhs._thetaCov, sizeof(_thetaCov));
66 memcpy(_Qcov, rhs._Qcov, sizeof(_Qcov));
niklase@google.com470e71d2011-07-07 08:21:25 +000067
philipel9d3ab612015-12-21 04:12:39 -080068 _vcmId = rhs._vcmId;
69 _receiverId = rhs._receiverId;
70 _avgFrameSize = rhs._avgFrameSize;
71 _varFrameSize = rhs._varFrameSize;
72 _maxFrameSize = rhs._maxFrameSize;
73 _fsSum = rhs._fsSum;
74 _fsCount = rhs._fsCount;
75 _lastUpdateT = rhs._lastUpdateT;
76 _prevEstimate = rhs._prevEstimate;
77 _prevFrameSize = rhs._prevFrameSize;
78 _avgNoise = rhs._avgNoise;
79 _alphaCount = rhs._alphaCount;
80 _filterJitterEstimate = rhs._filterJitterEstimate;
81 _startupCount = rhs._startupCount;
82 _latestNackTimestamp = rhs._latestNackTimestamp;
83 _nackCount = rhs._nackCount;
84 _rttFilter = rhs._rttFilter;
Erik Språngb1e031a2018-11-01 11:20:49 +010085 clock_ = rhs.clock_;
philipel9d3ab612015-12-21 04:12:39 -080086 }
87 return *this;
niklase@google.com470e71d2011-07-07 08:21:25 +000088}
89
90// Resets the JitterEstimate
philipel9d3ab612015-12-21 04:12:39 -080091void VCMJitterEstimator::Reset() {
92 _theta[0] = 1 / (512e3 / 8);
93 _theta[1] = 0;
94 _varNoise = 4.0;
niklase@google.com470e71d2011-07-07 08:21:25 +000095
philipel9d3ab612015-12-21 04:12:39 -080096 _thetaCov[0][0] = 1e-4;
97 _thetaCov[1][1] = 1e2;
98 _thetaCov[0][1] = _thetaCov[1][0] = 0;
99 _Qcov[0][0] = 2.5e-10;
100 _Qcov[1][1] = 1e-10;
101 _Qcov[0][1] = _Qcov[1][0] = 0;
102 _avgFrameSize = 500;
103 _maxFrameSize = 500;
104 _varFrameSize = 100;
105 _lastUpdateT = -1;
106 _prevEstimate = -1.0;
107 _prevFrameSize = 0;
108 _avgNoise = 0.0;
109 _alphaCount = 1;
110 _filterJitterEstimate = 0.0;
111 _latestNackTimestamp = 0;
112 _nackCount = 0;
Gustavo Garciaf7a7c8a2018-01-08 15:23:38 +0100113 _latestNackTimestamp = 0;
philipel9d3ab612015-12-21 04:12:39 -0800114 _fsSum = 0;
115 _fsCount = 0;
116 _startupCount = 0;
117 _rttFilter.Reset();
118 fps_counter_.Reset();
niklase@google.com470e71d2011-07-07 08:21:25 +0000119}
120
philipel9d3ab612015-12-21 04:12:39 -0800121void VCMJitterEstimator::ResetNackCount() {
122 _nackCount = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000123}
124
125// Updates the estimates with the new measurements
philipel9d3ab612015-12-21 04:12:39 -0800126void VCMJitterEstimator::UpdateEstimate(int64_t frameDelayMS,
127 uint32_t frameSizeBytes,
128 bool incompleteFrame /* = false */) {
129 if (frameSizeBytes == 0) {
130 return;
131 }
132 int deltaFS = frameSizeBytes - _prevFrameSize;
133 if (_fsCount < kFsAccuStartupSamples) {
134 _fsSum += frameSizeBytes;
135 _fsCount++;
136 } else if (_fsCount == kFsAccuStartupSamples) {
137 // Give the frame size filter
138 _avgFrameSize = static_cast<double>(_fsSum) / static_cast<double>(_fsCount);
139 _fsCount++;
140 }
141 if (!incompleteFrame || frameSizeBytes > _avgFrameSize) {
142 double avgFrameSize = _phi * _avgFrameSize + (1 - _phi) * frameSizeBytes;
143 if (frameSizeBytes < _avgFrameSize + 2 * sqrt(_varFrameSize)) {
144 // Only update the average frame size if this sample wasn't a
145 // key frame
146 _avgFrameSize = avgFrameSize;
niklase@google.com470e71d2011-07-07 08:21:25 +0000147 }
philipel9d3ab612015-12-21 04:12:39 -0800148 // Update the variance anyway since we want to capture cases where we only
149 // get
150 // key frames.
Yves Gerey665174f2018-06-19 15:03:05 +0200151 _varFrameSize = VCM_MAX(
152 _phi * _varFrameSize + (1 - _phi) * (frameSizeBytes - avgFrameSize) *
153 (frameSizeBytes - avgFrameSize),
154 1.0);
philipel9d3ab612015-12-21 04:12:39 -0800155 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000156
philipel9d3ab612015-12-21 04:12:39 -0800157 // Update max frameSize estimate
158 _maxFrameSize =
159 VCM_MAX(_psi * _maxFrameSize, static_cast<double>(frameSizeBytes));
niklase@google.com470e71d2011-07-07 08:21:25 +0000160
philipel9d3ab612015-12-21 04:12:39 -0800161 if (_prevFrameSize == 0) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000162 _prevFrameSize = frameSizeBytes;
philipel9d3ab612015-12-21 04:12:39 -0800163 return;
164 }
165 _prevFrameSize = frameSizeBytes;
niklase@google.com470e71d2011-07-07 08:21:25 +0000166
Erik Språngb1e031a2018-11-01 11:20:49 +0100167 // Cap frameDelayMS based on the current time deviation noise.
168 int64_t max_time_deviation_ms =
169 static_cast<int64_t>(time_deviation_upper_bound_ * sqrt(_varNoise) + 0.5);
170 frameDelayMS = std::max(std::min(frameDelayMS, max_time_deviation_ms),
171 -max_time_deviation_ms);
172
philipel9d3ab612015-12-21 04:12:39 -0800173 // Only update the Kalman filter if the sample is not considered
174 // an extreme outlier. Even if it is an extreme outlier from a
175 // delay point of view, if the frame size also is large the
176 // deviation is probably due to an incorrect line slope.
177 double deviation = DeviationFromExpectedDelay(frameDelayMS, deltaFS);
niklase@google.com470e71d2011-07-07 08:21:25 +0000178
philipel9d3ab612015-12-21 04:12:39 -0800179 if (fabs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) ||
180 frameSizeBytes >
181 _avgFrameSize + _numStdDevFrameSizeOutlier * sqrt(_varFrameSize)) {
182 // Update the variance of the deviation from the
183 // line given by the Kalman filter
184 EstimateRandomJitter(deviation, incompleteFrame);
185 // Prevent updating with frames which have been congested by a large
186 // frame, and therefore arrives almost at the same time as that frame.
187 // This can occur when we receive a large frame (key frame) which
188 // has been delayed. The next frame is of normal size (delta frame),
189 // and thus deltaFS will be << 0. This removes all frame samples
190 // which arrives after a key frame.
191 if ((!incompleteFrame || deviation >= 0.0) &&
192 static_cast<double>(deltaFS) > -0.25 * _maxFrameSize) {
193 // Update the Kalman filter with the new data
194 KalmanEstimateChannel(frameDelayMS, deltaFS);
niklase@google.com470e71d2011-07-07 08:21:25 +0000195 }
philipel9d3ab612015-12-21 04:12:39 -0800196 } else {
197 int nStdDev =
198 (deviation >= 0) ? _numStdDevDelayOutlier : -_numStdDevDelayOutlier;
199 EstimateRandomJitter(nStdDev * sqrt(_varNoise), incompleteFrame);
200 }
201 // Post process the total estimated jitter
202 if (_startupCount >= kStartupDelaySamples) {
203 PostProcessEstimate();
204 } else {
205 _startupCount++;
206 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000207}
208
209// Updates the nack/packet ratio
philipel9d3ab612015-12-21 04:12:39 -0800210void VCMJitterEstimator::FrameNacked() {
philipel9d3ab612015-12-21 04:12:39 -0800211 if (_nackCount < _nackLimit) {
212 _nackCount++;
213 }
Gustavo Garciaf7a7c8a2018-01-08 15:23:38 +0100214 _latestNackTimestamp = clock_->TimeInMicroseconds();
niklase@google.com470e71d2011-07-07 08:21:25 +0000215}
216
217// Updates Kalman estimate of the channel
218// The caller is expected to sanity check the inputs.
philipel9d3ab612015-12-21 04:12:39 -0800219void VCMJitterEstimator::KalmanEstimateChannel(int64_t frameDelayMS,
220 int32_t deltaFSBytes) {
221 double Mh[2];
222 double hMh_sigma;
223 double kalmanGain[2];
224 double measureRes;
225 double t00, t01;
niklase@google.com470e71d2011-07-07 08:21:25 +0000226
philipel9d3ab612015-12-21 04:12:39 -0800227 // Kalman filtering
niklase@google.com470e71d2011-07-07 08:21:25 +0000228
philipel9d3ab612015-12-21 04:12:39 -0800229 // Prediction
230 // M = M + Q
231 _thetaCov[0][0] += _Qcov[0][0];
232 _thetaCov[0][1] += _Qcov[0][1];
233 _thetaCov[1][0] += _Qcov[1][0];
234 _thetaCov[1][1] += _Qcov[1][1];
niklase@google.com470e71d2011-07-07 08:21:25 +0000235
philipel9d3ab612015-12-21 04:12:39 -0800236 // Kalman gain
237 // K = M*h'/(sigma2n + h*M*h') = M*h'/(1 + h*M*h')
238 // h = [dFS 1]
239 // Mh = M*h'
240 // hMh_sigma = h*M*h' + R
241 Mh[0] = _thetaCov[0][0] * deltaFSBytes + _thetaCov[0][1];
242 Mh[1] = _thetaCov[1][0] * deltaFSBytes + _thetaCov[1][1];
243 // sigma weights measurements with a small deltaFS as noisy and
244 // measurements with large deltaFS as good
245 if (_maxFrameSize < 1.0) {
246 return;
247 }
248 double sigma = (300.0 * exp(-fabs(static_cast<double>(deltaFSBytes)) /
249 (1e0 * _maxFrameSize)) +
250 1) *
251 sqrt(_varNoise);
252 if (sigma < 1.0) {
253 sigma = 1.0;
254 }
255 hMh_sigma = deltaFSBytes * Mh[0] + Mh[1] + sigma;
256 if ((hMh_sigma < 1e-9 && hMh_sigma >= 0) ||
257 (hMh_sigma > -1e-9 && hMh_sigma <= 0)) {
258 assert(false);
259 return;
260 }
261 kalmanGain[0] = Mh[0] / hMh_sigma;
262 kalmanGain[1] = Mh[1] / hMh_sigma;
niklase@google.com470e71d2011-07-07 08:21:25 +0000263
philipel9d3ab612015-12-21 04:12:39 -0800264 // Correction
265 // theta = theta + K*(dT - h*theta)
266 measureRes = frameDelayMS - (deltaFSBytes * _theta[0] + _theta[1]);
267 _theta[0] += kalmanGain[0] * measureRes;
268 _theta[1] += kalmanGain[1] * measureRes;
niklase@google.com470e71d2011-07-07 08:21:25 +0000269
philipel9d3ab612015-12-21 04:12:39 -0800270 if (_theta[0] < _thetaLow) {
271 _theta[0] = _thetaLow;
272 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000273
philipel9d3ab612015-12-21 04:12:39 -0800274 // M = (I - K*h)*M
275 t00 = _thetaCov[0][0];
276 t01 = _thetaCov[0][1];
277 _thetaCov[0][0] = (1 - kalmanGain[0] * deltaFSBytes) * t00 -
278 kalmanGain[0] * _thetaCov[1][0];
279 _thetaCov[0][1] = (1 - kalmanGain[0] * deltaFSBytes) * t01 -
280 kalmanGain[0] * _thetaCov[1][1];
281 _thetaCov[1][0] = _thetaCov[1][0] * (1 - kalmanGain[1]) -
282 kalmanGain[1] * deltaFSBytes * t00;
283 _thetaCov[1][1] = _thetaCov[1][1] * (1 - kalmanGain[1]) -
284 kalmanGain[1] * deltaFSBytes * t01;
niklase@google.com470e71d2011-07-07 08:21:25 +0000285
philipel9d3ab612015-12-21 04:12:39 -0800286 // Covariance matrix, must be positive semi-definite
287 assert(_thetaCov[0][0] + _thetaCov[1][1] >= 0 &&
288 _thetaCov[0][0] * _thetaCov[1][1] -
289 _thetaCov[0][1] * _thetaCov[1][0] >=
290 0 &&
291 _thetaCov[0][0] >= 0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000292}
293
294// Calculate difference in delay between a sample and the
295// expected delay estimated by the Kalman filter
philipel9d3ab612015-12-21 04:12:39 -0800296double VCMJitterEstimator::DeviationFromExpectedDelay(
297 int64_t frameDelayMS,
298 int32_t deltaFSBytes) const {
299 return frameDelayMS - (_theta[0] * deltaFSBytes + _theta[1]);
niklase@google.com470e71d2011-07-07 08:21:25 +0000300}
301
302// Estimates the random jitter by calculating the variance of the
303// sample distance from the line given by theta.
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000304void VCMJitterEstimator::EstimateRandomJitter(double d_dT,
305 bool incompleteFrame) {
306 uint64_t now = clock_->TimeInMicroseconds();
307 if (_lastUpdateT != -1) {
308 fps_counter_.AddSample(now - _lastUpdateT);
309 }
310 _lastUpdateT = now;
311
312 if (_alphaCount == 0) {
313 assert(false);
314 return;
315 }
316 double alpha =
317 static_cast<double>(_alphaCount - 1) / static_cast<double>(_alphaCount);
318 _alphaCount++;
319 if (_alphaCount > _alphaCountMax)
320 _alphaCount = _alphaCountMax;
321
Erik Språngb1e031a2018-11-01 11:20:49 +0100322 // In order to avoid a low frame rate stream to react slower to changes,
323 // scale the alpha weight relative a 30 fps stream.
324 double fps = GetFrameRate();
325 if (fps > 0.0) {
326 double rate_scale = 30.0 / fps;
327 // At startup, there can be a lot of noise in the fps estimate.
328 // Interpolate rate_scale linearly, from 1.0 at sample #1, to 30.0 / fps
329 // at sample #kStartupDelaySamples.
330 if (_alphaCount < kStartupDelaySamples) {
331 rate_scale =
332 (_alphaCount * rate_scale + (kStartupDelaySamples - _alphaCount)) /
333 kStartupDelaySamples;
niklase@google.com470e71d2011-07-07 08:21:25 +0000334 }
Erik Språngb1e031a2018-11-01 11:20:49 +0100335 alpha = pow(alpha, rate_scale);
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000336 }
337
338 double avgNoise = alpha * _avgNoise + (1 - alpha) * d_dT;
339 double varNoise =
340 alpha * _varNoise + (1 - alpha) * (d_dT - _avgNoise) * (d_dT - _avgNoise);
341 if (!incompleteFrame || varNoise > _varNoise) {
342 _avgNoise = avgNoise;
343 _varNoise = varNoise;
344 }
345 if (_varNoise < 1.0) {
346 // The variance should never be zero, since we might get
347 // stuck and consider all samples as outliers.
348 _varNoise = 1.0;
349 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000350}
351
philipel9d3ab612015-12-21 04:12:39 -0800352double VCMJitterEstimator::NoiseThreshold() const {
353 double noiseThreshold = _noiseStdDevs * sqrt(_varNoise) - _noiseStdDevOffset;
354 if (noiseThreshold < 1.0) {
355 noiseThreshold = 1.0;
356 }
357 return noiseThreshold;
niklase@google.com470e71d2011-07-07 08:21:25 +0000358}
359
360// Calculates the current jitter estimate from the filtered estimates
philipel9d3ab612015-12-21 04:12:39 -0800361double VCMJitterEstimator::CalculateEstimate() {
362 double ret = _theta[0] * (_maxFrameSize - _avgFrameSize) + NoiseThreshold();
niklase@google.com470e71d2011-07-07 08:21:25 +0000363
philipel9d3ab612015-12-21 04:12:39 -0800364 // A very low estimate (or negative) is neglected
365 if (ret < 1.0) {
366 if (_prevEstimate <= 0.01) {
367 ret = 1.0;
368 } else {
369 ret = _prevEstimate;
niklase@google.com470e71d2011-07-07 08:21:25 +0000370 }
philipel9d3ab612015-12-21 04:12:39 -0800371 }
372 if (ret > 10000.0) { // Sanity
373 ret = 10000.0;
374 }
375 _prevEstimate = ret;
376 return ret;
niklase@google.com470e71d2011-07-07 08:21:25 +0000377}
378
philipel9d3ab612015-12-21 04:12:39 -0800379void VCMJitterEstimator::PostProcessEstimate() {
380 _filterJitterEstimate = CalculateEstimate();
niklase@google.com470e71d2011-07-07 08:21:25 +0000381}
382
philipel9d3ab612015-12-21 04:12:39 -0800383void VCMJitterEstimator::UpdateRtt(int64_t rttMs) {
384 _rttFilter.Update(rttMs);
niklase@google.com470e71d2011-07-07 08:21:25 +0000385}
386
philipel9d3ab612015-12-21 04:12:39 -0800387void VCMJitterEstimator::UpdateMaxFrameSize(uint32_t frameSizeBytes) {
388 if (_maxFrameSize < frameSizeBytes) {
389 _maxFrameSize = frameSizeBytes;
390 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000391}
392
393// Returns the current filtered estimate if available,
394// otherwise tries to calculate an estimate.
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000395int VCMJitterEstimator::GetJitterEstimate(double rttMultiplier) {
396 double jitterMS = CalculateEstimate() + OPERATING_SYSTEM_JITTER;
Gustavo Garciaf7a7c8a2018-01-08 15:23:38 +0100397 uint64_t now = clock_->TimeInMicroseconds();
398
399 if (now - _latestNackTimestamp > kNackCountTimeoutMs * 1000)
400 _nackCount = 0;
401
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000402 if (_filterJitterEstimate > jitterMS)
403 jitterMS = _filterJitterEstimate;
404 if (_nackCount >= _nackLimit)
405 jitterMS += _rttFilter.RttMs() * rttMultiplier;
406
Erik Språngb1e031a2018-11-01 11:20:49 +0100407 static const double kJitterScaleLowThreshold = 5.0;
408 static const double kJitterScaleHighThreshold = 10.0;
409 double fps = GetFrameRate();
410 // Ignore jitter for very low fps streams.
411 if (fps < kJitterScaleLowThreshold) {
412 if (fps == 0.0) {
413 return rtc::checked_cast<int>(std::max(0.0, jitterMS) + 0.5);
niklase@google.com470e71d2011-07-07 08:21:25 +0000414 }
Erik Språngb1e031a2018-11-01 11:20:49 +0100415 return 0;
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000416 }
417
Erik Språngb1e031a2018-11-01 11:20:49 +0100418 // Semi-low frame rate; scale by factor linearly interpolated from 0.0 at
419 // kJitterScaleLowThreshold to 1.0 at kJitterScaleHighThreshold.
420 if (fps < kJitterScaleHighThreshold) {
421 jitterMS = (1.0 / (kJitterScaleHighThreshold - kJitterScaleLowThreshold)) *
422 (fps - kJitterScaleLowThreshold) * jitterMS;
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000423 }
Erik Språngb1e031a2018-11-01 11:20:49 +0100424
425 return rtc::checked_cast<int>(std::max(0.0, jitterMS) + 0.5);
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000426}
427
428double VCMJitterEstimator::GetFrameRate() const {
Peter Boström74451a52016-02-01 16:31:10 +0100429 if (fps_counter_.ComputeMean() == 0.0)
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000430 return 0;
431
432 double fps = 1000000.0 / fps_counter_.ComputeMean();
433 // Sanity check.
434 assert(fps >= 0.0);
435 if (fps > kMaxFramerateEstimate) {
436 fps = kMaxFramerateEstimate;
437 }
438 return fps;
niklase@google.com470e71d2011-07-07 08:21:25 +0000439}
philipel9d3ab612015-12-21 04:12:39 -0800440} // namespace webrtc