blob: 742f70f1c10a9d21e180a30dc07645cde5bf6a77 [file] [log] [blame]
/*
* 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