blob: bf9f726c4255e9efaf87c729a47034e62191ac62 [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 "rtc_base/time/timestamp_extrapolator.h"
#include <algorithm>
namespace webrtc {
TimestampExtrapolator::TimestampExtrapolator(int64_t start_ms)
: _rwLock(RWLockWrapper::CreateRWLock()),
_startMs(0),
_firstTimestamp(0),
_wrapArounds(0),
_prevUnwrappedTimestamp(-1),
_prevWrapTimestamp(-1),
_lambda(1),
_firstAfterReset(true),
_packetCount(0),
_startUpFilterDelayInPackets(2),
_detectorAccumulatorPos(0),
_detectorAccumulatorNeg(0),
_alarmThreshold(60e3),
_accDrift(6600), // in timestamp ticks, i.e. 15 ms
_accMaxError(7000),
_pP11(1e10) {
Reset(start_ms);
}
TimestampExtrapolator::~TimestampExtrapolator() {
delete _rwLock;
}
void TimestampExtrapolator::Reset(int64_t start_ms) {
WriteLockScoped wl(*_rwLock);
_startMs = start_ms;
_prevMs = _startMs;
_firstTimestamp = 0;
_w[0] = 90.0;
_w[1] = 0;
_pP[0][0] = 1;
_pP[1][1] = _pP11;
_pP[0][1] = _pP[1][0] = 0;
_firstAfterReset = true;
_prevUnwrappedTimestamp = -1;
_prevWrapTimestamp = -1;
_wrapArounds = 0;
_packetCount = 0;
_detectorAccumulatorPos = 0;
_detectorAccumulatorNeg = 0;
}
void TimestampExtrapolator::Update(int64_t tMs, uint32_t ts90khz) {
_rwLock->AcquireLockExclusive();
if (tMs - _prevMs > 10e3) {
// Ten seconds without a complete frame.
// Reset the extrapolator
_rwLock->ReleaseLockExclusive();
Reset(tMs);
_rwLock->AcquireLockExclusive();
} else {
_prevMs = tMs;
}
// Remove offset to prevent badly scaled matrices
tMs -= _startMs;
CheckForWrapArounds(ts90khz);
int64_t unwrapped_ts90khz =
static_cast<int64_t>(ts90khz) +
_wrapArounds * ((static_cast<int64_t>(1) << 32) - 1);
if (_firstAfterReset) {
// Make an initial guess of the offset,
// should be almost correct since tMs - _startMs
// should about zero at this time.
_w[1] = -_w[0] * tMs;
_firstTimestamp = unwrapped_ts90khz;
_firstAfterReset = false;
}
double residual = (static_cast<double>(unwrapped_ts90khz) - _firstTimestamp) -
static_cast<double>(tMs) * _w[0] - _w[1];
if (DelayChangeDetection(residual) &&
_packetCount >= _startUpFilterDelayInPackets) {
// A sudden change of average network delay has been detected.
// Force the filter to adjust its offset parameter by changing
// the offset uncertainty. Don't do this during startup.
_pP[1][1] = _pP11;
}
if (_prevUnwrappedTimestamp >= 0 &&
unwrapped_ts90khz < _prevUnwrappedTimestamp) {
// Drop reordered frames.
_rwLock->ReleaseLockExclusive();
return;
}
// T = [t(k) 1]';
// that = T'*w;
// K = P*T/(lambda + T'*P*T);
double K[2];
K[0] = _pP[0][0] * tMs + _pP[0][1];
K[1] = _pP[1][0] * tMs + _pP[1][1];
double TPT = _lambda + tMs * K[0] + K[1];
K[0] /= TPT;
K[1] /= TPT;
// w = w + K*(ts(k) - that);
_w[0] = _w[0] + K[0] * residual;
_w[1] = _w[1] + K[1] * residual;
// P = 1/lambda*(P - K*T'*P);
double p00 =
1 / _lambda * (_pP[0][0] - (K[0] * tMs * _pP[0][0] + K[0] * _pP[1][0]));
double p01 =
1 / _lambda * (_pP[0][1] - (K[0] * tMs * _pP[0][1] + K[0] * _pP[1][1]));
_pP[1][0] =
1 / _lambda * (_pP[1][0] - (K[1] * tMs * _pP[0][0] + K[1] * _pP[1][0]));
_pP[1][1] =
1 / _lambda * (_pP[1][1] - (K[1] * tMs * _pP[0][1] + K[1] * _pP[1][1]));
_pP[0][0] = p00;
_pP[0][1] = p01;
_prevUnwrappedTimestamp = unwrapped_ts90khz;
if (_packetCount < _startUpFilterDelayInPackets) {
_packetCount++;
}
_rwLock->ReleaseLockExclusive();
}
int64_t TimestampExtrapolator::ExtrapolateLocalTime(uint32_t timestamp90khz) {
ReadLockScoped rl(*_rwLock);
int64_t localTimeMs = 0;
CheckForWrapArounds(timestamp90khz);
double unwrapped_ts90khz =
static_cast<double>(timestamp90khz) +
_wrapArounds * ((static_cast<int64_t>(1) << 32) - 1);
if (_packetCount == 0) {
localTimeMs = -1;
} else if (_packetCount < _startUpFilterDelayInPackets) {
localTimeMs =
_prevMs +
static_cast<int64_t>(
static_cast<double>(unwrapped_ts90khz - _prevUnwrappedTimestamp) /
90.0 +
0.5);
} else {
if (_w[0] < 1e-3) {
localTimeMs = _startMs;
} else {
double timestampDiff =
unwrapped_ts90khz - static_cast<double>(_firstTimestamp);
localTimeMs = static_cast<int64_t>(static_cast<double>(_startMs) +
(timestampDiff - _w[1]) / _w[0] + 0.5);
}
}
return localTimeMs;
}
// Investigates if the timestamp clock has overflowed since the last timestamp
// and keeps track of the number of wrap arounds since reset.
void TimestampExtrapolator::CheckForWrapArounds(uint32_t ts90khz) {
if (_prevWrapTimestamp == -1) {
_prevWrapTimestamp = ts90khz;
return;
}
if (ts90khz < _prevWrapTimestamp) {
// This difference will probably be less than -2^31 if we have had a wrap
// around (e.g. timestamp = 1, _previousTimestamp = 2^32 - 1). Since it is
// casted to a Word32, it should be positive.
if (static_cast<int32_t>(ts90khz - _prevWrapTimestamp) > 0) {
// Forward wrap around
_wrapArounds++;
}
} else {
// This difference will probably be less than -2^31 if we have had a
// backward wrap around. Since it is casted to a Word32, it should be
// positive.
if (static_cast<int32_t>(_prevWrapTimestamp - ts90khz) > 0) {
// Backward wrap around
_wrapArounds--;
}
}
_prevWrapTimestamp = ts90khz;
}
bool TimestampExtrapolator::DelayChangeDetection(double error) {
// CUSUM detection of sudden delay changes
error = (error > 0) ? std::min(error, _accMaxError)
: std::max(error, -_accMaxError);
_detectorAccumulatorPos =
std::max(_detectorAccumulatorPos + error - _accDrift, double{0});
_detectorAccumulatorNeg =
std::min(_detectorAccumulatorNeg + error + _accDrift, double{0});
if (_detectorAccumulatorPos > _alarmThreshold ||
_detectorAccumulatorNeg < -_alarmThreshold) {
// Alarm
_detectorAccumulatorPos = _detectorAccumulatorNeg = 0;
return true;
}
return false;
}
} // namespace webrtc