/* * Copyright (c) 2017 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/rtcp_transceiver_impl.h" #include #include #include #include "absl/memory/memory.h" #include "api/rtp_headers.h" #include "api/test/create_time_controller.h" #include "api/test/time_controller.h" #include "api/units/data_rate.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "api/video/video_bitrate_allocation.h" #include "modules/rtp_rtcp/include/receive_statistics.h" #include "modules/rtp_rtcp/include/report_block_data.h" #include "modules/rtp_rtcp/mocks/mock_network_link_rtcp_observer.h" #include "modules/rtp_rtcp/mocks/mock_rtcp_rtt_stats.h" #include "modules/rtp_rtcp/source/rtcp_packet/app.h" #include "modules/rtp_rtcp/source/rtcp_packet/bye.h" #include "modules/rtp_rtcp/source/rtcp_packet/compound_packet.h" #include "modules/rtp_rtcp/source/time_util.h" #include "system_wrappers/include/clock.h" #include "test/gmock.h" #include "test/gtest.h" #include "test/rtcp_packet_parser.h" namespace webrtc { namespace { using ::testing::_; using ::testing::ElementsAre; using ::testing::ElementsAreArray; using ::testing::Ge; using ::testing::MockFunction; using ::testing::NiceMock; using ::testing::Property; using ::testing::Return; using ::testing::SizeIs; using ::testing::StrictMock; using ::testing::UnorderedElementsAre; using ::testing::WithArg; using ::webrtc::rtcp::Bye; using ::webrtc::rtcp::CompoundPacket; using ::webrtc::rtcp::ReportBlock; using ::webrtc::rtcp::SenderReport; using ::webrtc::test::RtcpPacketParser; class MockReceiveStatisticsProvider : public ReceiveStatisticsProvider { public: MOCK_METHOD(std::vector, RtcpReportBlocks, (size_t), (override)); }; class MockMediaReceiverRtcpObserver : public MediaReceiverRtcpObserver { public: MOCK_METHOD(void, OnSenderReport, (uint32_t, NtpTime, uint32_t), (override)); MOCK_METHOD(void, OnBye, (uint32_t), (override)); MOCK_METHOD(void, OnBitrateAllocation, (uint32_t, const VideoBitrateAllocation&), (override)); }; class MockRtpStreamRtcpHandler : public RtpStreamRtcpHandler { public: MockRtpStreamRtcpHandler() { // With each next call increase number of sent packets and bytes to simulate // active RTP sender. ON_CALL(*this, SentStats).WillByDefault([this] { RtpStats stats; stats.set_num_sent_packets(++num_calls_); stats.set_num_sent_bytes(1'000 * num_calls_); return stats; }); } MOCK_METHOD(RtpStats, SentStats, (), (override)); MOCK_METHOD(void, OnNack, (uint32_t, rtc::ArrayView), (override)); MOCK_METHOD(void, OnFir, (uint32_t), (override)); MOCK_METHOD(void, OnPli, (uint32_t), (override)); MOCK_METHOD(void, OnReport, (const ReportBlockData&), (override)); private: int num_calls_ = 0; }; constexpr TimeDelta kReportPeriod = TimeDelta::Seconds(1); constexpr TimeDelta kAlmostForever = TimeDelta::Seconds(2); constexpr TimeDelta kTimePrecision = TimeDelta::Millis(1); MATCHER_P(Near, value, "") { return arg > value - kTimePrecision && arg < value + kTimePrecision; } // Helper to wait for an rtcp packet produced on a different thread/task queue. class FakeRtcpTransport { public: explicit FakeRtcpTransport(TimeController& time) : time_(time) {} std::function)> AsStdFunction() { return [this](rtc::ArrayView) { sent_rtcp_ = true; }; } // Returns true when packet was received by the transport. bool WaitPacket() { bool got_packet = time_.Wait([this] { return sent_rtcp_; }, kAlmostForever); // Clear the 'event' to allow waiting for multiple packets. sent_rtcp_ = false; return got_packet; } private: TimeController& time_; bool sent_rtcp_ = false; }; std::function)> RtcpParserTransport( RtcpPacketParser& parser) { return [&parser](rtc::ArrayView packet) { return parser.Parse(packet); }; } class RtcpTransceiverImplTest : public ::testing::Test { public: RtcpTransceiverConfig DefaultTestConfig() { // RtcpTransceiverConfig default constructor sets default values for prod. // Test doesn't need to support all key features: Default test config // returns valid config with all features turned off. RtcpTransceiverConfig config; config.clock = time_->GetClock(); config.schedule_periodic_compound_packets = false; config.initial_report_delay = kReportPeriod / 2; config.report_period = kReportPeriod; return config; } TimeController& time_controller() { return *time_; } Timestamp CurrentTime() { return time_->GetClock()->CurrentTime(); } void AdvanceTime(TimeDelta time) { time_->AdvanceTime(time); } std::unique_ptr CreateTaskQueue() { return time_->GetTaskQueueFactory()->CreateTaskQueue( "rtcp", TaskQueueFactory::Priority::NORMAL); } private: std::unique_ptr time_ = CreateSimulatedTimeController(); }; TEST_F(RtcpTransceiverImplTest, NeedToStopPeriodicTaskToDestroyOnTaskQueue) { FakeRtcpTransport transport(time_controller()); auto queue = CreateTaskQueue(); RtcpTransceiverConfig config = DefaultTestConfig(); config.task_queue = queue.get(); config.schedule_periodic_compound_packets = true; config.rtcp_transport = transport.AsStdFunction(); auto* rtcp_transceiver = new RtcpTransceiverImpl(config); // Wait for a periodic packet. EXPECT_TRUE(transport.WaitPacket()); bool done = false; queue->PostTask([rtcp_transceiver, &done] { rtcp_transceiver->StopPeriodicTask(); delete rtcp_transceiver; done = true; }); ASSERT_TRUE(time_controller().Wait([&] { return done; }, kAlmostForever)); } TEST_F(RtcpTransceiverImplTest, CanBeDestroyedRightAfterCreation) { FakeRtcpTransport transport(time_controller()); auto queue = CreateTaskQueue(); RtcpTransceiverConfig config = DefaultTestConfig(); config.task_queue = queue.get(); config.schedule_periodic_compound_packets = true; config.rtcp_transport = transport.AsStdFunction(); bool done = false; queue->PostTask([&] { RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.StopPeriodicTask(); done = true; }); ASSERT_TRUE(time_controller().Wait([&] { return done; }, kAlmostForever)); } TEST_F(RtcpTransceiverImplTest, CanDestroyAfterTaskQueue) { FakeRtcpTransport transport(time_controller()); auto queue = CreateTaskQueue(); RtcpTransceiverConfig config = DefaultTestConfig(); config.task_queue = queue.get(); config.schedule_periodic_compound_packets = true; config.rtcp_transport = transport.AsStdFunction(); auto* rtcp_transceiver = new RtcpTransceiverImpl(config); // Wait for a periodic packet. EXPECT_TRUE(transport.WaitPacket()); queue = nullptr; delete rtcp_transceiver; } TEST_F(RtcpTransceiverImplTest, DelaysSendingFirstCompondPacket) { auto queue = CreateTaskQueue(); FakeRtcpTransport transport(time_controller()); RtcpTransceiverConfig config = DefaultTestConfig(); config.schedule_periodic_compound_packets = true; config.rtcp_transport = transport.AsStdFunction(); config.initial_report_delay = TimeDelta::Millis(10); config.task_queue = queue.get(); absl::optional rtcp_transceiver; Timestamp started = CurrentTime(); queue->PostTask([&] { rtcp_transceiver.emplace(config); }); EXPECT_TRUE(transport.WaitPacket()); EXPECT_GE(CurrentTime() - started, config.initial_report_delay); // Cleanup. bool done = false; queue->PostTask([&] { rtcp_transceiver->StopPeriodicTask(); rtcp_transceiver.reset(); done = true; }); ASSERT_TRUE(time_controller().Wait([&] { return done; }, kAlmostForever)); } TEST_F(RtcpTransceiverImplTest, PeriodicallySendsPackets) { auto queue = CreateTaskQueue(); FakeRtcpTransport transport(time_controller()); RtcpTransceiverConfig config = DefaultTestConfig(); config.schedule_periodic_compound_packets = true; config.rtcp_transport = transport.AsStdFunction(); config.initial_report_delay = TimeDelta::Zero(); config.report_period = kReportPeriod; config.task_queue = queue.get(); absl::optional rtcp_transceiver; Timestamp time_just_before_1st_packet = Timestamp::MinusInfinity(); queue->PostTask([&] { // Because initial_report_delay_ms is set to 0, time_just_before_the_packet // should be very close to the time_of_the_packet. time_just_before_1st_packet = CurrentTime(); rtcp_transceiver.emplace(config); }); EXPECT_TRUE(transport.WaitPacket()); EXPECT_TRUE(transport.WaitPacket()); Timestamp time_just_after_2nd_packet = CurrentTime(); EXPECT_GE(time_just_after_2nd_packet - time_just_before_1st_packet, config.report_period); // Cleanup. bool done = false; queue->PostTask([&] { rtcp_transceiver->StopPeriodicTask(); rtcp_transceiver.reset(); done = true; }); ASSERT_TRUE(time_controller().Wait([&] { return done; }, kAlmostForever)); } TEST_F(RtcpTransceiverImplTest, SendCompoundPacketDelaysPeriodicSendPackets) { auto queue = CreateTaskQueue(); FakeRtcpTransport transport(time_controller()); RtcpTransceiverConfig config = DefaultTestConfig(); config.schedule_periodic_compound_packets = true; config.rtcp_transport = transport.AsStdFunction(); config.initial_report_delay = TimeDelta::Zero(); config.report_period = kReportPeriod; config.task_queue = queue.get(); absl::optional rtcp_transceiver; queue->PostTask([&] { rtcp_transceiver.emplace(config); }); // Wait for the first packet. EXPECT_TRUE(transport.WaitPacket()); // Send non periodic one after half period. bool non_periodic = false; Timestamp time_of_non_periodic_packet = Timestamp::MinusInfinity(); queue->PostDelayedTask( [&] { time_of_non_periodic_packet = CurrentTime(); rtcp_transceiver->SendCompoundPacket(); non_periodic = true; }, config.report_period / 2); // Though non-periodic packet is scheduled just in between periodic, due to // small period and task queue flakiness it migth end-up 1ms after next // periodic packet. To be sure duration after non-periodic packet is tested // wait for transport after ensuring non-periodic packet was sent. EXPECT_TRUE( time_controller().Wait([&] { return non_periodic; }, kAlmostForever)); EXPECT_TRUE(transport.WaitPacket()); // Wait for next periodic packet. EXPECT_TRUE(transport.WaitPacket()); Timestamp time_of_last_periodic_packet = CurrentTime(); EXPECT_GE(time_of_last_periodic_packet - time_of_non_periodic_packet, config.report_period); // Cleanup. bool done = false; queue->PostTask([&] { rtcp_transceiver->StopPeriodicTask(); rtcp_transceiver.reset(); done = true; }); ASSERT_TRUE(time_controller().Wait([&] { return done; }, kAlmostForever)); } TEST_F(RtcpTransceiverImplTest, SendsNoRtcpWhenNetworkStateIsDown) { MockFunction)> mock_transport; RtcpTransceiverConfig config = DefaultTestConfig(); config.initial_ready_to_send = false; config.rtcp_transport = mock_transport.AsStdFunction(); RtcpTransceiverImpl rtcp_transceiver(config); EXPECT_CALL(mock_transport, Call).Times(0); const std::vector sequence_numbers = {45, 57}; const uint32_t ssrcs[] = {123}; rtcp_transceiver.SendCompoundPacket(); rtcp_transceiver.SendNack(ssrcs[0], sequence_numbers); rtcp_transceiver.SendPictureLossIndication(ssrcs[0]); rtcp_transceiver.SendFullIntraRequest(ssrcs, true); } TEST_F(RtcpTransceiverImplTest, SendsRtcpWhenNetworkStateIsUp) { MockFunction)> mock_transport; RtcpTransceiverConfig config = DefaultTestConfig(); config.initial_ready_to_send = false; config.rtcp_transport = mock_transport.AsStdFunction(); RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SetReadyToSend(true); EXPECT_CALL(mock_transport, Call).Times(4); const std::vector sequence_numbers = {45, 57}; const uint32_t ssrcs[] = {123}; rtcp_transceiver.SendCompoundPacket(); rtcp_transceiver.SendNack(ssrcs[0], sequence_numbers); rtcp_transceiver.SendPictureLossIndication(ssrcs[0]); rtcp_transceiver.SendFullIntraRequest(ssrcs, true); } TEST_F(RtcpTransceiverImplTest, SendsPeriodicRtcpWhenNetworkStateIsUp) { auto queue = CreateTaskQueue(); FakeRtcpTransport transport(time_controller()); RtcpTransceiverConfig config = DefaultTestConfig(); config.schedule_periodic_compound_packets = true; config.initial_ready_to_send = false; config.rtcp_transport = transport.AsStdFunction(); config.task_queue = queue.get(); absl::optional rtcp_transceiver; rtcp_transceiver.emplace(config); queue->PostTask([&] { rtcp_transceiver->SetReadyToSend(true); }); EXPECT_TRUE(transport.WaitPacket()); // Cleanup. bool done = false; queue->PostTask([&] { rtcp_transceiver->StopPeriodicTask(); rtcp_transceiver.reset(); done = true; }); ASSERT_TRUE(time_controller().Wait([&] { return done; }, kAlmostForever)); } TEST_F(RtcpTransceiverImplTest, SendsMinimalCompoundPacket) { const uint32_t kSenderSsrc = 12345; RtcpTransceiverConfig config = DefaultTestConfig(); config.feedback_ssrc = kSenderSsrc; config.cname = "cname"; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.schedule_periodic_compound_packets = false; RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SendCompoundPacket(); // Minimal compound RTCP packet contains sender or receiver report and sdes // with cname. ASSERT_GT(rtcp_parser.receiver_report()->num_packets(), 0); EXPECT_EQ(rtcp_parser.receiver_report()->sender_ssrc(), kSenderSsrc); ASSERT_GT(rtcp_parser.sdes()->num_packets(), 0); ASSERT_EQ(rtcp_parser.sdes()->chunks().size(), 1u); EXPECT_EQ(rtcp_parser.sdes()->chunks()[0].ssrc, kSenderSsrc); EXPECT_EQ(rtcp_parser.sdes()->chunks()[0].cname, config.cname); } TEST_F(RtcpTransceiverImplTest, AvoidsEmptyPacketsInReducedMode) { MockFunction)> transport; EXPECT_CALL(transport, Call).Times(0); NiceMock receive_statistics; RtcpTransceiverConfig config = DefaultTestConfig(); config.rtcp_transport = transport.AsStdFunction(); config.rtcp_mode = webrtc::RtcpMode::kReducedSize; config.receive_statistics = &receive_statistics; RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SendCompoundPacket(); } TEST_F(RtcpTransceiverImplTest, AvoidsEmptyReceiverReportsInReducedMode) { RtcpPacketParser rtcp_parser; NiceMock receive_statistics; RtcpTransceiverConfig config = DefaultTestConfig(); config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.rtcp_mode = webrtc::RtcpMode::kReducedSize; config.receive_statistics = &receive_statistics; // Set it to produce something (RRTR) in the "periodic" rtcp packets. config.non_sender_rtt_measurement = true; RtcpTransceiverImpl rtcp_transceiver(config); // Rather than waiting for the right time to produce the periodic packet, // trigger it manually. rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 0); EXPECT_GT(rtcp_parser.xr()->num_packets(), 0); } TEST_F(RtcpTransceiverImplTest, SendsNoRembInitially) { const uint32_t kSenderSsrc = 12345; RtcpTransceiverConfig config = DefaultTestConfig(); config.feedback_ssrc = kSenderSsrc; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.schedule_periodic_compound_packets = false; RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1}); EXPECT_EQ(rtcp_parser.remb()->num_packets(), 0); } TEST_F(RtcpTransceiverImplTest, SetRembIncludesRembInNextCompoundPacket) { const uint32_t kSenderSsrc = 12345; RtcpTransceiverConfig config = DefaultTestConfig(); config.feedback_ssrc = kSenderSsrc; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.schedule_periodic_compound_packets = false; RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{54321, 64321}); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1); EXPECT_EQ(rtcp_parser.remb()->sender_ssrc(), kSenderSsrc); EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 10000); EXPECT_THAT(rtcp_parser.remb()->ssrcs(), ElementsAre(54321, 64321)); } TEST_F(RtcpTransceiverImplTest, SetRembUpdatesValuesToSend) { const uint32_t kSenderSsrc = 12345; RtcpTransceiverConfig config = DefaultTestConfig(); config.feedback_ssrc = kSenderSsrc; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.schedule_periodic_compound_packets = false; RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{54321, 64321}); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1); EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 10000); EXPECT_THAT(rtcp_parser.remb()->ssrcs(), ElementsAre(54321, 64321)); rtcp_transceiver.SetRemb(/*bitrate_bps=*/70000, /*ssrcs=*/{67321}); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.remb()->num_packets(), 2); EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 70000); EXPECT_THAT(rtcp_parser.remb()->ssrcs(), ElementsAre(67321)); } TEST_F(RtcpTransceiverImplTest, SetRembSendsImmediatelyIfSendRembOnChange) { const uint32_t kSenderSsrc = 12345; RtcpTransceiverConfig config = DefaultTestConfig(); config.send_remb_on_change = true; config.feedback_ssrc = kSenderSsrc; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.schedule_periodic_compound_packets = false; RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{}); EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1); EXPECT_EQ(rtcp_parser.remb()->sender_ssrc(), kSenderSsrc); EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 10000); // If there is no change, the packet is not sent immediately. rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{}); EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1); rtcp_transceiver.SetRemb(/*bitrate_bps=*/20000, /*ssrcs=*/{}); EXPECT_EQ(rtcp_parser.remb()->num_packets(), 2); EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 20000); } TEST_F(RtcpTransceiverImplTest, SetRembSendsImmediatelyIfSendRembOnChangeReducedSize) { const uint32_t kSenderSsrc = 12345; RtcpTransceiverConfig config = DefaultTestConfig(); config.send_remb_on_change = true; config.rtcp_mode = webrtc::RtcpMode::kReducedSize; config.feedback_ssrc = kSenderSsrc; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.schedule_periodic_compound_packets = false; RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{}); EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1); EXPECT_EQ(rtcp_parser.remb()->sender_ssrc(), kSenderSsrc); EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 10000); } TEST_F(RtcpTransceiverImplTest, SetRembIncludesRembInAllCompoundPackets) { const uint32_t kSenderSsrc = 12345; RtcpTransceiverConfig config = DefaultTestConfig(); config.feedback_ssrc = kSenderSsrc; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.schedule_periodic_compound_packets = false; RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{54321, 64321}); rtcp_transceiver.SendCompoundPacket(); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{2}); EXPECT_EQ(rtcp_parser.remb()->num_packets(), 2); } TEST_F(RtcpTransceiverImplTest, SendsNoRembAfterUnset) { const uint32_t kSenderSsrc = 12345; RtcpTransceiverConfig config = DefaultTestConfig(); config.feedback_ssrc = kSenderSsrc; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.schedule_periodic_compound_packets = false; RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{54321, 64321}); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1}); ASSERT_EQ(rtcp_parser.remb()->num_packets(), 1); rtcp_transceiver.UnsetRemb(); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{2}); EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1); } TEST_F(RtcpTransceiverImplTest, ReceiverReportUsesReceiveStatistics) { const uint32_t kSenderSsrc = 12345; const uint32_t kMediaSsrc = 54321; MockReceiveStatisticsProvider receive_statistics; std::vector report_blocks(1); report_blocks[0].SetMediaSsrc(kMediaSsrc); EXPECT_CALL(receive_statistics, RtcpReportBlocks(_)) .WillRepeatedly(Return(report_blocks)); RtcpTransceiverConfig config = DefaultTestConfig(); config.feedback_ssrc = kSenderSsrc; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.receive_statistics = &receive_statistics; config.schedule_periodic_compound_packets = false; RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SendCompoundPacket(); ASSERT_GT(rtcp_parser.receiver_report()->num_packets(), 0); EXPECT_EQ(rtcp_parser.receiver_report()->sender_ssrc(), kSenderSsrc); ASSERT_THAT(rtcp_parser.receiver_report()->report_blocks(), SizeIs(report_blocks.size())); EXPECT_EQ(rtcp_parser.receiver_report()->report_blocks()[0].source_ssrc(), kMediaSsrc); } TEST_F(RtcpTransceiverImplTest, MultipleObserversOnSameSsrc) { const uint32_t kRemoteSsrc = 12345; StrictMock observer1; StrictMock observer2; RtcpTransceiverConfig config = DefaultTestConfig(); RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, &observer1); rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, &observer2); const NtpTime kRemoteNtp(0x9876543211); const uint32_t kRemoteRtp = 0x444555; SenderReport sr; sr.SetSenderSsrc(kRemoteSsrc); sr.SetNtp(kRemoteNtp); sr.SetRtpTimestamp(kRemoteRtp); auto raw_packet = sr.Build(); EXPECT_CALL(observer1, OnSenderReport(kRemoteSsrc, kRemoteNtp, kRemoteRtp)); EXPECT_CALL(observer2, OnSenderReport(kRemoteSsrc, kRemoteNtp, kRemoteRtp)); rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0)); } TEST_F(RtcpTransceiverImplTest, DoesntCallsObserverAfterRemoved) { const uint32_t kRemoteSsrc = 12345; StrictMock observer1; StrictMock observer2; RtcpTransceiverConfig config = DefaultTestConfig(); RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, &observer1); rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, &observer2); SenderReport sr; sr.SetSenderSsrc(kRemoteSsrc); auto raw_packet = sr.Build(); rtcp_transceiver.RemoveMediaReceiverRtcpObserver(kRemoteSsrc, &observer1); EXPECT_CALL(observer1, OnSenderReport(_, _, _)).Times(0); EXPECT_CALL(observer2, OnSenderReport(_, _, _)); rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0)); } TEST_F(RtcpTransceiverImplTest, CallsObserverOnSenderReportBySenderSsrc) { const uint32_t kRemoteSsrc1 = 12345; const uint32_t kRemoteSsrc2 = 22345; StrictMock observer1; StrictMock observer2; RtcpTransceiverConfig config = DefaultTestConfig(); RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc1, &observer1); rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc2, &observer2); const NtpTime kRemoteNtp(0x9876543211); const uint32_t kRemoteRtp = 0x444555; SenderReport sr; sr.SetSenderSsrc(kRemoteSsrc1); sr.SetNtp(kRemoteNtp); sr.SetRtpTimestamp(kRemoteRtp); auto raw_packet = sr.Build(); EXPECT_CALL(observer1, OnSenderReport(kRemoteSsrc1, kRemoteNtp, kRemoteRtp)); EXPECT_CALL(observer2, OnSenderReport).Times(0); rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0)); } TEST_F(RtcpTransceiverImplTest, CallsObserverOnByeBySenderSsrc) { const uint32_t kRemoteSsrc1 = 12345; const uint32_t kRemoteSsrc2 = 22345; StrictMock observer1; StrictMock observer2; RtcpTransceiverConfig config = DefaultTestConfig(); RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc1, &observer1); rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc2, &observer2); Bye bye; bye.SetSenderSsrc(kRemoteSsrc1); auto raw_packet = bye.Build(); EXPECT_CALL(observer1, OnBye(kRemoteSsrc1)); EXPECT_CALL(observer2, OnBye(_)).Times(0); rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0)); } TEST_F(RtcpTransceiverImplTest, CallsObserverOnTargetBitrateBySenderSsrc) { const uint32_t kRemoteSsrc1 = 12345; const uint32_t kRemoteSsrc2 = 22345; StrictMock observer1; StrictMock observer2; RtcpTransceiverConfig config = DefaultTestConfig(); RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc1, &observer1); rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc2, &observer2); webrtc::rtcp::TargetBitrate target_bitrate; target_bitrate.AddTargetBitrate(0, 0, /*target_bitrate_kbps=*/10); target_bitrate.AddTargetBitrate(0, 1, /*target_bitrate_kbps=*/20); target_bitrate.AddTargetBitrate(1, 0, /*target_bitrate_kbps=*/40); target_bitrate.AddTargetBitrate(1, 1, /*target_bitrate_kbps=*/80); webrtc::rtcp::ExtendedReports xr; xr.SetSenderSsrc(kRemoteSsrc1); xr.SetTargetBitrate(target_bitrate); auto raw_packet = xr.Build(); VideoBitrateAllocation bitrate_allocation; bitrate_allocation.SetBitrate(0, 0, /*bitrate_bps=*/10000); bitrate_allocation.SetBitrate(0, 1, /*bitrate_bps=*/20000); bitrate_allocation.SetBitrate(1, 0, /*bitrate_bps=*/40000); bitrate_allocation.SetBitrate(1, 1, /*bitrate_bps=*/80000); EXPECT_CALL(observer1, OnBitrateAllocation(kRemoteSsrc1, bitrate_allocation)); EXPECT_CALL(observer2, OnBitrateAllocation(_, _)).Times(0); rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0)); } TEST_F(RtcpTransceiverImplTest, SkipsIncorrectTargetBitrateEntries) { const uint32_t kRemoteSsrc = 12345; MockMediaReceiverRtcpObserver observer; RtcpTransceiverConfig config = DefaultTestConfig(); RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, &observer); webrtc::rtcp::TargetBitrate target_bitrate; target_bitrate.AddTargetBitrate(0, 0, /*target_bitrate_kbps=*/10); target_bitrate.AddTargetBitrate(0, webrtc::kMaxTemporalStreams, 20); target_bitrate.AddTargetBitrate(webrtc::kMaxSpatialLayers, 0, 40); webrtc::rtcp::ExtendedReports xr; xr.SetTargetBitrate(target_bitrate); xr.SetSenderSsrc(kRemoteSsrc); auto raw_packet = xr.Build(); VideoBitrateAllocation expected_allocation; expected_allocation.SetBitrate(0, 0, /*bitrate_bps=*/10000); EXPECT_CALL(observer, OnBitrateAllocation(kRemoteSsrc, expected_allocation)); rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0)); } TEST_F(RtcpTransceiverImplTest, CallsObserverOnByeBehindSenderReport) { const uint32_t kRemoteSsrc = 12345; MockMediaReceiverRtcpObserver observer; RtcpTransceiverConfig config = DefaultTestConfig(); RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, &observer); CompoundPacket compound; auto sr = std::make_unique(); sr->SetSenderSsrc(kRemoteSsrc); compound.Append(std::move(sr)); auto bye = std::make_unique(); bye->SetSenderSsrc(kRemoteSsrc); compound.Append(std::move(bye)); auto raw_packet = compound.Build(); EXPECT_CALL(observer, OnBye(kRemoteSsrc)); EXPECT_CALL(observer, OnSenderReport(kRemoteSsrc, _, _)); rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0)); } TEST_F(RtcpTransceiverImplTest, CallsObserverOnByeBehindUnknownRtcpPacket) { const uint32_t kRemoteSsrc = 12345; MockMediaReceiverRtcpObserver observer; RtcpTransceiverConfig config = DefaultTestConfig(); RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, &observer); CompoundPacket compound; // Use Application-Defined rtcp packet as unknown. auto app = std::make_unique(); compound.Append(std::move(app)); auto bye = std::make_unique(); bye->SetSenderSsrc(kRemoteSsrc); compound.Append(std::move(bye)); auto raw_packet = compound.Build(); EXPECT_CALL(observer, OnBye(kRemoteSsrc)); rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0)); } TEST_F(RtcpTransceiverImplTest, WhenSendsReceiverReportSetsLastSenderReportTimestampPerRemoteSsrc) { const uint32_t kRemoteSsrc1 = 4321; const uint32_t kRemoteSsrc2 = 5321; std::vector statistics_report_blocks(2); statistics_report_blocks[0].SetMediaSsrc(kRemoteSsrc1); statistics_report_blocks[1].SetMediaSsrc(kRemoteSsrc2); MockReceiveStatisticsProvider receive_statistics; EXPECT_CALL(receive_statistics, RtcpReportBlocks(_)) .WillOnce(Return(statistics_report_blocks)); RtcpTransceiverConfig config = DefaultTestConfig(); config.schedule_periodic_compound_packets = false; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.receive_statistics = &receive_statistics; RtcpTransceiverImpl rtcp_transceiver(config); const NtpTime kRemoteNtp(0x9876543211); // Receive SenderReport for RemoteSsrc1, but no report for RemoteSsrc2. SenderReport sr; sr.SetSenderSsrc(kRemoteSsrc1); sr.SetNtp(kRemoteNtp); auto raw_packet = sr.Build(); rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0)); // Trigger sending ReceiverReport. rtcp_transceiver.SendCompoundPacket(); EXPECT_GT(rtcp_parser.receiver_report()->num_packets(), 0); const auto& report_blocks = rtcp_parser.receiver_report()->report_blocks(); ASSERT_EQ(report_blocks.size(), 2u); // RtcpTransceiverImpl doesn't guarantee order of the report blocks // match result of ReceiveStatisticsProvider::RtcpReportBlocks callback, // but for simplicity of the test asume it is the same. ASSERT_EQ(report_blocks[0].source_ssrc(), kRemoteSsrc1); EXPECT_EQ(report_blocks[0].last_sr(), CompactNtp(kRemoteNtp)); ASSERT_EQ(report_blocks[1].source_ssrc(), kRemoteSsrc2); // No matching Sender Report for kRemoteSsrc2, LastSR fields has to be 0. EXPECT_EQ(report_blocks[1].last_sr(), 0u); } TEST_F(RtcpTransceiverImplTest, WhenSendsReceiverReportCalculatesDelaySinceLastSenderReport) { const uint32_t kRemoteSsrc1 = 4321; const uint32_t kRemoteSsrc2 = 5321; std::vector statistics_report_blocks(2); statistics_report_blocks[0].SetMediaSsrc(kRemoteSsrc1); statistics_report_blocks[1].SetMediaSsrc(kRemoteSsrc2); MockReceiveStatisticsProvider receive_statistics; EXPECT_CALL(receive_statistics, RtcpReportBlocks(_)) .WillOnce(Return(statistics_report_blocks)); RtcpTransceiverConfig config = DefaultTestConfig(); config.schedule_periodic_compound_packets = false; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.receive_statistics = &receive_statistics; RtcpTransceiverImpl rtcp_transceiver(config); auto receive_sender_report = [&](uint32_t remote_ssrc) { SenderReport sr; sr.SetSenderSsrc(remote_ssrc); rtcp_transceiver.ReceivePacket(sr.Build(), CurrentTime()); }; receive_sender_report(kRemoteSsrc1); time_controller().AdvanceTime(TimeDelta::Millis(100)); receive_sender_report(kRemoteSsrc2); time_controller().AdvanceTime(TimeDelta::Millis(100)); // Trigger ReceiverReport back. rtcp_transceiver.SendCompoundPacket(); EXPECT_GT(rtcp_parser.receiver_report()->num_packets(), 0); const auto& report_blocks = rtcp_parser.receiver_report()->report_blocks(); ASSERT_EQ(report_blocks.size(), 2u); // RtcpTransceiverImpl doesn't guarantee order of the report blocks // match result of ReceiveStatisticsProvider::RtcpReportBlocks callback, // but for simplicity of the test asume it is the same. ASSERT_EQ(report_blocks[0].source_ssrc(), kRemoteSsrc1); EXPECT_THAT(CompactNtpRttToTimeDelta(report_blocks[0].delay_since_last_sr()), Near(TimeDelta::Millis(200))); ASSERT_EQ(report_blocks[1].source_ssrc(), kRemoteSsrc2); EXPECT_THAT(CompactNtpRttToTimeDelta(report_blocks[1].delay_since_last_sr()), Near(TimeDelta::Millis(100))); } TEST_F(RtcpTransceiverImplTest, MaySendMultipleReceiverReportInSinglePacket) { std::vector statistics_report_blocks(40); MockReceiveStatisticsProvider receive_statistics; EXPECT_CALL(receive_statistics, RtcpReportBlocks(/*max_blocks=*/Ge(40u))) .WillOnce(Return(statistics_report_blocks)); RtcpTransceiverConfig config = DefaultTestConfig(); RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.receive_statistics = &receive_statistics; RtcpTransceiverImpl rtcp_transceiver(config); // Trigger ReceiverReports. rtcp_transceiver.SendCompoundPacket(); // Expect a single RTCP packet with multiple receiver reports in it. EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1}); // Receiver report may contain up to 31 report blocks, thus 2 reports are // needed to carry 40 blocks: 31 in the first, 9 in the last. EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 2); // RtcpParser remembers just the last receiver report, thus can't check number // of blocks in the first receiver report. EXPECT_THAT(rtcp_parser.receiver_report()->report_blocks(), SizeIs(9)); } TEST_F(RtcpTransceiverImplTest, AttachMaxNumberOfReportBlocksToCompoundPacket) { MockReceiveStatisticsProvider receive_statistics; EXPECT_CALL(receive_statistics, RtcpReportBlocks) .WillOnce([](size_t max_blocks) { return std::vector(max_blocks); }); RtcpTransceiverConfig config = DefaultTestConfig(); config.rtcp_mode = RtcpMode::kCompound; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.receive_statistics = &receive_statistics; RtcpTransceiverImpl rtcp_transceiver(config); EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{0}); // Send some fast feedback message. Because of compound mode, report blocks // should be attached. rtcp_transceiver.SendPictureLossIndication(/*ssrc=*/123); // Expect single RTCP packet with multiple receiver reports and a PLI. EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1}); EXPECT_GT(rtcp_parser.receiver_report()->num_packets(), 1); EXPECT_EQ(rtcp_parser.pli()->num_packets(), 1); } TEST_F(RtcpTransceiverImplTest, SendsNack) { const uint32_t kSenderSsrc = 1234; const uint32_t kRemoteSsrc = 4321; std::vector kMissingSequenceNumbers = {34, 37, 38}; RtcpTransceiverConfig config = DefaultTestConfig(); config.feedback_ssrc = kSenderSsrc; config.schedule_periodic_compound_packets = false; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SendNack(kRemoteSsrc, kMissingSequenceNumbers); EXPECT_EQ(rtcp_parser.nack()->num_packets(), 1); EXPECT_EQ(rtcp_parser.nack()->sender_ssrc(), kSenderSsrc); EXPECT_EQ(rtcp_parser.nack()->media_ssrc(), kRemoteSsrc); EXPECT_EQ(rtcp_parser.nack()->packet_ids(), kMissingSequenceNumbers); } TEST_F(RtcpTransceiverImplTest, ReceivesNack) { static constexpr uint32_t kRemoteSsrc = 4321; static constexpr uint32_t kMediaSsrc1 = 1234; static constexpr uint32_t kMediaSsrc2 = 1235; std::vector kMissingSequenceNumbers = {34, 37, 38}; RtcpTransceiverConfig config = DefaultTestConfig(); RtcpTransceiverImpl rtcp_transceiver(config); MockRtpStreamRtcpHandler local_stream1; MockRtpStreamRtcpHandler local_stream2; EXPECT_CALL(local_stream1, OnNack(kRemoteSsrc, ElementsAreArray(kMissingSequenceNumbers))); EXPECT_CALL(local_stream2, OnNack).Times(0); EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1)); EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc2, &local_stream2)); rtcp::Nack nack; nack.SetSenderSsrc(kRemoteSsrc); nack.SetMediaSsrc(kMediaSsrc1); nack.SetPacketIds(kMissingSequenceNumbers); rtcp_transceiver.ReceivePacket(nack.Build(), config.clock->CurrentTime()); } TEST_F(RtcpTransceiverImplTest, RequestKeyFrameWithPictureLossIndication) { const uint32_t kSenderSsrc = 1234; const uint32_t kRemoteSsrc = 4321; RtcpTransceiverConfig config = DefaultTestConfig(); config.feedback_ssrc = kSenderSsrc; config.schedule_periodic_compound_packets = false; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SendPictureLossIndication(kRemoteSsrc); EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1}); EXPECT_EQ(rtcp_parser.pli()->num_packets(), 1); EXPECT_EQ(rtcp_parser.pli()->sender_ssrc(), kSenderSsrc); EXPECT_EQ(rtcp_parser.pli()->media_ssrc(), kRemoteSsrc); } TEST_F(RtcpTransceiverImplTest, ReceivesPictureLossIndication) { 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, OnPli(kRemoteSsrc)); EXPECT_CALL(local_stream2, OnPli).Times(0); EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1)); EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc2, &local_stream2)); rtcp::Pli pli; pli.SetSenderSsrc(kRemoteSsrc); pli.SetMediaSsrc(kMediaSsrc1); rtcp_transceiver.ReceivePacket(pli.Build(), config.clock->CurrentTime()); } TEST_F(RtcpTransceiverImplTest, RequestKeyFrameWithFullIntraRequest) { const uint32_t kSenderSsrc = 1234; const uint32_t kRemoteSsrcs[] = {4321, 5321}; RtcpTransceiverConfig config = DefaultTestConfig(); config.feedback_ssrc = kSenderSsrc; config.schedule_periodic_compound_packets = false; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SendFullIntraRequest(kRemoteSsrcs, true); EXPECT_EQ(rtcp_parser.fir()->num_packets(), 1); EXPECT_EQ(rtcp_parser.fir()->sender_ssrc(), kSenderSsrc); EXPECT_EQ(rtcp_parser.fir()->requests()[0].ssrc, kRemoteSsrcs[0]); EXPECT_EQ(rtcp_parser.fir()->requests()[1].ssrc, kRemoteSsrcs[1]); } TEST_F(RtcpTransceiverImplTest, RequestKeyFrameWithFirIncreaseSeqNoPerSsrc) { RtcpTransceiverConfig config = DefaultTestConfig(); config.schedule_periodic_compound_packets = false; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); RtcpTransceiverImpl rtcp_transceiver(config); const uint32_t kBothRemoteSsrcs[] = {4321, 5321}; const uint32_t kOneRemoteSsrc[] = {4321}; rtcp_transceiver.SendFullIntraRequest(kBothRemoteSsrcs, true); ASSERT_EQ(rtcp_parser.fir()->requests()[0].ssrc, kBothRemoteSsrcs[0]); uint8_t fir_sequence_number0 = rtcp_parser.fir()->requests()[0].seq_nr; ASSERT_EQ(rtcp_parser.fir()->requests()[1].ssrc, kBothRemoteSsrcs[1]); uint8_t fir_sequence_number1 = rtcp_parser.fir()->requests()[1].seq_nr; rtcp_transceiver.SendFullIntraRequest(kOneRemoteSsrc, true); ASSERT_EQ(rtcp_parser.fir()->requests().size(), 1u); ASSERT_EQ(rtcp_parser.fir()->requests()[0].ssrc, kBothRemoteSsrcs[0]); EXPECT_EQ(rtcp_parser.fir()->requests()[0].seq_nr, fir_sequence_number0 + 1); rtcp_transceiver.SendFullIntraRequest(kBothRemoteSsrcs, true); ASSERT_EQ(rtcp_parser.fir()->requests().size(), 2u); ASSERT_EQ(rtcp_parser.fir()->requests()[0].ssrc, kBothRemoteSsrcs[0]); EXPECT_EQ(rtcp_parser.fir()->requests()[0].seq_nr, fir_sequence_number0 + 2); ASSERT_EQ(rtcp_parser.fir()->requests()[1].ssrc, kBothRemoteSsrcs[1]); EXPECT_EQ(rtcp_parser.fir()->requests()[1].seq_nr, fir_sequence_number1 + 1); } TEST_F(RtcpTransceiverImplTest, SendFirDoesNotIncreaseSeqNoIfOldRequest) { RtcpTransceiverConfig config = DefaultTestConfig(); config.schedule_periodic_compound_packets = false; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); RtcpTransceiverImpl rtcp_transceiver(config); const uint32_t kBothRemoteSsrcs[] = {4321, 5321}; rtcp_transceiver.SendFullIntraRequest(kBothRemoteSsrcs, true); ASSERT_EQ(rtcp_parser.fir()->requests().size(), 2u); ASSERT_EQ(rtcp_parser.fir()->requests()[0].ssrc, kBothRemoteSsrcs[0]); uint8_t fir_sequence_number0 = rtcp_parser.fir()->requests()[0].seq_nr; ASSERT_EQ(rtcp_parser.fir()->requests()[1].ssrc, kBothRemoteSsrcs[1]); uint8_t fir_sequence_number1 = rtcp_parser.fir()->requests()[1].seq_nr; rtcp_transceiver.SendFullIntraRequest(kBothRemoteSsrcs, false); ASSERT_EQ(rtcp_parser.fir()->requests().size(), 2u); ASSERT_EQ(rtcp_parser.fir()->requests()[0].ssrc, kBothRemoteSsrcs[0]); EXPECT_EQ(rtcp_parser.fir()->requests()[0].seq_nr, fir_sequence_number0); ASSERT_EQ(rtcp_parser.fir()->requests()[1].ssrc, kBothRemoteSsrcs[1]); EXPECT_EQ(rtcp_parser.fir()->requests()[1].seq_nr, fir_sequence_number1); } TEST_F(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_F(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_F(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_F(RtcpTransceiverImplTest, KeyFrameRequestCreatesCompoundPacket) { const uint32_t kRemoteSsrcs[] = {4321}; RtcpTransceiverConfig config = DefaultTestConfig(); // Turn periodic off to ensure sent rtcp packet is explicitly requested. config.schedule_periodic_compound_packets = false; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.rtcp_mode = webrtc::RtcpMode::kCompound; RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SendFullIntraRequest(kRemoteSsrcs, true); // Test sent packet is compound by expecting presense of receiver report. EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1}); EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 1); } TEST_F(RtcpTransceiverImplTest, KeyFrameRequestCreatesReducedSizePacket) { const uint32_t kRemoteSsrcs[] = {4321}; RtcpTransceiverConfig config = DefaultTestConfig(); // Turn periodic off to ensure sent rtcp packet is explicitly requested. config.schedule_periodic_compound_packets = false; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.rtcp_mode = webrtc::RtcpMode::kReducedSize; RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SendFullIntraRequest(kRemoteSsrcs, true); // Test sent packet is reduced size by expecting absense of receiver report. EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1}); EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 0); } TEST_F(RtcpTransceiverImplTest, SendsXrRrtrWhenEnabled) { const uint32_t kSenderSsrc = 4321; RtcpTransceiverConfig config = DefaultTestConfig(); config.feedback_ssrc = kSenderSsrc; config.schedule_periodic_compound_packets = false; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.non_sender_rtt_measurement = true; RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SendCompoundPacket(); NtpTime ntp_time_now = config.clock->CurrentNtpTime(); EXPECT_EQ(rtcp_parser.xr()->num_packets(), 1); EXPECT_EQ(rtcp_parser.xr()->sender_ssrc(), kSenderSsrc); ASSERT_TRUE(rtcp_parser.xr()->rrtr()); EXPECT_EQ(rtcp_parser.xr()->rrtr()->ntp(), ntp_time_now); } TEST_F(RtcpTransceiverImplTest, RepliesToRrtrWhenEnabled) { static constexpr uint32_t kSenderSsrc[] = {4321, 9876}; RtcpTransceiverConfig config = DefaultTestConfig(); config.reply_to_non_sender_rtt_measurement = true; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); RtcpTransceiverImpl rtcp_transceiver(config); rtcp::ExtendedReports xr; rtcp::Rrtr rrtr; rrtr.SetNtp(NtpTime(uint64_t{0x1111'2222'3333'4444})); xr.SetRrtr(rrtr); xr.SetSenderSsrc(kSenderSsrc[0]); rtcp_transceiver.ReceivePacket(xr.Build(), CurrentTime()); AdvanceTime(TimeDelta::Millis(1'500)); rrtr.SetNtp(NtpTime(uint64_t{0x4444'5555'6666'7777})); xr.SetRrtr(rrtr); xr.SetSenderSsrc(kSenderSsrc[1]); rtcp_transceiver.ReceivePacket(xr.Build(), CurrentTime()); AdvanceTime(TimeDelta::Millis(500)); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.xr()->num_packets(), 1); static constexpr uint32_t kComactNtpOneSecond = 0x0001'0000; EXPECT_THAT(rtcp_parser.xr()->dlrr().sub_blocks(), UnorderedElementsAre( rtcp::ReceiveTimeInfo(kSenderSsrc[0], 0x2222'3333, /*delay=*/2 * kComactNtpOneSecond), rtcp::ReceiveTimeInfo(kSenderSsrc[1], 0x5555'6666, /*delay=*/kComactNtpOneSecond / 2))); } TEST_F(RtcpTransceiverImplTest, CanReplyToRrtrOnceForAllLocalSsrcs) { static constexpr uint32_t kRemoteSsrc = 4321; static constexpr uint32_t kLocalSsrcs[] = {1234, 5678}; RtcpTransceiverConfig config = DefaultTestConfig(); config.reply_to_non_sender_rtt_measurement = true; config.reply_to_non_sender_rtt_mesaurments_on_all_ssrcs = false; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); RtcpTransceiverImpl rtcp_transceiver(config); MockRtpStreamRtcpHandler local_sender0; MockRtpStreamRtcpHandler local_sender1; rtcp_transceiver.AddMediaSender(kLocalSsrcs[0], &local_sender0); rtcp_transceiver.AddMediaSender(kLocalSsrcs[1], &local_sender1); rtcp::ExtendedReports xr; rtcp::Rrtr rrtr; rrtr.SetNtp(NtpTime(uint64_t{0x1111'2222'3333'4444})); xr.SetRrtr(rrtr); xr.SetSenderSsrc(kRemoteSsrc); rtcp_transceiver.ReceivePacket(xr.Build(), CurrentTime()); AdvanceTime(TimeDelta::Millis(1'500)); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.xr()->num_packets(), 1); } TEST_F(RtcpTransceiverImplTest, CanReplyToRrtrForEachLocalSsrc) { static constexpr uint32_t kRemoteSsrc = 4321; static constexpr uint32_t kLocalSsrc[] = {1234, 5678}; RtcpTransceiverConfig config = DefaultTestConfig(); config.reply_to_non_sender_rtt_measurement = true; config.reply_to_non_sender_rtt_mesaurments_on_all_ssrcs = true; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); RtcpTransceiverImpl rtcp_transceiver(config); MockRtpStreamRtcpHandler local_sender0; MockRtpStreamRtcpHandler local_sender1; rtcp_transceiver.AddMediaSender(kLocalSsrc[0], &local_sender0); rtcp_transceiver.AddMediaSender(kLocalSsrc[1], &local_sender1); rtcp::ExtendedReports xr; rtcp::Rrtr rrtr; rrtr.SetNtp(NtpTime(uint64_t{0x1111'2222'3333'4444})); xr.SetRrtr(rrtr); xr.SetSenderSsrc(kRemoteSsrc); rtcp_transceiver.ReceivePacket(xr.Build(), CurrentTime()); AdvanceTime(TimeDelta::Millis(1'500)); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.xr()->num_packets(), 2); } TEST_F(RtcpTransceiverImplTest, SendsNoXrRrtrWhenDisabled) { RtcpTransceiverConfig config = DefaultTestConfig(); config.schedule_periodic_compound_packets = false; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.non_sender_rtt_measurement = false; RtcpTransceiverImpl rtcp_transceiver(config); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1}); // Extended reports rtcp packet might be included for another reason, // but it shouldn't contain rrtr block. EXPECT_FALSE(rtcp_parser.xr()->rrtr()); } TEST_F(RtcpTransceiverImplTest, PassRttFromDlrrToLinkObserver) { const uint32_t kSenderSsrc = 4321; MockNetworkLinkRtcpObserver link_observer; RtcpTransceiverConfig config = DefaultTestConfig(); config.feedback_ssrc = kSenderSsrc; config.network_link_observer = &link_observer; config.non_sender_rtt_measurement = true; RtcpTransceiverImpl rtcp_transceiver(config); Timestamp send_time = Timestamp::Seconds(5678); Timestamp receive_time = send_time + TimeDelta::Millis(110); rtcp::ReceiveTimeInfo rti; rti.ssrc = kSenderSsrc; rti.last_rr = CompactNtp(config.clock->ConvertTimestampToNtpTime(send_time)); rti.delay_since_last_rr = SaturatedToCompactNtp(TimeDelta::Millis(10)); rtcp::ExtendedReports xr; xr.AddDlrrItem(rti); EXPECT_CALL(link_observer, OnRttUpdate(receive_time, Near(TimeDelta::Millis(100)))); rtcp_transceiver.ReceivePacket(xr.Build(), receive_time); } TEST_F(RtcpTransceiverImplTest, CalculatesRoundTripTimeFromReportBlocks) { MockNetworkLinkRtcpObserver link_observer; RtcpTransceiverConfig config = DefaultTestConfig(); config.network_link_observer = &link_observer; RtcpTransceiverImpl rtcp_transceiver(config); TimeDelta rtt = TimeDelta::Millis(100); Timestamp send_time = Timestamp::Seconds(5678); Timestamp receive_time = send_time + TimeDelta::Millis(110); rtcp::ReceiverReport rr; rtcp::ReportBlock rb1; rb1.SetLastSr(CompactNtp(config.clock->ConvertTimestampToNtpTime( receive_time - rtt - TimeDelta::Millis(10)))); rb1.SetDelayLastSr(SaturatedToCompactNtp(TimeDelta::Millis(10))); rr.AddReportBlock(rb1); rtcp::ReportBlock rb2; rb2.SetLastSr(CompactNtp(config.clock->ConvertTimestampToNtpTime( receive_time - rtt - TimeDelta::Millis(20)))); rb2.SetDelayLastSr(SaturatedToCompactNtp(TimeDelta::Millis(20))); rr.AddReportBlock(rb2); EXPECT_CALL(link_observer, OnRttUpdate(receive_time, Near(rtt))); rtcp_transceiver.ReceivePacket(rr.Build(), receive_time); } TEST_F(RtcpTransceiverImplTest, IgnoresUnknownSsrcInDlrr) { const uint32_t kSenderSsrc = 4321; const uint32_t kUnknownSsrc = 4322; MockNetworkLinkRtcpObserver link_observer; RtcpTransceiverConfig config = DefaultTestConfig(); config.feedback_ssrc = kSenderSsrc; config.schedule_periodic_compound_packets = false; config.non_sender_rtt_measurement = true; config.network_link_observer = &link_observer; RtcpTransceiverImpl rtcp_transceiver(config); Timestamp time = Timestamp::Micros(12345678); webrtc::rtcp::ReceiveTimeInfo rti; rti.ssrc = kUnknownSsrc; rti.last_rr = CompactNtp(config.clock->ConvertTimestampToNtpTime(time)); webrtc::rtcp::ExtendedReports xr; xr.AddDlrrItem(rti); auto raw_packet = xr.Build(); EXPECT_CALL(link_observer, OnRttUpdate).Times(0); rtcp_transceiver.ReceivePacket(raw_packet, time + TimeDelta::Millis(100)); } TEST_F(RtcpTransceiverImplTest, ParsesTransportFeedback) { MockNetworkLinkRtcpObserver link_observer; RtcpTransceiverConfig config = DefaultTestConfig(); config.network_link_observer = &link_observer; Timestamp receive_time = Timestamp::Seconds(5678); RtcpTransceiverImpl rtcp_transceiver(config); EXPECT_CALL(link_observer, OnTransportFeedback(receive_time, _)) .WillOnce(WithArg<1>([](const rtcp::TransportFeedback& message) { EXPECT_EQ(message.GetBaseSequence(), 321); EXPECT_THAT(message.GetReceivedPackets(), SizeIs(2)); })); rtcp::TransportFeedback tb; tb.SetBase(/*base_sequence=*/321, Timestamp::Micros(15)); tb.AddReceivedPacket(/*base_sequence=*/321, Timestamp::Micros(15)); tb.AddReceivedPacket(/*base_sequence=*/322, Timestamp::Micros(17)); rtcp_transceiver.ReceivePacket(tb.Build(), receive_time); } TEST_F(RtcpTransceiverImplTest, ParsesRemb) { MockNetworkLinkRtcpObserver link_observer; RtcpTransceiverConfig config = DefaultTestConfig(); config.network_link_observer = &link_observer; Timestamp receive_time = Timestamp::Seconds(5678); RtcpTransceiverImpl rtcp_transceiver(config); EXPECT_CALL(link_observer, OnReceiverEstimatedMaxBitrate(receive_time, DataRate::BitsPerSec(1'234'000))); rtcp::Remb remb; remb.SetBitrateBps(1'234'000); rtcp_transceiver.ReceivePacket(remb.Build(), receive_time); } TEST_F(RtcpTransceiverImplTest, CombinesReportBlocksFromSenderAndRecieverReports) { MockNetworkLinkRtcpObserver link_observer; RtcpTransceiverConfig config = DefaultTestConfig(); config.network_link_observer = &link_observer; Timestamp receive_time = Timestamp::Seconds(5678); RtcpTransceiverImpl rtcp_transceiver(config); // Assemble compound packet with multiple rtcp packets in it. rtcp::CompoundPacket packet; auto sr = std::make_unique(); sr->SetSenderSsrc(1234); sr->SetReportBlocks(std::vector(31)); packet.Append(std::move(sr)); auto rr1 = std::make_unique(); rr1->SetReportBlocks(std::vector(31)); packet.Append(std::move(rr1)); auto rr2 = std::make_unique(); rr2->SetReportBlocks(std::vector(2)); packet.Append(std::move(rr2)); EXPECT_CALL(link_observer, OnReport(receive_time, SizeIs(64))); rtcp_transceiver.ReceivePacket(packet.Build(), receive_time); } TEST_F(RtcpTransceiverImplTest, CallbackOnReportBlocksFromSenderAndReceiverReports) { static constexpr uint32_t kRemoteSsrc = 5678; // Has registered sender, report block attached to sender report. static constexpr uint32_t kMediaSsrc1 = 1234; // No registered sender, report block attached to receiver report. // Such report block shouldn't prevent handling following report block. static constexpr uint32_t kMediaSsrc2 = 1235; // Has registered sender, no report block attached. static constexpr uint32_t kMediaSsrc3 = 1236; // Has registered sender, report block attached to receiver report. static constexpr uint32_t kMediaSsrc4 = 1237; MockNetworkLinkRtcpObserver link_observer; RtcpTransceiverConfig config = DefaultTestConfig(); Timestamp receive_time = Timestamp::Seconds(5678); RtcpTransceiverImpl rtcp_transceiver(config); MockRtpStreamRtcpHandler local_stream1; MockRtpStreamRtcpHandler local_stream3; MockRtpStreamRtcpHandler local_stream4; EXPECT_CALL(local_stream1, OnReport(Property(&ReportBlockData::sender_ssrc, kRemoteSsrc))); EXPECT_CALL(local_stream3, OnReport).Times(0); EXPECT_CALL(local_stream4, OnReport(Property(&ReportBlockData::sender_ssrc, kRemoteSsrc))); ASSERT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1)); ASSERT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc3, &local_stream3)); ASSERT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc4, &local_stream4)); // Assemble compound packet with multiple RTCP packets in it. rtcp::CompoundPacket packet; auto sr = std::make_unique(); sr->SetSenderSsrc(kRemoteSsrc); std::vector rb(1); rb[0].SetMediaSsrc(kMediaSsrc1); sr->SetReportBlocks(std::move(rb)); packet.Append(std::move(sr)); auto rr = std::make_unique(); rr->SetSenderSsrc(kRemoteSsrc); rb = std::vector(2); rb[0].SetMediaSsrc(kMediaSsrc2); rb[1].SetMediaSsrc(kMediaSsrc4); rr->SetReportBlocks(std::move(rb)); packet.Append(std::move(rr)); rtcp_transceiver.ReceivePacket(packet.Build(), receive_time); } TEST_F(RtcpTransceiverImplTest, FailsToRegisterTwoSendersWithTheSameSsrc) { RtcpTransceiverImpl rtcp_transceiver(DefaultTestConfig()); MockRtpStreamRtcpHandler sender1; MockRtpStreamRtcpHandler sender2; EXPECT_TRUE(rtcp_transceiver.AddMediaSender(/*local_ssrc=*/10001, &sender1)); EXPECT_FALSE(rtcp_transceiver.AddMediaSender(/*local_ssrc=*/10001, &sender2)); EXPECT_TRUE(rtcp_transceiver.AddMediaSender(/*local_ssrc=*/10002, &sender2)); EXPECT_TRUE(rtcp_transceiver.RemoveMediaSender(/*local_ssrc=*/10001)); EXPECT_FALSE(rtcp_transceiver.RemoveMediaSender(/*local_ssrc=*/10001)); } TEST_F(RtcpTransceiverImplTest, SendsSenderReport) { static constexpr uint32_t kFeedbackSsrc = 123; static constexpr uint32_t kSenderSsrc = 12345; RtcpTransceiverConfig config = DefaultTestConfig(); config.feedback_ssrc = kFeedbackSsrc; RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.schedule_periodic_compound_packets = false; RtcpTransceiverImpl rtcp_transceiver(config); RtpStreamRtcpHandler::RtpStats sender_stats; sender_stats.set_num_sent_packets(10); sender_stats.set_num_sent_bytes(1000); sender_stats.set_last_rtp_timestamp(0x3333); sender_stats.set_last_capture_time(CurrentTime() - TimeDelta::Seconds(2)); sender_stats.set_last_clock_rate(0x1000); MockRtpStreamRtcpHandler sender; ON_CALL(sender, SentStats).WillByDefault(Return(sender_stats)); rtcp_transceiver.AddMediaSender(kSenderSsrc, &sender); rtcp_transceiver.SendCompoundPacket(); ASSERT_GT(rtcp_parser.sender_report()->num_packets(), 0); EXPECT_EQ(rtcp_parser.sender_report()->sender_ssrc(), kSenderSsrc); EXPECT_EQ(rtcp_parser.sender_report()->ntp(), time_controller().GetClock()->CurrentNtpTime()); EXPECT_EQ(rtcp_parser.sender_report()->rtp_timestamp(), 0x3333u + 0x2000u); EXPECT_EQ(rtcp_parser.sender_report()->sender_packet_count(), 10u); EXPECT_EQ(rtcp_parser.sender_report()->sender_octet_count(), 1000u); } TEST_F(RtcpTransceiverImplTest, MaySendBothSenderReportAndReceiverReportInTheSamePacket) { RtcpPacketParser rtcp_parser; std::vector statistics_report_blocks(40); MockReceiveStatisticsProvider receive_statistics; EXPECT_CALL(receive_statistics, RtcpReportBlocks(/*max_blocks=*/Ge(40u))) .WillOnce(Return(statistics_report_blocks)); RtcpTransceiverConfig config = DefaultTestConfig(); config.rtcp_transport = RtcpParserTransport(rtcp_parser); config.receive_statistics = &receive_statistics; RtcpTransceiverImpl rtcp_transceiver(config); MockRtpStreamRtcpHandler sender; rtcp_transceiver.AddMediaSender(/*ssrc=*/12345, &sender); rtcp_transceiver.SendCompoundPacket(); // Expect a single RTCP packet with a sender and a receiver reports in it. EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1}); ASSERT_EQ(rtcp_parser.sender_report()->num_packets(), 1); ASSERT_EQ(rtcp_parser.receiver_report()->num_packets(), 1); // Sender report may contain up to 31 report blocks, thus remaining 9 report // block should be attached to the receiver report. EXPECT_THAT(rtcp_parser.sender_report()->report_blocks(), SizeIs(31)); EXPECT_THAT(rtcp_parser.receiver_report()->report_blocks(), SizeIs(9)); } TEST_F(RtcpTransceiverImplTest, RotatesSendersWhenAllSenderReportDoNotFit) { // Send 6 compound packet, each should contain 5 sender reports, // each of 6 senders should be mentioned 5 times. static constexpr int kNumSenders = 6; static constexpr uint32_t kSenderSsrc[kNumSenders] = {10, 20, 30, 40, 50, 60}; static constexpr int kSendersPerPacket = 5; // RtcpPacketParser remembers only latest block for each type, but this test // is about sending multiple sender reports in the same packet, thus need // a more advance parser: RtcpTranceiver RtcpTransceiverConfig receiver_config = DefaultTestConfig(); RtcpTransceiverImpl rtcp_receiver(receiver_config); // Main expectatation: all senders are spread equally across multiple packets. NiceMock receiver[kNumSenders]; for (int i = 0; i < kNumSenders; ++i) { SCOPED_TRACE(i); EXPECT_CALL(receiver[i], OnSenderReport(kSenderSsrc[i], _, _)) .Times(kSendersPerPacket); rtcp_receiver.AddMediaReceiverRtcpObserver(kSenderSsrc[i], &receiver[i]); } MockFunction)> transport; EXPECT_CALL(transport, Call) .Times(kNumSenders) .WillRepeatedly([&](rtc::ArrayView packet) { rtcp_receiver.ReceivePacket(packet, CurrentTime()); return true; }); RtcpTransceiverConfig config = DefaultTestConfig(); config.rtcp_transport = transport.AsStdFunction(); // Limit packet to have space just for kSendersPerPacket sender reports. // Sender report without report blocks require 28 bytes. config.max_packet_size = kSendersPerPacket * 28; RtcpTransceiverImpl rtcp_transceiver(config); NiceMock sender[kNumSenders]; for (int i = 0; i < kNumSenders; ++i) { rtcp_transceiver.AddMediaSender(kSenderSsrc[i], &sender[i]); } for (int i = 1; i <= kNumSenders; ++i) { SCOPED_TRACE(i); rtcp_transceiver.SendCompoundPacket(); } } TEST_F(RtcpTransceiverImplTest, SkipsSenderReportForInactiveSender) { static constexpr uint32_t kSenderSsrc[] = {12345, 23456}; RtcpTransceiverConfig config = DefaultTestConfig(); RtcpPacketParser rtcp_parser; config.rtcp_transport = RtcpParserTransport(rtcp_parser); RtcpTransceiverImpl rtcp_transceiver(config); RtpStreamRtcpHandler::RtpStats sender_stats[2]; NiceMock sender[2]; ON_CALL(sender[0], SentStats).WillByDefault([&] { return sender_stats[0]; }); ON_CALL(sender[1], SentStats).WillByDefault([&] { return sender_stats[1]; }); rtcp_transceiver.AddMediaSender(kSenderSsrc[0], &sender[0]); rtcp_transceiver.AddMediaSender(kSenderSsrc[1], &sender[1]); // Start with both senders beeing active. sender_stats[0].set_num_sent_packets(10); sender_stats[0].set_num_sent_bytes(1'000); sender_stats[1].set_num_sent_packets(5); sender_stats[1].set_num_sent_bytes(2'000); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1}); EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 2); // Keep 1st sender active, but make 2nd second look inactive by returning the // same RtpStats. sender_stats[0].set_num_sent_packets(15); sender_stats[0].set_num_sent_bytes(2'000); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{2}); EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 3); EXPECT_EQ(rtcp_parser.sender_report()->sender_ssrc(), kSenderSsrc[0]); // Swap active sender. sender_stats[1].set_num_sent_packets(20); sender_stats[1].set_num_sent_bytes(3'000); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{3}); EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 4); EXPECT_EQ(rtcp_parser.sender_report()->sender_ssrc(), kSenderSsrc[1]); // Activate both senders again. sender_stats[0].set_num_sent_packets(20); sender_stats[0].set_num_sent_bytes(3'000); sender_stats[1].set_num_sent_packets(25); sender_stats[1].set_num_sent_bytes(3'500); rtcp_transceiver.SendCompoundPacket(); EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{4}); EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 6); } } // namespace } // namespace webrtc