diff --git a/call/call.cc b/call/call.cc index e676d7a30a..a849cd5914 100644 --- a/call/call.cc +++ b/call/call.cc @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -50,6 +51,7 @@ #include "modules/rtp_rtcp/include/flexfec_receiver.h" #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include "modules/rtp_rtcp/source/byte_io.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "modules/rtp_rtcp/source/rtp_util.h" #include "modules/video_coding/fec_controller_default.h" @@ -73,13 +75,6 @@ namespace webrtc { namespace { -bool SendPeriodicFeedback(const std::vector& extensions) { - for (const auto& extension : extensions) { - if (extension.uri == RtpExtension::kTransportSequenceNumberV2Uri) - return false; - } - return true; -} const int* FindKeyByValue(const std::map& m, int v) { for (const auto& kv : m) { @@ -1009,9 +1004,6 @@ webrtc::VideoReceiveStreamInterface* Call::CreateVideoReceiveStream( TRACE_EVENT0("webrtc", "Call::CreateVideoReceiveStream"); RTC_DCHECK_RUN_ON(worker_thread_); - receive_side_cc_.SetSendPeriodicFeedback( - SendPeriodicFeedback(configuration.rtp.extensions)); - EnsureStarted(); event_log_->Log(std::make_unique( @@ -1469,24 +1461,17 @@ void Call::DeliverRtpPacket( void Call::NotifyBweOfReceivedPacket(const RtpPacketReceived& packet, MediaType media_type) { RTC_DCHECK_RUN_ON(worker_thread_); - RTPHeader header; - packet.GetHeader(&header); ReceivedPacket packet_msg; packet_msg.size = DataSize::Bytes(packet.payload_size()); packet_msg.receive_time = packet.arrival_time(); - if (header.extension.hasAbsoluteSendTime) { - packet_msg.send_time = header.extension.GetAbsoluteSendTimestamp(); + uint32_t time_24; + if (packet.GetExtension(&time_24)) { + packet_msg.send_time = AbsoluteSendTime::ToTimestamp(time_24); } transport_send_->OnReceivedPacket(packet_msg); - // For audio, we only support send side BWE. - if (media_type == MediaType::VIDEO || - header.extension.hasTransportSequenceNumber) { - receive_side_cc_.OnReceivedPacket( - packet.arrival_time().ms(), - packet.payload_size() + packet.padding_size(), header); - } + receive_side_cc_.OnReceivedPacket(packet, media_type); } bool Call::RegisterReceiveStream(uint32_t ssrc, diff --git a/modules/congestion_controller/BUILD.gn b/modules/congestion_controller/BUILD.gn index 33f5508137..e814a63aea 100644 --- a/modules/congestion_controller/BUILD.gn +++ b/modules/congestion_controller/BUILD.gn @@ -27,6 +27,7 @@ rtc_library("congestion_controller") { ] deps = [ + "../../api:rtp_parameters", "../../api/transport:network_control", "../../api/units:data_rate", "../../api/units:time_delta", diff --git a/modules/congestion_controller/include/receive_side_congestion_controller.h b/modules/congestion_controller/include/receive_side_congestion_controller.h index 7696396016..82f098cba1 100644 --- a/modules/congestion_controller/include/receive_side_congestion_controller.h +++ b/modules/congestion_controller/include/receive_side_congestion_controller.h @@ -20,6 +20,7 @@ #include "modules/congestion_controller/remb_throttler.h" #include "modules/pacing/packet_router.h" #include "modules/remote_bitrate_estimator/remote_estimator_proxy.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" @@ -41,12 +42,13 @@ class ReceiveSideCongestionController : public CallStatsObserver { ~ReceiveSideCongestionController() override {} + void OnReceivedPacket(const RtpPacketReceived& packet, MediaType media_type); + + // TODO(perkj, bugs.webrtc.org/14859): Remove all usage. This method is + // currently not used by PeerConnections. virtual void OnReceivedPacket(int64_t arrival_time_ms, size_t payload_size, const RTPHeader& header); - - void SetSendPeriodicFeedback(bool send_periodic_feedback); - // Implements CallStatsObserver. void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override; diff --git a/modules/congestion_controller/receive_side_congestion_controller.cc b/modules/congestion_controller/receive_side_congestion_controller.cc index e43b020f6e..9352e50a69 100644 --- a/modules/congestion_controller/receive_side_congestion_controller.cc +++ b/modules/congestion_controller/receive_side_congestion_controller.cc @@ -10,6 +10,7 @@ #include "modules/congestion_controller/include/receive_side_congestion_controller.h" +#include "api/media_types.h" #include "api/units/data_rate.h" #include "modules/pacing/packet_router.h" #include "modules/remote_bitrate_estimator/include/bwe_defines.h" @@ -89,6 +90,31 @@ ReceiveSideCongestionController::ReceiveSideCongestionController( using_absolute_send_time_(false), packets_since_absolute_send_time_(0) {} +void ReceiveSideCongestionController::OnReceivedPacket( + const RtpPacketReceived& packet, + MediaType media_type) { + bool has_transport_sequence_number = + packet.HasExtension() || + packet.HasExtension(); + if (media_type == MediaType::AUDIO && !has_transport_sequence_number) { + // For audio, we only support send side BWE. + return; + } + + if (has_transport_sequence_number) { + // Send-side BWE. + remote_estimator_proxy_.IncomingPacket(packet); + } else { + // Receive-side BWE. + MutexLock lock(&mutex_); + RTPHeader header; + packet.GetHeader(&header); + PickEstimatorFromHeader(header); + rbe_->IncomingPacket(packet.arrival_time().ms(), + packet.payload_size() + packet.padding_size(), header); + } +} + void ReceiveSideCongestionController::OnReceivedPacket( int64_t arrival_time_ms, size_t payload_size, @@ -102,11 +128,6 @@ void ReceiveSideCongestionController::OnReceivedPacket( } } -void ReceiveSideCongestionController::SetSendPeriodicFeedback( - bool send_periodic_feedback) { - remote_estimator_proxy_.SetSendPeriodicFeedback(send_periodic_feedback); -} - void ReceiveSideCongestionController::OnBitrateChanged(int bitrate_bps) { remote_estimator_proxy_.OnBitrateChanged(bitrate_bps); } diff --git a/modules/remote_bitrate_estimator/BUILD.gn b/modules/remote_bitrate_estimator/BUILD.gn index 463530b973..0f1e5b3b3f 100644 --- a/modules/remote_bitrate_estimator/BUILD.gn +++ b/modules/remote_bitrate_estimator/BUILD.gn @@ -139,5 +139,6 @@ if (rtc_include_tests) { "../pacing", "../rtp_rtcp:rtp_rtcp_format", ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } } diff --git a/modules/remote_bitrate_estimator/remote_estimator_proxy.cc b/modules/remote_bitrate_estimator/remote_estimator_proxy.cc index 598279e0af..367260491f 100644 --- a/modules/remote_bitrate_estimator/remote_estimator_proxy.cc +++ b/modules/remote_bitrate_estimator/remote_estimator_proxy.cc @@ -11,13 +11,16 @@ #include "modules/remote_bitrate_estimator/remote_estimator_proxy.h" #include +#include #include #include #include +#include "absl/types/optional.h" #include "api/units/data_size.h" #include "modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h" #include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_minmax.h" @@ -80,6 +83,36 @@ void RemoteEstimatorProxy::MaybeCullOldPackets(int64_t sequence_number, } } +void RemoteEstimatorProxy::IncomingPacket(const RtpPacketReceived& packet) { + if (packet.arrival_time().IsInfinite()) { + RTC_LOG(LS_WARNING) << "Arrival time not set."; + return; + } + MutexLock lock(&lock_); + send_periodic_feedback_ = packet.HasExtension(); + + Packet internal_packet = {.arrival_time = packet.arrival_time(), + .size = DataSize::Bytes(packet.size()), + .ssrc = packet.Ssrc()}; + uint16_t seqnum; + absl::optional feedback_request; + if (!packet.GetExtension(&seqnum)) { + if (!packet.GetExtension(&seqnum, + &feedback_request)) { + RTC_DCHECK_NOTREACHED() << " Expected transport sequence number."; + return; + } + } + internal_packet.transport_sequence_number = seqnum; + internal_packet.feedback_request = feedback_request; + + uint32_t send_time_24_bits; + if (packet.GetExtension(&send_time_24_bits)) { + internal_packet.absolute_send_time_24bits = send_time_24_bits; + } + IncomingPacket(internal_packet); +} + void RemoteEstimatorProxy::IncomingPacket(int64_t arrival_time_ms, size_t payload_size, const RTPHeader& header) { @@ -98,11 +131,11 @@ void RemoteEstimatorProxy::IncomingPacket(int64_t arrival_time_ms, } packet.feedback_request = header.extension.feedback_request; + MutexLock lock(&lock_); IncomingPacket(packet); } void RemoteEstimatorProxy::IncomingPacket(Packet packet) { - MutexLock lock(&lock_); media_ssrc_ = packet.ssrc; int64_t seq = 0; @@ -154,6 +187,8 @@ void RemoteEstimatorProxy::IncomingPacket(Packet packet) { TimeDelta RemoteEstimatorProxy::Process(Timestamp now) { MutexLock lock(&lock_); if (!send_periodic_feedback_) { + // If TransportSequenceNumberV2 has been received in one packet, + // PeriodicFeedback is disabled for the rest of the call. return TimeDelta::PlusInfinity(); } Timestamp next_process_time = last_process_time_ + send_interval_; @@ -189,12 +224,6 @@ void RemoteEstimatorProxy::OnBitrateChanged(int bitrate_bps) { send_interval_ = send_interval; } -void RemoteEstimatorProxy::SetSendPeriodicFeedback( - bool send_periodic_feedback) { - MutexLock lock(&lock_); - send_periodic_feedback_ = send_periodic_feedback; -} - void RemoteEstimatorProxy::SetTransportOverhead(DataSize overhead_per_packet) { MutexLock lock(&lock_); packet_overhead_ = overhead_per_packet; diff --git a/modules/remote_bitrate_estimator/remote_estimator_proxy.h b/modules/remote_bitrate_estimator/remote_estimator_proxy.h index 54257ea6f0..a28e85c1a7 100644 --- a/modules/remote_bitrate_estimator/remote_estimator_proxy.h +++ b/modules/remote_bitrate_estimator/remote_estimator_proxy.h @@ -26,6 +26,7 @@ #include "modules/remote_bitrate_estimator/packet_arrival_map.h" #include "modules/rtp_rtcp/source/rtcp_packet.h" #include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "rtc_base/numerics/sequence_number_unwrapper.h" #include "rtc_base/synchronization/mutex.h" @@ -44,16 +45,10 @@ class RemoteEstimatorProxy { NetworkStateEstimator* network_state_estimator); ~RemoteEstimatorProxy(); - struct Packet { - Timestamp arrival_time; - DataSize size; - uint32_t ssrc; - absl::optional absolute_send_time_24bits; - absl::optional transport_sequence_number; - absl::optional feedback_request; - }; - void IncomingPacket(Packet packet); + void IncomingPacket(const RtpPacketReceived& packet); + // TODO(perkj, bugs.webrtc.org/14859): Remove all usage. This method is + // currently not used by PeerConnections. void IncomingPacket(int64_t arrival_time_ms, size_t payload_size, const RTPHeader& header); @@ -63,10 +58,19 @@ class RemoteEstimatorProxy { TimeDelta Process(Timestamp now); void OnBitrateChanged(int bitrate); - void SetSendPeriodicFeedback(bool send_periodic_feedback); void SetTransportOverhead(DataSize overhead_per_packet); private: + struct Packet { + Timestamp arrival_time; + DataSize size; + uint32_t ssrc; + absl::optional absolute_send_time_24bits; + absl::optional transport_sequence_number; + absl::optional feedback_request; + }; + void IncomingPacket(Packet packet) RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_); + void MaybeCullOldPackets(int64_t sequence_number, Timestamp arrival_time) RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_); void SendPeriodicFeedbacks() RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_); diff --git a/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc b/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc index 437fd6f1e2..c148ce6c62 100644 --- a/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc +++ b/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc @@ -10,9 +10,11 @@ #include "modules/remote_bitrate_estimator/remote_estimator_proxy.h" +#include #include #include +#include "absl/types/optional.h" #include "api/transport/network_types.h" #include "api/transport/test/mock_network_control.h" #include "api/units/data_size.h" @@ -20,6 +22,7 @@ #include "api/units/timestamp.h" #include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" #include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "system_wrappers/include/clock.h" #include "test/gmock.h" #include "test/gtest.h" @@ -34,7 +37,6 @@ using ::testing::MockFunction; using ::testing::Return; using ::testing::SizeIs; -constexpr DataSize kDefaultPacketSize = DataSize::Bytes(100); constexpr uint32_t kMediaSsrc = 456; constexpr uint16_t kBaseSeq = 10; constexpr Timestamp kBaseTime = Timestamp::Millis(123); @@ -81,15 +83,32 @@ class RemoteEstimatorProxyTest : public ::testing::Test { proxy_(feedback_sender_.AsStdFunction(), &network_state_estimator_) {} protected: - void IncomingPacket( + void IncomingPacket(uint16_t seq, + Timestamp arrival_time, + absl::optional abs_send_time = absl::nullopt) { + RtpHeaderExtensionMap map; + map.Register(1); + map.Register(2); + RtpPacketReceived packet(&map, arrival_time); + packet.SetSsrc(kMediaSsrc); + packet.SetExtension(seq); + if (abs_send_time) { + packet.SetExtension(*abs_send_time); + } + proxy_.IncomingPacket(packet); + } + + void IncomingPacketV2( uint16_t seq, Timestamp arrival_time, absl::optional feedback_request = absl::nullopt) { - proxy_.IncomingPacket({.arrival_time = arrival_time, - .size = DataSize::Bytes(100), - .ssrc = kMediaSsrc, - .transport_sequence_number = seq, - .feedback_request = feedback_request}); + RtpHeaderExtensionMap map; + map.Register(1); + RtpPacketReceived packet(&map, arrival_time); + packet.SetSsrc(kMediaSsrc); + packet.SetExtension(seq, + feedback_request); + proxy_.IncomingPacket(packet); } void Process() { @@ -453,22 +472,20 @@ TEST_F(RemoteEstimatorProxyTest, TwccReportsUse5PercentOfAvailableBandwidth) { ////////////////////////////////////////////////////////////////////////////// typedef RemoteEstimatorProxyTest RemoteEstimatorProxyOnRequestTest; TEST_F(RemoteEstimatorProxyOnRequestTest, DisablesPeriodicProcess) { - proxy_.SetSendPeriodicFeedback(false); + IncomingPacketV2(kBaseSeq, kBaseTime); EXPECT_EQ(proxy_.Process(clock_.CurrentTime()), TimeDelta::PlusInfinity()); } TEST_F(RemoteEstimatorProxyOnRequestTest, ProcessDoesNotSendFeedback) { - proxy_.SetSendPeriodicFeedback(false); - IncomingPacket(kBaseSeq, kBaseTime); + IncomingPacketV2(kBaseSeq, kBaseTime); EXPECT_CALL(feedback_sender_, Call).Times(0); Process(); } TEST_F(RemoteEstimatorProxyOnRequestTest, RequestSinglePacketFeedback) { - proxy_.SetSendPeriodicFeedback(false); - IncomingPacket(kBaseSeq, kBaseTime); - IncomingPacket(kBaseSeq + 1, kBaseTime + kMaxSmallDelta); - IncomingPacket(kBaseSeq + 2, kBaseTime + 2 * kMaxSmallDelta); + IncomingPacketV2(kBaseSeq, kBaseTime); + IncomingPacketV2(kBaseSeq + 1, kBaseTime + kMaxSmallDelta); + IncomingPacketV2(kBaseSeq + 2, kBaseTime + 2 * kMaxSmallDelta); EXPECT_CALL(feedback_sender_, Call) .WillOnce(Invoke( @@ -487,15 +504,14 @@ TEST_F(RemoteEstimatorProxyOnRequestTest, RequestSinglePacketFeedback) { constexpr FeedbackRequest kSinglePacketFeedbackRequest = { /*include_timestamps=*/true, /*sequence_count=*/1}; - IncomingPacket(kBaseSeq + 3, kBaseTime + 3 * kMaxSmallDelta, - kSinglePacketFeedbackRequest); + IncomingPacketV2(kBaseSeq + 3, kBaseTime + 3 * kMaxSmallDelta, + kSinglePacketFeedbackRequest); } TEST_F(RemoteEstimatorProxyOnRequestTest, RequestLastFivePacketFeedback) { - proxy_.SetSendPeriodicFeedback(false); int i = 0; for (; i < 10; ++i) { - IncomingPacket(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta); + IncomingPacketV2(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta); } EXPECT_CALL(feedback_sender_, Call) @@ -520,17 +536,16 @@ TEST_F(RemoteEstimatorProxyOnRequestTest, RequestLastFivePacketFeedback) { constexpr FeedbackRequest kFivePacketsFeedbackRequest = { /*include_timestamps=*/true, /*sequence_count=*/5}; - IncomingPacket(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta, - kFivePacketsFeedbackRequest); + IncomingPacketV2(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta, + kFivePacketsFeedbackRequest); } TEST_F(RemoteEstimatorProxyOnRequestTest, RequestLastFivePacketFeedbackMissingPackets) { - proxy_.SetSendPeriodicFeedback(false); int i = 0; for (; i < 10; ++i) { if (i != 7 && i != 9) - IncomingPacket(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta); + IncomingPacketV2(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta); } EXPECT_CALL(feedback_sender_, Call) @@ -552,8 +567,8 @@ TEST_F(RemoteEstimatorProxyOnRequestTest, constexpr FeedbackRequest kFivePacketsFeedbackRequest = { /*include_timestamps=*/true, /*sequence_count=*/5}; - IncomingPacket(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta, - kFivePacketsFeedbackRequest); + IncomingPacketV2(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta, + kFivePacketsFeedbackRequest); } TEST_F(RemoteEstimatorProxyTest, ReportsIncomingPacketToNetworkStateEstimator) { @@ -564,16 +579,11 @@ TEST_F(RemoteEstimatorProxyTest, ReportsIncomingPacketToNetworkStateEstimator) { EXPECT_CALL(network_state_estimator_, OnReceivedPacket(_)) .WillOnce(Invoke([&](const PacketResult& packet) { EXPECT_EQ(packet.receive_time, kBaseTime); - EXPECT_EQ(packet.sent_packet.size, - kDefaultPacketSize + kPacketOverhead); + EXPECT_GT(packet.sent_packet.size, kPacketOverhead); first_send_timestamp = packet.sent_packet.send_time; })); // Incoming packet with abs sendtime but without transport sequence number. - proxy_.IncomingPacket( - {.arrival_time = kBaseTime, - .size = kDefaultPacketSize, - .ssrc = kMediaSsrc, - .absolute_send_time_24bits = AbsoluteSendTime::To24Bits(kBaseTime)}); + IncomingPacket(kBaseSeq, kBaseTime, AbsoluteSendTime::To24Bits(kBaseTime)); // Expect packet with older abs send time to be treated as sent at the same // time as the previous packet due to reordering. @@ -583,12 +593,9 @@ TEST_F(RemoteEstimatorProxyTest, ReportsIncomingPacketToNetworkStateEstimator) { EXPECT_EQ(packet.sent_packet.send_time, first_send_timestamp); })); - proxy_.IncomingPacket( - {.arrival_time = kBaseTime, - .size = kDefaultPacketSize, - .ssrc = kMediaSsrc, - .absolute_send_time_24bits = - AbsoluteSendTime::To24Bits(kBaseTime - TimeDelta::Millis(12))}); + IncomingPacket(kBaseSeq + 1, kBaseTime, + /*abs_send_time=*/ + AbsoluteSendTime::To24Bits(kBaseTime - TimeDelta::Millis(12))); } TEST_F(RemoteEstimatorProxyTest, IncomingPacketHandlesWrapInAbsSendTime) { @@ -606,11 +613,7 @@ TEST_F(RemoteEstimatorProxyTest, IncomingPacketHandlesWrapInAbsSendTime) { EXPECT_EQ(packet.receive_time, kBaseTime); first_send_timestamp = packet.sent_packet.send_time; })); - proxy_.IncomingPacket({.arrival_time = kBaseTime, - .size = kDefaultPacketSize, - .ssrc = kMediaSsrc, - .absolute_send_time_24bits = kFirstAbsSendTime, - .transport_sequence_number = kBaseSeq}); + IncomingPacket(kBaseSeq, kBaseTime, kFirstAbsSendTime); EXPECT_CALL(network_state_estimator_, OnReceivedPacket(_)) .WillOnce(Invoke([first_send_timestamp, @@ -619,21 +622,14 @@ TEST_F(RemoteEstimatorProxyTest, IncomingPacketHandlesWrapInAbsSendTime) { EXPECT_EQ(packet.sent_packet.send_time.ms(), (first_send_timestamp + kExpectedAbsSendTimeDelta).ms()); })); - proxy_.IncomingPacket({.arrival_time = kBaseTime + TimeDelta::Millis(123), - .size = kDefaultPacketSize, - .ssrc = kMediaSsrc, - .absolute_send_time_24bits = kSecondAbsSendTime, - .transport_sequence_number = kBaseSeq + 1}); + IncomingPacket(kBaseSeq + 1, kBaseTime + TimeDelta::Millis(123), + kSecondAbsSendTime); } TEST_F(RemoteEstimatorProxyTest, SendTransportFeedbackAndNetworkStateUpdate) { - proxy_.IncomingPacket( - {.arrival_time = kBaseTime, - .size = kDefaultPacketSize, - .ssrc = kMediaSsrc, - .absolute_send_time_24bits = - AbsoluteSendTime::To24Bits(kBaseTime - TimeDelta::Millis(1)), - .transport_sequence_number = kBaseSeq}); + IncomingPacket(kBaseSeq, kBaseTime, + AbsoluteSendTime::To24Bits(kBaseTime - TimeDelta::Millis(1))); + EXPECT_CALL(network_state_estimator_, GetCurrentEstimate()) .WillOnce(Return(NetworkStateEstimate())); EXPECT_CALL(feedback_sender_, Call(SizeIs(2))); diff --git a/modules/rtp_rtcp/source/rtp_header_extensions.h b/modules/rtp_rtcp/source/rtp_header_extensions.h index d80e0da4f8..04b2cd63a6 100644 --- a/modules/rtp_rtcp/source/rtp_header_extensions.h +++ b/modules/rtp_rtcp/source/rtp_header_extensions.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -49,6 +50,11 @@ class AbsoluteSendTime { RTC_DCHECK_LT(time6x18, 1 << 24); return static_cast(time6x18); } + + static constexpr Timestamp ToTimestamp(uint32_t time_24bits) { + RTC_DCHECK_LT(time_24bits, (1 << 24)); + return Timestamp::Micros((time_24bits* int64_t{1'000'000}) >> 18); + } }; class AbsoluteCaptureTimeExtension {