summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/rtc_base/time
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/libwebrtc/rtc_base/time
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/rtc_base/time')
-rw-r--r--third_party/libwebrtc/rtc_base/time/BUILD.gn41
-rw-r--r--third_party/libwebrtc/rtc_base/time/timestamp_extrapolator.cc169
-rw-r--r--third_party/libwebrtc/rtc_base/time/timestamp_extrapolator.h48
-rw-r--r--third_party/libwebrtc/rtc_base/time/timestamp_extrapolator_gn/moz.build201
-rw-r--r--third_party/libwebrtc/rtc_base/time/timestamp_extrapolator_unittest.cc208
5 files changed, 667 insertions, 0 deletions
diff --git a/third_party/libwebrtc/rtc_base/time/BUILD.gn b/third_party/libwebrtc/rtc_base/time/BUILD.gn
new file mode 100644
index 0000000000..bac649e1d7
--- /dev/null
+++ b/third_party/libwebrtc/rtc_base/time/BUILD.gn
@@ -0,0 +1,41 @@
+# Copyright (c) 2018 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.
+
+import("../../webrtc.gni")
+if (is_android) {
+ import("//build/config/android/config.gni")
+ import("//build/config/android/rules.gni")
+}
+
+rtc_library("timestamp_extrapolator") {
+ sources = [
+ "timestamp_extrapolator.cc",
+ "timestamp_extrapolator.h",
+ ]
+ deps = [
+ "../../api/units:timestamp",
+ "../../modules:module_api_public",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
+}
+
+if (rtc_include_tests) {
+ rtc_library("timestamp_extrapolator_unittests") {
+ testonly = true
+ sources = [ "timestamp_extrapolator_unittest.cc" ]
+ deps = [
+ ":timestamp_extrapolator",
+ "../../api/units:frequency",
+ "../../api/units:time_delta",
+ "../../api/units:timestamp",
+ "../../system_wrappers",
+ "../../test:test_support",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
+ }
+}
diff --git a/third_party/libwebrtc/rtc_base/time/timestamp_extrapolator.cc b/third_party/libwebrtc/rtc_base/time/timestamp_extrapolator.cc
new file mode 100644
index 0000000000..91604d0e5a
--- /dev/null
+++ b/third_party/libwebrtc/rtc_base/time/timestamp_extrapolator.cc
@@ -0,0 +1,169 @@
+/*
+ * 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>
+
+#include "absl/types/optional.h"
+#include "modules/include/module_common_types_public.h"
+
+namespace webrtc {
+
+namespace {
+
+constexpr double kLambda = 1;
+constexpr uint32_t kStartUpFilterDelayInPackets = 2;
+constexpr double kAlarmThreshold = 60e3;
+// in timestamp ticks, i.e. 15 ms
+constexpr double kAccDrift = 6600;
+constexpr double kAccMaxError = 7000;
+constexpr double kP11 = 1e10;
+
+} // namespace
+
+TimestampExtrapolator::TimestampExtrapolator(Timestamp start)
+ : start_(Timestamp::Zero()),
+ prev_(Timestamp::Zero()),
+ packet_count_(0),
+ detector_accumulator_pos_(0),
+ detector_accumulator_neg_(0) {
+ Reset(start);
+}
+
+void TimestampExtrapolator::Reset(Timestamp start) {
+ start_ = start;
+ prev_ = start_;
+ first_unwrapped_timestamp_ = absl::nullopt;
+ w_[0] = 90.0;
+ w_[1] = 0;
+ p_[0][0] = 1;
+ p_[1][1] = kP11;
+ p_[0][1] = p_[1][0] = 0;
+ unwrapper_ = TimestampUnwrapper();
+ packet_count_ = 0;
+ detector_accumulator_pos_ = 0;
+ detector_accumulator_neg_ = 0;
+}
+
+void TimestampExtrapolator::Update(Timestamp now, uint32_t ts90khz) {
+ if (now - prev_ > TimeDelta::Seconds(10)) {
+ // Ten seconds without a complete frame.
+ // Reset the extrapolator
+ Reset(now);
+ } else {
+ prev_ = now;
+ }
+
+ // Remove offset to prevent badly scaled matrices
+ const TimeDelta offset = now - start_;
+ double t_ms = offset.ms();
+
+ int64_t unwrapped_ts90khz = unwrapper_.Unwrap(ts90khz);
+
+ if (!first_unwrapped_timestamp_) {
+ // Make an initial guess of the offset,
+ // should be almost correct since t_ms - start
+ // should about zero at this time.
+ w_[1] = -w_[0] * t_ms;
+ first_unwrapped_timestamp_ = unwrapped_ts90khz;
+ }
+
+ double residual =
+ (static_cast<double>(unwrapped_ts90khz) - *first_unwrapped_timestamp_) -
+ t_ms * w_[0] - w_[1];
+ if (DelayChangeDetection(residual) &&
+ packet_count_ >= kStartUpFilterDelayInPackets) {
+ // 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.
+ p_[1][1] = kP11;
+ }
+
+ if (prev_unwrapped_timestamp_ &&
+ unwrapped_ts90khz < prev_unwrapped_timestamp_) {
+ // Drop reordered frames.
+ return;
+ }
+
+ // T = [t(k) 1]';
+ // that = T'*w;
+ // K = P*T/(lambda + T'*P*T);
+ double K[2];
+ K[0] = p_[0][0] * t_ms + p_[0][1];
+ K[1] = p_[1][0] * t_ms + p_[1][1];
+ double TPT = kLambda + t_ms * 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 / kLambda * (p_[0][0] - (K[0] * t_ms * p_[0][0] + K[0] * p_[1][0]));
+ double p01 =
+ 1 / kLambda * (p_[0][1] - (K[0] * t_ms * p_[0][1] + K[0] * p_[1][1]));
+ p_[1][0] =
+ 1 / kLambda * (p_[1][0] - (K[1] * t_ms * p_[0][0] + K[1] * p_[1][0]));
+ p_[1][1] =
+ 1 / kLambda * (p_[1][1] - (K[1] * t_ms * p_[0][1] + K[1] * p_[1][1]));
+ p_[0][0] = p00;
+ p_[0][1] = p01;
+ prev_unwrapped_timestamp_ = unwrapped_ts90khz;
+ if (packet_count_ < kStartUpFilterDelayInPackets) {
+ packet_count_++;
+ }
+}
+
+absl::optional<Timestamp> TimestampExtrapolator::ExtrapolateLocalTime(
+ uint32_t timestamp90khz) const {
+ int64_t unwrapped_ts90khz = unwrapper_.UnwrapWithoutUpdate(timestamp90khz);
+ RTC_DCHECK_GE(unwrapped_ts90khz, 0);
+
+ if (!first_unwrapped_timestamp_) {
+ return absl::nullopt;
+ } else if (packet_count_ < kStartUpFilterDelayInPackets) {
+ constexpr double kRtpTicksPerMs = 90;
+ TimeDelta diff = TimeDelta::Millis(
+ (unwrapped_ts90khz - *prev_unwrapped_timestamp_) / kRtpTicksPerMs);
+ if (diff.ms() < 0) {
+ RTC_DCHECK_GE(prev_.ms(), -diff.ms());
+ }
+ return prev_ + diff;
+ } else if (w_[0] < 1e-3) {
+ return start_;
+ } else {
+ double timestampDiff = unwrapped_ts90khz - *first_unwrapped_timestamp_;
+ auto diff_ms = static_cast<int64_t>((timestampDiff - w_[1]) / w_[0] + 0.5);
+ if (diff_ms < 0) {
+ RTC_DCHECK_GE(start_.ms(), -diff_ms);
+ }
+ return start_ + TimeDelta::Millis(diff_ms);
+ }
+}
+
+bool TimestampExtrapolator::DelayChangeDetection(double error) {
+ // CUSUM detection of sudden delay changes
+ error = (error > 0) ? std::min(error, kAccMaxError)
+ : std::max(error, -kAccMaxError);
+ detector_accumulator_pos_ =
+ std::max(detector_accumulator_pos_ + error - kAccDrift, double{0});
+ detector_accumulator_neg_ =
+ std::min(detector_accumulator_neg_ + error + kAccDrift, double{0});
+ if (detector_accumulator_pos_ > kAlarmThreshold ||
+ detector_accumulator_neg_ < -kAlarmThreshold) {
+ // Alarm
+ detector_accumulator_pos_ = detector_accumulator_neg_ = 0;
+ return true;
+ }
+ return false;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/rtc_base/time/timestamp_extrapolator.h b/third_party/libwebrtc/rtc_base/time/timestamp_extrapolator.h
new file mode 100644
index 0000000000..23e7975453
--- /dev/null
+++ b/third_party/libwebrtc/rtc_base/time/timestamp_extrapolator.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#ifndef RTC_BASE_TIME_TIMESTAMP_EXTRAPOLATOR_H_
+#define RTC_BASE_TIME_TIMESTAMP_EXTRAPOLATOR_H_
+
+#include <stdint.h>
+
+#include "absl/types/optional.h"
+#include "api/units/timestamp.h"
+#include "modules/include/module_common_types_public.h"
+
+namespace webrtc {
+
+// Not thread safe.
+class TimestampExtrapolator {
+ public:
+ explicit TimestampExtrapolator(Timestamp start);
+ void Update(Timestamp now, uint32_t ts90khz);
+ absl::optional<Timestamp> ExtrapolateLocalTime(uint32_t timestamp90khz) const;
+ void Reset(Timestamp start);
+
+ private:
+ void CheckForWrapArounds(uint32_t ts90khz);
+ bool DelayChangeDetection(double error);
+
+ double w_[2];
+ double p_[2][2];
+ Timestamp start_;
+ Timestamp prev_;
+ absl::optional<int64_t> first_unwrapped_timestamp_;
+ TimestampUnwrapper unwrapper_;
+ absl::optional<int64_t> prev_unwrapped_timestamp_;
+ uint32_t packet_count_;
+ double detector_accumulator_pos_;
+ double detector_accumulator_neg_;
+};
+
+} // namespace webrtc
+
+#endif // RTC_BASE_TIME_TIMESTAMP_EXTRAPOLATOR_H_
diff --git a/third_party/libwebrtc/rtc_base/time/timestamp_extrapolator_gn/moz.build b/third_party/libwebrtc/rtc_base/time/timestamp_extrapolator_gn/moz.build
new file mode 100644
index 0000000000..af464b00bd
--- /dev/null
+++ b/third_party/libwebrtc/rtc_base/time/timestamp_extrapolator_gn/moz.build
@@ -0,0 +1,201 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/rtc_base/time/timestamp_extrapolator.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("timestamp_extrapolator_gn")
diff --git a/third_party/libwebrtc/rtc_base/time/timestamp_extrapolator_unittest.cc b/third_party/libwebrtc/rtc_base/time/timestamp_extrapolator_unittest.cc
new file mode 100644
index 0000000000..b153d7ae15
--- /dev/null
+++ b/third_party/libwebrtc/rtc_base/time/timestamp_extrapolator_unittest.cc
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2022 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 <stdint.h>
+
+#include <limits>
+
+#include "absl/types/optional.h"
+#include "api/units/frequency.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "system_wrappers/include/clock.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+using ::testing::Eq;
+using ::testing::Optional;
+
+namespace {
+
+constexpr Frequency kRtpHz = Frequency::KiloHertz(90);
+constexpr Frequency k25Fps = Frequency::Hertz(25);
+constexpr TimeDelta k25FpsDelay = 1 / k25Fps;
+
+} // namespace
+
+TEST(TimestampExtrapolatorTest, ExtrapolationOccursAfter2Packets) {
+ SimulatedClock clock(Timestamp::Millis(1337));
+ TimestampExtrapolator ts_extrapolator(clock.CurrentTime());
+
+ // No packets so no timestamp.
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(90000), Eq(absl::nullopt));
+
+ uint32_t rtp = 90000;
+ clock.AdvanceTime(k25FpsDelay);
+ // First result is a bit confusing since it is based off the "start" time,
+ // which is arbitrary.
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
+ Optional(clock.CurrentTime()));
+
+ rtp += kRtpHz / k25Fps;
+ clock.AdvanceTime(k25FpsDelay);
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
+ Optional(clock.CurrentTime()));
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp + 90000),
+ Optional(clock.CurrentTime() + TimeDelta::Seconds(1)));
+}
+
+TEST(TimestampExtrapolatorTest, ResetsAfter10SecondPause) {
+ SimulatedClock clock(Timestamp::Millis(1337));
+ TimestampExtrapolator ts_extrapolator(clock.CurrentTime());
+
+ uint32_t rtp = 90000;
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
+ Optional(clock.CurrentTime()));
+
+ rtp += kRtpHz / k25Fps;
+ clock.AdvanceTime(k25FpsDelay);
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
+ Optional(clock.CurrentTime()));
+
+ rtp += 10 * kRtpHz.hertz();
+ clock.AdvanceTime(TimeDelta::Seconds(10) + TimeDelta::Micros(1));
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
+ Optional(clock.CurrentTime()));
+}
+
+TEST(TimestampExtrapolatorTest, TimestampExtrapolatesMultipleRtpWrapArounds) {
+ SimulatedClock clock(Timestamp::Millis(1337));
+ TimestampExtrapolator ts_extrapolator(clock.CurrentTime());
+
+ uint32_t rtp = std::numeric_limits<uint32_t>::max();
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
+ Optional(clock.CurrentTime()));
+
+ // One overflow. Static cast to avoid undefined behaviour with +=.
+ rtp += static_cast<uint32_t>(kRtpHz / k25Fps);
+ clock.AdvanceTime(k25FpsDelay);
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
+ Optional(clock.CurrentTime()));
+
+ // Assert that extrapolation works across the boundary as expected.
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp + 90000),
+ Optional(clock.CurrentTime() + TimeDelta::Seconds(1)));
+ // This is not quite 1s since the math always rounds up.
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp - 90000),
+ Optional(clock.CurrentTime() - TimeDelta::Millis(999)));
+
+ // In order to avoid a wrap arounds reset, add a packet every 10s until we
+ // overflow twice.
+ constexpr TimeDelta kRtpOverflowDelay =
+ std::numeric_limits<uint32_t>::max() / kRtpHz;
+ const Timestamp overflow_time = clock.CurrentTime() + kRtpOverflowDelay * 2;
+
+ while (clock.CurrentTime() < overflow_time) {
+ clock.AdvanceTime(TimeDelta::Seconds(10));
+ // Static-cast before += to avoid undefined behaviour of overflow.
+ rtp += static_cast<uint32_t>(kRtpHz * TimeDelta::Seconds(10));
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
+ Optional(clock.CurrentTime()));
+ }
+}
+
+TEST(TimestampExtrapolatorTest, Slow90KHzClock) {
+ // This simulates a slow camera, which produces frames at 24Hz instead of
+ // 25Hz. The extrapolator should be able to resolve this with enough data.
+ SimulatedClock clock(Timestamp::Millis(1337));
+ TimestampExtrapolator ts_extrapolator(clock.CurrentTime());
+
+ constexpr TimeDelta k24FpsDelay = 1 / Frequency::Hertz(24);
+ uint32_t rtp = 90000;
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+
+ // Slow camera will increment RTP at 25 FPS rate even though its producing at
+ // 24 FPS. After 25 frames the extrapolator should settle at this rate.
+ for (int i = 0; i < 25; ++i) {
+ rtp += kRtpHz / k25Fps;
+ clock.AdvanceTime(k24FpsDelay);
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+ }
+
+ // The camera would normally produce 25 frames in 90K ticks, but is slow
+ // so takes 1s + k24FpsDelay for 90K ticks.
+ constexpr Frequency kSlowRtpHz = 90000 / (25 * k24FpsDelay);
+ // The extrapolator will be predicting that time at millisecond precision.
+ auto ts = ts_extrapolator.ExtrapolateLocalTime(rtp + kSlowRtpHz.hertz());
+ ASSERT_TRUE(ts.has_value());
+ EXPECT_EQ(ts->ms(), clock.TimeInMilliseconds() + 1000);
+}
+
+TEST(TimestampExtrapolatorTest, Fast90KHzClock) {
+ // This simulates a fast camera, which produces frames at 26Hz instead of
+ // 25Hz. The extrapolator should be able to resolve this with enough data.
+ SimulatedClock clock(Timestamp::Millis(1337));
+ TimestampExtrapolator ts_extrapolator(clock.CurrentTime());
+
+ constexpr TimeDelta k26FpsDelay = 1 / Frequency::Hertz(26);
+ uint32_t rtp = 90000;
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+
+ // Fast camera will increment RTP at 25 FPS rate even though its producing at
+ // 26 FPS. After 25 frames the extrapolator should settle at this rate.
+ for (int i = 0; i < 25; ++i) {
+ rtp += kRtpHz / k25Fps;
+ clock.AdvanceTime(k26FpsDelay);
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+ }
+
+ // The camera would normally produce 25 frames in 90K ticks, but is slow
+ // so takes 1s + k24FpsDelay for 90K ticks.
+ constexpr Frequency kSlowRtpHz = 90000 / (25 * k26FpsDelay);
+ // The extrapolator will be predicting that time at millisecond precision.
+ auto ts = ts_extrapolator.ExtrapolateLocalTime(rtp + kSlowRtpHz.hertz());
+ ASSERT_TRUE(ts.has_value());
+ EXPECT_EQ(ts->ms(), clock.TimeInMilliseconds() + 1000);
+}
+
+TEST(TimestampExtrapolatorTest, TimestampJump) {
+ // This simulates a jump in RTP timestamp, which could occur if a camera was
+ // swapped for example.
+ SimulatedClock clock(Timestamp::Millis(1337));
+ TimestampExtrapolator ts_extrapolator(clock.CurrentTime());
+
+ uint32_t rtp = 90000;
+ clock.AdvanceTime(k25FpsDelay);
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+ rtp += kRtpHz / k25Fps;
+ clock.AdvanceTime(k25FpsDelay);
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+ rtp += kRtpHz / k25Fps;
+ clock.AdvanceTime(k25FpsDelay);
+ ts_extrapolator.Update(clock.CurrentTime(), rtp);
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
+ Optional(clock.CurrentTime()));
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp + 90000),
+ Optional(clock.CurrentTime() + TimeDelta::Seconds(1)));
+
+ // Jump RTP.
+ uint32_t new_rtp = 1337 * 90000;
+ clock.AdvanceTime(k25FpsDelay);
+ ts_extrapolator.Update(clock.CurrentTime(), new_rtp);
+ new_rtp += kRtpHz / k25Fps;
+ clock.AdvanceTime(k25FpsDelay);
+ ts_extrapolator.Update(clock.CurrentTime(), new_rtp);
+ EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(new_rtp),
+ Optional(clock.CurrentTime()));
+}
+
+} // namespace webrtc