Migrate RemoteNtpTimeEstimator to more precise time representations
Bug: webrtc:13757 Change-Id: I880ab3cc6e4f72da587ae42ddca051332907c07f Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/261311 Commit-Queue: Danil Chapovalov <danilchap@webrtc.org> Reviewed-by: Jakob Ivarsson <jakobi@webrtc.org> Reviewed-by: Erik Språng <sprang@webrtc.org> Reviewed-by: Emil Lundmark <lndmrk@webrtc.org> Cr-Commit-Position: refs/heads/main@{#36817}
This commit is contained in:

committed by
WebRTC LUCI CQ

parent
14d01508be
commit
a154a15c97
@ -748,12 +748,13 @@ void ChannelReceive::ReceivedRTCPPacket(const uint8_t* data, size_t length) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
MutexLock lock(&ts_stats_lock_);
|
MutexLock lock(&ts_stats_lock_);
|
||||||
ntp_estimator_.UpdateRtcpTimestamp(rtt, ntp_secs, ntp_frac, rtp_timestamp);
|
ntp_estimator_.UpdateRtcpTimestamp(
|
||||||
absl::optional<int64_t> remote_to_local_clock_offset_ms =
|
TimeDelta::Millis(rtt), NtpTime(ntp_secs, ntp_frac), rtp_timestamp);
|
||||||
ntp_estimator_.EstimateRemoteToLocalClockOffsetMs();
|
absl::optional<int64_t> remote_to_local_clock_offset =
|
||||||
if (remote_to_local_clock_offset_ms.has_value()) {
|
ntp_estimator_.EstimateRemoteToLocalClockOffset();
|
||||||
|
if (remote_to_local_clock_offset.has_value()) {
|
||||||
capture_clock_offset_updater_.SetRemoteToLocalClockOffset(
|
capture_clock_offset_updater_.SetRemoteToLocalClockOffset(
|
||||||
Int64MsToQ32x32(*remote_to_local_clock_offset_ms));
|
*remote_to_local_clock_offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,7 +226,8 @@ void AudioIngress::ReceivedRTCPPacket(
|
|||||||
|
|
||||||
{
|
{
|
||||||
MutexLock lock(&lock_);
|
MutexLock lock(&lock_);
|
||||||
ntp_estimator_.UpdateRtcpTimestamp(rtt, ntp_secs, ntp_frac, rtp_timestamp);
|
ntp_estimator_.UpdateRtcpTimestamp(
|
||||||
|
TimeDelta::Millis(rtt), NtpTime(ntp_secs, ntp_frac), rtp_timestamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,10 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "absl/base/attributes.h"
|
||||||
#include "absl/types/optional.h"
|
#include "absl/types/optional.h"
|
||||||
|
#include "api/units/time_delta.h"
|
||||||
|
#include "api/units/timestamp.h"
|
||||||
#include "rtc_base/numerics/moving_median_filter.h"
|
#include "rtc_base/numerics/moving_median_filter.h"
|
||||||
#include "system_wrappers/include/rtp_to_ntp_estimator.h"
|
#include "system_wrappers/include/rtp_to_ntp_estimator.h"
|
||||||
|
|
||||||
@ -28,32 +31,63 @@ class Clock;
|
|||||||
class RemoteNtpTimeEstimator {
|
class RemoteNtpTimeEstimator {
|
||||||
public:
|
public:
|
||||||
explicit RemoteNtpTimeEstimator(Clock* clock);
|
explicit RemoteNtpTimeEstimator(Clock* clock);
|
||||||
|
|
||||||
~RemoteNtpTimeEstimator();
|
|
||||||
|
|
||||||
RemoteNtpTimeEstimator(const RemoteNtpTimeEstimator&) = delete;
|
RemoteNtpTimeEstimator(const RemoteNtpTimeEstimator&) = delete;
|
||||||
RemoteNtpTimeEstimator& operator=(const RemoteNtpTimeEstimator&) = delete;
|
RemoteNtpTimeEstimator& operator=(const RemoteNtpTimeEstimator&) = delete;
|
||||||
|
~RemoteNtpTimeEstimator() = default;
|
||||||
|
|
||||||
// Updates the estimator with round trip time `rtt`, NTP seconds `ntp_secs`,
|
// Updates the estimator with round trip time `rtt`, NTP seconds `ntp_secs`,
|
||||||
// NTP fraction `ntp_frac` and RTP timestamp `rtp_timestamp`.
|
// NTP fraction `ntp_frac` and RTP timestamp `rtp_timestamp`.
|
||||||
|
ABSL_DEPRECATED(
|
||||||
|
"Use UpdateRtcpTimestamp with strict time types: TimeDelta and NtpTime.")
|
||||||
bool UpdateRtcpTimestamp(int64_t rtt,
|
bool UpdateRtcpTimestamp(int64_t rtt,
|
||||||
uint32_t ntp_secs,
|
uint32_t ntp_secs,
|
||||||
uint32_t ntp_frac,
|
uint32_t ntp_frac,
|
||||||
|
uint32_t rtp_timestamp) {
|
||||||
|
return UpdateRtcpTimestamp(TimeDelta::Millis(rtt),
|
||||||
|
NtpTime(ntp_secs, ntp_frac), rtp_timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UpdateRtcpTimestamp(TimeDelta rtt,
|
||||||
|
NtpTime sender_send_time,
|
||||||
uint32_t rtp_timestamp);
|
uint32_t rtp_timestamp);
|
||||||
|
|
||||||
// Estimates the NTP timestamp in local timebase from `rtp_timestamp`.
|
// Estimates the NTP timestamp in local timebase from `rtp_timestamp`.
|
||||||
// Returns the NTP timestamp in ms when success. -1 if failed.
|
// Returns the NTP timestamp in ms when success. -1 if failed.
|
||||||
int64_t Estimate(uint32_t rtp_timestamp);
|
int64_t Estimate(uint32_t rtp_timestamp) {
|
||||||
|
NtpTime ntp_time = EstimateNtp(rtp_timestamp);
|
||||||
|
if (!ntp_time.Valid()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return ntp_time.ToMs();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estimates the NTP timestamp in local timebase from `rtp_timestamp`.
|
||||||
|
// Returns invalid NtpTime (i.e. NtpTime(0)) on failure.
|
||||||
|
NtpTime EstimateNtp(uint32_t rtp_timestamp);
|
||||||
|
|
||||||
// Estimates the offset, in milliseconds, between the remote clock and the
|
// Estimates the offset, in milliseconds, between the remote clock and the
|
||||||
// local one. This is equal to local NTP clock - remote NTP clock.
|
// local one. This is equal to local NTP clock - remote NTP clock.
|
||||||
absl::optional<int64_t> EstimateRemoteToLocalClockOffsetMs();
|
ABSL_DEPRECATED("Use EstimateRemoteToLocalClockOffset.")
|
||||||
|
absl::optional<int64_t> EstimateRemoteToLocalClockOffsetMs() {
|
||||||
|
if (absl::optional<int64_t> offset = EstimateRemoteToLocalClockOffset()) {
|
||||||
|
return (*offset * 1'000) / (int64_t{1} << 32);
|
||||||
|
}
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estimates the offset between the remote clock and the
|
||||||
|
// local one. This is equal to local NTP clock - remote NTP clock.
|
||||||
|
// The offset is returned in ntp time resolution, i.e. 1/2^32 sec ~= 0.2 ns.
|
||||||
|
// Returns nullopt on failure.
|
||||||
|
absl::optional<int64_t> EstimateRemoteToLocalClockOffset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Clock* clock_;
|
Clock* clock_;
|
||||||
|
// Offset is measured with the same precision as NtpTime: in 1/2^32 seconds ~=
|
||||||
|
// 0.2 ns.
|
||||||
MovingMedianFilter<int64_t> ntp_clocks_offset_estimator_;
|
MovingMedianFilter<int64_t> ntp_clocks_offset_estimator_;
|
||||||
RtpToNtpEstimator rtp_to_ntp_;
|
RtpToNtpEstimator rtp_to_ntp_;
|
||||||
int64_t last_timing_log_ms_;
|
Timestamp last_timing_log_ = Timestamp::MinusInfinity();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -22,25 +22,37 @@ namespace webrtc {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr int kMinimumNumberOfSamples = 2;
|
constexpr int kMinimumNumberOfSamples = 2;
|
||||||
constexpr int kTimingLogIntervalMs = 10000;
|
constexpr TimeDelta kTimingLogInterval = TimeDelta::Seconds(10);
|
||||||
constexpr int kClocksOffsetSmoothingWindow = 100;
|
constexpr int kClocksOffsetSmoothingWindow = 100;
|
||||||
|
|
||||||
|
// Subtracts two NtpTime values keeping maximum precision.
|
||||||
|
int64_t Subtract(NtpTime minuend, NtpTime subtrahend) {
|
||||||
|
uint64_t a = static_cast<uint64_t>(minuend);
|
||||||
|
uint64_t b = static_cast<uint64_t>(subtrahend);
|
||||||
|
return a >= b ? static_cast<int64_t>(a - b) : -static_cast<int64_t>(b - a);
|
||||||
|
}
|
||||||
|
|
||||||
|
NtpTime Add(NtpTime lhs, int64_t rhs) {
|
||||||
|
uint64_t result = static_cast<uint64_t>(lhs);
|
||||||
|
if (rhs >= 0) {
|
||||||
|
result += static_cast<uint64_t>(rhs);
|
||||||
|
} else {
|
||||||
|
result -= static_cast<uint64_t>(-rhs);
|
||||||
|
}
|
||||||
|
return NtpTime(result);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// TODO(wu): Refactor this class so that it can be shared with
|
// TODO(wu): Refactor this class so that it can be shared with
|
||||||
// vie_sync_module.cc.
|
// vie_sync_module.cc.
|
||||||
RemoteNtpTimeEstimator::RemoteNtpTimeEstimator(Clock* clock)
|
RemoteNtpTimeEstimator::RemoteNtpTimeEstimator(Clock* clock)
|
||||||
: clock_(clock),
|
: clock_(clock),
|
||||||
ntp_clocks_offset_estimator_(kClocksOffsetSmoothingWindow),
|
ntp_clocks_offset_estimator_(kClocksOffsetSmoothingWindow) {}
|
||||||
last_timing_log_ms_(-1) {}
|
|
||||||
|
|
||||||
RemoteNtpTimeEstimator::~RemoteNtpTimeEstimator() {}
|
bool RemoteNtpTimeEstimator::UpdateRtcpTimestamp(TimeDelta rtt,
|
||||||
|
NtpTime sender_send_time,
|
||||||
bool RemoteNtpTimeEstimator::UpdateRtcpTimestamp(int64_t rtt,
|
|
||||||
uint32_t ntp_secs,
|
|
||||||
uint32_t ntp_frac,
|
|
||||||
uint32_t rtp_timestamp) {
|
uint32_t rtp_timestamp) {
|
||||||
NtpTime sender_send_time(ntp_secs, ntp_frac);
|
|
||||||
switch (rtp_to_ntp_.UpdateMeasurements(sender_send_time, rtp_timestamp)) {
|
switch (rtp_to_ntp_.UpdateMeasurements(sender_send_time, rtp_timestamp)) {
|
||||||
case RtpToNtpEstimator::kInvalidMeasurement:
|
case RtpToNtpEstimator::kInvalidMeasurement:
|
||||||
return false;
|
return false;
|
||||||
@ -51,42 +63,42 @@ bool RemoteNtpTimeEstimator::UpdateRtcpTimestamp(int64_t rtt,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assume connection is symmetric and thus time to deliver the packet is half
|
||||||
|
// the round trip time.
|
||||||
|
int64_t deliver_time_ntp = ToNtpUnits(rtt) / 2;
|
||||||
|
|
||||||
// Update extrapolator with the new arrival time.
|
// Update extrapolator with the new arrival time.
|
||||||
// The extrapolator assumes the ntp time.
|
NtpTime receiver_arrival_time = clock_->CurrentNtpTime();
|
||||||
int64_t receiver_arrival_time_ms = clock_->CurrentNtpInMilliseconds();
|
|
||||||
int64_t sender_arrival_time_ms = sender_send_time.ToMs() + rtt / 2;
|
|
||||||
int64_t remote_to_local_clocks_offset =
|
int64_t remote_to_local_clocks_offset =
|
||||||
receiver_arrival_time_ms - sender_arrival_time_ms;
|
Subtract(receiver_arrival_time, sender_send_time) - deliver_time_ntp;
|
||||||
ntp_clocks_offset_estimator_.Insert(remote_to_local_clocks_offset);
|
ntp_clocks_offset_estimator_.Insert(remote_to_local_clocks_offset);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t RemoteNtpTimeEstimator::Estimate(uint32_t rtp_timestamp) {
|
NtpTime RemoteNtpTimeEstimator::EstimateNtp(uint32_t rtp_timestamp) {
|
||||||
NtpTime sender_capture = rtp_to_ntp_.Estimate(rtp_timestamp);
|
NtpTime sender_capture = rtp_to_ntp_.Estimate(rtp_timestamp);
|
||||||
if (!sender_capture.Valid()) {
|
if (!sender_capture.Valid()) {
|
||||||
return -1;
|
return sender_capture;
|
||||||
}
|
}
|
||||||
int64_t sender_capture_ntp_ms = sender_capture.ToMs();
|
|
||||||
|
|
||||||
int64_t remote_to_local_clocks_offset =
|
int64_t remote_to_local_clocks_offset =
|
||||||
ntp_clocks_offset_estimator_.GetFilteredValue();
|
ntp_clocks_offset_estimator_.GetFilteredValue();
|
||||||
int64_t receiver_capture_ntp_ms =
|
NtpTime receiver_capture = Add(sender_capture, remote_to_local_clocks_offset);
|
||||||
sender_capture_ntp_ms + remote_to_local_clocks_offset;
|
|
||||||
|
|
||||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
Timestamp now = clock_->CurrentTime();
|
||||||
if (now_ms - last_timing_log_ms_ > kTimingLogIntervalMs) {
|
if (now - last_timing_log_ > kTimingLogInterval) {
|
||||||
RTC_LOG(LS_INFO) << "RTP timestamp: " << rtp_timestamp
|
RTC_LOG(LS_INFO) << "RTP timestamp: " << rtp_timestamp
|
||||||
<< " in NTP clock: " << sender_capture_ntp_ms
|
<< " in NTP clock: " << sender_capture.ToMs()
|
||||||
<< " estimated time in receiver NTP clock: "
|
<< " estimated time in receiver NTP clock: "
|
||||||
<< receiver_capture_ntp_ms;
|
<< receiver_capture.ToMs();
|
||||||
last_timing_log_ms_ = now_ms;
|
last_timing_log_ = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
return receiver_capture_ntp_ms;
|
return receiver_capture;
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::optional<int64_t>
|
absl::optional<int64_t>
|
||||||
RemoteNtpTimeEstimator::EstimateRemoteToLocalClockOffsetMs() {
|
RemoteNtpTimeEstimator::EstimateRemoteToLocalClockOffset() {
|
||||||
if (ntp_clocks_offset_estimator_.GetNumberOfSamplesStored() <
|
if (ntp_clocks_offset_estimator_.GetNumberOfSamplesStored() <
|
||||||
kMinimumNumberOfSamples) {
|
kMinimumNumberOfSamples) {
|
||||||
return absl::nullopt;
|
return absl::nullopt;
|
||||||
|
@ -9,32 +9,29 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "modules/rtp_rtcp/include/remote_ntp_time_estimator.h"
|
#include "modules/rtp_rtcp/include/remote_ntp_time_estimator.h"
|
||||||
|
|
||||||
#include "absl/types/optional.h"
|
#include "absl/types/optional.h"
|
||||||
|
#include "modules/rtp_rtcp/source/time_util.h"
|
||||||
#include "system_wrappers/include/clock.h"
|
#include "system_wrappers/include/clock.h"
|
||||||
#include "system_wrappers/include/ntp_time.h"
|
#include "system_wrappers/include/ntp_time.h"
|
||||||
#include "test/gmock.h"
|
#include "test/gmock.h"
|
||||||
#include "test/gtest.h"
|
#include "test/gtest.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
namespace {
|
||||||
|
|
||||||
constexpr int64_t kTestRtt = 10;
|
constexpr TimeDelta kTestRtt = TimeDelta::Millis(10);
|
||||||
constexpr int64_t kLocalClockInitialTimeMs = 123;
|
constexpr Timestamp kLocalClockInitialTime = Timestamp::Millis(123);
|
||||||
constexpr int64_t kRemoteClockInitialTimeMs = 345;
|
constexpr Timestamp kRemoteClockInitialTime = Timestamp::Millis(373);
|
||||||
constexpr uint32_t kTimestampOffset = 567;
|
constexpr uint32_t kTimestampOffset = 567;
|
||||||
constexpr int64_t kRemoteToLocalClockOffsetMs =
|
constexpr int64_t kRemoteToLocalClockOffsetNtp =
|
||||||
kLocalClockInitialTimeMs - kRemoteClockInitialTimeMs;
|
ToNtpUnits(kLocalClockInitialTime - kRemoteClockInitialTime);
|
||||||
|
|
||||||
class RemoteNtpTimeEstimatorTest : public ::testing::Test {
|
class RemoteNtpTimeEstimatorTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
RemoteNtpTimeEstimatorTest()
|
void AdvanceTime(TimeDelta delta) {
|
||||||
: local_clock_(kLocalClockInitialTimeMs * 1000),
|
local_clock_.AdvanceTime(delta);
|
||||||
remote_clock_(kRemoteClockInitialTimeMs * 1000),
|
remote_clock_.AdvanceTime(delta);
|
||||||
estimator_(new RemoteNtpTimeEstimator(&local_clock_)) {}
|
|
||||||
~RemoteNtpTimeEstimatorTest() override = default;
|
|
||||||
|
|
||||||
void AdvanceTimeMilliseconds(int64_t ms) {
|
|
||||||
local_clock_.AdvanceTimeMilliseconds(ms);
|
|
||||||
remote_clock_.AdvanceTimeMilliseconds(ms);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t GetRemoteTimestamp() {
|
uint32_t GetRemoteTimestamp() {
|
||||||
@ -42,107 +39,90 @@ class RemoteNtpTimeEstimatorTest : public ::testing::Test {
|
|||||||
kTimestampOffset;
|
kTimestampOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
NtpTime GetRemoteNtpTime() { return remote_clock_.CurrentNtpTime(); }
|
|
||||||
|
|
||||||
void SendRtcpSr() {
|
void SendRtcpSr() {
|
||||||
uint32_t rtcp_timestamp = GetRemoteTimestamp();
|
uint32_t rtcp_timestamp = GetRemoteTimestamp();
|
||||||
NtpTime ntp = GetRemoteNtpTime();
|
NtpTime ntp = remote_clock_.CurrentNtpTime();
|
||||||
|
|
||||||
AdvanceTimeMilliseconds(kTestRtt / 2);
|
AdvanceTime(kTestRtt / 2);
|
||||||
ReceiveRtcpSr(kTestRtt, rtcp_timestamp, ntp.seconds(), ntp.fractions());
|
RTC_DCHECK(estimator_.UpdateRtcpTimestamp(kTestRtt, ntp, rtcp_timestamp));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendRtcpSrInaccurately(int64_t ntp_error_ms,
|
void SendRtcpSrInaccurately(TimeDelta ntp_error, TimeDelta networking_delay) {
|
||||||
int64_t networking_delay_ms) {
|
|
||||||
uint32_t rtcp_timestamp = GetRemoteTimestamp();
|
uint32_t rtcp_timestamp = GetRemoteTimestamp();
|
||||||
int64_t ntp_error_fractions =
|
int64_t ntp_error_fractions = ToNtpUnits(ntp_error);
|
||||||
ntp_error_ms * static_cast<int64_t>(NtpTime::kFractionsPerSecond) /
|
NtpTime ntp(static_cast<uint64_t>(remote_clock_.CurrentNtpTime()) +
|
||||||
1000;
|
|
||||||
NtpTime ntp(static_cast<uint64_t>(GetRemoteNtpTime()) +
|
|
||||||
ntp_error_fractions);
|
ntp_error_fractions);
|
||||||
AdvanceTimeMilliseconds(kTestRtt / 2 + networking_delay_ms);
|
AdvanceTime(kTestRtt / 2 + networking_delay);
|
||||||
ReceiveRtcpSr(kTestRtt, rtcp_timestamp, ntp.seconds(), ntp.fractions());
|
RTC_DCHECK(estimator_.UpdateRtcpTimestamp(kTestRtt, ntp, rtcp_timestamp));
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateRtcpTimestamp(int64_t rtt,
|
SimulatedClock local_clock_{kLocalClockInitialTime};
|
||||||
uint32_t ntp_secs,
|
SimulatedClock remote_clock_{kRemoteClockInitialTime};
|
||||||
uint32_t ntp_frac,
|
RemoteNtpTimeEstimator estimator_{&local_clock_};
|
||||||
uint32_t rtp_timestamp,
|
|
||||||
bool expected_result) {
|
|
||||||
EXPECT_EQ(expected_result, estimator_->UpdateRtcpTimestamp(
|
|
||||||
rtt, ntp_secs, ntp_frac, rtp_timestamp));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReceiveRtcpSr(int64_t rtt,
|
|
||||||
uint32_t rtcp_timestamp,
|
|
||||||
uint32_t ntp_seconds,
|
|
||||||
uint32_t ntp_fractions) {
|
|
||||||
UpdateRtcpTimestamp(rtt, ntp_seconds, ntp_fractions, rtcp_timestamp, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
SimulatedClock local_clock_;
|
|
||||||
SimulatedClock remote_clock_;
|
|
||||||
std::unique_ptr<RemoteNtpTimeEstimator> estimator_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(RemoteNtpTimeEstimatorTest, Estimate) {
|
TEST_F(RemoteNtpTimeEstimatorTest, FailsWithoutValidNtpTime) {
|
||||||
// Failed without valid NTP.
|
EXPECT_FALSE(
|
||||||
UpdateRtcpTimestamp(kTestRtt, 0, 0, 0, false);
|
estimator_.UpdateRtcpTimestamp(kTestRtt, NtpTime(), /*rtp_timestamp=*/0));
|
||||||
|
}
|
||||||
|
|
||||||
AdvanceTimeMilliseconds(1000);
|
TEST_F(RemoteNtpTimeEstimatorTest, Estimate) {
|
||||||
// Remote peer sends first RTCP SR.
|
// Remote peer sends first RTCP SR.
|
||||||
SendRtcpSr();
|
SendRtcpSr();
|
||||||
|
|
||||||
// Remote sends a RTP packet.
|
// Remote sends a RTP packet.
|
||||||
AdvanceTimeMilliseconds(15);
|
AdvanceTime(TimeDelta::Millis(15));
|
||||||
uint32_t rtp_timestamp = GetRemoteTimestamp();
|
uint32_t rtp_timestamp = GetRemoteTimestamp();
|
||||||
int64_t capture_ntp_time_ms = local_clock_.CurrentNtpInMilliseconds();
|
int64_t capture_ntp_time_ms = local_clock_.CurrentNtpInMilliseconds();
|
||||||
|
|
||||||
// Local peer needs at least 2 RTCP SR to calculate the capture time.
|
// Local peer needs at least 2 RTCP SR to calculate the capture time.
|
||||||
const int64_t kNotEnoughRtcpSr = -1;
|
const int64_t kNotEnoughRtcpSr = -1;
|
||||||
EXPECT_EQ(kNotEnoughRtcpSr, estimator_->Estimate(rtp_timestamp));
|
EXPECT_EQ(kNotEnoughRtcpSr, estimator_.Estimate(rtp_timestamp));
|
||||||
EXPECT_EQ(absl::nullopt, estimator_->EstimateRemoteToLocalClockOffsetMs());
|
EXPECT_EQ(estimator_.EstimateRemoteToLocalClockOffset(), absl::nullopt);
|
||||||
|
|
||||||
AdvanceTimeMilliseconds(800);
|
AdvanceTime(TimeDelta::Millis(800));
|
||||||
// Remote sends second RTCP SR.
|
// Remote sends second RTCP SR.
|
||||||
SendRtcpSr();
|
SendRtcpSr();
|
||||||
|
|
||||||
// Local peer gets enough RTCP SR to calculate the capture time.
|
// Local peer gets enough RTCP SR to calculate the capture time.
|
||||||
EXPECT_EQ(capture_ntp_time_ms, estimator_->Estimate(rtp_timestamp));
|
EXPECT_EQ(capture_ntp_time_ms, estimator_.Estimate(rtp_timestamp));
|
||||||
EXPECT_EQ(kRemoteToLocalClockOffsetMs,
|
EXPECT_EQ(estimator_.EstimateRemoteToLocalClockOffset(),
|
||||||
estimator_->EstimateRemoteToLocalClockOffsetMs());
|
kRemoteToLocalClockOffsetNtp);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RemoteNtpTimeEstimatorTest, AveragesErrorsOut) {
|
TEST_F(RemoteNtpTimeEstimatorTest, AveragesErrorsOut) {
|
||||||
// Remote peer sends first 10 RTCP SR without errors.
|
// Remote peer sends first 10 RTCP SR without errors.
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
AdvanceTimeMilliseconds(1000);
|
AdvanceTime(TimeDelta::Seconds(1));
|
||||||
SendRtcpSr();
|
SendRtcpSr();
|
||||||
}
|
}
|
||||||
|
|
||||||
AdvanceTimeMilliseconds(150);
|
AdvanceTime(TimeDelta::Millis(150));
|
||||||
uint32_t rtp_timestamp = GetRemoteTimestamp();
|
uint32_t rtp_timestamp = GetRemoteTimestamp();
|
||||||
int64_t capture_ntp_time_ms = local_clock_.CurrentNtpInMilliseconds();
|
int64_t capture_ntp_time_ms = local_clock_.CurrentNtpInMilliseconds();
|
||||||
// Local peer gets enough RTCP SR to calculate the capture time.
|
// Local peer gets enough RTCP SR to calculate the capture time.
|
||||||
EXPECT_EQ(capture_ntp_time_ms, estimator_->Estimate(rtp_timestamp));
|
EXPECT_EQ(capture_ntp_time_ms, estimator_.Estimate(rtp_timestamp));
|
||||||
EXPECT_EQ(kRemoteToLocalClockOffsetMs,
|
EXPECT_EQ(kRemoteToLocalClockOffsetNtp,
|
||||||
estimator_->EstimateRemoteToLocalClockOffsetMs());
|
estimator_.EstimateRemoteToLocalClockOffset());
|
||||||
|
|
||||||
// Remote sends corrupted RTCP SRs
|
// Remote sends corrupted RTCP SRs
|
||||||
AdvanceTimeMilliseconds(1000);
|
AdvanceTime(TimeDelta::Seconds(1));
|
||||||
SendRtcpSrInaccurately(/*ntp_error_ms=*/2, /*networking_delay_ms=*/-1);
|
SendRtcpSrInaccurately(/*ntp_error=*/TimeDelta::Millis(2),
|
||||||
AdvanceTimeMilliseconds(1000);
|
/*networking_delay=*/TimeDelta::Millis(-1));
|
||||||
SendRtcpSrInaccurately(/*ntp_error_ms=*/-2, /*networking_delay_ms=*/1);
|
AdvanceTime(TimeDelta::Seconds(1));
|
||||||
|
SendRtcpSrInaccurately(/*ntp_error=*/TimeDelta::Millis(-2),
|
||||||
|
/*networking_delay=*/TimeDelta::Millis(1));
|
||||||
|
|
||||||
// New RTP packet to estimate timestamp.
|
// New RTP packet to estimate timestamp.
|
||||||
AdvanceTimeMilliseconds(150);
|
AdvanceTime(TimeDelta::Millis(150));
|
||||||
rtp_timestamp = GetRemoteTimestamp();
|
rtp_timestamp = GetRemoteTimestamp();
|
||||||
capture_ntp_time_ms = local_clock_.CurrentNtpInMilliseconds();
|
capture_ntp_time_ms = local_clock_.CurrentNtpInMilliseconds();
|
||||||
|
|
||||||
// Errors should be averaged out.
|
// Errors should be averaged out.
|
||||||
EXPECT_EQ(capture_ntp_time_ms, estimator_->Estimate(rtp_timestamp));
|
EXPECT_EQ(capture_ntp_time_ms, estimator_.Estimate(rtp_timestamp));
|
||||||
EXPECT_EQ(kRemoteToLocalClockOffsetMs,
|
EXPECT_EQ(kRemoteToLocalClockOffsetNtp,
|
||||||
estimator_->EstimateRemoteToLocalClockOffsetMs());
|
estimator_.EstimateRemoteToLocalClockOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -34,6 +34,14 @@ inline uint32_t CompactNtp(NtpTime ntp) {
|
|||||||
// Negative values converted to 0, Overlarge values converted to max uint32_t.
|
// Negative values converted to 0, Overlarge values converted to max uint32_t.
|
||||||
uint32_t SaturatedToCompactNtp(TimeDelta delta);
|
uint32_t SaturatedToCompactNtp(TimeDelta delta);
|
||||||
|
|
||||||
|
// Convert interval to the NTP time resolution (1/2^32 seconds ~= 0.2 ns).
|
||||||
|
inline constexpr int64_t ToNtpUnits(TimeDelta delta) {
|
||||||
|
// For better precision `delta` is taken with best TimeDelta precision (us),
|
||||||
|
// then multiplaction and conversion to seconds are swapped to avoid float
|
||||||
|
// arithmetic.
|
||||||
|
return (delta.us() * (int64_t{1} << 32)) / 1'000'000;
|
||||||
|
}
|
||||||
|
|
||||||
// Converts interval from compact ntp (1/2^16 seconds) resolution to TimeDelta.
|
// Converts interval from compact ntp (1/2^16 seconds) resolution to TimeDelta.
|
||||||
// This interval can be up to ~9.1 hours (2^15 seconds).
|
// This interval can be up to ~9.1 hours (2^15 seconds).
|
||||||
// Values close to 2^16 seconds are considered negative and are converted to
|
// Values close to 2^16 seconds are considered negative and are converted to
|
||||||
|
@ -95,4 +95,20 @@ TEST(TimeUtilTest, SaturatedToCompactNtp) {
|
|||||||
5'515, 16);
|
5'515, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(TimeUtilTest, ToNtpUnits) {
|
||||||
|
EXPECT_EQ(ToNtpUnits(TimeDelta::Zero()), 0);
|
||||||
|
EXPECT_EQ(ToNtpUnits(TimeDelta::Seconds(1)), int64_t{1} << 32);
|
||||||
|
EXPECT_EQ(ToNtpUnits(TimeDelta::Seconds(-1)), -(int64_t{1} << 32));
|
||||||
|
|
||||||
|
EXPECT_EQ(ToNtpUnits(TimeDelta::Millis(500)), int64_t{1} << 31);
|
||||||
|
EXPECT_EQ(ToNtpUnits(TimeDelta::Millis(-1'500)), -(int64_t{3} << 31));
|
||||||
|
|
||||||
|
// Smallest TimeDelta that can be converted without precision loss.
|
||||||
|
EXPECT_EQ(ToNtpUnits(TimeDelta::Micros(15'625)), int64_t{1} << 26);
|
||||||
|
|
||||||
|
// 1 us ~= 4'294.97 NTP units. ToNtpUnits makes no rounding promises.
|
||||||
|
EXPECT_GE(ToNtpUnits(TimeDelta::Micros(1)), 4'294);
|
||||||
|
EXPECT_LE(ToNtpUnits(TimeDelta::Micros(1)), 4'295);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -1122,12 +1122,13 @@ bool RtpVideoStreamReceiver::DeliverRtcp(const uint8_t* rtcp_packet,
|
|||||||
clock_->CurrentNtpInMilliseconds() - received_ntp.ToMs();
|
clock_->CurrentNtpInMilliseconds() - received_ntp.ToMs();
|
||||||
// Don't use old SRs to estimate time.
|
// Don't use old SRs to estimate time.
|
||||||
if (time_since_received <= 1) {
|
if (time_since_received <= 1) {
|
||||||
ntp_estimator_.UpdateRtcpTimestamp(rtt, ntp_secs, ntp_frac, rtp_timestamp);
|
ntp_estimator_.UpdateRtcpTimestamp(
|
||||||
absl::optional<int64_t> remote_to_local_clock_offset_ms =
|
TimeDelta::Millis(rtt), NtpTime(ntp_secs, ntp_frac), rtp_timestamp);
|
||||||
ntp_estimator_.EstimateRemoteToLocalClockOffsetMs();
|
absl::optional<int64_t> remote_to_local_clock_offset =
|
||||||
if (remote_to_local_clock_offset_ms.has_value()) {
|
ntp_estimator_.EstimateRemoteToLocalClockOffset();
|
||||||
|
if (remote_to_local_clock_offset.has_value()) {
|
||||||
capture_clock_offset_updater_.SetRemoteToLocalClockOffset(
|
capture_clock_offset_updater_.SetRemoteToLocalClockOffset(
|
||||||
Int64MsToQ32x32(*remote_to_local_clock_offset_ms));
|
*remote_to_local_clock_offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1042,12 +1042,13 @@ bool RtpVideoStreamReceiver2::DeliverRtcp(const uint8_t* rtcp_packet,
|
|||||||
clock_->CurrentNtpInMilliseconds() - received_ntp.ToMs();
|
clock_->CurrentNtpInMilliseconds() - received_ntp.ToMs();
|
||||||
// Don't use old SRs to estimate time.
|
// Don't use old SRs to estimate time.
|
||||||
if (time_since_received <= 1) {
|
if (time_since_received <= 1) {
|
||||||
ntp_estimator_.UpdateRtcpTimestamp(rtt, ntp_secs, ntp_frac, rtp_timestamp);
|
ntp_estimator_.UpdateRtcpTimestamp(
|
||||||
absl::optional<int64_t> remote_to_local_clock_offset_ms =
|
TimeDelta::Millis(rtt), NtpTime(ntp_secs, ntp_frac), rtp_timestamp);
|
||||||
ntp_estimator_.EstimateRemoteToLocalClockOffsetMs();
|
absl::optional<int64_t> remote_to_local_clock_offset =
|
||||||
if (remote_to_local_clock_offset_ms.has_value()) {
|
ntp_estimator_.EstimateRemoteToLocalClockOffset();
|
||||||
|
if (remote_to_local_clock_offset.has_value()) {
|
||||||
capture_clock_offset_updater_.SetRemoteToLocalClockOffset(
|
capture_clock_offset_updater_.SetRemoteToLocalClockOffset(
|
||||||
Int64MsToQ32x32(*remote_to_local_clock_offset_ms));
|
*remote_to_local_clock_offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user