Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(11)

Side by Side Diff: webrtc/modules/video_coding/main/source/jitter_estimator.cc

Issue 1417283007: modules/video_coding refactorings (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Fix the other copy of the mock include header Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "webrtc/modules/video_coding/main/source/internal_defines.h"
12 #include "webrtc/modules/video_coding/main/source/jitter_estimator.h"
13 #include "webrtc/modules/video_coding/main/source/rtt_filter.h"
14 #include "webrtc/system_wrappers/include/clock.h"
15 #include "webrtc/system_wrappers/include/field_trial.h"
16
17 #include <assert.h>
18 #include <math.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 namespace webrtc {
23
24 enum { kStartupDelaySamples = 30 };
25 enum { kFsAccuStartupSamples = 5 };
26 enum { kMaxFramerateEstimate = 200 };
27
28 VCMJitterEstimator::VCMJitterEstimator(const Clock* clock,
29 int32_t vcmId,
30 int32_t receiverId)
31 : _vcmId(vcmId),
32 _receiverId(receiverId),
33 _phi(0.97),
34 _psi(0.9999),
35 _alphaCountMax(400),
36 _thetaLow(0.000001),
37 _nackLimit(3),
38 _numStdDevDelayOutlier(15),
39 _numStdDevFrameSizeOutlier(3),
40 _noiseStdDevs(2.33), // ~Less than 1% chance
41 // (look up in normal distribution table)...
42 _noiseStdDevOffset(30.0), // ...of getting 30 ms freezes
43 _rttFilter(),
44 fps_counter_(30), // TODO(sprang): Use an estimator with limit based on
45 // time, rather than number of samples.
46 low_rate_experiment_(kInit),
47 clock_(clock) {
48 Reset();
49 }
50
51 VCMJitterEstimator::~VCMJitterEstimator() {
52 }
53
54 VCMJitterEstimator&
55 VCMJitterEstimator::operator=(const VCMJitterEstimator& rhs)
56 {
57 if (this != &rhs)
58 {
59 memcpy(_thetaCov, rhs._thetaCov, sizeof(_thetaCov));
60 memcpy(_Qcov, rhs._Qcov, sizeof(_Qcov));
61
62 _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;
81 }
82
83 // Resets the JitterEstimate
84 void
85 VCMJitterEstimator::Reset()
86 {
87 _theta[0] = 1/(512e3/8);
88 _theta[1] = 0;
89 _varNoise = 4.0;
90
91 _thetaCov[0][0] = 1e-4;
92 _thetaCov[1][1] = 1e2;
93 _thetaCov[0][1] = _thetaCov[1][0] = 0;
94 _Qcov[0][0] = 2.5e-10;
95 _Qcov[1][1] = 1e-10;
96 _Qcov[0][1] = _Qcov[1][0] = 0;
97 _avgFrameSize = 500;
98 _maxFrameSize = 500;
99 _varFrameSize = 100;
100 _lastUpdateT = -1;
101 _prevEstimate = -1.0;
102 _prevFrameSize = 0;
103 _avgNoise = 0.0;
104 _alphaCount = 1;
105 _filterJitterEstimate = 0.0;
106 _latestNackTimestamp = 0;
107 _nackCount = 0;
108 _fsSum = 0;
109 _fsCount = 0;
110 _startupCount = 0;
111 _rttFilter.Reset();
112 fps_counter_.Reset();
113 }
114
115 void
116 VCMJitterEstimator::ResetNackCount()
117 {
118 _nackCount = 0;
119 }
120
121 // Updates the estimates with the new measurements
122 void
123 VCMJitterEstimator::UpdateEstimate(int64_t frameDelayMS, uint32_t frameSizeBytes ,
124 bool incompleteFrame /* = false */)
125 {
126 if (frameSizeBytes == 0)
127 {
128 return;
129 }
130 int deltaFS = frameSizeBytes - _prevFrameSize;
131 if (_fsCount < kFsAccuStartupSamples)
132 {
133 _fsSum += frameSizeBytes;
134 _fsCount++;
135 }
136 else if (_fsCount == kFsAccuStartupSamples)
137 {
138 // Give the frame size filter
139 _avgFrameSize = static_cast<double>(_fsSum) /
140 static_cast<double>(_fsCount);
141 _fsCount++;
142 }
143 if (!incompleteFrame || frameSizeBytes > _avgFrameSize)
144 {
145 double avgFrameSize = _phi * _avgFrameSize +
146 (1 - _phi) * frameSizeBytes;
147 if (frameSizeBytes < _avgFrameSize + 2 * sqrt(_varFrameSize))
148 {
149 // Only update the average frame size if this sample wasn't a
150 // key frame
151 _avgFrameSize = avgFrameSize;
152 }
153 // Update the variance anyway since we want to capture cases where we on ly get
154 // key frames.
155 _varFrameSize = VCM_MAX(_phi * _varFrameSize + (1 - _phi) *
156 (frameSizeBytes - avgFrameSize) *
157 (frameSizeBytes - avgFrameSize), 1.0);
158 }
159
160 // Update max frameSize estimate
161 _maxFrameSize = VCM_MAX(_psi * _maxFrameSize, static_cast<double>(frameSizeB ytes));
162
163 if (_prevFrameSize == 0)
164 {
165 _prevFrameSize = frameSizeBytes;
166 return;
167 }
168 _prevFrameSize = frameSizeBytes;
169
170 // Only update the Kalman filter if the sample is not considered
171 // an extreme outlier. Even if it is an extreme outlier from a
172 // delay point of view, if the frame size also is large the
173 // deviation is probably due to an incorrect line slope.
174 double deviation = DeviationFromExpectedDelay(frameDelayMS, deltaFS);
175
176 if (fabs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) ||
177 frameSizeBytes > _avgFrameSize + _numStdDevFrameSizeOutlier * sqrt(_varF rameSize))
178 {
179 // Update the variance of the deviation from the
180 // line given by the Kalman filter
181 EstimateRandomJitter(deviation, incompleteFrame);
182 // Prevent updating with frames which have been congested by a large
183 // frame, and therefore arrives almost at the same time as that frame.
184 // This can occur when we receive a large frame (key frame) which
185 // has been delayed. The next frame is of normal size (delta frame),
186 // and thus deltaFS will be << 0. This removes all frame samples
187 // which arrives after a key frame.
188 if ((!incompleteFrame || deviation >= 0.0) &&
189 static_cast<double>(deltaFS) > - 0.25 * _maxFrameSize)
190 {
191 // Update the Kalman filter with the new data
192 KalmanEstimateChannel(frameDelayMS, deltaFS);
193 }
194 }
195 else
196 {
197 int nStdDev = (deviation >= 0) ? _numStdDevDelayOutlier : -_numStdDevDel ayOutlier;
198 EstimateRandomJitter(nStdDev * sqrt(_varNoise), incompleteFrame);
199 }
200 // Post process the total estimated jitter
201 if (_startupCount >= kStartupDelaySamples)
202 {
203 PostProcessEstimate();
204 }
205 else
206 {
207 _startupCount++;
208 }
209 }
210
211 // Updates the nack/packet ratio
212 void
213 VCMJitterEstimator::FrameNacked()
214 {
215 // Wait until _nackLimit retransmissions has been received,
216 // then always add ~1 RTT delay.
217 // TODO(holmer): Should we ever remove the additional delay if the
218 // the packet losses seem to have stopped? We could for instance scale
219 // the number of RTTs to add with the amount of retransmissions in a given
220 // time interval, or similar.
221 if (_nackCount < _nackLimit)
222 {
223 _nackCount++;
224 }
225 }
226
227 // Updates Kalman estimate of the channel
228 // The caller is expected to sanity check the inputs.
229 void
230 VCMJitterEstimator::KalmanEstimateChannel(int64_t frameDelayMS,
231 int32_t deltaFSBytes)
232 {
233 double Mh[2];
234 double hMh_sigma;
235 double kalmanGain[2];
236 double measureRes;
237 double t00, t01;
238
239 // Kalman filtering
240
241 // Prediction
242 // M = M + Q
243 _thetaCov[0][0] += _Qcov[0][0];
244 _thetaCov[0][1] += _Qcov[0][1];
245 _thetaCov[1][0] += _Qcov[1][0];
246 _thetaCov[1][1] += _Qcov[1][1];
247
248 // Kalman gain
249 // K = M*h'/(sigma2n + h*M*h') = M*h'/(1 + h*M*h')
250 // h = [dFS 1]
251 // Mh = M*h'
252 // hMh_sigma = h*M*h' + R
253 Mh[0] = _thetaCov[0][0] * deltaFSBytes + _thetaCov[0][1];
254 Mh[1] = _thetaCov[1][0] * deltaFSBytes + _thetaCov[1][1];
255 // sigma weights measurements with a small deltaFS as noisy and
256 // measurements with large deltaFS as good
257 if (_maxFrameSize < 1.0)
258 {
259 return;
260 }
261 double sigma = (300.0 * exp(-fabs(static_cast<double>(deltaFSBytes)) /
262 (1e0 * _maxFrameSize)) + 1) * sqrt(_varNoise);
263 if (sigma < 1.0)
264 {
265 sigma = 1.0;
266 }
267 hMh_sigma = deltaFSBytes * Mh[0] + Mh[1] + sigma;
268 if ((hMh_sigma < 1e-9 && hMh_sigma >= 0) || (hMh_sigma > -1e-9 && hMh_sigma <= 0))
269 {
270 assert(false);
271 return;
272 }
273 kalmanGain[0] = Mh[0] / hMh_sigma;
274 kalmanGain[1] = Mh[1] / hMh_sigma;
275
276 // Correction
277 // theta = theta + K*(dT - h*theta)
278 measureRes = frameDelayMS - (deltaFSBytes * _theta[0] + _theta[1]);
279 _theta[0] += kalmanGain[0] * measureRes;
280 _theta[1] += kalmanGain[1] * measureRes;
281
282 if (_theta[0] < _thetaLow)
283 {
284 _theta[0] = _thetaLow;
285 }
286
287 // M = (I - K*h)*M
288 t00 = _thetaCov[0][0];
289 t01 = _thetaCov[0][1];
290 _thetaCov[0][0] = (1 - kalmanGain[0] * deltaFSBytes) * t00 -
291 kalmanGain[0] * _thetaCov[1][0];
292 _thetaCov[0][1] = (1 - kalmanGain[0] * deltaFSBytes) * t01 -
293 kalmanGain[0] * _thetaCov[1][1];
294 _thetaCov[1][0] = _thetaCov[1][0] * (1 - kalmanGain[1]) -
295 kalmanGain[1] * deltaFSBytes * t00;
296 _thetaCov[1][1] = _thetaCov[1][1] * (1 - kalmanGain[1]) -
297 kalmanGain[1] * deltaFSBytes * t01;
298
299 // Covariance matrix, must be positive semi-definite
300 assert(_thetaCov[0][0] + _thetaCov[1][1] >= 0 &&
301 _thetaCov[0][0] * _thetaCov[1][1] - _thetaCov[0][1] * _thetaCov[1][0] >= 0 &&
302 _thetaCov[0][0] >= 0);
303 }
304
305 // Calculate difference in delay between a sample and the
306 // expected delay estimated by the Kalman filter
307 double
308 VCMJitterEstimator::DeviationFromExpectedDelay(int64_t frameDelayMS,
309 int32_t deltaFSBytes) const
310 {
311 return frameDelayMS - (_theta[0] * deltaFSBytes + _theta[1]);
312 }
313
314 // Estimates the random jitter by calculating the variance of the
315 // sample distance from the line given by theta.
316 void VCMJitterEstimator::EstimateRandomJitter(double d_dT,
317 bool incompleteFrame) {
318 uint64_t now = clock_->TimeInMicroseconds();
319 if (_lastUpdateT != -1) {
320 fps_counter_.AddSample(now - _lastUpdateT);
321 }
322 _lastUpdateT = now;
323
324 if (_alphaCount == 0) {
325 assert(false);
326 return;
327 }
328 double alpha =
329 static_cast<double>(_alphaCount - 1) / static_cast<double>(_alphaCount);
330 _alphaCount++;
331 if (_alphaCount > _alphaCountMax)
332 _alphaCount = _alphaCountMax;
333
334 if (LowRateExperimentEnabled()) {
335 // In order to avoid a low frame rate stream to react slower to changes,
336 // scale the alpha weight relative a 30 fps stream.
337 double fps = GetFrameRate();
338 if (fps > 0.0) {
339 double rate_scale = 30.0 / fps;
340 // At startup, there can be a lot of noise in the fps estimate.
341 // Interpolate rate_scale linearly, from 1.0 at sample #1, to 30.0 / fps
342 // at sample #kStartupDelaySamples.
343 if (_alphaCount < kStartupDelaySamples) {
344 rate_scale =
345 (_alphaCount * rate_scale + (kStartupDelaySamples - _alphaCount)) /
346 kStartupDelaySamples;
347 }
348 alpha = pow(alpha, rate_scale);
349 }
350 }
351
352 double avgNoise = alpha * _avgNoise + (1 - alpha) * d_dT;
353 double varNoise =
354 alpha * _varNoise + (1 - alpha) * (d_dT - _avgNoise) * (d_dT - _avgNoise);
355 if (!incompleteFrame || varNoise > _varNoise) {
356 _avgNoise = avgNoise;
357 _varNoise = varNoise;
358 }
359 if (_varNoise < 1.0) {
360 // The variance should never be zero, since we might get
361 // stuck and consider all samples as outliers.
362 _varNoise = 1.0;
363 }
364 }
365
366 double
367 VCMJitterEstimator::NoiseThreshold() const
368 {
369 double noiseThreshold = _noiseStdDevs * sqrt(_varNoise) - _noiseStdDevOffset ;
370 if (noiseThreshold < 1.0)
371 {
372 noiseThreshold = 1.0;
373 }
374 return noiseThreshold;
375 }
376
377 // Calculates the current jitter estimate from the filtered estimates
378 double
379 VCMJitterEstimator::CalculateEstimate()
380 {
381 double ret = _theta[0] * (_maxFrameSize - _avgFrameSize) + NoiseThreshold();
382
383 // A very low estimate (or negative) is neglected
384 if (ret < 1.0) {
385 if (_prevEstimate <= 0.01)
386 {
387 ret = 1.0;
388 }
389 else
390 {
391 ret = _prevEstimate;
392 }
393 }
394 if (ret > 10000.0) // Sanity
395 {
396 ret = 10000.0;
397 }
398 _prevEstimate = ret;
399 return ret;
400 }
401
402 void
403 VCMJitterEstimator::PostProcessEstimate()
404 {
405 _filterJitterEstimate = CalculateEstimate();
406 }
407
408 void
409 VCMJitterEstimator::UpdateRtt(int64_t rttMs)
410 {
411 _rttFilter.Update(rttMs);
412 }
413
414 void
415 VCMJitterEstimator::UpdateMaxFrameSize(uint32_t frameSizeBytes)
416 {
417 if (_maxFrameSize < frameSizeBytes)
418 {
419 _maxFrameSize = frameSizeBytes;
420 }
421 }
422
423 // Returns the current filtered estimate if available,
424 // otherwise tries to calculate an estimate.
425 int VCMJitterEstimator::GetJitterEstimate(double rttMultiplier) {
426 double jitterMS = CalculateEstimate() + OPERATING_SYSTEM_JITTER;
427 if (_filterJitterEstimate > jitterMS)
428 jitterMS = _filterJitterEstimate;
429 if (_nackCount >= _nackLimit)
430 jitterMS += _rttFilter.RttMs() * rttMultiplier;
431
432 if (LowRateExperimentEnabled()) {
433 static const double kJitterScaleLowThreshold = 5.0;
434 static const double kJitterScaleHighThreshold = 10.0;
435 double fps = GetFrameRate();
436 // Ignore jitter for very low fps streams.
437 if (fps < kJitterScaleLowThreshold) {
438 if (fps == 0.0) {
439 return jitterMS;
440 }
441 return 0;
442 }
443
444 // Semi-low frame rate; scale by factor linearly interpolated from 0.0 at
445 // kJitterScaleLowThreshold to 1.0 at kJitterScaleHighThreshold.
446 if (fps < kJitterScaleHighThreshold) {
447 jitterMS =
448 (1.0 / (kJitterScaleHighThreshold - kJitterScaleLowThreshold)) *
449 (fps - kJitterScaleLowThreshold) * jitterMS;
450 }
451 }
452
453 return static_cast<uint32_t>(jitterMS + 0.5);
454 }
455
456 bool VCMJitterEstimator::LowRateExperimentEnabled() {
457 if (low_rate_experiment_ == kInit) {
458 std::string group =
459 webrtc::field_trial::FindFullName("WebRTC-ReducedJitterDelay");
460 if (group == "Disabled") {
461 low_rate_experiment_ = kDisabled;
462 } else {
463 low_rate_experiment_ = kEnabled;
464 }
465 }
466 return low_rate_experiment_ == kEnabled ? true : false;
467 }
468
469 double VCMJitterEstimator::GetFrameRate() const {
470 if (fps_counter_.count() == 0)
471 return 0;
472
473 double fps = 1000000.0 / fps_counter_.ComputeMean();
474 // Sanity check.
475 assert(fps >= 0.0);
476 if (fps > kMaxFramerateEstimate) {
477 fps = kMaxFramerateEstimate;
478 }
479 return fps;
480 }
481
482 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698