/*
|
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
*
|
* Use of this source code is governed by a BSD-style license
|
* that can be found in the LICENSE file in the root of the source
|
* tree. An additional intellectual property rights grant can be found
|
* in the file PATENTS. All contributing project authors may
|
* be found in the AUTHORS file in the root of the source tree.
|
*/
|
|
#include "webrtc/modules/video_coding/rtt_filter.h"
|
|
#include <math.h>
|
#include <stdlib.h>
|
#include <string.h>
|
|
#include "webrtc/modules/video_coding/internal_defines.h"
|
|
namespace webrtc {
|
|
VCMRttFilter::VCMRttFilter()
|
: _filtFactMax(35),
|
_jumpStdDevs(2.5),
|
_driftStdDevs(3.5),
|
_detectThreshold(kMaxDriftJumpCount) {
|
Reset();
|
}
|
|
VCMRttFilter& VCMRttFilter::operator=(const VCMRttFilter& rhs) {
|
if (this != &rhs) {
|
_gotNonZeroUpdate = rhs._gotNonZeroUpdate;
|
_avgRtt = rhs._avgRtt;
|
_varRtt = rhs._varRtt;
|
_maxRtt = rhs._maxRtt;
|
_filtFactCount = rhs._filtFactCount;
|
_jumpCount = rhs._jumpCount;
|
_driftCount = rhs._driftCount;
|
memcpy(_jumpBuf, rhs._jumpBuf, sizeof(_jumpBuf));
|
memcpy(_driftBuf, rhs._driftBuf, sizeof(_driftBuf));
|
}
|
return *this;
|
}
|
|
void VCMRttFilter::Reset() {
|
_gotNonZeroUpdate = false;
|
_avgRtt = 0;
|
_varRtt = 0;
|
_maxRtt = 0;
|
_filtFactCount = 1;
|
_jumpCount = 0;
|
_driftCount = 0;
|
memset(_jumpBuf, 0, kMaxDriftJumpCount);
|
memset(_driftBuf, 0, kMaxDriftJumpCount);
|
}
|
|
void VCMRttFilter::Update(int64_t rttMs) {
|
if (!_gotNonZeroUpdate) {
|
if (rttMs == 0) {
|
return;
|
}
|
_gotNonZeroUpdate = true;
|
}
|
|
// Sanity check
|
if (rttMs > 3000) {
|
rttMs = 3000;
|
}
|
|
double filtFactor = 0;
|
if (_filtFactCount > 1) {
|
filtFactor = static_cast<double>(_filtFactCount - 1) / _filtFactCount;
|
}
|
_filtFactCount++;
|
if (_filtFactCount > _filtFactMax) {
|
// This prevents filtFactor from going above
|
// (_filtFactMax - 1) / _filtFactMax,
|
// e.g., _filtFactMax = 50 => filtFactor = 49/50 = 0.98
|
_filtFactCount = _filtFactMax;
|
}
|
double oldAvg = _avgRtt;
|
double oldVar = _varRtt;
|
_avgRtt = filtFactor * _avgRtt + (1 - filtFactor) * rttMs;
|
_varRtt = filtFactor * _varRtt +
|
(1 - filtFactor) * (rttMs - _avgRtt) * (rttMs - _avgRtt);
|
_maxRtt = VCM_MAX(rttMs, _maxRtt);
|
if (!JumpDetection(rttMs) || !DriftDetection(rttMs)) {
|
// In some cases we don't want to update the statistics
|
_avgRtt = oldAvg;
|
_varRtt = oldVar;
|
}
|
}
|
|
bool VCMRttFilter::JumpDetection(int64_t rttMs) {
|
double diffFromAvg = _avgRtt - rttMs;
|
if (fabs(diffFromAvg) > _jumpStdDevs * sqrt(_varRtt)) {
|
int diffSign = (diffFromAvg >= 0) ? 1 : -1;
|
int jumpCountSign = (_jumpCount >= 0) ? 1 : -1;
|
if (diffSign != jumpCountSign) {
|
// Since the signs differ the samples currently
|
// in the buffer is useless as they represent a
|
// jump in a different direction.
|
_jumpCount = 0;
|
}
|
if (abs(_jumpCount) < kMaxDriftJumpCount) {
|
// Update the buffer used for the short time
|
// statistics.
|
// The sign of the diff is used for updating the counter since
|
// we want to use the same buffer for keeping track of when
|
// the RTT jumps down and up.
|
_jumpBuf[abs(_jumpCount)] = rttMs;
|
_jumpCount += diffSign;
|
}
|
if (abs(_jumpCount) >= _detectThreshold) {
|
// Detected an RTT jump
|
ShortRttFilter(_jumpBuf, abs(_jumpCount));
|
_filtFactCount = _detectThreshold + 1;
|
_jumpCount = 0;
|
} else {
|
return false;
|
}
|
} else {
|
_jumpCount = 0;
|
}
|
return true;
|
}
|
|
bool VCMRttFilter::DriftDetection(int64_t rttMs) {
|
if (_maxRtt - _avgRtt > _driftStdDevs * sqrt(_varRtt)) {
|
if (_driftCount < kMaxDriftJumpCount) {
|
// Update the buffer used for the short time
|
// statistics.
|
_driftBuf[_driftCount] = rttMs;
|
_driftCount++;
|
}
|
if (_driftCount >= _detectThreshold) {
|
// Detected an RTT drift
|
ShortRttFilter(_driftBuf, _driftCount);
|
_filtFactCount = _detectThreshold + 1;
|
_driftCount = 0;
|
}
|
} else {
|
_driftCount = 0;
|
}
|
return true;
|
}
|
|
void VCMRttFilter::ShortRttFilter(int64_t* buf, uint32_t length) {
|
if (length == 0) {
|
return;
|
}
|
_maxRtt = 0;
|
_avgRtt = 0;
|
for (uint32_t i = 0; i < length; i++) {
|
if (buf[i] > _maxRtt) {
|
_maxRtt = buf[i];
|
}
|
_avgRtt += buf[i];
|
}
|
_avgRtt = _avgRtt / static_cast<double>(length);
|
}
|
|
int64_t VCMRttFilter::RttMs() const {
|
return static_cast<int64_t>(_maxRtt + 0.5);
|
}
|
} // namespace webrtc
|