diff --git a/modules/rtp_rtcp/source/rtcp_transceiver.cc b/modules/rtp_rtcp/source/rtcp_transceiver.cc index fa104f1bd3..7c668b91ae 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver.cc @@ -90,4 +90,18 @@ void RtcpTransceiver::UnsetRemb() { }); } +void RtcpTransceiver::RequestKeyFrame(std::vector ssrcs) { + // TODO(danilchap): Replace with lambda with move capture when available. + struct RequestKeyFrameClosure { + void operator()() { + if (ptr) + ptr->RequestKeyFrame(ssrcs); + } + + rtc::WeakPtr ptr; + std::vector ssrcs; + }; + task_queue_->PostTask(RequestKeyFrameClosure{ptr_, std::move(ssrcs)}); +} + } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtcp_transceiver.h b/modules/rtp_rtcp/source/rtcp_transceiver.h index 5a9998b0b0..4e571ee85a 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver.h +++ b/modules/rtp_rtcp/source/rtcp_transceiver.h @@ -44,6 +44,8 @@ class RtcpTransceiver { // Stops sending REMB in following compound packets. void UnsetRemb(); + void RequestKeyFrame(std::vector ssrcs); + private: rtc::TaskQueue* const task_queue_; std::unique_ptr rtcp_transceiver_; diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_config.cc b/modules/rtp_rtcp/source/rtcp_transceiver_config.cc index 9256d2474e..d6313ae1cd 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_config.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_config.cc @@ -48,6 +48,11 @@ bool RtcpTransceiverConfig::Validate() const { RTC_LOG(LS_ERROR) << debug_id << "outgoing transport must be set"; return false; } + if (initial_report_delay_ms < 0) { + RTC_LOG(LS_ERROR) << debug_id << "delay " << initial_report_delay_ms + << "ms before first report shouldn't be negative."; + return false; + } if (report_period_ms <= 0) { RTC_LOG(LS_ERROR) << debug_id << "period " << report_period_ms << "ms between reports should be positive."; @@ -58,6 +63,10 @@ bool RtcpTransceiverConfig::Validate() const { << "missing task queue for periodic compound packets"; return false; } + if (rtcp_mode != RtcpMode::kCompound && rtcp_mode != RtcpMode::kReducedSize) { + RTC_LOG(LS_ERROR) << debug_id << "unsupported rtcp mode"; + return false; + } // TODO(danilchap): Remove or update the warning when RtcpTransceiver supports // send-only sessions. if (receive_statistics == nullptr) diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_config.h b/modules/rtp_rtcp/source/rtcp_transceiver_config.h index 79f18c80c1..07f4c51523 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_config.h +++ b/modules/rtp_rtcp/source/rtcp_transceiver_config.h @@ -13,6 +13,7 @@ #include +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "rtc_base/task_queue.h" namespace webrtc { @@ -51,6 +52,11 @@ struct RtcpTransceiverConfig { // Rtcp report block generator for outgoing receiver reports. ReceiveStatisticsProvider* receive_statistics = nullptr; + // Configures if sending should + // enforce compound packets: https://tools.ietf.org/html/rfc4585#section-3.1 + // or allow reduced size packets: https://tools.ietf.org/html/rfc5506 + // Receiving accepts both compound and reduced-size packets. + RtcpMode rtcp_mode = RtcpMode::kCompound; // // Tuning parameters. // diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc index 2c6eb8d48b..52bc4890c9 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc @@ -17,6 +17,7 @@ #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtcp_packet.h" #include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" +#include "modules/rtp_rtcp/source/rtcp_packet/pli.h" #include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" #include "modules/rtp_rtcp/source/rtcp_packet/report_block.h" #include "modules/rtp_rtcp/source/rtcp_packet/sdes.h" @@ -28,11 +29,13 @@ #include "rtc_base/timeutils.h" namespace webrtc { -namespace { // Helper to put several RTCP packets into lower layer datagram composing // Compound or Reduced-Size RTCP packet, as defined by RFC 5506 section 2. -class PacketSender : public rtcp::RtcpPacket::PacketReadyCallback { +// TODO(danilchap): When in compound mode and packets are so many that several +// compound RTCP packets need to be generated, ensure each packet is compound. +class RtcpTransceiverImpl::PacketSender + : public rtcp::RtcpPacket::PacketReadyCallback { public: PacketSender(Transport* transport, size_t max_packet_size) : transport_(transport), max_packet_size_(max_packet_size) { @@ -56,6 +59,8 @@ class PacketSender : public rtcp::RtcpPacket::PacketReadyCallback { } } + bool IsEmpty() const { return index_ == 0; } + private: // Implements RtcpPacket::PacketReadyCallback void OnPacketReady(uint8_t* data, size_t length) override { @@ -68,8 +73,6 @@ class PacketSender : public rtcp::RtcpPacket::PacketReadyCallback { uint8_t buffer_[IP_PACKET_SIZE]; }; -} // namespace - RtcpTransceiverImpl::RtcpTransceiverImpl(const RtcpTransceiverConfig& config) : config_(config), ptr_factory_(this) { RTC_CHECK(config_.Validate()); @@ -94,12 +97,8 @@ void RtcpTransceiverImpl::ReceivePacket(rtc::ArrayView packet, } void RtcpTransceiverImpl::SendCompoundPacket() { - SendPacket(); - if (config_.schedule_periodic_compound_packets) { - // Stop existent send task. - ptr_factory_.InvalidateWeakPtrs(); - SchedulePeriodicCompoundPackets(config_.report_period_ms); - } + SendPeriodicCompoundPacket(); + ReschedulePeriodicCompoundPackets(); } void RtcpTransceiverImpl::SetRemb(int bitrate_bps, @@ -117,6 +116,27 @@ void RtcpTransceiverImpl::UnsetRemb() { remb_.reset(); } +void RtcpTransceiverImpl::RequestKeyFrame( + rtc::ArrayView ssrcs) { + RTC_DCHECK(!ssrcs.empty()); + const uint32_t sender_ssrc = config_.feedback_ssrc; + PacketSender sender(config_.outgoing_transport, config_.max_packet_size); + if (config_.rtcp_mode == RtcpMode::kCompound) + CreateCompoundPacket(&sender); + + for (uint32_t media_ssrc : ssrcs) { + rtcp::Pli pli; + pli.SetSenderSsrc(sender_ssrc); + pli.SetMediaSsrc(media_ssrc); + sender.AppendPacket(pli); + } + + sender.Send(); + + if (config_.rtcp_mode == RtcpMode::kCompound) + ReschedulePeriodicCompoundPackets(); +} + void RtcpTransceiverImpl::HandleReceivedPacket( const rtcp::CommonHeader& rtcp_packet_header, int64_t now_us) { @@ -134,17 +154,25 @@ void RtcpTransceiverImpl::HandleReceivedPacket( } } +void RtcpTransceiverImpl::ReschedulePeriodicCompoundPackets() { + if (!config_.schedule_periodic_compound_packets) + return; + // Stop existent send task. + ptr_factory_.InvalidateWeakPtrs(); + SchedulePeriodicCompoundPackets(config_.report_period_ms); +} + void RtcpTransceiverImpl::SchedulePeriodicCompoundPackets(int64_t delay_ms) { - class SendPeriodicCompoundPacket : public rtc::QueuedTask { + class SendPeriodicCompoundPacketTask : public rtc::QueuedTask { public: - SendPeriodicCompoundPacket(rtc::TaskQueue* task_queue, - rtc::WeakPtr ptr) + SendPeriodicCompoundPacketTask(rtc::TaskQueue* task_queue, + rtc::WeakPtr ptr) : task_queue_(task_queue), ptr_(std::move(ptr)) {} bool Run() override { RTC_DCHECK(task_queue_->IsCurrent()); if (!ptr_) return true; - ptr_->SendPacket(); + ptr_->SendPeriodicCompoundPacket(); task_queue_->PostDelayedTask(rtc::WrapUnique(this), ptr_->config_.report_period_ms); return false; @@ -157,7 +185,7 @@ void RtcpTransceiverImpl::SchedulePeriodicCompoundPackets(int64_t delay_ms) { RTC_DCHECK(config_.schedule_periodic_compound_packets); - auto task = rtc::MakeUnique( + auto task = rtc::MakeUnique( config_.task_queue, ptr_factory_.GetWeakPtr()); if (delay_ms > 0) config_.task_queue->PostDelayedTask(std::move(task), delay_ms); @@ -165,27 +193,30 @@ void RtcpTransceiverImpl::SchedulePeriodicCompoundPackets(int64_t delay_ms) { config_.task_queue->PostTask(std::move(task)); } -void RtcpTransceiverImpl::SendPacket() { - PacketSender sender(config_.outgoing_transport, config_.max_packet_size); +void RtcpTransceiverImpl::CreateCompoundPacket(PacketSender* sender) { + RTC_DCHECK(sender->IsEmpty()); const uint32_t sender_ssrc = config_.feedback_ssrc; - rtcp::ReceiverReport receiver_report; receiver_report.SetSenderSsrc(sender_ssrc); receiver_report.SetReportBlocks(CreateReportBlocks()); - sender.AppendPacket(receiver_report); + sender->AppendPacket(receiver_report); if (!config_.cname.empty()) { rtcp::Sdes sdes; bool added = sdes.AddCName(config_.feedback_ssrc, config_.cname); RTC_DCHECK(added) << "Failed to add cname " << config_.cname << " to rtcp sdes packet."; - sender.AppendPacket(sdes); + sender->AppendPacket(sdes); } if (remb_) { remb_->SetSenderSsrc(sender_ssrc); - sender.AppendPacket(*remb_); + sender->AppendPacket(*remb_); } +} +void RtcpTransceiverImpl::SendPeriodicCompoundPacket() { + PacketSender sender(config_.outgoing_transport, config_.max_packet_size); + CreateCompoundPacket(&sender); sender.Send(); } diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h index 689ed1e9d1..aed850b07a 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h @@ -48,7 +48,10 @@ class RtcpTransceiverImpl { // Stops sending REMB in following compound packets. void UnsetRemb(); + void RequestKeyFrame(rtc::ArrayView ssrcs); + private: + class PacketSender; struct SenderReportTimes { int64_t local_received_time_us; NtpTime remote_sent_time; @@ -57,9 +60,13 @@ class RtcpTransceiverImpl { void HandleReceivedPacket(const rtcp::CommonHeader& rtcp_packet_header, int64_t now_us); + void ReschedulePeriodicCompoundPackets(); void SchedulePeriodicCompoundPackets(int64_t delay_ms); + // Creates compound RTCP packet, as defined in + // https://tools.ietf.org/html/rfc5506#section-2 + void CreateCompoundPacket(PacketSender* sender); // Sends RTCP packets. - void SendPacket(); + void SendPeriodicCompoundPacket(); // Generate Report Blocks to be send in Sender or Receiver Report. std::vector CreateReportBlocks(); diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc index b144ec4c02..e5f7a65c7c 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc @@ -447,4 +447,69 @@ TEST(RtcpTransceiverImplTest, EXPECT_EQ(CompactNtpRttToMs(report_blocks[1].delay_since_last_sr()), 100); } +TEST(RtcpTransceiverImplTest, RequestKeyFrameWithPictureLossIndication) { + const uint32_t kSenderSsrc = 1234; + const uint32_t kRemoteSsrcs[] = {4321, 5321}; + MockTransport outgoing_transport; + RtcpTransceiverConfig config; + config.feedback_ssrc = kSenderSsrc; + config.schedule_periodic_compound_packets = false; + config.outgoing_transport = &outgoing_transport; + RtcpTransceiverImpl rtcp_transceiver(config); + RtcpPacketParser rtcp_parser; + EXPECT_CALL(outgoing_transport, SendRtcp(_, _)) + .WillOnce(Invoke(&rtcp_parser, &RtcpPacketParser::Parse)); + + rtcp_transceiver.RequestKeyFrame(kRemoteSsrcs); + + // Expect a pli packet per ssrc in the sent single compound packet. + EXPECT_EQ(rtcp_parser.pli()->num_packets(), 2); + EXPECT_EQ(rtcp_parser.pli()->sender_ssrc(), kSenderSsrc); + // test::RtcpPacketParser overwrites first pli packet with second one. + EXPECT_EQ(rtcp_parser.pli()->media_ssrc(), kRemoteSsrcs[1]); +} + +TEST(RtcpTransceiverImplTest, KeyFrameRequestCreatesCompoundPacket) { + const uint32_t kRemoteSsrcs[] = {4321}; + MockTransport outgoing_transport; + RtcpTransceiverConfig config; + // Turn periodic off to ensure sent rtcp packet is explicitly requested. + config.schedule_periodic_compound_packets = false; + config.outgoing_transport = &outgoing_transport; + + config.rtcp_mode = webrtc::RtcpMode::kCompound; + + RtcpTransceiverImpl rtcp_transceiver(config); + RtcpPacketParser rtcp_parser; + EXPECT_CALL(outgoing_transport, SendRtcp(_, _)) + .WillOnce(Invoke(&rtcp_parser, &RtcpPacketParser::Parse)); + + rtcp_transceiver.RequestKeyFrame(kRemoteSsrcs); + + // Test sent packet is compound by expecting presense of receiver report. + EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 1); +} + +TEST(RtcpTransceiverImplTest, KeyFrameRequestCreatesReducedSizePacket) { + const uint32_t kRemoteSsrcs[] = {4321}; + MockTransport outgoing_transport; + RtcpTransceiverConfig config; + // Turn periodic off to ensure sent rtcp packet is explicitly requested. + config.schedule_periodic_compound_packets = false; + config.outgoing_transport = &outgoing_transport; + + config.rtcp_mode = webrtc::RtcpMode::kReducedSize; + + RtcpTransceiverImpl rtcp_transceiver(config); + RtcpPacketParser rtcp_parser; + // Expect some rtcp packet is triggered by the RequestKeyFrame. + EXPECT_CALL(outgoing_transport, SendRtcp(_, _)) + .WillOnce(Invoke(&rtcp_parser, &RtcpPacketParser::Parse)); + + rtcp_transceiver.RequestKeyFrame(kRemoteSsrcs); + + // Test sent packet is reduced size by expecting absense of receiver report. + EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 0); +} + } // namespace