diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc index eafcdf084e..7b7e018464 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video.cc @@ -22,11 +22,13 @@ #include "api/crypto/frame_encryptor_interface.h" #include "modules/remote_bitrate_estimator/test/bwe_test_logging.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "modules/rtp_rtcp/source/absolute_capture_time_sender.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/rtp_format.h" #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h" #include "modules/rtp_rtcp/source/rtp_header_extensions.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" +#include "modules/rtp_rtcp/source/time_util.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/trace_event.h" @@ -66,14 +68,16 @@ void BuildRedPayload(const RtpPacketToSend& media_packet, media_payload.size()); } -void AddRtpHeaderExtensions(const RTPVideoHeader& video_header, - const absl::optional& playout_delay, - bool set_video_rotation, - bool set_color_space, - bool set_frame_marking, - bool first_packet, - bool last_packet, - RtpPacketToSend* packet) { +void AddRtpHeaderExtensions( + const RTPVideoHeader& video_header, + const absl::optional& playout_delay, + const absl::optional& absolute_capture_time, + bool set_video_rotation, + bool set_color_space, + bool set_frame_marking, + bool first_packet, + bool last_packet, + RtpPacketToSend* packet) { // Color space requires two-byte header extensions if HDR metadata is // included. Therefore, it's best to add this extension first so that the // other extensions in the same packet are written as two-byte headers at @@ -99,6 +103,10 @@ void AddRtpHeaderExtensions(const RTPVideoHeader& video_header, packet->SetExtension(*playout_delay); } + if (first_packet && absolute_capture_time) { + packet->SetExtension(*absolute_capture_time); + } + if (set_frame_marking) { FrameMarking frame_marking = video_header.frame_marking; frame_marking.start_of_frame = first_packet; @@ -246,7 +254,8 @@ RTPSenderVideo::RTPSenderVideo(const Config& config) exclude_transport_sequence_number_from_fec_experiment_( config.field_trials ->Lookup(kExcludeTransportSequenceNumberFromFecFieldTrial) - .find("Enabled") == 0) { + .find("Enabled") == 0), + absolute_capture_time_sender_(config.clock) { RTC_DCHECK(playout_delay_oracle_); } @@ -501,21 +510,29 @@ bool RTPSenderVideo::SendVideo( single_packet->SetTimestamp(rtp_timestamp); single_packet->set_capture_time_ms(capture_time_ms); + const absl::optional absolute_capture_time = + absolute_capture_time_sender_.OnSendPacket( + AbsoluteCaptureTimeSender::GetSource(single_packet->Ssrc(), + single_packet->Csrcs()), + single_packet->Timestamp(), kVideoPayloadTypeFrequency, + Int64MsToUQ32x32(single_packet->capture_time_ms() + NtpOffsetMs()), + /*estimated_capture_clock_offset=*/absl::nullopt); + auto first_packet = std::make_unique(*single_packet); auto middle_packet = std::make_unique(*single_packet); auto last_packet = std::make_unique(*single_packet); // Simplest way to estimate how much extensions would occupy is to set them. - AddRtpHeaderExtensions(video_header, playout_delay, set_video_rotation, - set_color_space, set_frame_marking, + AddRtpHeaderExtensions(video_header, playout_delay, absolute_capture_time, + set_video_rotation, set_color_space, set_frame_marking, /*first=*/true, /*last=*/true, single_packet.get()); - AddRtpHeaderExtensions(video_header, playout_delay, set_video_rotation, - set_color_space, set_frame_marking, + AddRtpHeaderExtensions(video_header, playout_delay, absolute_capture_time, + set_video_rotation, set_color_space, set_frame_marking, /*first=*/true, /*last=*/false, first_packet.get()); - AddRtpHeaderExtensions(video_header, playout_delay, set_video_rotation, - set_color_space, set_frame_marking, + AddRtpHeaderExtensions(video_header, playout_delay, absolute_capture_time, + set_video_rotation, set_color_space, set_frame_marking, /*first=*/false, /*last=*/false, middle_packet.get()); - AddRtpHeaderExtensions(video_header, playout_delay, set_video_rotation, - set_color_space, set_frame_marking, + AddRtpHeaderExtensions(video_header, playout_delay, absolute_capture_time, + set_video_rotation, set_color_space, set_frame_marking, /*first=*/false, /*last=*/true, last_packet.get()); RTC_DCHECK_GT(packet_capacity, single_packet->headers_size()); diff --git a/modules/rtp_rtcp/source/rtp_sender_video.h b/modules/rtp_rtcp/source/rtp_sender_video.h index 9cc7e4ff03..3f4c676435 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.h +++ b/modules/rtp_rtcp/source/rtp_sender_video.h @@ -23,6 +23,7 @@ #include "modules/include/module_common_types.h" #include "modules/rtp_rtcp/include/flexfec_sender.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "modules/rtp_rtcp/source/absolute_capture_time_sender.h" #include "modules/rtp_rtcp/source/playout_delay_oracle.h" #include "modules/rtp_rtcp/source/rtp_rtcp_config.h" #include "modules/rtp_rtcp/source/rtp_sender.h" @@ -234,6 +235,8 @@ class RTPSenderVideo { const bool generic_descriptor_auth_experiment_; const bool exclude_transport_sequence_number_from_fec_experiment_; + + AbsoluteCaptureTimeSender absolute_capture_time_sender_; }; } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc index 1663ad5ea7..7ccd0ac028 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc @@ -24,6 +24,7 @@ #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h" #include "modules/rtp_rtcp/source/rtp_header_extensions.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "modules/rtp_rtcp/source/time_util.h" #include "rtc_base/arraysize.h" #include "rtc_base/rate_limiter.h" #include "test/gmock.h" @@ -44,6 +45,7 @@ enum : int { // The first valid value is 1. kTransportSequenceNumberExtensionId, kVideoRotationExtensionId, kVideoTimingExtensionId, + kAbsoluteCaptureTimeExtensionId, }; constexpr int kPayload = 100; @@ -73,6 +75,8 @@ class LoopbackTransportTest : public webrtc::Transport { kGenericDescriptorId01); receivers_extensions_.Register( kFrameMarkingExtensionId); + receivers_extensions_.Register( + kAbsoluteCaptureTimeExtensionId); } bool SendRtp(const uint8_t* data, @@ -85,6 +89,9 @@ class LoopbackTransportTest : public webrtc::Transport { bool SendRtcp(const uint8_t* data, size_t len) override { return false; } const RtpPacketReceived& last_sent_packet() { return sent_packets_.back(); } int packets_sent() { return sent_packets_.size(); } + const std::vector& sent_packets() const { + return sent_packets_; + } private: RtpHeaderExtensionMap receivers_extensions_; @@ -606,6 +613,33 @@ TEST_P(RtpSenderVideoTest, UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed(1); } +TEST_P(RtpSenderVideoTest, AbsoluteCaptureTime) { + constexpr int64_t kAbsoluteCaptureTimestampMs = 12345678; + uint8_t kFrame[kMaxPacketLength]; + rtp_module_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::kUri, + kAbsoluteCaptureTimeExtensionId); + + RTPVideoHeader hdr; + hdr.frame_type = VideoFrameType::kVideoFrameKey; + rtp_sender_video_.SendVideo(kPayload, kType, kTimestamp, + kAbsoluteCaptureTimestampMs, kFrame, nullptr, hdr, + kDefaultExpectedRetransmissionTimeMs); + + // It is expected that one and only one of the packets sent on this video + // frame has absolute capture time header extension. + int packets_with_abs_capture_time = 0; + for (const RtpPacketReceived& packet : transport_.sent_packets()) { + auto absolute_capture_time = + packet.GetExtension(); + if (absolute_capture_time) { + ++packets_with_abs_capture_time; + EXPECT_EQ(absolute_capture_time->absolute_capture_timestamp, + Int64MsToUQ32x32(kAbsoluteCaptureTimestampMs + NtpOffsetMs())); + } + } + EXPECT_EQ(packets_with_abs_capture_time, 1); +} + INSTANTIATE_TEST_SUITE_P(WithAndWithoutOverhead, RtpSenderVideoTest, ::testing::Bool());