diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn index e410fdad23..d0ce7829d7 100644 --- a/modules/rtp_rtcp/BUILD.gn +++ b/modules/rtp_rtcp/BUILD.gn @@ -121,6 +121,8 @@ rtc_static_library("rtp_rtcp") { "source/forward_error_correction.h", "source/forward_error_correction_internal.cc", "source/forward_error_correction_internal.h", + "source/mid_oracle.cc", + "source/mid_oracle.h", "source/packet_loss_stats.cc", "source/packet_loss_stats.h", "source/playout_delay_oracle.cc", @@ -332,6 +334,7 @@ if (rtc_include_tests) { "source/flexfec_header_reader_writer_unittest.cc", "source/flexfec_receiver_unittest.cc", "source/flexfec_sender_unittest.cc", + "source/mid_oracle_unittest.cc", "source/nack_rtx_unittest.cc", "source/packet_loss_stats_unittest.cc", "source/playout_delay_oracle_unittest.cc", diff --git a/modules/rtp_rtcp/include/rtp_rtcp.h b/modules/rtp_rtcp/include/rtp_rtcp.h index dd25d5677f..de9016a5ed 100644 --- a/modules/rtp_rtcp/include/rtp_rtcp.h +++ b/modules/rtp_rtcp/include/rtp_rtcp.h @@ -171,6 +171,11 @@ class RtpRtcp : public Module, public RtcpFeedbackSenderInterface { // Sets SSRC, default is a random number. virtual void SetSSRC(uint32_t ssrc) = 0; + // Sets the value for sending in the MID RTP header extension. + // The MID RTP header extension should be registered for this to do anything. + // Once set, this value can not be changed or removed. + virtual void SetMid(const std::string& mid) = 0; + // Sets CSRC. // |csrcs| - vector of CSRCs virtual void SetCsrcs(const std::vector& csrcs) = 0; diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h index 4dc3642ccd..51ed6fcded 100644 --- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h +++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h @@ -12,6 +12,7 @@ #define MODULES_RTP_RTCP_MOCKS_MOCK_RTP_RTCP_H_ #include +#include #include #include @@ -80,6 +81,7 @@ class MockRtpRtcp : public RtpRtcp { MOCK_CONST_METHOD0(GetRtxState, RtpState()); MOCK_CONST_METHOD0(SSRC, uint32_t()); MOCK_METHOD1(SetSSRC, void(uint32_t ssrc)); + MOCK_METHOD1(SetMid, void(const std::string& mid)); MOCK_CONST_METHOD1(CSRCs, int32_t(uint32_t csrcs[kRtpCsrcSize])); MOCK_METHOD1(SetCsrcs, void(const std::vector& csrcs)); MOCK_METHOD1(SetCSRCStatus, int32_t(bool include)); diff --git a/modules/rtp_rtcp/source/mid_oracle.cc b/modules/rtp_rtcp/source/mid_oracle.cc new file mode 100644 index 0000000000..6d91ad5e3b --- /dev/null +++ b/modules/rtp_rtcp/source/mid_oracle.cc @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 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/mid_oracle.h" + +namespace webrtc { + +MidOracle::MidOracle(const std::string& mid) : mid_(mid) {} + +MidOracle::~MidOracle() = default; + +void MidOracle::OnReceivedRtcpReportBlocks( + const ReportBlockList& report_blocks) { + if (!send_mid_) { + return; + } + for (const RTCPReportBlock& report_block : report_blocks) { + if (report_block.source_ssrc == ssrc_) { + send_mid_ = false; + break; + } + } +} + +} // namespace webrtc diff --git a/modules/rtp_rtcp/source/mid_oracle.h b/modules/rtp_rtcp/source/mid_oracle.h new file mode 100644 index 0000000000..541ee27753 --- /dev/null +++ b/modules/rtp_rtcp/source/mid_oracle.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_RTP_RTCP_SOURCE_MID_ORACLE_H_ +#define MODULES_RTP_RTCP_SOURCE_MID_ORACLE_H_ + +#include +#include + +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "rtc_base/constructormagic.h" + +namespace webrtc { + +// The MidOracle instructs an RTPSender to send the MID header extension on a +// new SSRC stream until it receives an RTCP report block for that stream (which +// implies that the remote side is able to demultiplex it and can remember the +// MID --> SSRC mapping). +class MidOracle { + public: + explicit MidOracle(const std::string& mid); + ~MidOracle(); + + // MID value to put into the header extension. + const std::string& mid() const { return mid_; } + + // True if the MID header extension should be included on the next outgoing + // packet. + bool send_mid() const { return send_mid_; } + + // Change the RTP stream SSRC. This will cause MIDs to be included until an + // RTCP report block lists this SSRC as received. + void SetSsrc(uint32_t ssrc) { + ssrc_ = ssrc; + send_mid_ = true; + } + + // Feedback to decide when to stop sending the MID header extension. + void OnReceivedRtcpReportBlocks(const ReportBlockList& report_blocks); + + private: + const std::string mid_; + bool send_mid_ = false; + uint32_t ssrc_ = 0; + + RTC_DISALLOW_COPY_AND_ASSIGN(MidOracle); +}; + +} // namespace webrtc + +#endif // MODULES_RTP_RTCP_SOURCE_MID_ORACLE_H_ diff --git a/modules/rtp_rtcp/source/mid_oracle_unittest.cc b/modules/rtp_rtcp/source/mid_oracle_unittest.cc new file mode 100644 index 0000000000..06e79307e3 --- /dev/null +++ b/modules/rtp_rtcp/source/mid_oracle_unittest.cc @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018 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/mid_oracle.h" + +#include "rtc_base/logging.h" +#include "test/gtest.h" + +namespace { + +using ::webrtc::RTCPReportBlock; +using ::webrtc::MidOracle; + +RTCPReportBlock ReportBlockWithSourceSsrc(uint32_t ssrc) { + RTCPReportBlock report_block; + report_block.source_ssrc = ssrc; + return report_block; +} + +TEST(MidOracleTest, DoNotSendMidInitially) { + MidOracle mid_oracle("mid"); + EXPECT_FALSE(mid_oracle.send_mid()); +} + +TEST(MidOracleTest, SendMidOnceSsrcSet) { + MidOracle mid_oracle("mid"); + mid_oracle.SetSsrc(52); + EXPECT_TRUE(mid_oracle.send_mid()); +} + +TEST(MidOracleTest, IgnoreReportBlockWithUnknownSourceSsrc) { + MidOracle mid_oracle("mid"); + mid_oracle.SetSsrc(52); + mid_oracle.OnReceivedRtcpReportBlocks({ReportBlockWithSourceSsrc(63)}); + EXPECT_TRUE(mid_oracle.send_mid()); +} + +TEST(MidOracleTest, StopSendingMidAfterReceivingRtcpReportWithKnownSourceSsrc) { + constexpr uint32_t kSsrc = 52; + + MidOracle mid_oracle("mid"); + mid_oracle.SetSsrc(kSsrc); + mid_oracle.OnReceivedRtcpReportBlocks({ReportBlockWithSourceSsrc(kSsrc)}); + + EXPECT_FALSE(mid_oracle.send_mid()); +} + +TEST(MidOracleTest, RestartSendingMidWhenSsrcChanges) { + constexpr uint32_t kInitialSsrc = 52; + constexpr uint32_t kChangedSsrc = 63; + + MidOracle mid_oracle("mid"); + mid_oracle.SetSsrc(kInitialSsrc); + mid_oracle.OnReceivedRtcpReportBlocks( + {ReportBlockWithSourceSsrc(kInitialSsrc)}); + mid_oracle.SetSsrc(kChangedSsrc); + + EXPECT_TRUE(mid_oracle.send_mid()); +} + +} // namespace diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index 7bdfa968fa..27d39624f1 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -340,6 +340,14 @@ void ModuleRtpRtcpImpl::SetSSRC(const uint32_t ssrc) { SetRtcpReceiverSsrcs(ssrc); } +void ModuleRtpRtcpImpl::SetMid(const std::string& mid) { + if (rtp_sender_) { + rtp_sender_->SetMid(mid); + } + // TODO(bugs.webrtc.org/4050): If we end up supporting the MID SDES item for + // RTCP, this will need to be passed down to the RTCPSender also. +} + void ModuleRtpRtcpImpl::SetCsrcs(const std::vector& csrcs) { rtcp_sender_.SetCsrcs(csrcs); rtp_sender_->SetCsrcs(csrcs); diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/modules/rtp_rtcp/source/rtp_rtcp_impl.h index b08fdf8b12..fa47c0ae14 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl.h +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -86,6 +87,8 @@ class ModuleRtpRtcpImpl : public RtpRtcp, public RTCPReceiver::ModuleRtpRtcp { // Configure SSRC, default is a random number. void SetSSRC(uint32_t ssrc) override; + void SetMid(const std::string& mid) override; + void SetCsrcs(const std::vector& csrcs) override; RTCPSender::FeedbackState GetFeedbackState(); diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc index b0006359d0..aa133e882f 100644 --- a/modules/rtp_rtcp/source/rtp_sender.cc +++ b/modules/rtp_rtcp/source/rtp_sender.cc @@ -11,6 +11,7 @@ #include "modules/rtp_rtcp/source/rtp_sender.h" #include +#include #include #include "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h" @@ -328,6 +329,9 @@ int RTPSender::RtxStatus() const { void RTPSender::SetRtxSsrc(uint32_t ssrc) { rtc::CritScope lock(&send_critsect_); ssrc_rtx_.emplace(ssrc); + if (mid_oracle_rtx_) { + mid_oracle_rtx_->SetSsrc(ssrc); + } } uint32_t RTPSender::RtxSsrc() const { @@ -727,6 +731,16 @@ void RTPSender::OnReceivedNack( void RTPSender::OnReceivedRtcpReportBlocks( const ReportBlockList& report_blocks) { playout_delay_oracle_.OnReceivedRtcpReportBlocks(report_blocks); + + { + rtc::CritScope lock(&send_critsect_); + if (mid_oracle_) { + mid_oracle_->OnReceivedRtcpReportBlocks(report_blocks); + } + if (mid_oracle_rtx_) { + mid_oracle_rtx_->OnReceivedRtcpReportBlocks(report_blocks); + } + } } // Called from pacer when we can send the packet. @@ -1073,6 +1087,10 @@ std::unique_ptr RTPSender::AllocatePacket() const { packet->SetExtension( playout_delay_oracle_.playout_delay()); } + if (mid_oracle_ && mid_oracle_->send_mid()) { + // This is a no-op if the MID header extension is not registered. + packet->SetExtension(mid_oracle_->mid()); + } return packet; } @@ -1145,6 +1163,9 @@ void RTPSender::SetSSRC(uint32_t ssrc) { if (!sequence_number_forced_) { sequence_number_ = random_.Rand(1, kMaxInitRtpSeqNumber); } + if (mid_oracle_) { + mid_oracle_->SetSsrc(ssrc); + } } uint32_t RTPSender::SSRC() const { @@ -1153,6 +1174,33 @@ uint32_t RTPSender::SSRC() const { return *ssrc_; } +void RTPSender::SetMid(const std::string& mid) { + // This is configured via the API. + rtc::CritScope lock(&send_critsect_); + + // Cannot change MID once sending. + RTC_DCHECK(!sending_media_); + + // Cannot change the MID if it is already set. + if (mid_oracle_) { + RTC_DCHECK_EQ(mid_oracle_->mid(), mid); + return; + } + if (mid_oracle_rtx_) { + RTC_DCHECK_EQ(mid_oracle_rtx_->mid(), mid); + return; + } + + mid_oracle_ = rtc::MakeUnique(mid); + if (ssrc_) { + mid_oracle_->SetSsrc(*ssrc_); + } + mid_oracle_rtx_ = rtc::MakeUnique(mid); + if (ssrc_rtx_) { + mid_oracle_rtx_->SetSsrc(*ssrc_rtx_); + } +} + rtc::Optional RTPSender::FlexfecSsrc() const { if (video_) { return video_->FlexfecSsrc(); @@ -1236,6 +1284,12 @@ std::unique_ptr RTPSender::BuildRtxPacket( // Replace SSRC. rtx_packet->SetSsrc(*ssrc_rtx_); + + // Possibly include the MID header extension. + if (mid_oracle_rtx_ && mid_oracle_rtx_->send_mid()) { + // This is a no-op if the MID header extension is not registered. + rtx_packet->SetExtension(mid_oracle_rtx_->mid()); + } } uint8_t* rtx_payload = diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h index a471c9dd69..c0af3400c6 100644 --- a/modules/rtp_rtcp/source/rtp_sender.h +++ b/modules/rtp_rtcp/source/rtp_sender.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -23,6 +24,7 @@ #include "modules/rtp_rtcp/include/flexfec_sender.h" #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "modules/rtp_rtcp/source/mid_oracle.h" #include "modules/rtp_rtcp/source/playout_delay_oracle.h" #include "modules/rtp_rtcp/source/rtp_packet_history.h" #include "modules/rtp_rtcp/source/rtp_rtcp_config.h" @@ -92,6 +94,8 @@ class RTPSender { void SetSSRC(uint32_t ssrc); + void SetMid(const std::string& mid); + uint16_t SequenceNumber() const; void SetSequenceNumber(uint16_t seq); @@ -134,7 +138,8 @@ class RTPSender { int32_t ReSendPacket(uint16_t packet_id); - // Feedback to decide when to stop sending playout delay. + // Feedback to decide when to stop sending the playout delay and MID header + // extensions. void OnReceivedRtcpReportBlocks(const ReportBlockList& report_blocks); // RTX. @@ -316,6 +321,7 @@ class RTPSender { // Must be explicitly set by the application, use of rtc::Optional // only to keep track of correct use. rtc::Optional ssrc_ RTC_GUARDED_BY(send_critsect_); + std::unique_ptr mid_oracle_ RTC_GUARDED_BY(send_critsect_); uint32_t last_rtp_timestamp_ RTC_GUARDED_BY(send_critsect_); int64_t capture_time_ms_ RTC_GUARDED_BY(send_critsect_); int64_t last_timestamp_time_ms_ RTC_GUARDED_BY(send_critsect_); @@ -324,6 +330,7 @@ class RTPSender { std::vector csrcs_ RTC_GUARDED_BY(send_critsect_); int rtx_ RTC_GUARDED_BY(send_critsect_); rtc::Optional ssrc_rtx_ RTC_GUARDED_BY(send_critsect_); + std::unique_ptr mid_oracle_rtx_ RTC_GUARDED_BY(send_critsect_); // Mapping rtx_payload_type_map_[associated] = rtx. std::map rtx_payload_type_map_ RTC_GUARDED_BY(send_critsect_); size_t rtp_overhead_bytes_per_packet_ RTC_GUARDED_BY(send_critsect_); diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc index 6d8de07a46..d769ec31cd 100644 --- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc @@ -43,6 +43,7 @@ const int kTransmissionTimeOffsetExtensionId = 1; const int kAbsoluteSendTimeExtensionId = 14; const int kTransportSequenceNumberExtensionId = 13; const int kVideoTimingExtensionId = 12; +const int kMidExtensionId = 11; const int kPayload = 100; const int kRtxPayload = 98; const uint32_t kTimestamp = 10; @@ -83,6 +84,7 @@ class LoopbackTransportTest : public webrtc::Transport { kAudioLevelExtensionId); receivers_extensions_.Register(kRtpExtensionVideoTiming, kVideoTimingExtensionId); + receivers_extensions_.Register(kRtpExtensionMid, kMidExtensionId); } bool SendRtp(const uint8_t* data, @@ -1171,6 +1173,30 @@ TEST_P(RtpSenderTestWithoutPacer, SendFlexfecPackets) { EXPECT_EQ(kFlexfecSsrc, flexfec_packet.Ssrc()); } +// Test that the MID header extension is included on sent packets when +// configured. +TEST_P(RtpSenderTestWithoutPacer, MidIncludedOnSentPackets) { + const char kMid[] = "mid"; + + // Register MID header extension and set the MID for the RTPSender. + rtp_sender_->SetSendingMediaStatus(false); + rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionMid, kMidExtensionId); + rtp_sender_->SetMid(kMid); + rtp_sender_->SetSendingMediaStatus(true); + + // Send a couple packets. + SendGenericPayload(); + SendGenericPayload(); + + // Expect both packets to have the MID set. + ASSERT_EQ(2u, transport_.sent_packets_.size()); + for (const RtpPacketReceived& packet : transport_.sent_packets_) { + std::string mid; + ASSERT_TRUE(packet.GetExtension(&mid)); + EXPECT_EQ(kMid, mid); + } +} + TEST_P(RtpSenderTest, FecOverheadRate) { constexpr int kFlexfecPayloadType = 118; constexpr uint32_t kMediaSsrc = 1234; diff --git a/video/video_send_stream.cc b/video/video_send_stream.cc index 61c369bfb8..0bf1cf2378 100644 --- a/video/video_send_stream.cc +++ b/video/video_send_stream.cc @@ -147,6 +147,8 @@ std::unique_ptr MaybeCreateFlexfecSender( } RTC_DCHECK_EQ(1U, config.rtp.flexfec.protected_media_ssrcs.size()); + // TODO(bugs.webrtc.org/4050): Pass down MID value once it is exposed in the + // API. return std::unique_ptr(new FlexfecSender( config.rtp.flexfec.payload_type, config.rtp.flexfec.ssrc, config.rtp.flexfec.protected_media_ssrcs[0], config.rtp.extensions,