blob: dfe7f465514a13e9a8d63e06ab4de4cd1b3a620a [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>
15#include <stdlib.h>
16#include <string.h>
philipel9d3ab612015-12-21 04:12:39 -080017#include <string>
18
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "modules/video_coding/internal_defines.h"
20#include "modules/video_coding/rtt_filter.h"
21#include "system_wrappers/include/clock.h"
22#include "system_wrappers/include/field_trial.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000023
24namespace webrtc {
25
sprang@webrtc.org70e2d112014-09-24 14:06:56 +000026enum { kStartupDelaySamples = 30 };
27enum { kFsAccuStartupSamples = 5 };
28enum { kMaxFramerateEstimate = 200 };
Gustavo Garciaf7a7c8a2018-01-08 15:23:38 +010029enum { kNackCountTimeoutMs = 60000 };
sprang@webrtc.org70e2d112014-09-24 14:06:56 +000030
31VCMJitterEstimator::VCMJitterEstimator(const Clock* clock,
32 int32_t vcmId,
33 int32_t receiverId)
stefan@webrtc.org34c5da62014-04-11 14:08:35 +000034 : _vcmId(vcmId),
35 _receiverId(receiverId),
36 _phi(0.97),
37 _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.
49 low_rate_experiment_(kInit),
50 clock_(clock) {
51 Reset();
52}
53
philipel9d3ab612015-12-21 04:12:39 -080054VCMJitterEstimator::~VCMJitterEstimator() {}
niklase@google.com470e71d2011-07-07 08:21:25 +000055
philipel9d3ab612015-12-21 04:12:39 -080056VCMJitterEstimator& VCMJitterEstimator::operator=(
57 const VCMJitterEstimator& rhs) {
58 if (this != &rhs) {
59 memcpy(_thetaCov, rhs._thetaCov, sizeof(_thetaCov));
60 memcpy(_Qcov, rhs._Qcov, sizeof(_Qcov));
niklase@google.com470e71d2011-07-07 08:21:25 +000061
philipel9d3ab612015-12-21 04:12:39 -080062 _vcmId = rhs._vcmId;
63 _receiverId = rhs._receiverId;
64 _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;
79 }
80 return *this;
niklase@google.com470e71d2011-07-07 08:21:25 +000081}
82
83// Resets the JitterEstimate
philipel9d3ab612015-12-21 04:12:39 -080084void VCMJitterEstimator::Reset() {
85 _theta[0] = 1 / (512e3 / 8);
86 _theta[1] = 0;
87 _varNoise = 4.0;
niklase@google.com470e71d2011-07-07 08:21:25 +000088
philipel9d3ab612015-12-21 04:12:39 -080089 _thetaCov[0][0] = 1e-4;
90 _thetaCov[1][1] = 1e2;
91 _thetaCov[0][1] = _thetaCov[1][0] = 0;
92 _Qcov[0][0] = 2.5e-10;
93 _Qcov[1][1] = 1e-10;
94 _Qcov[0][1] = _Qcov[1][0] = 0;
95 _avgFrameSize = 500;
96 _maxFrameSize = 500;
97 _varFrameSize = 100;
98 _lastUpdateT = -1;
99 _prevEstimate = -1.0;
100 _prevFrameSize = 0;
101 _avgNoise = 0.0;
102 _alphaCount = 1;
103 _filterJitterEstimate = 0.0;
104 _latestNackTimestamp = 0;
105 _nackCount = 0;
Gustavo Garciaf7a7c8a2018-01-08 15:23:38 +0100106 _latestNackTimestamp = 0;
philipel9d3ab612015-12-21 04:12:39 -0800107 _fsSum = 0;
108 _fsCount = 0;
109 _startupCount = 0;
110 _rttFilter.Reset();
111 fps_counter_.Reset();
niklase@google.com470e71d2011-07-07 08:21:25 +0000112}
113
philipel9d3ab612015-12-21 04:12:39 -0800114void VCMJitterEstimator::ResetNackCount() {
115 _nackCount = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000116}
117
118// Updates the estimates with the new measurements
philipel9d3ab612015-12-21 04:12:39 -0800119void VCMJitterEstimator::UpdateEstimate(int64_t frameDelayMS,
120 uint32_t frameSizeBytes,
121 bool incompleteFrame /* = false */) {
122 if (frameSizeBytes == 0) {
123 return;
124 }
125 int deltaFS = frameSizeBytes - _prevFrameSize;
126 if (_fsCount < kFsAccuStartupSamples) {
127 _fsSum += frameSizeBytes;
128 _fsCount++;
129 } else if (_fsCount == kFsAccuStartupSamples) {
130 // Give the frame size filter
131 _avgFrameSize = static_cast<double>(_fsSum) / static_cast<double>(_fsCount);
132 _fsCount++;
133 }
134 if (!incompleteFrame || frameSizeBytes > _avgFrameSize) {
135 double avgFrameSize = _phi * _avgFrameSize + (1 - _phi) * frameSizeBytes;
136 if (frameSizeBytes < _avgFrameSize + 2 * sqrt(_varFrameSize)) {
137 // Only update the average frame size if this sample wasn't a
138 // key frame
139 _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
142 // get
143 // key frames.
Yves Gerey665174f2018-06-19 15:03:05 +0200144 _varFrameSize = VCM_MAX(
145 _phi * _varFrameSize + (1 - _phi) * (frameSizeBytes - avgFrameSize) *
146 (frameSizeBytes - avgFrameSize),
147 1.0);
philipel9d3ab612015-12-21 04:12:39 -0800148 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000149
philipel9d3ab612015-12-21 04:12:39 -0800150 // Update max frameSize estimate
151 _maxFrameSize =
152 VCM_MAX(_psi * _maxFrameSize, static_cast<double>(frameSizeBytes));
niklase@google.com470e71d2011-07-07 08:21:25 +0000153
philipel9d3ab612015-12-21 04:12:39 -0800154 if (_prevFrameSize == 0) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000155 _prevFrameSize = frameSizeBytes;
philipel9d3ab612015-12-21 04:12:39 -0800156 return;
157 }
158 _prevFrameSize = frameSizeBytes;
niklase@google.com470e71d2011-07-07 08:21:25 +0000159
philipel9d3ab612015-12-21 04:12:39 -0800160 // Only update the Kalman filter if the sample is not considered
161 // an extreme outlier. Even if it is an extreme outlier from a
162 // delay point of view, if the frame size also is large the
163 // deviation is probably due to an incorrect line slope.
164 double deviation = DeviationFromExpectedDelay(frameDelayMS, deltaFS);
niklase@google.com470e71d2011-07-07 08:21:25 +0000165
philipel9d3ab612015-12-21 04:12:39 -0800166 if (fabs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) ||
167 frameSizeBytes >
168 _avgFrameSize + _numStdDevFrameSizeOutlier * sqrt(_varFrameSize)) {
169 // Update the variance of the deviation from the
170 // line given by the Kalman filter
171 EstimateRandomJitter(deviation, incompleteFrame);
172 // Prevent updating with frames which have been congested by a large
173 // frame, and therefore arrives almost at the same time as that frame.
174 // This can occur when we receive a large frame (key frame) which
175 // has been delayed. The next frame is of normal size (delta frame),
176 // and thus deltaFS will be << 0. This removes all frame samples
177 // which arrives after a key frame.
178 if ((!incompleteFrame || deviation >= 0.0) &&
179 static_cast<double>(deltaFS) > -0.25 * _maxFrameSize) {
180 // Update the Kalman filter with the new data
181 KalmanEstimateChannel(frameDelayMS, deltaFS);
niklase@google.com470e71d2011-07-07 08:21:25 +0000182 }
philipel9d3ab612015-12-21 04:12:39 -0800183 } else {
184 int nStdDev =
185 (deviation >= 0) ? _numStdDevDelayOutlier : -_numStdDevDelayOutlier;
186 EstimateRandomJitter(nStdDev * sqrt(_varNoise), incompleteFrame);
187 }
188 // Post process the total estimated jitter
189 if (_startupCount >= kStartupDelaySamples) {
190 PostProcessEstimate();
191 } else {
192 _startupCount++;
193 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000194}
195
196// Updates the nack/packet ratio
philipel9d3ab612015-12-21 04:12:39 -0800197void VCMJitterEstimator::FrameNacked() {
philipel9d3ab612015-12-21 04:12:39 -0800198 if (_nackCount < _nackLimit) {
199 _nackCount++;
200 }
Gustavo Garciaf7a7c8a2018-01-08 15:23:38 +0100201 _latestNackTimestamp = clock_->TimeInMicroseconds();
niklase@google.com470e71d2011-07-07 08:21:25 +0000202}
203
204// Updates Kalman estimate of the channel
205// The caller is expected to sanity check the inputs.
philipel9d3ab612015-12-21 04:12:39 -0800206void VCMJitterEstimator::KalmanEstimateChannel(int64_t frameDelayMS,
207 int32_t deltaFSBytes) {
208 double Mh[2];
209 double hMh_sigma;
210 double kalmanGain[2];
211 double measureRes;
212 double t00, t01;
niklase@google.com470e71d2011-07-07 08:21:25 +0000213
philipel9d3ab612015-12-21 04:12:39 -0800214 // Kalman filtering
niklase@google.com470e71d2011-07-07 08:21:25 +0000215
philipel9d3ab612015-12-21 04:12:39 -0800216 // Prediction
217 // M = M + Q
218 _thetaCov[0][0] += _Qcov[0][0];
219 _thetaCov[0][1] += _Qcov[0][1];
220 _thetaCov[1][0] += _Qcov[1][0];
221 _thetaCov[1][1] += _Qcov[1][1];
niklase@google.com470e71d2011-07-07 08:21:25 +0000222
philipel9d3ab612015-12-21 04:12:39 -0800223 // Kalman gain
224 // K = M*h'/(sigma2n + h*M*h') = M*h'/(1 + h*M*h')
225 // h = [dFS 1]
226 // Mh = M*h'
227 // hMh_sigma = h*M*h' + R
228 Mh[0] = _thetaCov[0][0] * deltaFSBytes + _thetaCov[0][1];
229 Mh[1] = _thetaCov[1][0] * deltaFSBytes + _thetaCov[1][1];
230 // sigma weights measurements with a small deltaFS as noisy and
231 // measurements with large deltaFS as good
232 if (_maxFrameSize < 1.0) {
233 return;
234 }
235 double sigma = (300.0 * exp(-fabs(static_cast<double>(deltaFSBytes)) /
236 (1e0 * _maxFrameSize)) +
237 1) *
238 sqrt(_varNoise);
239 if (sigma < 1.0) {
240 sigma = 1.0;
241 }
242 hMh_sigma = deltaFSBytes * Mh[0] + Mh[1] + sigma;
243 if ((hMh_sigma < 1e-9 && hMh_sigma >= 0) ||
244 (hMh_sigma > -1e-9 && hMh_sigma <= 0)) {
245 assert(false);
246 return;
247 }
248 kalmanGain[0] = Mh[0] / hMh_sigma;
249 kalmanGain[1] = Mh[1] / hMh_sigma;
niklase@google.com470e71d2011-07-07 08:21:25 +0000250
philipel9d3ab612015-12-21 04:12:39 -0800251 // Correction
252 // theta = theta + K*(dT - h*theta)
253 measureRes = frameDelayMS - (deltaFSBytes * _theta[0] + _theta[1]);
254 _theta[0] += kalmanGain[0] * measureRes;
255 _theta[1] += kalmanGain[1] * measureRes;
niklase@google.com470e71d2011-07-07 08:21:25 +0000256
philipel9d3ab612015-12-21 04:12:39 -0800257 if (_theta[0] < _thetaLow) {
258 _theta[0] = _thetaLow;
259 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000260
philipel9d3ab612015-12-21 04:12:39 -0800261 // M = (I - K*h)*M
262 t00 = _thetaCov[0][0];
263 t01 = _thetaCov[0][1];
264 _thetaCov[0][0] = (1 - kalmanGain[0] * deltaFSBytes) * t00 -
265 kalmanGain[0] * _thetaCov[1][0];
266 _thetaCov[0][1] = (1 - kalmanGain[0] * deltaFSBytes) * t01 -
267 kalmanGain[0] * _thetaCov[1][1];
268 _thetaCov[1][0] = _thetaCov[1][0] * (1 - kalmanGain[1]) -
269 kalmanGain[1] * deltaFSBytes * t00;
270 _thetaCov[1][1] = _thetaCov[1][1] * (1 - kalmanGain[1]) -
271 kalmanGain[1] * deltaFSBytes * t01;
niklase@google.com470e71d2011-07-07 08:21:25 +0000272
philipel9d3ab612015-12-21 04:12:39 -0800273 // Covariance matrix, must be positive semi-definite
274 assert(_thetaCov[0][0] + _thetaCov[1][1] >= 0 &&
275 _thetaCov[0][0] * _thetaCov[1][1] -
276 _thetaCov[0][1] * _thetaCov[1][0] >=
277 0 &&
278 _thetaCov[0][0] >= 0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000279}
280
281// Calculate difference in delay between a sample and the
282// expected delay estimated by the Kalman filter
philipel9d3ab612015-12-21 04:12:39 -0800283double VCMJitterEstimator::DeviationFromExpectedDelay(
284 int64_t frameDelayMS,
285 int32_t deltaFSBytes) const {
286 return frameDelayMS - (_theta[0] * deltaFSBytes + _theta[1]);
niklase@google.com470e71d2011-07-07 08:21:25 +0000287}
288
289// Estimates the random jitter by calculating the variance of the
290// sample distance from the line given by theta.
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000291void VCMJitterEstimator::EstimateRandomJitter(double d_dT,
292 bool incompleteFrame) {
293 uint64_t now = clock_->TimeInMicroseconds();
294 if (_lastUpdateT != -1) {
295 fps_counter_.AddSample(now - _lastUpdateT);
296 }
297 _lastUpdateT = now;
298
299 if (_alphaCount == 0) {
300 assert(false);
301 return;
302 }
303 double alpha =
304 static_cast<double>(_alphaCount - 1) / static_cast<double>(_alphaCount);
305 _alphaCount++;
306 if (_alphaCount > _alphaCountMax)
307 _alphaCount = _alphaCountMax;
308
309 if (LowRateExperimentEnabled()) {
310 // 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;
322 }
323 alpha = pow(alpha, rate_scale);
niklase@google.com470e71d2011-07-07 08:21:25 +0000324 }
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000325 }
326
327 double avgNoise = alpha * _avgNoise + (1 - alpha) * d_dT;
328 double varNoise =
329 alpha * _varNoise + (1 - alpha) * (d_dT - _avgNoise) * (d_dT - _avgNoise);
330 if (!incompleteFrame || varNoise > _varNoise) {
331 _avgNoise = avgNoise;
332 _varNoise = varNoise;
333 }
334 if (_varNoise < 1.0) {
335 // The variance should never be zero, since we might get
336 // stuck and consider all samples as outliers.
337 _varNoise = 1.0;
338 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000339}
340
philipel9d3ab612015-12-21 04:12:39 -0800341double VCMJitterEstimator::NoiseThreshold() const {
342 double noiseThreshold = _noiseStdDevs * sqrt(_varNoise) - _noiseStdDevOffset;
343 if (noiseThreshold < 1.0) {
344 noiseThreshold = 1.0;
345 }
346 return noiseThreshold;
niklase@google.com470e71d2011-07-07 08:21:25 +0000347}
348
349// Calculates the current jitter estimate from the filtered estimates
philipel9d3ab612015-12-21 04:12:39 -0800350double VCMJitterEstimator::CalculateEstimate() {
351 double ret = _theta[0] * (_maxFrameSize - _avgFrameSize) + NoiseThreshold();
niklase@google.com470e71d2011-07-07 08:21:25 +0000352
philipel9d3ab612015-12-21 04:12:39 -0800353 // A very low estimate (or negative) is neglected
354 if (ret < 1.0) {
355 if (_prevEstimate <= 0.01) {
356 ret = 1.0;
357 } else {
358 ret = _prevEstimate;
niklase@google.com470e71d2011-07-07 08:21:25 +0000359 }
philipel9d3ab612015-12-21 04:12:39 -0800360 }
361 if (ret > 10000.0) { // Sanity
362 ret = 10000.0;
363 }
364 _prevEstimate = ret;
365 return ret;
niklase@google.com470e71d2011-07-07 08:21:25 +0000366}
367
philipel9d3ab612015-12-21 04:12:39 -0800368void VCMJitterEstimator::PostProcessEstimate() {
369 _filterJitterEstimate = CalculateEstimate();
niklase@google.com470e71d2011-07-07 08:21:25 +0000370}
371
philipel9d3ab612015-12-21 04:12:39 -0800372void VCMJitterEstimator::UpdateRtt(int64_t rttMs) {
373 _rttFilter.Update(rttMs);
niklase@google.com470e71d2011-07-07 08:21:25 +0000374}
375
philipel9d3ab612015-12-21 04:12:39 -0800376void VCMJitterEstimator::UpdateMaxFrameSize(uint32_t frameSizeBytes) {
377 if (_maxFrameSize < frameSizeBytes) {
378 _maxFrameSize = frameSizeBytes;
379 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000380}
381
382// Returns the current filtered estimate if available,
383// otherwise tries to calculate an estimate.
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000384int VCMJitterEstimator::GetJitterEstimate(double rttMultiplier) {
385 double jitterMS = CalculateEstimate() + OPERATING_SYSTEM_JITTER;
Gustavo Garciaf7a7c8a2018-01-08 15:23:38 +0100386 uint64_t now = clock_->TimeInMicroseconds();
387
388 if (now - _latestNackTimestamp > kNackCountTimeoutMs * 1000)
389 _nackCount = 0;
390
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000391 if (_filterJitterEstimate > jitterMS)
392 jitterMS = _filterJitterEstimate;
393 if (_nackCount >= _nackLimit)
394 jitterMS += _rttFilter.RttMs() * rttMultiplier;
395
396 if (LowRateExperimentEnabled()) {
397 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 jitterMS;
404 }
405 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000406 }
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000407
408 // 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 =
412 (1.0 / (kJitterScaleHighThreshold - kJitterScaleLowThreshold)) *
413 (fps - kJitterScaleLowThreshold) * jitterMS;
niklase@google.com470e71d2011-07-07 08:21:25 +0000414 }
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000415 }
416
417 return static_cast<uint32_t>(jitterMS + 0.5);
418}
419
420bool VCMJitterEstimator::LowRateExperimentEnabled() {
421 if (low_rate_experiment_ == kInit) {
422 std::string group =
423 webrtc::field_trial::FindFullName("WebRTC-ReducedJitterDelay");
424 if (group == "Disabled") {
425 low_rate_experiment_ = kDisabled;
426 } else {
427 low_rate_experiment_ = kEnabled;
428 }
429 }
430 return low_rate_experiment_ == kEnabled ? true : false;
431}
432
433double VCMJitterEstimator::GetFrameRate() const {
Peter Boström74451a52016-02-01 16:31:10 +0100434 if (fps_counter_.ComputeMean() == 0.0)
sprang@webrtc.org70e2d112014-09-24 14:06:56 +0000435 return 0;
436
437 double fps = 1000000.0 / fps_counter_.ComputeMean();
438 // Sanity check.
439 assert(fps >= 0.0);
440 if (fps > kMaxFramerateEstimate) {
441 fps = kMaxFramerateEstimate;
442 }
443 return fps;
niklase@google.com470e71d2011-07-07 08:21:25 +0000444}
philipel9d3ab612015-12-21 04:12:39 -0800445} // namespace webrtc