diff options
Diffstat (limited to 'third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc')
-rw-r--r-- | third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc | 1741 |
1 files changed, 1741 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc new file mode 100644 index 0000000000..ad9561f66b --- /dev/null +++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc @@ -0,0 +1,1741 @@ +/* + * 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 <memory> +#include <utility> +#include <vector> + +#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/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/mock_transport.h" +#include "test/rtcp_packet_parser.h" + +namespace webrtc { +namespace { + +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::ElementsAreArray; +using ::testing::Ge; +using ::testing::NiceMock; +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<ReportBlock>, 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<const uint16_t>), + (override)); + MOCK_METHOD(void, OnFir, (uint32_t), (override)); + MOCK_METHOD(void, OnPli, (uint32_t), (override)); + MOCK_METHOD(void, + OnReportBlock, + (uint32_t, const rtcp::ReportBlock&), + (override)); + + private: + int num_calls_ = 0; +}; + +class MockNetworkLinkRtcpObserver : public NetworkLinkRtcpObserver { + public: + MOCK_METHOD(void, + OnRttUpdate, + (Timestamp receive_time, TimeDelta rtt), + (override)); + MOCK_METHOD(void, + OnTransportFeedback, + (Timestamp receive_time, const rtcp::TransportFeedback& feedback), + (override)); + MOCK_METHOD(void, + OnReceiverEstimatedMaxBitrate, + (Timestamp receive_time, DataRate bitrate), + (override)); + MOCK_METHOD(void, + OnReportBlocks, + (Timestamp receive_time, + rtc::ArrayView<const rtcp::ReportBlock> report_blocks), + (override)); +}; + +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 webrtc::Transport { + public: + explicit FakeRtcpTransport(TimeController& time) : time_(time) {} + + bool SendRtcp(const uint8_t* data, size_t size) override { + sent_rtcp_ = true; + return true; + } + bool SendRtp(const uint8_t*, size_t, const webrtc::PacketOptions&) override { + ADD_FAILURE() << "RtcpTransciver shouldn't send rtp packets."; + return 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; +}; + +class RtcpParserTransport : public webrtc::Transport { + public: + explicit RtcpParserTransport(RtcpPacketParser* parser) : parser_(parser) {} + // Returns total number of rtcp packet received. + int num_packets() const { return num_packets_; } + + private: + bool SendRtcp(const uint8_t* data, size_t size) override { + ++num_packets_; + parser_->Parse(data, size); + return true; + } + + bool SendRtp(const uint8_t*, size_t, const webrtc::PacketOptions&) override { + ADD_FAILURE() << "RtcpTransciver shouldn't send rtp packets."; + return true; + } + + RtcpPacketParser* const parser_; + int num_packets_ = 0; +}; + +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.outgoing_transport = &null_transport_; + 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<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue() { + return time_->GetTaskQueueFactory()->CreateTaskQueue( + "rtcp", TaskQueueFactory::Priority::NORMAL); + } + + private: + MockTransport null_transport_; + std::unique_ptr<TimeController> 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.outgoing_transport = &transport; + 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.outgoing_transport = &transport; + + 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.outgoing_transport = &transport; + 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.outgoing_transport = &transport; + config.initial_report_delay = TimeDelta::Millis(10); + config.task_queue = queue.get(); + absl::optional<RtcpTransceiverImpl> 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.outgoing_transport = &transport; + config.initial_report_delay = TimeDelta::Zero(); + config.report_period = kReportPeriod; + config.task_queue = queue.get(); + absl::optional<RtcpTransceiverImpl> 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.outgoing_transport = &transport; + config.initial_report_delay = TimeDelta::Zero(); + config.report_period = kReportPeriod; + config.task_queue = queue.get(); + absl::optional<RtcpTransceiverImpl> 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) { + MockTransport mock_transport; + RtcpTransceiverConfig config = DefaultTestConfig(); + config.initial_ready_to_send = false; + config.outgoing_transport = &mock_transport; + RtcpTransceiverImpl rtcp_transceiver(config); + + EXPECT_CALL(mock_transport, SendRtcp(_, _)).Times(0); + + const uint8_t raw[] = {1, 2, 3, 4}; + const std::vector<uint16_t> sequence_numbers = {45, 57}; + const uint32_t ssrcs[] = {123}; + rtcp_transceiver.SendCompoundPacket(); + rtcp_transceiver.SendRawPacket(raw); + rtcp_transceiver.SendNack(ssrcs[0], sequence_numbers); + rtcp_transceiver.SendPictureLossIndication(ssrcs[0]); + rtcp_transceiver.SendFullIntraRequest(ssrcs, true); +} + +TEST_F(RtcpTransceiverImplTest, SendsRtcpWhenNetworkStateIsUp) { + MockTransport mock_transport; + RtcpTransceiverConfig config = DefaultTestConfig(); + config.initial_ready_to_send = false; + config.outgoing_transport = &mock_transport; + RtcpTransceiverImpl rtcp_transceiver(config); + + rtcp_transceiver.SetReadyToSend(true); + + EXPECT_CALL(mock_transport, SendRtcp(_, _)).Times(5); + + const uint8_t raw[] = {1, 2, 3, 4}; + const std::vector<uint16_t> sequence_numbers = {45, 57}; + const uint32_t ssrcs[] = {123}; + rtcp_transceiver.SendCompoundPacket(); + rtcp_transceiver.SendRawPacket(raw); + 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.outgoing_transport = &transport; + config.task_queue = queue.get(); + absl::optional<RtcpTransceiverImpl> 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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) { + MockTransport transport; + EXPECT_CALL(transport, SendRtcp).Times(0); + NiceMock<MockReceiveStatisticsProvider> receive_statistics; + + RtcpTransceiverConfig config = DefaultTestConfig(); + config.outgoing_transport = &transport; + 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; + RtcpParserTransport transport(&rtcp_parser); + NiceMock<MockReceiveStatisticsProvider> receive_statistics; + + RtcpTransceiverConfig config = DefaultTestConfig(); + config.outgoing_transport = &transport; + 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + config.schedule_periodic_compound_packets = false; + RtcpTransceiverImpl rtcp_transceiver(config); + + rtcp_transceiver.SendCompoundPacket(); + + EXPECT_EQ(transport.num_packets(), 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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(transport.num_packets(), 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + config.schedule_periodic_compound_packets = false; + RtcpTransceiverImpl rtcp_transceiver(config); + + rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{54321, 64321}); + rtcp_transceiver.SendCompoundPacket(); + EXPECT_EQ(transport.num_packets(), 1); + ASSERT_EQ(rtcp_parser.remb()->num_packets(), 1); + + rtcp_transceiver.UnsetRemb(); + rtcp_transceiver.SendCompoundPacket(); + + EXPECT_EQ(transport.num_packets(), 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<ReportBlock> 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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<MockMediaReceiverRtcpObserver> observer1; + StrictMock<MockMediaReceiverRtcpObserver> 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<MockMediaReceiverRtcpObserver> observer1; + StrictMock<MockMediaReceiverRtcpObserver> 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<MockMediaReceiverRtcpObserver> observer1; + StrictMock<MockMediaReceiverRtcpObserver> 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<MockMediaReceiverRtcpObserver> observer1; + StrictMock<MockMediaReceiverRtcpObserver> 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<MockMediaReceiverRtcpObserver> observer1; + StrictMock<MockMediaReceiverRtcpObserver> 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<SenderReport>(); + sr->SetSenderSsrc(kRemoteSsrc); + compound.Append(std::move(sr)); + auto bye = std::make_unique<Bye>(); + 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<webrtc::rtcp::App>(); + compound.Append(std::move(app)); + auto bye = std::make_unique<Bye>(); + 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<ReportBlock> 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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<ReportBlock> 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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<ReportBlock> 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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(transport.num_packets(), 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<ReportBlock>(max_blocks); + }); + RtcpTransceiverConfig config = DefaultTestConfig(); + config.rtcp_mode = RtcpMode::kCompound; + RtcpPacketParser rtcp_parser; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + config.receive_statistics = &receive_statistics; + RtcpTransceiverImpl rtcp_transceiver(config); + + EXPECT_EQ(transport.num_packets(), 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(transport.num_packets(), 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<uint16_t> kMissingSequenceNumbers = {34, 37, 38}; + RtcpTransceiverConfig config = DefaultTestConfig(); + config.feedback_ssrc = kSenderSsrc; + config.schedule_periodic_compound_packets = false; + RtcpPacketParser rtcp_parser; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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<uint16_t> 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + RtcpTransceiverImpl rtcp_transceiver(config); + + rtcp_transceiver.SendPictureLossIndication(kRemoteSsrc); + + EXPECT_EQ(transport.num_packets(), 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + + 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(transport.num_packets(), 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + + 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(transport.num_packets(), 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + config.non_sender_rtt_measurement = false; + RtcpTransceiverImpl rtcp_transceiver(config); + + rtcp_transceiver.SendCompoundPacket(); + + EXPECT_EQ(transport.num_packets(), 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; + MockTransport null_transport; + RtcpTransceiverConfig config = DefaultTestConfig(); + config.feedback_ssrc = kSenderSsrc; + config.schedule_periodic_compound_packets = false; + config.outgoing_transport = &null_transport; + 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<rtcp::SenderReport>(); + sr->SetSenderSsrc(1234); + sr->SetReportBlocks(std::vector<ReportBlock>(31)); + packet.Append(std::move(sr)); + auto rr1 = std::make_unique<rtcp::ReceiverReport>(); + rr1->SetReportBlocks(std::vector<ReportBlock>(31)); + packet.Append(std::move(rr1)); + auto rr2 = std::make_unique<rtcp::ReceiverReport>(); + rr2->SetReportBlocks(std::vector<ReportBlock>(2)); + packet.Append(std::move(rr2)); + + EXPECT_CALL(link_observer, OnReportBlocks(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, OnReportBlock(kRemoteSsrc, _)); + EXPECT_CALL(local_stream3, OnReportBlock).Times(0); + EXPECT_CALL(local_stream4, OnReportBlock(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<rtcp::SenderReport>(); + sr->SetSenderSsrc(kRemoteSsrc); + std::vector<ReportBlock> rb(1); + rb[0].SetMediaSsrc(kMediaSsrc1); + sr->SetReportBlocks(std::move(rb)); + packet.Append(std::move(sr)); + auto rr = std::make_unique<rtcp::ReceiverReport>(); + rr->SetSenderSsrc(kRemoteSsrc); + rb = std::vector<ReportBlock>(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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + 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; + RtcpParserTransport transport(&rtcp_parser); + std::vector<ReportBlock> 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.outgoing_transport = &transport; + 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(transport.num_packets(), 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<MockMediaReceiverRtcpObserver> 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]); + } + + MockTransport transport; + EXPECT_CALL(transport, SendRtcp) + .Times(kNumSenders) + .WillRepeatedly([&](const uint8_t* data, size_t size) { + rtcp_receiver.ReceivePacket(rtc::MakeArrayView(data, size), + CurrentTime()); + return true; + }); + RtcpTransceiverConfig config = DefaultTestConfig(); + config.outgoing_transport = &transport; + // 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<MockRtpStreamRtcpHandler> 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; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + RtcpTransceiverImpl rtcp_transceiver(config); + + RtpStreamRtcpHandler::RtpStats sender_stats[2]; + NiceMock<MockRtpStreamRtcpHandler> 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(transport.num_packets(), 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(transport.num_packets(), 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(transport.num_packets(), 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(transport.num_packets(), 4); + EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 6); +} + +} // namespace +} // namespace webrtc |