Add helper classes to send and receive abs-capture-time extensions.
This change adds helper classes to manipulate Absolute Capture Time header extensions. Both classes support the "timestamp interpolation" optimization.
Bug: webrtc:10739
Change-Id: I08eff46eb8910842a6dbaa3288b976004fabe1c7
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/149801
Commit-Queue: Chen Xing <chxg@google.com>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28936}
diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn
index 3608bae..3d6c0e7 100644
--- a/modules/rtp_rtcp/BUILD.gn
+++ b/modules/rtp_rtcp/BUILD.gn
@@ -131,6 +131,10 @@
"include/rtp_header_parser.h",
"include/rtp_rtcp.h",
"include/ulpfec_receiver.h",
+ "source/absolute_capture_time_receiver.cc",
+ "source/absolute_capture_time_receiver.h",
+ "source/absolute_capture_time_sender.cc",
+ "source/absolute_capture_time_sender.h",
"source/dtmf_queue.cc",
"source/dtmf_queue.h",
"source/fec_private_tables_bursty.cc",
@@ -224,6 +228,8 @@
"../../api/rtc_event_log",
"../../api/transport:field_trial_based_config",
"../../api/transport:webrtc_key_value_config",
+ "../../api/units:time_delta",
+ "../../api/units:timestamp",
"../../api/video:video_bitrate_allocation",
"../../api/video:video_bitrate_allocator",
"../../api/video:video_frame",
@@ -385,6 +391,8 @@
testonly = true
sources = [
+ "source/absolute_capture_time_receiver_unittest.cc",
+ "source/absolute_capture_time_sender_unittest.cc",
"source/byte_io_unittest.cc",
"source/fec_private_tables_bursty_unittest.cc",
"source/flexfec_header_reader_writer_unittest.cc",
diff --git a/modules/rtp_rtcp/source/absolute_capture_time_receiver.cc b/modules/rtp_rtcp/source/absolute_capture_time_receiver.cc
new file mode 100644
index 0000000..62f300d
--- /dev/null
+++ b/modules/rtp_rtcp/source/absolute_capture_time_receiver.cc
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2019 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 "modules/rtp_rtcp/source/absolute_capture_time_receiver.h"
+
+#include <limits>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace {
+
+constexpr Timestamp kInvalidLastReceiveTime = Timestamp::MinusInfinity();
+} // namespace
+
+constexpr TimeDelta AbsoluteCaptureTimeReceiver::kInterpolationMaxInterval;
+
+AbsoluteCaptureTimeReceiver::AbsoluteCaptureTimeReceiver(Clock* clock)
+ : clock_(clock),
+ remote_to_local_clock_offset_(absl::nullopt),
+ last_receive_time_(kInvalidLastReceiveTime) {}
+
+uint32_t AbsoluteCaptureTimeReceiver::GetSource(
+ uint32_t ssrc,
+ rtc::ArrayView<const uint32_t> csrcs) {
+ if (csrcs.empty()) {
+ return ssrc;
+ }
+
+ return csrcs[0];
+}
+
+void AbsoluteCaptureTimeReceiver::SetRemoteToLocalClockOffset(
+ absl::optional<int64_t> value_q32x32) {
+ rtc::CritScope cs(&crit_);
+
+ remote_to_local_clock_offset_ = value_q32x32;
+}
+
+absl::optional<AbsoluteCaptureTime>
+AbsoluteCaptureTimeReceiver::OnReceivePacket(
+ uint32_t source,
+ uint32_t rtp_timestamp,
+ uint32_t rtp_clock_frequency,
+ const absl::optional<AbsoluteCaptureTime>& received_extension) {
+ const Timestamp receive_time = clock_->CurrentTime();
+
+ rtc::CritScope cs(&crit_);
+
+ AbsoluteCaptureTime extension;
+ if (received_extension == absl::nullopt) {
+ if (!ShouldInterpolateExtension(receive_time, source, rtp_timestamp,
+ rtp_clock_frequency)) {
+ last_receive_time_ = kInvalidLastReceiveTime;
+ return absl::nullopt;
+ }
+
+ extension.absolute_capture_timestamp = InterpolateAbsoluteCaptureTimestamp(
+ rtp_timestamp, rtp_clock_frequency, last_rtp_timestamp_,
+ last_absolute_capture_timestamp_);
+ extension.estimated_capture_clock_offset =
+ last_estimated_capture_clock_offset_;
+ } else {
+ last_source_ = source;
+ last_rtp_timestamp_ = rtp_timestamp;
+ last_rtp_clock_frequency_ = rtp_clock_frequency;
+ last_absolute_capture_timestamp_ =
+ received_extension->absolute_capture_timestamp;
+ last_estimated_capture_clock_offset_ =
+ received_extension->estimated_capture_clock_offset;
+
+ last_receive_time_ = receive_time;
+
+ extension = *received_extension;
+ }
+
+ extension.estimated_capture_clock_offset = AdjustEstimatedCaptureClockOffset(
+ extension.estimated_capture_clock_offset);
+
+ return extension;
+}
+
+uint64_t AbsoluteCaptureTimeReceiver::InterpolateAbsoluteCaptureTimestamp(
+ uint32_t rtp_timestamp,
+ uint32_t rtp_clock_frequency,
+ uint32_t last_rtp_timestamp,
+ uint64_t last_absolute_capture_timestamp) {
+ RTC_DCHECK_GT(rtp_clock_frequency, 0);
+
+ return last_absolute_capture_timestamp +
+ static_cast<int64_t>(
+ rtc::dchecked_cast<uint64_t>(rtp_timestamp - last_rtp_timestamp)
+ << 32) /
+ rtp_clock_frequency;
+}
+
+bool AbsoluteCaptureTimeReceiver::ShouldInterpolateExtension(
+ Timestamp receive_time,
+ uint32_t source,
+ uint32_t rtp_timestamp,
+ uint32_t rtp_clock_frequency) const {
+ // Shouldn't if we don't have a previously received extension stored.
+ if (last_receive_time_ == kInvalidLastReceiveTime) {
+ return false;
+ }
+
+ // Shouldn't if the last received extension is too old.
+ if ((receive_time - last_receive_time_) > kInterpolationMaxInterval) {
+ return false;
+ }
+
+ // Shouldn't if the source has changed.
+ if (last_source_ != source) {
+ return false;
+ }
+
+ // Shouldn't if the RTP clock frequency has changed.
+ if (last_rtp_clock_frequency_ != rtp_clock_frequency) {
+ return false;
+ }
+
+ // Shouldn't if the RTP clock frequency is invalid.
+ if (rtp_clock_frequency <= 0) {
+ return false;
+ }
+
+ return true;
+}
+
+absl::optional<int64_t>
+AbsoluteCaptureTimeReceiver::AdjustEstimatedCaptureClockOffset(
+ absl::optional<int64_t> received_value) const {
+ if (received_value == absl::nullopt ||
+ remote_to_local_clock_offset_ == absl::nullopt) {
+ return absl::nullopt;
+ }
+
+ // Do calculations as "unsigned" to make overflows deterministic.
+ return static_cast<uint64_t>(*received_value) +
+ static_cast<uint64_t>(*remote_to_local_clock_offset_);
+}
+
+} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/absolute_capture_time_receiver.h b/modules/rtp_rtcp/source/absolute_capture_time_receiver.h
new file mode 100644
index 0000000..10f9539
--- /dev/null
+++ b/modules/rtp_rtcp/source/absolute_capture_time_receiver.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2019 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 MODULES_RTP_RTCP_SOURCE_ABSOLUTE_CAPTURE_TIME_RECEIVER_H_
+#define MODULES_RTP_RTCP_SOURCE_ABSOLUTE_CAPTURE_TIME_RECEIVER_H_
+
+#include "api/array_view.h"
+#include "api/rtp_headers.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/critical_section.h"
+#include "rtc_base/thread_annotations.h"
+#include "system_wrappers/include/clock.h"
+
+namespace webrtc {
+
+//
+// Helper class for receiving the |AbsoluteCaptureTime| header extension.
+//
+// Supports the "timestamp interpolation" optimization:
+// A receiver SHOULD memorize the capture system (i.e. CSRC/SSRC), capture
+// timestamp, and RTP timestamp of the most recently received abs-capture-time
+// packet on each received stream. It can then use that information, in
+// combination with RTP timestamps of packets without abs-capture-time, to
+// extrapolate missing capture timestamps.
+//
+// See: https://webrtc.org/experiments/rtp-hdrext/abs-capture-time/
+//
+class AbsoluteCaptureTimeReceiver {
+ public:
+ static constexpr TimeDelta kInterpolationMaxInterval =
+ TimeDelta::Millis<5000>();
+
+ explicit AbsoluteCaptureTimeReceiver(Clock* clock);
+
+ // Returns the source (i.e. SSRC or CSRC) of the capture system.
+ static uint32_t GetSource(uint32_t ssrc,
+ rtc::ArrayView<const uint32_t> csrcs);
+
+ // Sets the NTP clock offset between the sender system (which may be different
+ // from the capture system) and the local system. This information is normally
+ // provided by passing half the value of the Round-Trip Time estimation given
+ // by RTCP sender reports (see DLSR/DLRR).
+ //
+ // Note that the value must be in Q32.32-formatted fixed-point seconds.
+ void SetRemoteToLocalClockOffset(absl::optional<int64_t> value_q32x32);
+
+ // Returns a received header extension, an interpolated header extension, or
+ // |absl::nullopt| if it's not possible to interpolate a header extension.
+ absl::optional<AbsoluteCaptureTime> OnReceivePacket(
+ uint32_t source,
+ uint32_t rtp_timestamp,
+ uint32_t rtp_clock_frequency,
+ const absl::optional<AbsoluteCaptureTime>& received_extension);
+
+ private:
+ friend class AbsoluteCaptureTimeSender;
+
+ static uint64_t InterpolateAbsoluteCaptureTimestamp(
+ uint32_t rtp_timestamp,
+ uint32_t rtp_clock_frequency,
+ uint32_t last_rtp_timestamp,
+ uint64_t last_absolute_capture_timestamp);
+
+ bool ShouldInterpolateExtension(Timestamp receive_time,
+ uint32_t source,
+ uint32_t rtp_timestamp,
+ uint32_t rtp_clock_frequency) const
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_);
+
+ absl::optional<int64_t> AdjustEstimatedCaptureClockOffset(
+ absl::optional<int64_t> received_value) const
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_);
+
+ Clock* const clock_;
+
+ rtc::CriticalSection crit_;
+
+ absl::optional<int64_t> remote_to_local_clock_offset_ RTC_GUARDED_BY(crit_);
+
+ Timestamp last_receive_time_ RTC_GUARDED_BY(crit_);
+
+ uint32_t last_source_ RTC_GUARDED_BY(crit_);
+ uint32_t last_rtp_timestamp_ RTC_GUARDED_BY(crit_);
+ uint32_t last_rtp_clock_frequency_ RTC_GUARDED_BY(crit_);
+ uint64_t last_absolute_capture_timestamp_ RTC_GUARDED_BY(crit_);
+ absl::optional<int64_t> last_estimated_capture_clock_offset_
+ RTC_GUARDED_BY(crit_);
+}; // AbsoluteCaptureTimeReceiver
+
+} // namespace webrtc
+
+#endif // MODULES_RTP_RTCP_SOURCE_ABSOLUTE_CAPTURE_TIME_RECEIVER_H_
diff --git a/modules/rtp_rtcp/source/absolute_capture_time_receiver_unittest.cc b/modules/rtp_rtcp/source/absolute_capture_time_receiver_unittest.cc
new file mode 100644
index 0000000..ecf2567
--- /dev/null
+++ b/modules/rtp_rtcp/source/absolute_capture_time_receiver_unittest.cc
@@ -0,0 +1,416 @@
+/*
+ * Copyright (c) 2019 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 "modules/rtp_rtcp/source/absolute_capture_time_receiver.h"
+
+#include "system_wrappers/include/ntp_time.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(AbsoluteCaptureTimeReceiverTest, GetSourceWithoutCsrcs) {
+ constexpr uint32_t kSsrc = 12;
+
+ EXPECT_EQ(AbsoluteCaptureTimeReceiver::GetSource(kSsrc, nullptr), kSsrc);
+}
+
+TEST(AbsoluteCaptureTimeReceiverTest, GetSourceWithCsrcs) {
+ constexpr uint32_t kSsrc = 12;
+ constexpr uint32_t kCsrcs[] = {34, 56, 78, 90};
+
+ EXPECT_EQ(AbsoluteCaptureTimeReceiver::GetSource(kSsrc, kCsrcs), kCsrcs[0]);
+}
+
+TEST(AbsoluteCaptureTimeReceiverTest, ReceiveExtensionReturnsExtension) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 1280;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9020), absl::nullopt};
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeReceiver receiver(&clock);
+
+ receiver.SetRemoteToLocalClockOffset(0);
+
+ EXPECT_EQ(receiver.OnReceivePacket(kSource, kRtpTimestamp0,
+ kRtpClockFrequency, kExtension0),
+ kExtension0);
+
+ EXPECT_EQ(receiver.OnReceivePacket(kSource, kRtpTimestamp1,
+ kRtpClockFrequency, kExtension1),
+ kExtension1);
+}
+
+TEST(AbsoluteCaptureTimeReceiverTest, ReceiveNoExtensionReturnsNoExtension) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 1280;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 = absl::nullopt;
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 = absl::nullopt;
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeReceiver receiver(&clock);
+
+ receiver.SetRemoteToLocalClockOffset(0);
+
+ EXPECT_EQ(receiver.OnReceivePacket(kSource, kRtpTimestamp0,
+ kRtpClockFrequency, kExtension0),
+ absl::nullopt);
+
+ EXPECT_EQ(receiver.OnReceivePacket(kSource, kRtpTimestamp1,
+ kRtpClockFrequency, kExtension1),
+ absl::nullopt);
+}
+
+TEST(AbsoluteCaptureTimeReceiverTest, InterpolateLaterPacketArrivingLater) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 1280;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp0 + 2560;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 = absl::nullopt;
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 = absl::nullopt;
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeReceiver receiver(&clock);
+
+ receiver.SetRemoteToLocalClockOffset(0);
+
+ EXPECT_EQ(receiver.OnReceivePacket(kSource, kRtpTimestamp0,
+ kRtpClockFrequency, kExtension0),
+ kExtension0);
+
+ absl::optional<AbsoluteCaptureTime> extension = receiver.OnReceivePacket(
+ kSource, kRtpTimestamp1, kRtpClockFrequency, kExtension1);
+ EXPECT_TRUE(extension.has_value());
+ EXPECT_EQ(UQ32x32ToInt64Ms(extension->absolute_capture_timestamp),
+ UQ32x32ToInt64Ms(kExtension0->absolute_capture_timestamp) + 20);
+ EXPECT_EQ(extension->estimated_capture_clock_offset,
+ kExtension0->estimated_capture_clock_offset);
+
+ extension = receiver.OnReceivePacket(kSource, kRtpTimestamp2,
+ kRtpClockFrequency, kExtension2);
+ EXPECT_TRUE(extension.has_value());
+ EXPECT_EQ(UQ32x32ToInt64Ms(extension->absolute_capture_timestamp),
+ UQ32x32ToInt64Ms(kExtension0->absolute_capture_timestamp) + 40);
+ EXPECT_EQ(extension->estimated_capture_clock_offset,
+ kExtension0->estimated_capture_clock_offset);
+}
+
+TEST(AbsoluteCaptureTimeReceiverTest, InterpolateEarlierPacketArrivingLater) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 - 1280;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp0 - 2560;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 = absl::nullopt;
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 = absl::nullopt;
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeReceiver receiver(&clock);
+
+ receiver.SetRemoteToLocalClockOffset(0);
+
+ EXPECT_EQ(receiver.OnReceivePacket(kSource, kRtpTimestamp0,
+ kRtpClockFrequency, kExtension0),
+ kExtension0);
+
+ absl::optional<AbsoluteCaptureTime> extension = receiver.OnReceivePacket(
+ kSource, kRtpTimestamp1, kRtpClockFrequency, kExtension1);
+ EXPECT_TRUE(extension.has_value());
+ EXPECT_EQ(UQ32x32ToInt64Ms(extension->absolute_capture_timestamp),
+ UQ32x32ToInt64Ms(kExtension0->absolute_capture_timestamp) - 20);
+ EXPECT_EQ(extension->estimated_capture_clock_offset,
+ kExtension0->estimated_capture_clock_offset);
+
+ extension = receiver.OnReceivePacket(kSource, kRtpTimestamp2,
+ kRtpClockFrequency, kExtension2);
+ EXPECT_TRUE(extension.has_value());
+ EXPECT_EQ(UQ32x32ToInt64Ms(extension->absolute_capture_timestamp),
+ UQ32x32ToInt64Ms(kExtension0->absolute_capture_timestamp) - 40);
+ EXPECT_EQ(extension->estimated_capture_clock_offset,
+ kExtension0->estimated_capture_clock_offset);
+}
+
+TEST(AbsoluteCaptureTimeReceiverTest,
+ InterpolateLaterPacketArrivingLaterWithRtpTimestampWrapAround) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = ~uint32_t{0} - 79;
+ constexpr uint32_t kRtpTimestamp1 = 1280 - 80;
+ constexpr uint32_t kRtpTimestamp2 = 2560 - 80;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 = absl::nullopt;
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 = absl::nullopt;
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeReceiver receiver(&clock);
+
+ receiver.SetRemoteToLocalClockOffset(0);
+
+ EXPECT_EQ(receiver.OnReceivePacket(kSource, kRtpTimestamp0,
+ kRtpClockFrequency, kExtension0),
+ kExtension0);
+
+ absl::optional<AbsoluteCaptureTime> extension = receiver.OnReceivePacket(
+ kSource, kRtpTimestamp1, kRtpClockFrequency, kExtension1);
+ EXPECT_TRUE(extension.has_value());
+ EXPECT_EQ(UQ32x32ToInt64Ms(extension->absolute_capture_timestamp),
+ UQ32x32ToInt64Ms(kExtension0->absolute_capture_timestamp) + 20);
+ EXPECT_EQ(extension->estimated_capture_clock_offset,
+ kExtension0->estimated_capture_clock_offset);
+
+ extension = receiver.OnReceivePacket(kSource, kRtpTimestamp2,
+ kRtpClockFrequency, kExtension2);
+ EXPECT_TRUE(extension.has_value());
+ EXPECT_EQ(UQ32x32ToInt64Ms(extension->absolute_capture_timestamp),
+ UQ32x32ToInt64Ms(kExtension0->absolute_capture_timestamp) + 40);
+ EXPECT_EQ(extension->estimated_capture_clock_offset,
+ kExtension0->estimated_capture_clock_offset);
+}
+
+TEST(AbsoluteCaptureTimeReceiverTest,
+ InterpolateEarlierPacketArrivingLaterWithRtpTimestampWrapAround) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 799;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 - 1280;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp0 - 2560;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 = absl::nullopt;
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 = absl::nullopt;
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeReceiver receiver(&clock);
+
+ receiver.SetRemoteToLocalClockOffset(0);
+
+ EXPECT_EQ(receiver.OnReceivePacket(kSource, kRtpTimestamp0,
+ kRtpClockFrequency, kExtension0),
+ kExtension0);
+
+ absl::optional<AbsoluteCaptureTime> extension = receiver.OnReceivePacket(
+ kSource, kRtpTimestamp1, kRtpClockFrequency, kExtension1);
+ EXPECT_TRUE(extension.has_value());
+ EXPECT_EQ(UQ32x32ToInt64Ms(extension->absolute_capture_timestamp),
+ UQ32x32ToInt64Ms(kExtension0->absolute_capture_timestamp) - 20);
+ EXPECT_EQ(extension->estimated_capture_clock_offset,
+ kExtension0->estimated_capture_clock_offset);
+
+ extension = receiver.OnReceivePacket(kSource, kRtpTimestamp2,
+ kRtpClockFrequency, kExtension2);
+ EXPECT_TRUE(extension.has_value());
+ EXPECT_EQ(UQ32x32ToInt64Ms(extension->absolute_capture_timestamp),
+ UQ32x32ToInt64Ms(kExtension0->absolute_capture_timestamp) - 40);
+ EXPECT_EQ(extension->estimated_capture_clock_offset,
+ kExtension0->estimated_capture_clock_offset);
+}
+
+TEST(AbsoluteCaptureTimeReceiverTest,
+ SkipEstimatedCaptureClockOffsetIfRemoteToLocalClockOffsetIsUnknown) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 1280;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp0 + 2560;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 = absl::nullopt;
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 = absl::nullopt;
+ static const absl::optional<int64_t> kRemoteToLocalClockOffset2 =
+ Int64MsToQ32x32(-7000007);
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeReceiver receiver(&clock);
+
+ receiver.SetRemoteToLocalClockOffset(0);
+
+ EXPECT_EQ(receiver.OnReceivePacket(kSource, kRtpTimestamp0,
+ kRtpClockFrequency, kExtension0),
+ kExtension0);
+
+ receiver.SetRemoteToLocalClockOffset(absl::nullopt);
+
+ absl::optional<AbsoluteCaptureTime> extension = receiver.OnReceivePacket(
+ kSource, kRtpTimestamp1, kRtpClockFrequency, kExtension1);
+ EXPECT_TRUE(extension.has_value());
+ EXPECT_EQ(UQ32x32ToInt64Ms(extension->absolute_capture_timestamp),
+ UQ32x32ToInt64Ms(kExtension0->absolute_capture_timestamp) + 20);
+ EXPECT_EQ(extension->estimated_capture_clock_offset, absl::nullopt);
+
+ receiver.SetRemoteToLocalClockOffset(kRemoteToLocalClockOffset2);
+
+ extension = receiver.OnReceivePacket(kSource, kRtpTimestamp2,
+ kRtpClockFrequency, kExtension2);
+ EXPECT_TRUE(extension.has_value());
+ EXPECT_EQ(UQ32x32ToInt64Ms(extension->absolute_capture_timestamp),
+ UQ32x32ToInt64Ms(kExtension0->absolute_capture_timestamp) + 40);
+ EXPECT_EQ(extension->estimated_capture_clock_offset,
+ *kExtension0->estimated_capture_clock_offset +
+ *kRemoteToLocalClockOffset2);
+}
+
+TEST(AbsoluteCaptureTimeReceiverTest, SkipInterpolateIfTooLate) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 1280;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp1 + 1280;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 = absl::nullopt;
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 = absl::nullopt;
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeReceiver receiver(&clock);
+
+ receiver.SetRemoteToLocalClockOffset(0);
+
+ EXPECT_EQ(receiver.OnReceivePacket(kSource, kRtpTimestamp0,
+ kRtpClockFrequency, kExtension0),
+ kExtension0);
+
+ clock.AdvanceTime(AbsoluteCaptureTimeReceiver::kInterpolationMaxInterval);
+
+ EXPECT_TRUE(receiver
+ .OnReceivePacket(kSource, kRtpTimestamp1, kRtpClockFrequency,
+ kExtension1)
+ .has_value());
+
+ clock.AdvanceTimeMilliseconds(1);
+
+ EXPECT_FALSE(receiver
+ .OnReceivePacket(kSource, kRtpTimestamp2, kRtpClockFrequency,
+ kExtension2)
+ .has_value());
+}
+
+TEST(AbsoluteCaptureTimeReceiverTest, SkipInterpolateIfSourceChanged) {
+ constexpr uint32_t kSource0 = 1337;
+ constexpr uint32_t kSource1 = 1338;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 1280;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 = absl::nullopt;
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeReceiver receiver(&clock);
+
+ receiver.SetRemoteToLocalClockOffset(0);
+
+ EXPECT_EQ(receiver.OnReceivePacket(kSource0, kRtpTimestamp0,
+ kRtpClockFrequency, kExtension0),
+ kExtension0);
+
+ EXPECT_FALSE(receiver
+ .OnReceivePacket(kSource1, kRtpTimestamp1,
+ kRtpClockFrequency, kExtension1)
+ .has_value());
+}
+
+TEST(AbsoluteCaptureTimeReceiverTest,
+ SkipInterpolateIfRtpClockFrequencyChanged) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency0 = 64000;
+ constexpr uint32_t kRtpClockFrequency1 = 32000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 640;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 = absl::nullopt;
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeReceiver receiver(&clock);
+
+ receiver.SetRemoteToLocalClockOffset(0);
+
+ EXPECT_EQ(receiver.OnReceivePacket(kSource, kRtpTimestamp0,
+ kRtpClockFrequency0, kExtension0),
+ kExtension0);
+
+ EXPECT_FALSE(receiver
+ .OnReceivePacket(kSource, kRtpTimestamp1,
+ kRtpClockFrequency1, kExtension1)
+ .has_value());
+}
+
+TEST(AbsoluteCaptureTimeReceiverTest,
+ SkipInterpolateIfRtpClockFrequencyIsInvalid) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 0;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 640;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 = absl::nullopt;
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeReceiver receiver(&clock);
+
+ receiver.SetRemoteToLocalClockOffset(0);
+
+ EXPECT_EQ(receiver.OnReceivePacket(kSource, kRtpTimestamp0,
+ kRtpClockFrequency, kExtension0),
+ kExtension0);
+
+ EXPECT_FALSE(receiver
+ .OnReceivePacket(kSource, kRtpTimestamp1, kRtpClockFrequency,
+ kExtension1)
+ .has_value());
+}
+
+TEST(AbsoluteCaptureTimeReceiverTest, SkipInterpolateIsSticky) {
+ constexpr uint32_t kSource0 = 1337;
+ constexpr uint32_t kSource1 = 1338;
+ constexpr uint32_t kSource2 = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 1280;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp1 + 1280;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 = absl::nullopt;
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 = absl::nullopt;
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeReceiver receiver(&clock);
+
+ receiver.SetRemoteToLocalClockOffset(0);
+
+ EXPECT_EQ(receiver.OnReceivePacket(kSource0, kRtpTimestamp0,
+ kRtpClockFrequency, kExtension0),
+ kExtension0);
+
+ EXPECT_FALSE(receiver
+ .OnReceivePacket(kSource1, kRtpTimestamp1,
+ kRtpClockFrequency, kExtension1)
+ .has_value());
+
+ EXPECT_FALSE(receiver
+ .OnReceivePacket(kSource2, kRtpTimestamp2,
+ kRtpClockFrequency, kExtension2)
+ .has_value());
+}
+
+} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/absolute_capture_time_sender.cc b/modules/rtp_rtcp/source/absolute_capture_time_sender.cc
new file mode 100644
index 0000000..f614c0c
--- /dev/null
+++ b/modules/rtp_rtcp/source/absolute_capture_time_sender.cc
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2019 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 "modules/rtp_rtcp/source/absolute_capture_time_sender.h"
+
+#include <limits>
+
+#include "modules/rtp_rtcp/source/absolute_capture_time_receiver.h"
+#include "system_wrappers/include/ntp_time.h"
+
+namespace webrtc {
+namespace {
+
+constexpr Timestamp kInvalidLastSendTime = Timestamp::MinusInfinity();
+
+} // namespace
+
+constexpr TimeDelta AbsoluteCaptureTimeSender::kInterpolationMaxInterval;
+constexpr TimeDelta AbsoluteCaptureTimeSender::kInterpolationMaxError;
+
+static_assert(
+ AbsoluteCaptureTimeReceiver::kInterpolationMaxInterval >=
+ AbsoluteCaptureTimeSender::kInterpolationMaxInterval,
+ "Receivers should be as willing to interpolate timestamps as senders.");
+
+AbsoluteCaptureTimeSender::AbsoluteCaptureTimeSender(Clock* clock)
+ : clock_(clock), last_send_time_(kInvalidLastSendTime) {}
+
+uint32_t AbsoluteCaptureTimeSender::GetSource(
+ uint32_t ssrc,
+ rtc::ArrayView<const uint32_t> csrcs) {
+ return AbsoluteCaptureTimeReceiver::GetSource(ssrc, csrcs);
+}
+
+absl::optional<AbsoluteCaptureTime> AbsoluteCaptureTimeSender::OnSendPacket(
+ uint32_t source,
+ uint32_t rtp_timestamp,
+ uint32_t rtp_clock_frequency,
+ uint64_t absolute_capture_timestamp,
+ absl::optional<int64_t> estimated_capture_clock_offset) {
+ const Timestamp send_time = clock_->CurrentTime();
+
+ rtc::CritScope cs(&crit_);
+
+ if (!ShouldSendExtension(send_time, source, rtp_timestamp,
+ rtp_clock_frequency, absolute_capture_timestamp,
+ estimated_capture_clock_offset)) {
+ return absl::nullopt;
+ }
+
+ last_source_ = source;
+ last_rtp_timestamp_ = rtp_timestamp;
+ last_rtp_clock_frequency_ = rtp_clock_frequency;
+ last_absolute_capture_timestamp_ = absolute_capture_timestamp;
+ last_estimated_capture_clock_offset_ = estimated_capture_clock_offset;
+
+ last_send_time_ = send_time;
+
+ AbsoluteCaptureTime extension;
+ extension.absolute_capture_timestamp = absolute_capture_timestamp;
+ extension.estimated_capture_clock_offset = estimated_capture_clock_offset;
+ return extension;
+}
+
+bool AbsoluteCaptureTimeSender::ShouldSendExtension(
+ Timestamp send_time,
+ uint32_t source,
+ uint32_t rtp_timestamp,
+ uint32_t rtp_clock_frequency,
+ uint64_t absolute_capture_timestamp,
+ absl::optional<int64_t> estimated_capture_clock_offset) const {
+ // Should if we've never sent anything before.
+ if (last_send_time_ == kInvalidLastSendTime) {
+ return true;
+ }
+
+ // Should if the last sent extension is too old.
+ if ((send_time - last_send_time_) > kInterpolationMaxInterval) {
+ return true;
+ }
+
+ // Should if the source has changed.
+ if (last_source_ != source) {
+ return true;
+ }
+
+ // Should if the RTP clock frequency has changed.
+ if (last_rtp_clock_frequency_ != rtp_clock_frequency) {
+ return true;
+ }
+
+ // Should if the RTP clock frequency is invalid.
+ if (rtp_clock_frequency <= 0) {
+ return true;
+ }
+
+ // Should if the estimated capture clock offset has changed.
+ if (last_estimated_capture_clock_offset_ != estimated_capture_clock_offset) {
+ return true;
+ }
+
+ // Should if interpolation would introduce too much error.
+ const uint64_t interpolated_absolute_capture_timestamp =
+ AbsoluteCaptureTimeReceiver::InterpolateAbsoluteCaptureTimestamp(
+ rtp_timestamp, rtp_clock_frequency, last_rtp_timestamp_,
+ last_absolute_capture_timestamp_);
+ const int64_t interpolation_error_ms = UQ32x32ToInt64Ms(std::min(
+ interpolated_absolute_capture_timestamp - absolute_capture_timestamp,
+ absolute_capture_timestamp - interpolated_absolute_capture_timestamp));
+ if (interpolation_error_ms > kInterpolationMaxError.ms()) {
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/absolute_capture_time_sender.h b/modules/rtp_rtcp/source/absolute_capture_time_sender.h
new file mode 100644
index 0000000..86158a8
--- /dev/null
+++ b/modules/rtp_rtcp/source/absolute_capture_time_sender.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2019 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 MODULES_RTP_RTCP_SOURCE_ABSOLUTE_CAPTURE_TIME_SENDER_H_
+#define MODULES_RTP_RTCP_SOURCE_ABSOLUTE_CAPTURE_TIME_SENDER_H_
+
+#include "api/array_view.h"
+#include "api/rtp_headers.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/critical_section.h"
+#include "rtc_base/thread_annotations.h"
+#include "system_wrappers/include/clock.h"
+
+namespace webrtc {
+
+//
+// Helper class for sending the |AbsoluteCaptureTime| header extension.
+//
+// Supports the "timestamp interpolation" optimization:
+// A sender SHOULD save bandwidth by not sending abs-capture-time with every
+// RTP packet. It SHOULD still send them at regular intervals (e.g. every
+// second) to help mitigate the impact of clock drift and packet loss. Mixers
+// SHOULD always send abs-capture-time with the first RTP packet after
+// changing capture system.
+//
+// Timestamp interpolation works fine as long as there’s reasonably low
+// NTP/RTP clock drift. This is not always true. Senders that detect “jumps”
+// between its NTP and RTP clock mappings SHOULD send abs-capture-time with
+// the first RTP packet after such a thing happening.
+//
+// See: https://webrtc.org/experiments/rtp-hdrext/abs-capture-time/
+//
+class AbsoluteCaptureTimeSender {
+ public:
+ static constexpr TimeDelta kInterpolationMaxInterval =
+ TimeDelta::Millis<1000>();
+ static constexpr TimeDelta kInterpolationMaxError = TimeDelta::Millis<1>();
+
+ explicit AbsoluteCaptureTimeSender(Clock* clock);
+
+ // Returns the source (i.e. SSRC or CSRC) of the capture system.
+ static uint32_t GetSource(uint32_t ssrc,
+ rtc::ArrayView<const uint32_t> csrcs);
+
+ // Returns a header extension to be sent, or |absl::nullopt| if the header
+ // extension shouldn't be sent.
+ absl::optional<AbsoluteCaptureTime> OnSendPacket(
+ uint32_t source,
+ uint32_t rtp_timestamp,
+ uint32_t rtp_clock_frequency,
+ uint64_t absolute_capture_timestamp,
+ absl::optional<int64_t> estimated_capture_clock_offset);
+
+ private:
+ bool ShouldSendExtension(
+ Timestamp send_time,
+ uint32_t source,
+ uint32_t rtp_timestamp,
+ uint32_t rtp_clock_frequency,
+ uint64_t absolute_capture_timestamp,
+ absl::optional<int64_t> estimated_capture_clock_offset) const
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_);
+
+ Clock* const clock_;
+
+ rtc::CriticalSection crit_;
+
+ Timestamp last_send_time_ RTC_GUARDED_BY(crit_);
+
+ uint32_t last_source_ RTC_GUARDED_BY(crit_);
+ uint32_t last_rtp_timestamp_ RTC_GUARDED_BY(crit_);
+ uint32_t last_rtp_clock_frequency_ RTC_GUARDED_BY(crit_);
+ uint64_t last_absolute_capture_timestamp_ RTC_GUARDED_BY(crit_);
+ absl::optional<int64_t> last_estimated_capture_clock_offset_
+ RTC_GUARDED_BY(crit_);
+}; // AbsoluteCaptureTimeSender
+
+} // namespace webrtc
+
+#endif // MODULES_RTP_RTCP_SOURCE_ABSOLUTE_CAPTURE_TIME_SENDER_H_
diff --git a/modules/rtp_rtcp/source/absolute_capture_time_sender_unittest.cc b/modules/rtp_rtcp/source/absolute_capture_time_sender_unittest.cc
new file mode 100644
index 0000000..db3fc75
--- /dev/null
+++ b/modules/rtp_rtcp/source/absolute_capture_time_sender_unittest.cc
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2019 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 "modules/rtp_rtcp/source/absolute_capture_time_sender.h"
+
+#include "system_wrappers/include/ntp_time.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(AbsoluteCaptureTimeSenderTest, GetSourceWithoutCsrcs) {
+ constexpr uint32_t kSsrc = 12;
+
+ EXPECT_EQ(AbsoluteCaptureTimeSender::GetSource(kSsrc, nullptr), kSsrc);
+}
+
+TEST(AbsoluteCaptureTimeSenderTest, GetSourceWithCsrcs) {
+ constexpr uint32_t kSsrc = 12;
+ constexpr uint32_t kCsrcs[] = {34, 56, 78, 90};
+
+ EXPECT_EQ(AbsoluteCaptureTimeSender::GetSource(kSsrc, kCsrcs), kCsrcs[0]);
+}
+
+TEST(AbsoluteCaptureTimeSenderTest, InterpolateLaterPacketSentLater) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 1280;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp0 + 2560;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 + 20), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 + 40), Int64MsToQ32x32(-350)};
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeSender sender(&clock);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp0, kRtpClockFrequency,
+ kExtension0->absolute_capture_timestamp,
+ kExtension0->estimated_capture_clock_offset),
+ kExtension0);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp1, kRtpClockFrequency,
+ kExtension1->absolute_capture_timestamp,
+ kExtension1->estimated_capture_clock_offset),
+ absl::nullopt);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp2, kRtpClockFrequency,
+ kExtension2->absolute_capture_timestamp,
+ kExtension2->estimated_capture_clock_offset),
+ absl::nullopt);
+}
+
+TEST(AbsoluteCaptureTimeSenderTest, InterpolateEarlierPacketSentLater) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 - 1280;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp0 - 2560;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 - 20), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 - 40), Int64MsToQ32x32(-350)};
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeSender sender(&clock);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp0, kRtpClockFrequency,
+ kExtension0->absolute_capture_timestamp,
+ kExtension0->estimated_capture_clock_offset),
+ kExtension0);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp1, kRtpClockFrequency,
+ kExtension1->absolute_capture_timestamp,
+ kExtension1->estimated_capture_clock_offset),
+ absl::nullopt);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp2, kRtpClockFrequency,
+ kExtension2->absolute_capture_timestamp,
+ kExtension2->estimated_capture_clock_offset),
+ absl::nullopt);
+}
+
+TEST(AbsoluteCaptureTimeSenderTest,
+ InterpolateLaterPacketSentLaterWithRtpTimestampWrapAround) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = ~uint32_t{0} - 79;
+ constexpr uint32_t kRtpTimestamp1 = 1280 - 80;
+ constexpr uint32_t kRtpTimestamp2 = 2560 - 80;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 + 20), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 + 40), Int64MsToQ32x32(-350)};
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeSender sender(&clock);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp0, kRtpClockFrequency,
+ kExtension0->absolute_capture_timestamp,
+ kExtension0->estimated_capture_clock_offset),
+ kExtension0);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp1, kRtpClockFrequency,
+ kExtension1->absolute_capture_timestamp,
+ kExtension1->estimated_capture_clock_offset),
+ absl::nullopt);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp2, kRtpClockFrequency,
+ kExtension2->absolute_capture_timestamp,
+ kExtension2->estimated_capture_clock_offset),
+ absl::nullopt);
+}
+
+TEST(AbsoluteCaptureTimeSenderTest,
+ InterpolateEarlierPacketSentLaterWithRtpTimestampWrapAround) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 799;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 - 1280;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp0 - 2560;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 - 20), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 - 40), Int64MsToQ32x32(-350)};
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeSender sender(&clock);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp0, kRtpClockFrequency,
+ kExtension0->absolute_capture_timestamp,
+ kExtension0->estimated_capture_clock_offset),
+ kExtension0);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp1, kRtpClockFrequency,
+ kExtension1->absolute_capture_timestamp,
+ kExtension1->estimated_capture_clock_offset),
+ absl::nullopt);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp2, kRtpClockFrequency,
+ kExtension2->absolute_capture_timestamp,
+ kExtension2->estimated_capture_clock_offset),
+ absl::nullopt);
+}
+
+TEST(AbsoluteCaptureTimeSenderTest, SkipInterpolateIfTooLate) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 1280;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp0 + 2560;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 + 20), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 + 40), Int64MsToQ32x32(-350)};
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeSender sender(&clock);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp0, kRtpClockFrequency,
+ kExtension0->absolute_capture_timestamp,
+ kExtension0->estimated_capture_clock_offset),
+ kExtension0);
+
+ clock.AdvanceTime(AbsoluteCaptureTimeSender::kInterpolationMaxInterval);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp1, kRtpClockFrequency,
+ kExtension1->absolute_capture_timestamp,
+ kExtension1->estimated_capture_clock_offset),
+ absl::nullopt);
+
+ clock.AdvanceTimeMicroseconds(1);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp2, kRtpClockFrequency,
+ kExtension2->absolute_capture_timestamp,
+ kExtension2->estimated_capture_clock_offset),
+ kExtension2);
+}
+
+TEST(AbsoluteCaptureTimeSenderTest, SkipInterpolateIfSourceChanged) {
+ constexpr uint32_t kSource0 = 1337;
+ constexpr uint32_t kSource1 = 1338;
+ constexpr uint32_t kSource2 = 1338;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 1280;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp0 + 2560;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 + 20), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 + 40), Int64MsToQ32x32(-350)};
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeSender sender(&clock);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource0, kRtpTimestamp0, kRtpClockFrequency,
+ kExtension0->absolute_capture_timestamp,
+ kExtension0->estimated_capture_clock_offset),
+ kExtension0);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource1, kRtpTimestamp1, kRtpClockFrequency,
+ kExtension1->absolute_capture_timestamp,
+ kExtension1->estimated_capture_clock_offset),
+ kExtension1);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource2, kRtpTimestamp2, kRtpClockFrequency,
+ kExtension2->absolute_capture_timestamp,
+ kExtension2->estimated_capture_clock_offset),
+ absl::nullopt);
+}
+
+TEST(AbsoluteCaptureTimeSenderTest, SkipInterpolateIfRtpClockFrequencyChanged) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency0 = 64000;
+ constexpr uint32_t kRtpClockFrequency1 = 32000;
+ constexpr uint32_t kRtpClockFrequency2 = 32000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 640;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp0 + 1280;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 + 20), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 + 40), Int64MsToQ32x32(-350)};
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeSender sender(&clock);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp0, kRtpClockFrequency0,
+ kExtension0->absolute_capture_timestamp,
+ kExtension0->estimated_capture_clock_offset),
+ kExtension0);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp1, kRtpClockFrequency1,
+ kExtension1->absolute_capture_timestamp,
+ kExtension1->estimated_capture_clock_offset),
+ kExtension1);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp2, kRtpClockFrequency2,
+ kExtension2->absolute_capture_timestamp,
+ kExtension2->estimated_capture_clock_offset),
+ absl::nullopt);
+}
+
+TEST(AbsoluteCaptureTimeSenderTest,
+ SkipInterpolateIfRtpClockFrequencyIsInvalid) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency0 = 0;
+ constexpr uint32_t kRtpClockFrequency1 = 0;
+ constexpr uint32_t kRtpClockFrequency2 = 0;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp0;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 + 20), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 + 40), Int64MsToQ32x32(-350)};
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeSender sender(&clock);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp0, kRtpClockFrequency0,
+ kExtension0->absolute_capture_timestamp,
+ kExtension0->estimated_capture_clock_offset),
+ kExtension0);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp1, kRtpClockFrequency1,
+ kExtension1->absolute_capture_timestamp,
+ kExtension1->estimated_capture_clock_offset),
+ kExtension1);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp2, kRtpClockFrequency2,
+ kExtension2->absolute_capture_timestamp,
+ kExtension2->estimated_capture_clock_offset),
+ kExtension2);
+}
+
+TEST(AbsoluteCaptureTimeSenderTest,
+ SkipInterpolateIfEstimatedCaptureClockOffsetChanged) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 1280;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp0 + 2560;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 + 20), Int64MsToQ32x32(370)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000 + 40), absl::nullopt};
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeSender sender(&clock);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp0, kRtpClockFrequency,
+ kExtension0->absolute_capture_timestamp,
+ kExtension0->estimated_capture_clock_offset),
+ kExtension0);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp1, kRtpClockFrequency,
+ kExtension1->absolute_capture_timestamp,
+ kExtension1->estimated_capture_clock_offset),
+ kExtension1);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp2, kRtpClockFrequency,
+ kExtension2->absolute_capture_timestamp,
+ kExtension2->estimated_capture_clock_offset),
+ kExtension2);
+}
+
+TEST(AbsoluteCaptureTimeSenderTest,
+ SkipInterpolateIfTooMuchInterpolationError) {
+ constexpr uint32_t kSource = 1337;
+ constexpr uint32_t kRtpClockFrequency = 64000;
+ constexpr uint32_t kRtpTimestamp0 = 1020300000;
+ constexpr uint32_t kRtpTimestamp1 = kRtpTimestamp0 + 1280;
+ constexpr uint32_t kRtpTimestamp2 = kRtpTimestamp0 + 2560;
+ static const absl::optional<AbsoluteCaptureTime> kExtension0 =
+ AbsoluteCaptureTime{Int64MsToUQ32x32(9000), Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension1 =
+ AbsoluteCaptureTime{
+ Int64MsToUQ32x32(
+ 9000 + 20 +
+ AbsoluteCaptureTimeSender::kInterpolationMaxError.ms()),
+ Int64MsToQ32x32(-350)};
+ static const absl::optional<AbsoluteCaptureTime> kExtension2 =
+ AbsoluteCaptureTime{
+ Int64MsToUQ32x32(
+ 9000 + 40 +
+ AbsoluteCaptureTimeSender::kInterpolationMaxError.ms() + 1),
+ Int64MsToQ32x32(-350)};
+
+ SimulatedClock clock(0);
+ AbsoluteCaptureTimeSender sender(&clock);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp0, kRtpClockFrequency,
+ kExtension0->absolute_capture_timestamp,
+ kExtension0->estimated_capture_clock_offset),
+ kExtension0);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp1, kRtpClockFrequency,
+ kExtension1->absolute_capture_timestamp,
+ kExtension1->estimated_capture_clock_offset),
+ absl::nullopt);
+
+ EXPECT_EQ(sender.OnSendPacket(kSource, kRtpTimestamp2, kRtpClockFrequency,
+ kExtension2->absolute_capture_timestamp,
+ kExtension2->estimated_capture_clock_offset),
+ kExtension2);
+}
+
+} // namespace webrtc