|  | /* | 
|  | *  Copyright (c) 2012 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_sender.h" | 
|  |  | 
|  | #include <cstddef> | 
|  | #include <cstdint> | 
|  | #include <memory> | 
|  | #include <optional> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "api/array_view.h" | 
|  | #include "api/call/transport.h" | 
|  | #include "api/environment/environment.h" | 
|  | #include "api/environment/environment_factory.h" | 
|  | #include "api/rtp_headers.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/rtcp_statistics.h" | 
|  | #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" | 
|  | #include "modules/rtp_rtcp/source/rtcp_packet.h" | 
|  | #include "modules/rtp_rtcp/source/rtcp_packet/bye.h" | 
|  | #include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" | 
|  | #include "modules/rtp_rtcp/source/rtcp_packet/dlrr.h" | 
|  | #include "modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h" | 
|  | #include "modules/rtp_rtcp/source/rtcp_packet/report_block.h" | 
|  | #include "modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h" | 
|  | #include "modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h" | 
|  | #include "modules/rtp_rtcp/source/rtp_packet_received.h" | 
|  | #include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h" | 
|  | #include "modules/rtp_rtcp/source/rtp_rtcp_interface.h" | 
|  | #include "rtc_base/rate_limiter.h" | 
|  | #include "rtc_base/thread.h" | 
|  | #include "system_wrappers/include/clock.h" | 
|  | #include "system_wrappers/include/ntp_time.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::Eq; | 
|  | using ::testing::Property; | 
|  | using ::testing::SizeIs; | 
|  |  | 
|  | class RtcpPacketTypeCounterObserverImpl : public RtcpPacketTypeCounterObserver { | 
|  | public: | 
|  | RtcpPacketTypeCounterObserverImpl() : ssrc_(0) {} | 
|  | ~RtcpPacketTypeCounterObserverImpl() override = default; | 
|  | void RtcpPacketTypesCounterUpdated( | 
|  | uint32_t ssrc, | 
|  | const RtcpPacketTypeCounter& packet_counter) override { | 
|  | ssrc_ = ssrc; | 
|  | counter_ = packet_counter; | 
|  | } | 
|  | uint32_t ssrc_; | 
|  | RtcpPacketTypeCounter counter_; | 
|  | }; | 
|  |  | 
|  | class TestTransport : public Transport { | 
|  | public: | 
|  | TestTransport() {} | 
|  |  | 
|  | bool SendRtp(ArrayView<const uint8_t> /*data*/, | 
|  | const PacketOptions& /* options */) override { | 
|  | return false; | 
|  | } | 
|  | bool SendRtcp(ArrayView<const uint8_t> data, | 
|  | const PacketOptions& options) override { | 
|  | EXPECT_FALSE(options.is_media); | 
|  | parser_.Parse(data); | 
|  | return true; | 
|  | } | 
|  | test::RtcpPacketParser parser_; | 
|  | }; | 
|  |  | 
|  | constexpr uint32_t kSenderSsrc = 0x11111111; | 
|  | constexpr uint32_t kRemoteSsrc = 0x22222222; | 
|  | constexpr uint32_t kStartRtpTimestamp = 0x34567; | 
|  | constexpr uint32_t kRtpTimestamp = 0x45678; | 
|  |  | 
|  | class RtcpSenderTest : public ::testing::Test { | 
|  | protected: | 
|  | RtcpSenderTest() | 
|  | : clock_(1335900000), | 
|  | env_(CreateEnvironment(&clock_)), | 
|  | receive_statistics_(ReceiveStatistics::Create(&clock_)), | 
|  | rtp_rtcp_impl_(env_, GetDefaultRtpRtcpConfig()) {} | 
|  |  | 
|  | RTCPSender::Configuration GetDefaultConfig() { | 
|  | RTCPSender::Configuration configuration; | 
|  | configuration.audio = false; | 
|  | configuration.outgoing_transport = &test_transport_; | 
|  | configuration.rtcp_report_interval = TimeDelta::Seconds(1); | 
|  | configuration.receive_statistics = receive_statistics_.get(); | 
|  | configuration.local_media_ssrc = kSenderSsrc; | 
|  | configuration.schedule_next_rtcp_send_evaluation = [](TimeDelta) {}; | 
|  | return configuration; | 
|  | } | 
|  |  | 
|  | RtpRtcpInterface::Configuration GetDefaultRtpRtcpConfig() { | 
|  | RTCPSender::Configuration config = GetDefaultConfig(); | 
|  | RtpRtcpInterface::Configuration result; | 
|  | result.audio = config.audio; | 
|  | result.outgoing_transport = config.outgoing_transport; | 
|  | result.rtcp_report_interval_ms = config.rtcp_report_interval.ms(); | 
|  | result.receive_statistics = config.receive_statistics; | 
|  | result.local_media_ssrc = config.local_media_ssrc; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<RTCPSender> CreateRtcpSender(RTCPSender::Configuration config, | 
|  | bool init_timestamps = true) { | 
|  | auto rtcp_sender = std::make_unique<RTCPSender>(env_, std::move(config)); | 
|  | rtcp_sender->SetRemoteSSRC(kRemoteSsrc); | 
|  | if (init_timestamps) { | 
|  | rtcp_sender->SetTimestampOffset(kStartRtpTimestamp); | 
|  | rtcp_sender->SetLastRtpTime(kRtpTimestamp, env_.clock().CurrentTime(), | 
|  | /*payload_type=*/0); | 
|  | } | 
|  | return rtcp_sender; | 
|  | } | 
|  |  | 
|  | void InsertIncomingPacket(uint32_t remote_ssrc, uint16_t seq_num) { | 
|  | RtpPacketReceived packet; | 
|  | packet.SetSsrc(remote_ssrc); | 
|  | packet.SetSequenceNumber(seq_num); | 
|  | packet.SetTimestamp(12345); | 
|  | packet.SetPayloadSize(100 - 12); | 
|  | receive_statistics_->OnRtpPacket(packet); | 
|  | } | 
|  |  | 
|  | test::RtcpPacketParser* parser() { return &test_transport_.parser_; } | 
|  |  | 
|  | RTCPSender::FeedbackState feedback_state() { | 
|  | return rtp_rtcp_impl_.GetFeedbackState(); | 
|  | } | 
|  |  | 
|  | AutoThread main_thread_; | 
|  | SimulatedClock clock_; | 
|  | const Environment env_; | 
|  | TestTransport test_transport_; | 
|  | std::unique_ptr<ReceiveStatistics> receive_statistics_; | 
|  | ModuleRtpRtcpImpl2 rtp_rtcp_impl_; | 
|  | }; | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SetRtcpStatus) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | EXPECT_EQ(RtcpMode::kOff, rtcp_sender->Status()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | EXPECT_EQ(RtcpMode::kReducedSize, rtcp_sender->Status()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SetSendingStatus) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | EXPECT_FALSE(rtcp_sender->Sending()); | 
|  | rtcp_sender->SetSendingStatus(feedback_state(), true); | 
|  | EXPECT_TRUE(rtcp_sender->Sending()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, NoPacketSentIfOff) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kOff); | 
|  | EXPECT_EQ(-1, rtcp_sender->SendRTCP(feedback_state(), kRtcpSr)); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendSr) { | 
|  | const uint32_t kPacketCount = 0x12345; | 
|  | const uint32_t kOctetCount = 0x23456; | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_.GetFeedbackState(); | 
|  | rtcp_sender->SetSendingStatus(feedback_state, true); | 
|  | feedback_state.packets_sent = kPacketCount; | 
|  | feedback_state.media_bytes_sent = kOctetCount; | 
|  | NtpTime ntp = clock_.CurrentNtpTime(); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state, kRtcpSr)); | 
|  | EXPECT_EQ(1, parser()->sender_report()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->sender_report()->sender_ssrc()); | 
|  | EXPECT_EQ(ntp, parser()->sender_report()->ntp()); | 
|  | EXPECT_EQ(kPacketCount, parser()->sender_report()->sender_packet_count()); | 
|  | EXPECT_EQ(kOctetCount, parser()->sender_report()->sender_octet_count()); | 
|  | EXPECT_EQ(kStartRtpTimestamp + kRtpTimestamp, | 
|  | parser()->sender_report()->rtp_timestamp()); | 
|  | EXPECT_EQ(0U, parser()->sender_report()->report_blocks().size()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendConsecutiveSrWithExactSlope) { | 
|  | const uint32_t kPacketCount = 0x12345; | 
|  | const uint32_t kOctetCount = 0x23456; | 
|  | const int kTimeBetweenSRsUs = 10043;  // Not exact value in milliseconds. | 
|  | const int kExtraPackets = 30; | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | // Make sure clock is not exactly at some milliseconds point. | 
|  | clock_.AdvanceTimeMicroseconds(kTimeBetweenSRsUs); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_.GetFeedbackState(); | 
|  | rtcp_sender->SetSendingStatus(feedback_state, true); | 
|  | feedback_state.packets_sent = kPacketCount; | 
|  | feedback_state.media_bytes_sent = kOctetCount; | 
|  |  | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state, kRtcpSr)); | 
|  | EXPECT_EQ(1, parser()->sender_report()->num_packets()); | 
|  | NtpTime ntp1 = parser()->sender_report()->ntp(); | 
|  | uint32_t rtp1 = parser()->sender_report()->rtp_timestamp(); | 
|  |  | 
|  | // Send more SRs to ensure slope is always exact for different offsets | 
|  | for (int packets = 1; packets <= kExtraPackets; ++packets) { | 
|  | clock_.AdvanceTimeMicroseconds(kTimeBetweenSRsUs); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state, kRtcpSr)); | 
|  | EXPECT_EQ(packets + 1, parser()->sender_report()->num_packets()); | 
|  |  | 
|  | NtpTime ntp2 = parser()->sender_report()->ntp(); | 
|  | uint32_t rtp2 = parser()->sender_report()->rtp_timestamp(); | 
|  |  | 
|  | uint32_t ntp_diff_in_rtp_units = | 
|  | (ntp2.ToMs() - ntp1.ToMs()) * (kVideoPayloadTypeFrequency / 1000); | 
|  | EXPECT_EQ(rtp2 - rtp1, ntp_diff_in_rtp_units); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, DoNotSendSrBeforeRtp) { | 
|  | auto rtcp_sender = | 
|  | CreateRtcpSender(GetDefaultConfig(), /*init_timestamps=*/false); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | rtcp_sender->SetSendingStatus(feedback_state(), true); | 
|  |  | 
|  | // Sender Report shouldn't be send as an SR nor as a Report. | 
|  | rtcp_sender->SendRTCP(feedback_state(), kRtcpSr); | 
|  | EXPECT_EQ(0, parser()->sender_report()->num_packets()); | 
|  | rtcp_sender->SendRTCP(feedback_state(), kRtcpReport); | 
|  | EXPECT_EQ(0, parser()->sender_report()->num_packets()); | 
|  | // Other packets (e.g. Pli) are allowed, even if useless. | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpPli)); | 
|  | EXPECT_EQ(1, parser()->pli()->num_packets()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, DoNotSendCompundBeforeRtp) { | 
|  | auto rtcp_sender = | 
|  | CreateRtcpSender(GetDefaultConfig(), /*init_timestamps=*/false); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | rtcp_sender->SetSendingStatus(feedback_state(), true); | 
|  |  | 
|  | // In compound mode no packets are allowed (e.g. Pli) because compound mode | 
|  | // should start with Sender Report. | 
|  | EXPECT_EQ(-1, rtcp_sender->SendRTCP(feedback_state(), kRtcpPli)); | 
|  | EXPECT_EQ(0, parser()->pli()->num_packets()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendRr) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpRr)); | 
|  | EXPECT_EQ(1, parser()->receiver_report()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->receiver_report()->sender_ssrc()); | 
|  | EXPECT_EQ(0U, parser()->receiver_report()->report_blocks().size()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, DoesntSendEmptyRrInReducedSizeMode) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | rtcp_sender->SendRTCP(feedback_state(), kRtcpRr); | 
|  | EXPECT_EQ(parser()->receiver_report()->num_packets(), 0); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendRrWithOneReportBlock) { | 
|  | const uint16_t kSeqNum = 11111; | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | InsertIncomingPacket(kRemoteSsrc, kSeqNum); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpRr)); | 
|  | EXPECT_EQ(1, parser()->receiver_report()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->receiver_report()->sender_ssrc()); | 
|  | ASSERT_EQ(1U, parser()->receiver_report()->report_blocks().size()); | 
|  | const rtcp::ReportBlock& rb = parser()->receiver_report()->report_blocks()[0]; | 
|  | EXPECT_EQ(kRemoteSsrc, rb.source_ssrc()); | 
|  | EXPECT_EQ(0U, rb.fraction_lost()); | 
|  | EXPECT_EQ(0, rb.cumulative_lost()); | 
|  | EXPECT_EQ(kSeqNum, rb.extended_high_seq_num()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendRrWithTwoReportBlocks) { | 
|  | const uint16_t kSeqNum = 11111; | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | InsertIncomingPacket(kRemoteSsrc, kSeqNum); | 
|  | InsertIncomingPacket(kRemoteSsrc + 1, kSeqNum + 1); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpRr)); | 
|  | EXPECT_EQ(1, parser()->receiver_report()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->receiver_report()->sender_ssrc()); | 
|  | EXPECT_THAT( | 
|  | parser()->receiver_report()->report_blocks(), | 
|  | UnorderedElementsAre( | 
|  | Property(&rtcp::ReportBlock::source_ssrc, Eq(kRemoteSsrc)), | 
|  | Property(&rtcp::ReportBlock::source_ssrc, Eq(kRemoteSsrc + 1)))); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendSdes) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | EXPECT_EQ(0, rtcp_sender->SetCNAME("alice@host")); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpSdes)); | 
|  | EXPECT_EQ(1, parser()->sdes()->num_packets()); | 
|  | EXPECT_EQ(1U, parser()->sdes()->chunks().size()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->sdes()->chunks()[0].ssrc); | 
|  | EXPECT_EQ("alice@host", parser()->sdes()->chunks()[0].cname); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SdesIncludedInCompoundPacket) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | EXPECT_EQ(0, rtcp_sender->SetCNAME("alice@host")); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpReport)); | 
|  | EXPECT_EQ(1, parser()->receiver_report()->num_packets()); | 
|  | EXPECT_EQ(1, parser()->sdes()->num_packets()); | 
|  | EXPECT_EQ(1U, parser()->sdes()->chunks().size()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendBye) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpBye)); | 
|  | EXPECT_EQ(1, parser()->bye()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->bye()->sender_ssrc()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, StopSendingDoesNotTriggersBye) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | rtcp_sender->SetSendingStatus(feedback_state(), true); | 
|  | rtcp_sender->SetSendingStatus(feedback_state(), false); | 
|  | EXPECT_EQ(0, parser()->bye()->num_packets()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendFir) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpFir)); | 
|  | EXPECT_EQ(1, parser()->fir()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->fir()->sender_ssrc()); | 
|  | EXPECT_EQ(1U, parser()->fir()->requests().size()); | 
|  | EXPECT_EQ(kRemoteSsrc, parser()->fir()->requests()[0].ssrc); | 
|  | uint8_t seq = parser()->fir()->requests()[0].seq_nr; | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpFir)); | 
|  | EXPECT_EQ(2, parser()->fir()->num_packets()); | 
|  | EXPECT_EQ(seq + 1, parser()->fir()->requests()[0].seq_nr); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendPli) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpPli)); | 
|  | EXPECT_EQ(1, parser()->pli()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->pli()->sender_ssrc()); | 
|  | EXPECT_EQ(kRemoteSsrc, parser()->pli()->media_ssrc()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendNack) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | const uint16_t kList[] = {0, 1, 16}; | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpNack, kList)); | 
|  | EXPECT_EQ(1, parser()->nack()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->nack()->sender_ssrc()); | 
|  | EXPECT_EQ(kRemoteSsrc, parser()->nack()->media_ssrc()); | 
|  | EXPECT_THAT(parser()->nack()->packet_ids(), ElementsAre(0, 1, 16)); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendLossNotificationBufferingNotAllowed) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | constexpr uint16_t kLastDecoded = 0x1234; | 
|  | constexpr uint16_t kLastReceived = 0x4321; | 
|  | constexpr bool kDecodabilityFlag = true; | 
|  | constexpr bool kBufferingAllowed = false; | 
|  | EXPECT_EQ(rtcp_sender->SendLossNotification(feedback_state(), kLastDecoded, | 
|  | kLastReceived, kDecodabilityFlag, | 
|  | kBufferingAllowed), | 
|  | 0); | 
|  | EXPECT_EQ(parser()->processed_rtcp_packets(), 1u); | 
|  | EXPECT_EQ(parser()->loss_notification()->num_packets(), 1); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->loss_notification()->sender_ssrc()); | 
|  | EXPECT_EQ(kRemoteSsrc, parser()->loss_notification()->media_ssrc()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendLossNotificationBufferingAllowed) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | constexpr uint16_t kLastDecoded = 0x1234; | 
|  | constexpr uint16_t kLastReceived = 0x4321; | 
|  | constexpr bool kDecodabilityFlag = true; | 
|  | constexpr bool kBufferingAllowed = true; | 
|  | EXPECT_EQ(rtcp_sender->SendLossNotification(feedback_state(), kLastDecoded, | 
|  | kLastReceived, kDecodabilityFlag, | 
|  | kBufferingAllowed), | 
|  | 0); | 
|  |  | 
|  | // No RTCP messages sent yet. | 
|  | ASSERT_EQ(parser()->processed_rtcp_packets(), 0u); | 
|  |  | 
|  | // Sending another messages triggers sending the LNTF messages as well. | 
|  | const uint16_t kList[] = {0, 1, 16}; | 
|  | EXPECT_EQ(rtcp_sender->SendRTCP(feedback_state(), kRtcpNack, kList), 0); | 
|  |  | 
|  | // Exactly one packet was produced, and it contained both the buffered LNTF | 
|  | // as well as the message that had triggered the packet. | 
|  | EXPECT_EQ(parser()->processed_rtcp_packets(), 1u); | 
|  | EXPECT_EQ(parser()->loss_notification()->num_packets(), 1); | 
|  | EXPECT_EQ(parser()->loss_notification()->sender_ssrc(), kSenderSsrc); | 
|  | EXPECT_EQ(parser()->loss_notification()->media_ssrc(), kRemoteSsrc); | 
|  | EXPECT_EQ(parser()->nack()->num_packets(), 1); | 
|  | EXPECT_EQ(parser()->nack()->sender_ssrc(), kSenderSsrc); | 
|  | EXPECT_EQ(parser()->nack()->media_ssrc(), kRemoteSsrc); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, RembNotIncludedBeforeSet) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  |  | 
|  | rtcp_sender->SendRTCP(feedback_state(), kRtcpRr); | 
|  |  | 
|  | ASSERT_EQ(1, parser()->receiver_report()->num_packets()); | 
|  | EXPECT_EQ(0, parser()->remb()->num_packets()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, RembNotIncludedAfterUnset) { | 
|  | const int64_t kBitrate = 261011; | 
|  | const std::vector<uint32_t> kSsrcs = {kRemoteSsrc, kRemoteSsrc + 1}; | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | rtcp_sender->SetRemb(kBitrate, kSsrcs); | 
|  | rtcp_sender->SendRTCP(feedback_state(), kRtcpRr); | 
|  | ASSERT_EQ(1, parser()->receiver_report()->num_packets()); | 
|  | EXPECT_EQ(1, parser()->remb()->num_packets()); | 
|  |  | 
|  | // Turn off REMB. rtcp_sender no longer should send it. | 
|  | rtcp_sender->UnsetRemb(); | 
|  | rtcp_sender->SendRTCP(feedback_state(), kRtcpRr); | 
|  | ASSERT_EQ(2, parser()->receiver_report()->num_packets()); | 
|  | EXPECT_EQ(1, parser()->remb()->num_packets()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendRemb) { | 
|  | const int64_t kBitrate = 261011; | 
|  | const std::vector<uint32_t> kSsrcs = {kRemoteSsrc, kRemoteSsrc + 1}; | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | rtcp_sender->SetRemb(kBitrate, kSsrcs); | 
|  |  | 
|  | rtcp_sender->SendRTCP(feedback_state(), kRtcpRemb); | 
|  |  | 
|  | EXPECT_EQ(1, parser()->remb()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->remb()->sender_ssrc()); | 
|  | EXPECT_EQ(kBitrate, parser()->remb()->bitrate_bps()); | 
|  | EXPECT_THAT(parser()->remb()->ssrcs(), | 
|  | ElementsAre(kRemoteSsrc, kRemoteSsrc + 1)); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, RembIncludedInEachCompoundPacketAfterSet) { | 
|  | const int kBitrate = 261011; | 
|  | const std::vector<uint32_t> kSsrcs = {kRemoteSsrc, kRemoteSsrc + 1}; | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | rtcp_sender->SetRemb(kBitrate, kSsrcs); | 
|  |  | 
|  | rtcp_sender->SendRTCP(feedback_state(), kRtcpReport); | 
|  | EXPECT_EQ(1, parser()->remb()->num_packets()); | 
|  | // REMB should be included in each compound packet. | 
|  | rtcp_sender->SendRTCP(feedback_state(), kRtcpReport); | 
|  | EXPECT_EQ(2, parser()->remb()->num_packets()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendXrWithDlrr) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_.GetFeedbackState(); | 
|  | rtcp::ReceiveTimeInfo last_xr_rr; | 
|  | last_xr_rr.ssrc = 0x11111111; | 
|  | last_xr_rr.last_rr = 0x22222222; | 
|  | last_xr_rr.delay_since_last_rr = 0x33333333; | 
|  | feedback_state.last_xr_rtis.push_back(last_xr_rr); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state, kRtcpReport)); | 
|  | EXPECT_EQ(1, parser()->xr()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->xr()->sender_ssrc()); | 
|  | ASSERT_THAT(parser()->xr()->dlrr().sub_blocks(), SizeIs(1)); | 
|  | EXPECT_EQ(last_xr_rr.ssrc, parser()->xr()->dlrr().sub_blocks()[0].ssrc); | 
|  | EXPECT_EQ(last_xr_rr.last_rr, parser()->xr()->dlrr().sub_blocks()[0].last_rr); | 
|  | EXPECT_EQ(last_xr_rr.delay_since_last_rr, | 
|  | parser()->xr()->dlrr().sub_blocks()[0].delay_since_last_rr); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendXrWithMultipleDlrrSubBlocks) { | 
|  | const size_t kNumReceivers = 2; | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_.GetFeedbackState(); | 
|  | for (size_t i = 0; i < kNumReceivers; ++i) { | 
|  | rtcp::ReceiveTimeInfo last_xr_rr; | 
|  | last_xr_rr.ssrc = i; | 
|  | last_xr_rr.last_rr = (i + 1) * 100; | 
|  | last_xr_rr.delay_since_last_rr = (i + 2) * 200; | 
|  | feedback_state.last_xr_rtis.push_back(last_xr_rr); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state, kRtcpReport)); | 
|  | EXPECT_EQ(1, parser()->xr()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->xr()->sender_ssrc()); | 
|  | ASSERT_THAT(parser()->xr()->dlrr().sub_blocks(), SizeIs(kNumReceivers)); | 
|  | for (size_t i = 0; i < kNumReceivers; ++i) { | 
|  | EXPECT_EQ(feedback_state.last_xr_rtis[i].ssrc, | 
|  | parser()->xr()->dlrr().sub_blocks()[i].ssrc); | 
|  | EXPECT_EQ(feedback_state.last_xr_rtis[i].last_rr, | 
|  | parser()->xr()->dlrr().sub_blocks()[i].last_rr); | 
|  | EXPECT_EQ(feedback_state.last_xr_rtis[i].delay_since_last_rr, | 
|  | parser()->xr()->dlrr().sub_blocks()[i].delay_since_last_rr); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendXrWithRrtr) { | 
|  | RTCPSender::Configuration config = GetDefaultConfig(); | 
|  | config.non_sender_rtt_measurement = true; | 
|  | auto rtcp_sender = CreateRtcpSender(std::move(config)); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | rtcp_sender->SetSendingStatus(feedback_state(), false); | 
|  | NtpTime ntp = clock_.CurrentNtpTime(); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpReport)); | 
|  | EXPECT_EQ(1, parser()->xr()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->xr()->sender_ssrc()); | 
|  | EXPECT_FALSE(parser()->xr()->dlrr()); | 
|  | ASSERT_TRUE(parser()->xr()->rrtr()); | 
|  | EXPECT_EQ(ntp, parser()->xr()->rrtr()->ntp()); | 
|  | } | 
|  |  | 
|  | // Same test as above, but enable Rrtr with the setter. | 
|  | TEST_F(RtcpSenderTest, SendXrWithRrtrUsingSetter) { | 
|  | RTCPSender::Configuration config = GetDefaultConfig(); | 
|  | config.non_sender_rtt_measurement = false; | 
|  | auto rtcp_sender = CreateRtcpSender(std::move(config)); | 
|  | rtcp_sender->SetNonSenderRttMeasurement(true); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | rtcp_sender->SetSendingStatus(feedback_state(), false); | 
|  | NtpTime ntp = clock_.CurrentNtpTime(); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpReport)); | 
|  | EXPECT_EQ(1, parser()->xr()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->xr()->sender_ssrc()); | 
|  | EXPECT_FALSE(parser()->xr()->dlrr()); | 
|  | ASSERT_TRUE(parser()->xr()->rrtr()); | 
|  | EXPECT_EQ(ntp, parser()->xr()->rrtr()->ntp()); | 
|  | } | 
|  |  | 
|  | // Same test as above, but disable Rrtr with the setter. | 
|  | TEST_F(RtcpSenderTest, SendsNoRrtrUsingSetter) { | 
|  | RTCPSender::Configuration config = GetDefaultConfig(); | 
|  | config.non_sender_rtt_measurement = true; | 
|  | auto rtcp_sender = CreateRtcpSender(std::move(config)); | 
|  | rtcp_sender->SetNonSenderRttMeasurement(false); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | rtcp_sender->SetSendingStatus(feedback_state(), false); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpReport)); | 
|  | EXPECT_EQ(0, parser()->xr()->num_packets()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, TestNoXrRrtrSentIfSending) { | 
|  | RTCPSender::Configuration config = GetDefaultConfig(); | 
|  | config.non_sender_rtt_measurement = true; | 
|  | auto rtcp_sender = CreateRtcpSender(std::move(config)); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | rtcp_sender->SetSendingStatus(feedback_state(), true); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpReport)); | 
|  | EXPECT_EQ(0, parser()->xr()->num_packets()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, TestNoXrRrtrSentIfNotEnabled) { | 
|  | RTCPSender::Configuration config = GetDefaultConfig(); | 
|  | config.non_sender_rtt_measurement = false; | 
|  | auto rtcp_sender = CreateRtcpSender(std::move(config)); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | rtcp_sender->SetSendingStatus(feedback_state(), false); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpReport)); | 
|  | EXPECT_EQ(0, parser()->xr()->num_packets()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, TestRegisterRtcpPacketTypeObserver) { | 
|  | RtcpPacketTypeCounterObserverImpl observer; | 
|  | RTCPSender::Configuration config = GetDefaultConfig(); | 
|  | config.rtcp_packet_type_counter_observer = &observer; | 
|  | auto rtcp_sender = CreateRtcpSender(std::move(config)); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpPli)); | 
|  | EXPECT_EQ(1, parser()->pli()->num_packets()); | 
|  | EXPECT_EQ(kRemoteSsrc, observer.ssrc_); | 
|  | EXPECT_EQ(1U, observer.counter_.pli_packets); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendTmmbr) { | 
|  | const unsigned int kBitrateBps = 312000; | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | rtcp_sender->SetTargetBitrate(kBitrateBps); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpTmmbr)); | 
|  | EXPECT_EQ(1, parser()->tmmbr()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->tmmbr()->sender_ssrc()); | 
|  | EXPECT_EQ(1U, parser()->tmmbr()->requests().size()); | 
|  | EXPECT_EQ(kBitrateBps, parser()->tmmbr()->requests()[0].bitrate_bps()); | 
|  | // TODO(asapersson): tmmbr_item()->Overhead() looks broken, always zero. | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendTmmbn) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | rtcp_sender->SetSendingStatus(feedback_state(), true); | 
|  | std::vector<rtcp::TmmbItem> bounding_set; | 
|  | const uint32_t kBitrateBps = 32768000; | 
|  | const uint32_t kPacketOh = 40; | 
|  | const uint32_t kSourceSsrc = 12345; | 
|  | const rtcp::TmmbItem tmmbn(kSourceSsrc, kBitrateBps, kPacketOh); | 
|  | bounding_set.push_back(tmmbn); | 
|  | rtcp_sender->SetTmmbn(bounding_set); | 
|  |  | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpSr)); | 
|  | EXPECT_EQ(1, parser()->sender_report()->num_packets()); | 
|  | EXPECT_EQ(1, parser()->tmmbn()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->tmmbn()->sender_ssrc()); | 
|  | EXPECT_EQ(1U, parser()->tmmbn()->items().size()); | 
|  | EXPECT_EQ(kBitrateBps, parser()->tmmbn()->items()[0].bitrate_bps()); | 
|  | EXPECT_EQ(kPacketOh, parser()->tmmbn()->items()[0].packet_overhead()); | 
|  | EXPECT_EQ(kSourceSsrc, parser()->tmmbn()->items()[0].ssrc()); | 
|  | } | 
|  |  | 
|  | // This test is written to verify actual behaviour. It does not seem | 
|  | // to make much sense to send an empty TMMBN, since there is no place | 
|  | // to put an actual limit here. It's just information that no limit | 
|  | // is set, which is kind of the starting assumption. | 
|  | // See http://code.google.com/p/webrtc/issues/detail?id=468 for one | 
|  | // situation where this caused confusion. | 
|  | TEST_F(RtcpSenderTest, SendsTmmbnIfSetAndEmpty) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | rtcp_sender->SetSendingStatus(feedback_state(), true); | 
|  | std::vector<rtcp::TmmbItem> bounding_set; | 
|  | rtcp_sender->SetTmmbn(bounding_set); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpSr)); | 
|  | EXPECT_EQ(1, parser()->sender_report()->num_packets()); | 
|  | EXPECT_EQ(1, parser()->tmmbn()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->tmmbn()->sender_ssrc()); | 
|  | EXPECT_EQ(0U, parser()->tmmbn()->items().size()); | 
|  | } | 
|  |  | 
|  | // This test is written to verify that BYE is always the last packet | 
|  | // type in a RTCP compoud packet.  The rtcp_sender is recreated with | 
|  | // mock_transport, which is used to check for whether BYE at the end | 
|  | // of a RTCP compound packet. | 
|  | TEST_F(RtcpSenderTest, ByeMustBeLast) { | 
|  | MockTransport mock_transport; | 
|  | EXPECT_CALL(mock_transport, SendRtcp(_, _)) | 
|  | .WillOnce([](ArrayView<const uint8_t> data, ::testing::Unused) { | 
|  | const uint8_t* next_packet = data.data(); | 
|  | const uint8_t* const packet_end = data.data() + data.size(); | 
|  | rtcp::CommonHeader packet; | 
|  | while (next_packet < packet_end) { | 
|  | EXPECT_TRUE(packet.Parse(next_packet, packet_end - next_packet)); | 
|  | next_packet = packet.NextPacket(); | 
|  | if (packet.type() == | 
|  | rtcp::Bye::kPacketType)  // Main test expectation. | 
|  | EXPECT_EQ(0, packet_end - next_packet) | 
|  | << "Bye packet should be last in a compound RTCP packet."; | 
|  | if (next_packet == packet_end)  // Validate test was set correctly. | 
|  | EXPECT_EQ(packet.type(), rtcp::Bye::kPacketType) | 
|  | << "Last packet in this test expected to be Bye."; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | }); | 
|  |  | 
|  | // Re-configure rtcp_sender with mock_transport_ | 
|  | RTCPSender::Configuration config = GetDefaultConfig(); | 
|  | config.outgoing_transport = &mock_transport; | 
|  | auto rtcp_sender = CreateRtcpSender(std::move(config)); | 
|  |  | 
|  | rtcp_sender->SetTimestampOffset(kStartRtpTimestamp); | 
|  | rtcp_sender->SetLastRtpTime(kRtpTimestamp, clock_.CurrentTime(), | 
|  | /*payload_type=*/0); | 
|  |  | 
|  | // Set up REMB info to be included with BYE. | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | rtcp_sender->SetRemb(1234, {}); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpBye)); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendXrWithTargetBitrate) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | const size_t kNumSpatialLayers = 2; | 
|  | const size_t kNumTemporalLayers = 2; | 
|  | VideoBitrateAllocation allocation; | 
|  | for (size_t sl = 0; sl < kNumSpatialLayers; ++sl) { | 
|  | uint32_t start_bitrate_bps = (sl + 1) * 100000; | 
|  | for (size_t tl = 0; tl < kNumTemporalLayers; ++tl) | 
|  | allocation.SetBitrate(sl, tl, start_bitrate_bps + (tl * 20000)); | 
|  | } | 
|  | rtcp_sender->SetVideoBitrateAllocation(allocation); | 
|  |  | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpReport)); | 
|  | EXPECT_EQ(1, parser()->xr()->num_packets()); | 
|  | EXPECT_EQ(kSenderSsrc, parser()->xr()->sender_ssrc()); | 
|  | const std::optional<rtcp::TargetBitrate>& target_bitrate = | 
|  | parser()->xr()->target_bitrate(); | 
|  | ASSERT_TRUE(target_bitrate); | 
|  | const std::vector<rtcp::TargetBitrate::BitrateItem>& bitrates = | 
|  | target_bitrate->GetTargetBitrates(); | 
|  | EXPECT_EQ(kNumSpatialLayers * kNumTemporalLayers, bitrates.size()); | 
|  |  | 
|  | for (size_t sl = 0; sl < kNumSpatialLayers; ++sl) { | 
|  | uint32_t start_bitrate_bps = (sl + 1) * 100000; | 
|  | for (size_t tl = 0; tl < kNumTemporalLayers; ++tl) { | 
|  | size_t index = (sl * kNumSpatialLayers) + tl; | 
|  | const rtcp::TargetBitrate::BitrateItem& item = bitrates[index]; | 
|  | EXPECT_EQ(sl, item.spatial_layer); | 
|  | EXPECT_EQ(tl, item.temporal_layer); | 
|  | EXPECT_EQ(start_bitrate_bps + (tl * 20000), | 
|  | item.target_bitrate_kbps * 1000); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendImmediateXrWithTargetBitrate) { | 
|  | // Initialize. Send a first report right away. | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpReport)); | 
|  | clock_.AdvanceTimeMilliseconds(5); | 
|  |  | 
|  | // Video bitrate allocation generated, save until next time we send a report. | 
|  | VideoBitrateAllocation allocation; | 
|  | allocation.SetBitrate(0, 0, 100000); | 
|  | rtcp_sender->SetVideoBitrateAllocation(allocation); | 
|  | // First seen instance will be sent immediately. | 
|  | EXPECT_TRUE(rtcp_sender->TimeToSendRTCPReport(false)); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpReport)); | 
|  | clock_.AdvanceTimeMilliseconds(5); | 
|  |  | 
|  | // Update bitrate of existing layer, does not quality for immediate sending. | 
|  | allocation.SetBitrate(0, 0, 150000); | 
|  | rtcp_sender->SetVideoBitrateAllocation(allocation); | 
|  | EXPECT_FALSE(rtcp_sender->TimeToSendRTCPReport(false)); | 
|  |  | 
|  | // A new spatial layer enabled, signal this as soon as possible. | 
|  | allocation.SetBitrate(1, 0, 200000); | 
|  | rtcp_sender->SetVideoBitrateAllocation(allocation); | 
|  | EXPECT_TRUE(rtcp_sender->TimeToSendRTCPReport(false)); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpReport)); | 
|  | clock_.AdvanceTimeMilliseconds(5); | 
|  |  | 
|  | // Explicitly disable top layer. The same set of layers now has a bitrate | 
|  | // defined, but the explicit 0 indicates shutdown. Signal immediately. | 
|  | allocation.SetBitrate(1, 0, 0); | 
|  | EXPECT_FALSE(rtcp_sender->TimeToSendRTCPReport(false)); | 
|  | rtcp_sender->SetVideoBitrateAllocation(allocation); | 
|  | EXPECT_TRUE(rtcp_sender->TimeToSendRTCPReport(false)); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendTargetBitrateExplicitZeroOnStreamRemoval) { | 
|  | // Set up and send a bitrate allocation with two layers. | 
|  |  | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); | 
|  | VideoBitrateAllocation allocation; | 
|  | allocation.SetBitrate(0, 0, 100000); | 
|  | allocation.SetBitrate(1, 0, 200000); | 
|  | rtcp_sender->SetVideoBitrateAllocation(allocation); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpReport)); | 
|  | std::optional<rtcp::TargetBitrate> target_bitrate = | 
|  | parser()->xr()->target_bitrate(); | 
|  | ASSERT_TRUE(target_bitrate); | 
|  | std::vector<rtcp::TargetBitrate::BitrateItem> bitrates = | 
|  | target_bitrate->GetTargetBitrates(); | 
|  | ASSERT_EQ(2u, bitrates.size()); | 
|  | EXPECT_EQ(bitrates[0].target_bitrate_kbps, | 
|  | allocation.GetBitrate(0, 0) / 1000); | 
|  | EXPECT_EQ(bitrates[1].target_bitrate_kbps, | 
|  | allocation.GetBitrate(1, 0) / 1000); | 
|  |  | 
|  | // Create a new allocation, where the second stream is no longer available. | 
|  | VideoBitrateAllocation new_allocation; | 
|  | new_allocation.SetBitrate(0, 0, 150000); | 
|  | rtcp_sender->SetVideoBitrateAllocation(new_allocation); | 
|  | EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpReport)); | 
|  | target_bitrate = parser()->xr()->target_bitrate(); | 
|  | ASSERT_TRUE(target_bitrate); | 
|  | bitrates = target_bitrate->GetTargetBitrates(); | 
|  |  | 
|  | // Two bitrates should still be set, with an explicit entry indicating the | 
|  | // removed stream is gone. | 
|  | ASSERT_EQ(2u, bitrates.size()); | 
|  | EXPECT_EQ(bitrates[0].target_bitrate_kbps, | 
|  | new_allocation.GetBitrate(0, 0) / 1000); | 
|  | EXPECT_EQ(bitrates[1].target_bitrate_kbps, 0u); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, DoesntSchedulesInitialReportWhenSsrcSetOnConstruction) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  | rtcp_sender->SetRemoteSSRC(kRemoteSsrc); | 
|  | // New report should not have been scheduled yet. | 
|  | clock_.AdvanceTimeMilliseconds(100); | 
|  | EXPECT_FALSE(rtcp_sender->TimeToSendRTCPReport(false)); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendsCombinedRtcpPacket) { | 
|  | auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); | 
|  | rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); | 
|  |  | 
|  | std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets; | 
|  | auto transport_feedback = std::make_unique<rtcp::TransportFeedback>(); | 
|  | transport_feedback->AddReceivedPacket(321, Timestamp::Millis(10)); | 
|  | packets.push_back(std::move(transport_feedback)); | 
|  | auto remote_estimate = std::make_unique<rtcp::RemoteEstimate>(); | 
|  | packets.push_back(std::move(remote_estimate)); | 
|  | rtcp_sender->SendCombinedRtcpPacket(std::move(packets)); | 
|  |  | 
|  | EXPECT_EQ(parser()->transport_feedback()->num_packets(), 1); | 
|  | EXPECT_EQ(parser()->transport_feedback()->sender_ssrc(), kSenderSsrc); | 
|  | EXPECT_EQ(parser()->app()->num_packets(), 1); | 
|  | EXPECT_EQ(parser()->app()->sender_ssrc(), kSenderSsrc); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace webrtc |