diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn index 88929dd498..0aa6678913 100644 --- a/modules/rtp_rtcp/BUILD.gn +++ b/modules/rtp_rtcp/BUILD.gn @@ -230,6 +230,7 @@ rtc_source_set("rtcp_transceiver") { deps = [ ":rtp_rtcp", "../../api:array_view", + "../../api:optional", "../../api:transport_api", "../../rtc_base:rtc_base_approved", "../../rtc_base:rtc_task_queue", diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc index 5b1224dcbd..d6127a9592 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc @@ -97,6 +97,21 @@ void RtcpTransceiverImpl::SendCompoundPacket() { ReschedulePeriodicCompoundPackets(config_.report_period_ms); } +void RtcpTransceiverImpl::SetRemb(int bitrate_bps, + std::vector ssrcs) { + RTC_DCHECK_GE(bitrate_bps, 0); + remb_.emplace(); + remb_->SetSsrcs(std::move(ssrcs)); + remb_->SetBitrateBps(bitrate_bps); + // TODO(bugs.webrtc.org/8239): Move logic from PacketRouter for sending remb + // immideately on large bitrate change when there is one RtcpTransceiver per + // rtp transport. +} + +void RtcpTransceiverImpl::UnsetRemb() { + remb_.reset(); +} + void RtcpTransceiverImpl::HandleReceivedPacket( const rtcp::CommonHeader& rtcp_packet_header) { switch (rtcp_packet_header.type()) { @@ -149,9 +164,10 @@ void RtcpTransceiverImpl::ReschedulePeriodicCompoundPackets(int64_t delay_ms) { void RtcpTransceiverImpl::SendPacket() { PacketSender sender(config_.outgoing_transport, config_.max_packet_size); + const uint32_t sender_ssrc = config_.feedback_ssrc; rtcp::ReceiverReport receiver_report; - receiver_report.SetSenderSsrc(config_.feedback_ssrc); + receiver_report.SetSenderSsrc(sender_ssrc); receiver_report.SetReportBlocks(CreateReportBlocks()); sender.AppendPacket(receiver_report); @@ -162,6 +178,10 @@ void RtcpTransceiverImpl::SendPacket() { << " to rtcp sdes packet."; sender.AppendPacket(sdes); } + if (remb_) { + remb_->SetSenderSsrc(sender_ssrc); + sender.AppendPacket(*remb_); + } sender.Send(); } diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h index db702ec2d2..81f0cace9c 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h @@ -17,7 +17,9 @@ #include #include "api/array_view.h" +#include "api/optional.h" #include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" +#include "modules/rtp_rtcp/source/rtcp_packet/remb.h" #include "modules/rtp_rtcp/source/rtcp_packet/report_block.h" #include "modules/rtp_rtcp/source/rtcp_transceiver_config.h" #include "rtc_base/constructormagic.h" @@ -40,6 +42,12 @@ class RtcpTransceiverImpl { // Sends RTCP packets starting with a sender or receiver report. void SendCompoundPacket(); + // (REMB) Receiver Estimated Max Bitrate. + // Includes REMB in following compound packets. + void SetRemb(int bitrate_bps, std::vector ssrcs); + // Stops sending REMB in following compound packets. + void UnsetRemb(); + private: struct SenderReportTimes { int64_t local_received_time_us; @@ -56,6 +64,7 @@ class RtcpTransceiverImpl { const RtcpTransceiverConfig config_; + rtc::Optional remb_; std::map last_received_sender_reports_; rtc::WeakPtrFactory ptr_factory_; diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc index c3885dec88..e38d706199 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc @@ -26,6 +26,7 @@ namespace { using ::testing::_; +using ::testing::ElementsAre; using ::testing::Invoke; using ::testing::Return; using ::testing::SizeIs; @@ -208,6 +209,112 @@ TEST(RtcpTransceiverImplTest, SendsMinimalCompoundPacket) { EXPECT_EQ(rtcp_parser.sdes()->chunks()[0].cname, config.cname); } +TEST(RtcpTransceiverImplTest, SendsNoRembInitially) { + const uint32_t kSenderSsrc = 12345; + MockTransport outgoing_transport; + RtcpTransceiverConfig config; + config.feedback_ssrc = kSenderSsrc; + config.outgoing_transport = &outgoing_transport; + config.schedule_periodic_compound_packets = false; + RtcpTransceiverImpl rtcp_transceiver(config); + RtcpPacketParser rtcp_parser; + EXPECT_CALL(outgoing_transport, SendRtcp(_, _)) + .WillOnce(Invoke(&rtcp_parser, &RtcpPacketParser::Parse)); + + rtcp_transceiver.SendCompoundPacket(); + + EXPECT_EQ(rtcp_parser.remb()->num_packets(), 0); +} + +TEST(RtcpTransceiverImplTest, SetRembIncludesRembInNextCompoundPacket) { + const uint32_t kSenderSsrc = 12345; + MockTransport outgoing_transport; + RtcpTransceiverConfig config; + config.feedback_ssrc = kSenderSsrc; + config.outgoing_transport = &outgoing_transport; + config.schedule_periodic_compound_packets = false; + RtcpTransceiverImpl rtcp_transceiver(config); + RtcpPacketParser rtcp_parser; + EXPECT_CALL(outgoing_transport, SendRtcp(_, _)) + .WillOnce(Invoke(&rtcp_parser, &RtcpPacketParser::Parse)); + + rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrc=*/{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(), 10000u); + EXPECT_THAT(rtcp_parser.remb()->ssrcs(), ElementsAre(54321, 64321)); +} + +TEST(RtcpTransceiverImplTest, SetRembUpdatesValuesToSend) { + const uint32_t kSenderSsrc = 12345; + MockTransport outgoing_transport; + RtcpTransceiverConfig config; + config.feedback_ssrc = kSenderSsrc; + config.outgoing_transport = &outgoing_transport; + config.schedule_periodic_compound_packets = false; + RtcpTransceiverImpl rtcp_transceiver(config); + RtcpPacketParser rtcp_parser; + EXPECT_CALL(outgoing_transport, SendRtcp(_, _)) + .WillRepeatedly(Invoke(&rtcp_parser, &RtcpPacketParser::Parse)); + + rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrc=*/{54321, 64321}); + rtcp_transceiver.SendCompoundPacket(); + + EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1); + EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 10000u); + EXPECT_THAT(rtcp_parser.remb()->ssrcs(), ElementsAre(54321, 64321)); + + rtcp_transceiver.SetRemb(/*bitrate_bps=*/70000, /*ssrc=*/{67321}); + rtcp_transceiver.SendCompoundPacket(); + + EXPECT_EQ(rtcp_parser.remb()->num_packets(), 2); + EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 70000u); + EXPECT_THAT(rtcp_parser.remb()->ssrcs(), ElementsAre(67321)); +} + +TEST(RtcpTransceiverImplTest, SetRembIncludesRembInAllCompoundPackets) { + const uint32_t kSenderSsrc = 12345; + MockTransport outgoing_transport; + RtcpTransceiverConfig config; + config.feedback_ssrc = kSenderSsrc; + config.outgoing_transport = &outgoing_transport; + config.schedule_periodic_compound_packets = false; + RtcpTransceiverImpl rtcp_transceiver(config); + RtcpPacketParser rtcp_parser; + EXPECT_CALL(outgoing_transport, SendRtcp(_, _)) + .WillRepeatedly(Invoke(&rtcp_parser, &RtcpPacketParser::Parse)); + + rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrc=*/{54321, 64321}); + rtcp_transceiver.SendCompoundPacket(); + rtcp_transceiver.SendCompoundPacket(); + + EXPECT_EQ(rtcp_parser.remb()->num_packets(), 2); +} + +TEST(RtcpTransceiverImplTest, SendsNoRembAfterUnset) { + const uint32_t kSenderSsrc = 12345; + MockTransport outgoing_transport; + RtcpTransceiverConfig config; + config.feedback_ssrc = kSenderSsrc; + config.outgoing_transport = &outgoing_transport; + config.schedule_periodic_compound_packets = false; + RtcpTransceiverImpl rtcp_transceiver(config); + RtcpPacketParser rtcp_parser; + EXPECT_CALL(outgoing_transport, SendRtcp(_, _)) + .WillRepeatedly(Invoke(&rtcp_parser, &RtcpPacketParser::Parse)); + + rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrc=*/{54321, 64321}); + rtcp_transceiver.SendCompoundPacket(); + ASSERT_EQ(rtcp_parser.remb()->num_packets(), 1); + + rtcp_transceiver.UnsetRemb(); + rtcp_transceiver.SendCompoundPacket(); + + EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1); +} + TEST(RtcpTransceiverImplTest, ReceiverReportUsesReceiveStatistics) { const uint32_t kSenderSsrc = 12345; const uint32_t kMediaSsrc = 54321;