diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_config.h b/modules/rtp_rtcp/source/rtcp_transceiver_config.h index 568abb5c12..774e2510af 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_config.h +++ b/modules/rtp_rtcp/source/rtcp_transceiver_config.h @@ -100,6 +100,7 @@ class RtpStreamRtcpHandler { virtual void OnNack(uint32_t sender_ssrc, rtc::ArrayView sequence_numbers) {} + virtual void OnFir(uint32_t sender_ssrc) {} virtual void OnPli(uint32_t sender_ssrc) {} }; diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc index 967777f1f5..b85d1bc967 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc @@ -32,6 +32,7 @@ #include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" #include "modules/rtp_rtcp/source/time_util.h" #include "rtc_base/checks.h" +#include "rtc_base/containers/flat_map.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/divide_round.h" #include "rtc_base/task_utils/repeating_task.h" @@ -57,6 +58,8 @@ struct RtcpTransceiverImpl::RemoteSenderState { struct RtcpTransceiverImpl::LocalSenderState { uint32_t ssrc; size_t last_num_sent_bytes = 0; + // Sequence number of the last FIR message per sender SSRC. + flat_map last_fir; RtpStreamRtcpHandler* handler = nullptr; }; @@ -337,6 +340,9 @@ void RtcpTransceiverImpl::HandlePayloadSpecificFeedback( const rtcp::CommonHeader& rtcp_packet_header, Timestamp now) { switch (rtcp_packet_header.fmt()) { + case rtcp::Fir::kFeedbackMessageType: + HandleFir(rtcp_packet_header); + break; case rtcp::Pli::kFeedbackMessageType: HandlePli(rtcp_packet_header); break; @@ -346,6 +352,26 @@ void RtcpTransceiverImpl::HandlePayloadSpecificFeedback( } } +void RtcpTransceiverImpl::HandleFir( + const rtcp::CommonHeader& rtcp_packet_header) { + rtcp::Fir fir; + if (local_senders_.empty() || !fir.Parse(rtcp_packet_header)) { + return; + } + for (const rtcp::Fir::Request& r : fir.requests()) { + auto it = local_senders_by_ssrc_.find(r.ssrc); + if (it == local_senders_by_ssrc_.end()) { + continue; + } + auto [fir_it, is_new] = + it->second->last_fir.emplace(fir.sender_ssrc(), r.seq_nr); + if (is_new || fir_it->second != r.seq_nr) { + it->second->handler->OnFir(fir.sender_ssrc()); + fir_it->second = r.seq_nr; + } + } +} + void RtcpTransceiverImpl::HandlePli( const rtcp::CommonHeader& rtcp_packet_header) { rtcp::Pli pli; diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h index 9332691a8b..d4483f084d 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h @@ -105,6 +105,7 @@ class RtcpTransceiverImpl { Timestamp now); void HandleRtpFeedback(const rtcp::CommonHeader& rtcp_packet_header, Timestamp now); + void HandleFir(const rtcp::CommonHeader& rtcp_packet_header); void HandlePli(const rtcp::CommonHeader& rtcp_packet_header); void HandleRemb(const rtcp::CommonHeader& rtcp_packet_header, Timestamp now); void HandleNack(const rtcp::CommonHeader& rtcp_packet_header); diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc index 8161d74b21..08bc4632a9 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc @@ -87,6 +87,7 @@ class MockRtpStreamRtcpHandler : public RtpStreamRtcpHandler { OnNack, (uint32_t, rtc::ArrayView), (override)); + MOCK_METHOD(void, OnFir, (uint32_t), (override)); MOCK_METHOD(void, OnPli, (uint32_t), (override)); private: @@ -1204,6 +1205,83 @@ TEST(RtcpTransceiverImplTest, SendFirDoesNotIncreaseSeqNoIfOldRequest) { EXPECT_EQ(rtcp_parser.fir()->requests()[1].seq_nr, fir_sequence_number1); } +TEST(RtcpTransceiverImplTest, ReceivesFir) { + static constexpr uint32_t kRemoteSsrc = 4321; + static constexpr uint32_t kMediaSsrc1 = 1234; + static constexpr uint32_t kMediaSsrc2 = 1235; + RtcpTransceiverConfig config = DefaultTestConfig(); + RtcpTransceiverImpl rtcp_transceiver(config); + + MockRtpStreamRtcpHandler local_stream1; + MockRtpStreamRtcpHandler local_stream2; + EXPECT_CALL(local_stream1, OnFir(kRemoteSsrc)); + EXPECT_CALL(local_stream2, OnFir).Times(0); + + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1)); + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc2, &local_stream2)); + + rtcp::Fir fir; + fir.SetSenderSsrc(kRemoteSsrc); + fir.AddRequestTo(kMediaSsrc1, /*seq_num=*/13); + + rtcp_transceiver.ReceivePacket(fir.Build(), config.clock->CurrentTime()); +} + +TEST(RtcpTransceiverImplTest, IgnoresReceivedFirWithRepeatedSequenceNumber) { + static constexpr uint32_t kRemoteSsrc = 4321; + static constexpr uint32_t kMediaSsrc1 = 1234; + static constexpr uint32_t kMediaSsrc2 = 1235; + RtcpTransceiverConfig config = DefaultTestConfig(); + RtcpTransceiverImpl rtcp_transceiver(config); + + MockRtpStreamRtcpHandler local_stream1; + MockRtpStreamRtcpHandler local_stream2; + EXPECT_CALL(local_stream1, OnFir(kRemoteSsrc)).Times(1); + EXPECT_CALL(local_stream2, OnFir(kRemoteSsrc)).Times(2); + + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1)); + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc2, &local_stream2)); + + rtcp::Fir fir1; + fir1.SetSenderSsrc(kRemoteSsrc); + fir1.AddRequestTo(kMediaSsrc1, /*seq_num=*/132); + fir1.AddRequestTo(kMediaSsrc2, /*seq_num=*/10); + rtcp_transceiver.ReceivePacket(fir1.Build(), config.clock->CurrentTime()); + + // Repeat request for MediaSsrc1 - expect it to be ignored, + // Change FIR sequence number for MediaSsrc2 - expect a 2nd callback. + rtcp::Fir fir2; + fir2.SetSenderSsrc(kRemoteSsrc); + fir2.AddRequestTo(kMediaSsrc1, /*seq_num=*/132); + fir2.AddRequestTo(kMediaSsrc2, /*seq_num=*/13); + rtcp_transceiver.ReceivePacket(fir2.Build(), config.clock->CurrentTime()); +} + +TEST(RtcpTransceiverImplTest, ReceivedFirTracksSequenceNumberPerRemoteSsrc) { + static constexpr uint32_t kRemoteSsrc1 = 4321; + static constexpr uint32_t kRemoteSsrc2 = 4323; + static constexpr uint32_t kMediaSsrc = 1234; + RtcpTransceiverConfig config = DefaultTestConfig(); + RtcpTransceiverImpl rtcp_transceiver(config); + + MockRtpStreamRtcpHandler local_stream; + EXPECT_CALL(local_stream, OnFir(kRemoteSsrc1)); + EXPECT_CALL(local_stream, OnFir(kRemoteSsrc2)); + + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc, &local_stream)); + + rtcp::Fir fir1; + fir1.SetSenderSsrc(kRemoteSsrc1); + fir1.AddRequestTo(kMediaSsrc, /*seq_num=*/13); + rtcp_transceiver.ReceivePacket(fir1.Build(), config.clock->CurrentTime()); + + // Use the same FIR sequence number, but different sender SSRC. + rtcp::Fir fir2; + fir2.SetSenderSsrc(kRemoteSsrc2); + fir2.AddRequestTo(kMediaSsrc, /*seq_num=*/13); + rtcp_transceiver.ReceivePacket(fir2.Build(), config.clock->CurrentTime()); +} + TEST(RtcpTransceiverImplTest, KeyFrameRequestCreatesCompoundPacket) { const uint32_t kRemoteSsrcs[] = {4321}; SimulatedClock clock(0);