diff --git a/api/fec_controller.h b/api/fec_controller.h index f3d7a8aa26..a9be656d6e 100644 --- a/api/fec_controller.h +++ b/api/fec_controller.h @@ -31,6 +31,9 @@ class VCMProtectionCallback { uint32_t* sent_nack_rate_bps, uint32_t* sent_fec_rate_bps) = 0; + // 'retransmission_mode' is either a value of enum RetransmissionMode, or + // computed with bitwise operators on values of enum RetransmissionMode. + virtual void SetRetransmissionMode(int retransmission_mode) = 0; protected: virtual ~VCMProtectionCallback() {} }; diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc index d952fec8bc..35288457f9 100644 --- a/call/rtp_video_sender.cc +++ b/call/rtp_video_sender.cc @@ -935,6 +935,13 @@ int RtpVideoSender::ProtectionRequest(const FecProtectionParams* delta_params, return 0; } +void RtpVideoSender::SetRetransmissionMode(int retransmission_mode) { + MutexLock lock(&mutex_); + for (const RtpStreamSender& stream : rtp_streams_) { + stream.sender_video->SetRetransmissionSetting(retransmission_mode); + } +} + void RtpVideoSender::SetFecAllowed(bool fec_allowed) { MutexLock lock(&mutex_); fec_allowed_ = fec_allowed; diff --git a/call/rtp_video_sender.h b/call/rtp_video_sender.h index 3d0dbc1db6..10b0d19d05 100644 --- a/call/rtp_video_sender.h +++ b/call/rtp_video_sender.h @@ -120,6 +120,11 @@ class RtpVideoSender : public RtpVideoSenderInterface, uint32_t* sent_fec_rate_bps) RTC_LOCKS_EXCLUDED(mutex_) override; + // 'retransmission_mode' is either a value of enum RetransmissionMode, or + // computed with bitwise operators on values of enum RetransmissionMode. + void SetRetransmissionMode(int retransmission_mode) + RTC_LOCKS_EXCLUDED(mutex_) override; + // Implements FecControllerOverride. void SetFecAllowed(bool fec_allowed) RTC_LOCKS_EXCLUDED(mutex_) override; diff --git a/call/rtp_video_sender_unittest.cc b/call/rtp_video_sender_unittest.cc index f7407e7d65..16d0e54110 100644 --- a/call/rtp_video_sender_unittest.cc +++ b/call/rtp_video_sender_unittest.cc @@ -1160,4 +1160,82 @@ TEST(RtpVideoSenderTest, ClearsPendingPacketsOnInactivation) { EXPECT_NE(sent_packets[0].Timestamp(), first_frame_timestamp); } +// Integration test verifying that when retransmission mode is set to +// kRetransmitBaseLayer,only base layer is retransmitted. +TEST(RtpVideoSenderTest, RetransmitsBaseLayerOnly) { + RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, + kPayloadType, {}); + test.SetActiveModules({true, true}); + + test.router()->SetRetransmissionMode(kRetransmitBaseLayer); + constexpr uint8_t kPayload = 'a'; + EncodedImage encoded_image; + encoded_image.SetTimestamp(1); + encoded_image.capture_time_ms_ = 2; + encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); + + // Send two tiny images, mapping to two RTP packets. Capture sequence numbers. + std::vector rtp_sequence_numbers; + std::vector transport_sequence_numbers; + std::vector base_sequence_numbers; + EXPECT_CALL(test.transport(), SendRtp) + .Times(2) + .WillRepeatedly([&rtp_sequence_numbers, &transport_sequence_numbers]( + const uint8_t* packet, size_t length, + const PacketOptions& options) { + RtpPacket rtp_packet; + EXPECT_TRUE(rtp_packet.Parse(packet, length)); + rtp_sequence_numbers.push_back(rtp_packet.SequenceNumber()); + transport_sequence_numbers.push_back(options.packet_id); + return true; + }); + CodecSpecificInfo key_codec_info; + key_codec_info.codecType = kVideoCodecVP8; + key_codec_info.codecSpecific.VP8.temporalIdx = 0; + EXPECT_EQ(EncodedImageCallback::Result::OK, + test.router()->OnEncodedImage( + encoded_image, &key_codec_info).error); + encoded_image.SetTimestamp(2); + encoded_image.capture_time_ms_ = 3; + encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + CodecSpecificInfo delta_codec_info; + delta_codec_info.codecType = kVideoCodecVP8; + delta_codec_info.codecSpecific.VP8.temporalIdx = 1; + EXPECT_EQ(EncodedImageCallback::Result::OK, + test.router()->OnEncodedImage( + encoded_image, &delta_codec_info).error); + + test.AdvanceTime(TimeDelta::Millis(33)); + + // Construct a NACK message for requesting retransmission of both packet. + rtcp::Nack nack; + nack.SetMediaSsrc(kSsrc1); + nack.SetPacketIds(rtp_sequence_numbers); + rtc::Buffer nack_buffer = nack.Build(); + + std::vector retransmitted_rtp_sequence_numbers; + EXPECT_CALL(test.transport(), SendRtp) + .Times(1) + .WillRepeatedly([&retransmitted_rtp_sequence_numbers]( + const uint8_t* packet, size_t length, + const PacketOptions& options) { + RtpPacket rtp_packet; + EXPECT_TRUE(rtp_packet.Parse(packet, length)); + EXPECT_EQ(rtp_packet.Ssrc(), kRtxSsrc1); + // Capture the retransmitted sequence number from the RTX header. + rtc::ArrayView payload = rtp_packet.payload(); + retransmitted_rtp_sequence_numbers.push_back( + ByteReader::ReadBigEndian(payload.data())); + return true; + }); + test.router()->DeliverRtcp(nack_buffer.data(), nack_buffer.size()); + test.AdvanceTime(TimeDelta::Millis(33)); + + // Verify that only base layer packet was retransmitted. + std::vector base_rtp_sequence_numbers(rtp_sequence_numbers.begin(), + rtp_sequence_numbers.begin() + 1); + EXPECT_EQ(retransmitted_rtp_sequence_numbers, base_rtp_sequence_numbers); +} + } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc index 3d0b348fa7..4b1b37591c 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video.cc @@ -223,6 +223,11 @@ size_t RTPSenderVideo::FecPacketOverhead() const { return overhead; } +void RTPSenderVideo::SetRetransmissionSetting(int32_t retransmission_settings) { + RTC_DCHECK_RUNS_SERIALIZED(&send_checker_); + retransmission_settings_ = retransmission_settings; +} + void RTPSenderVideo::SetVideoStructure( const FrameDependencyStructure* video_structure) { if (frame_transformer_delegate_) { diff --git a/modules/rtp_rtcp/source/rtp_sender_video.h b/modules/rtp_rtcp/source/rtp_sender_video.h index 9f74d15d8a..4da92d10ac 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.h +++ b/modules/rtp_rtcp/source/rtp_sender_video.h @@ -149,6 +149,10 @@ class RTPSenderVideo : public RTPVideoFrameSenderInterface { // place as the other rate stats. DataRate PostEncodeOverhead() const; + // 'retransmission_mode' is either a value of enum RetransmissionMode, or + // computed with bitwise operators on values of enum RetransmissionMode. + void SetRetransmissionSetting(int32_t retransmission_settings); + protected: static uint8_t GetTemporalId(const RTPVideoHeader& header); bool AllowRetransmission(uint8_t temporal_id, @@ -201,11 +205,10 @@ class RTPSenderVideo : public RTPVideoFrameSenderInterface { RTPSender* const rtp_sender_; Clock* const clock_; - const int32_t retransmission_settings_; - // These members should only be accessed from within SendVideo() to avoid // potential race conditions. rtc::RaceChecker send_checker_; + int32_t retransmission_settings_ RTC_GUARDED_BY(send_checker_); VideoRotation last_rotation_ RTC_GUARDED_BY(send_checker_); absl::optional last_color_space_ RTC_GUARDED_BY(send_checker_); bool transmit_color_space_next_frame_ RTC_GUARDED_BY(send_checker_); diff --git a/modules/video_coding/fec_controller_unittest.cc b/modules/video_coding/fec_controller_unittest.cc index fda3d309a4..fa3cc7a93b 100644 --- a/modules/video_coding/fec_controller_unittest.cc +++ b/modules/video_coding/fec_controller_unittest.cc @@ -41,6 +41,7 @@ class ProtectionBitrateCalculatorTest : public ::testing::Test { *sent_fec_rate_bps = fec_rate_bps_; return 0; } + void SetRetransmissionMode(int retransmission_mode) {} uint32_t fec_rate_bps_ = 0; uint32_t nack_rate_bps_ = 0;