diff --git a/api/rtpparameters.cc b/api/rtpparameters.cc index 2b20c56d22..6b253dc646 100644 --- a/api/rtpparameters.cc +++ b/api/rtpparameters.cc @@ -137,12 +137,20 @@ const char RtpExtension::kGenericFrameDescriptorUri[] = "http://www.webrtc.org/experiments/rtp-hdrext/generic-frame-descriptor-00"; const int RtpExtension::kGenericFrameDescriptorDefaultId = 11; +const char RtpExtension::kEncryptHeaderExtensionsUri[] = + "urn:ietf:params:rtp-hdrext:encrypt"; + const char RtpExtension::kColorSpaceUri[] = "http://www.webrtc.org/experiments/rtp-hdrext/color-space"; const int RtpExtension::kColorSpaceDefaultId = 12; -const char RtpExtension::kEncryptHeaderExtensionsUri[] = - "urn:ietf:params:rtp-hdrext:encrypt"; +const char RtpExtension::kRidUri[] = + "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"; +const int RtpExtension::kRidDefaultId = 13; + +const char RtpExtension::kRepairedRidUri[] = + "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id"; +const int RtpExtension::kRepairedRidDefaultId = 14; constexpr int RtpExtension::kMinId; constexpr int RtpExtension::kMaxId; @@ -153,7 +161,9 @@ constexpr int RtpExtension::kOneByteHeaderExtensionMaxValueSize; bool RtpExtension::IsSupportedForAudio(const std::string& uri) { return uri == webrtc::RtpExtension::kAudioLevelUri || uri == webrtc::RtpExtension::kTransportSequenceNumberUri || - uri == webrtc::RtpExtension::kMidUri; + uri == webrtc::RtpExtension::kMidUri || + uri == webrtc::RtpExtension::kRidUri || + uri == webrtc::RtpExtension::kRepairedRidUri; } bool RtpExtension::IsSupportedForVideo(const std::string& uri) { @@ -167,7 +177,9 @@ bool RtpExtension::IsSupportedForVideo(const std::string& uri) { uri == webrtc::RtpExtension::kMidUri || uri == webrtc::RtpExtension::kFrameMarkingUri || uri == webrtc::RtpExtension::kGenericFrameDescriptorUri || - uri == webrtc::RtpExtension::kColorSpaceUri; + uri == webrtc::RtpExtension::kColorSpaceUri || + uri == webrtc::RtpExtension::kRidUri || + uri == webrtc::RtpExtension::kRepairedRidUri; } bool RtpExtension::IsEncryptionSupported(const std::string& uri) { @@ -185,7 +197,9 @@ bool RtpExtension::IsEncryptionSupported(const std::string& uri) { uri == webrtc::RtpExtension::kTransportSequenceNumberUri || uri == webrtc::RtpExtension::kPlayoutDelayUri || uri == webrtc::RtpExtension::kVideoContentTypeUri || - uri == webrtc::RtpExtension::kMidUri; + uri == webrtc::RtpExtension::kMidUri || + uri == webrtc::RtpExtension::kRidUri || + uri == webrtc::RtpExtension::kRepairedRidUri; } const RtpExtension* RtpExtension::FindHeaderExtensionByUri( diff --git a/api/rtpparameters.h b/api/rtpparameters.h index 47df22e7ff..bb599ce4ae 100644 --- a/api/rtpparameters.h +++ b/api/rtpparameters.h @@ -313,6 +313,14 @@ struct RtpExtension { static const char kColorSpaceUri[]; static const int kColorSpaceDefaultId; + // Header extension for RIDs and Repaired RIDs + // https://tools.ietf.org/html/draft-ietf-avtext-rid-09 + // https://tools.ietf.org/html/draft-ietf-mmusic-rid-15 + static const char kRidUri[]; + static const int kRidDefaultId; + static const char kRepairedRidUri[]; + static const int kRepairedRidDefaultId; + // Inclusive min and max IDs for two-byte header extensions and one-byte // header extensions, per RFC8285 Section 4.2-4.3. static constexpr int kMinId = 1; diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc index 8d2362200a..c5c25c2e57 100644 --- a/audio/audio_send_stream.cc +++ b/audio/audio_send_stream.cc @@ -197,6 +197,10 @@ AudioSendStream::ExtensionIds AudioSendStream::FindExtensionIds( ids.transport_sequence_number = extension.id; } else if (extension.uri == RtpExtension::kMidUri) { ids.mid = extension.id; + } else if (extension.uri == RtpExtension::kRidUri) { + ids.rid = extension.id; + } else if (extension.uri == RtpExtension::kRepairedRidUri) { + ids.repaired_rid = extension.id; } } return ids; @@ -281,6 +285,13 @@ void AudioSendStream::ConfigureStream( channel_send->SetMid(new_config.rtp.mid, new_ids.mid); } + // RID RTP header extension + if ((first_time || new_ids.rid != old_ids.rid || + new_ids.repaired_rid != old_ids.repaired_rid || + new_config.rtp.rid != old_config.rtp.rid)) { + channel_send->SetRid(new_config.rtp.rid, new_ids.rid, new_ids.repaired_rid); + } + if (!ReconfigureSendCodec(stream, new_config)) { RTC_LOG(LS_ERROR) << "Failed to set up send codec state."; } diff --git a/audio/audio_send_stream.h b/audio/audio_send_stream.h index bf94901ef0..cea8551daf 100644 --- a/audio/audio_send_stream.h +++ b/audio/audio_send_stream.h @@ -148,6 +148,8 @@ class AudioSendStream final : public webrtc::AudioSendStream, int audio_level = 0; int transport_sequence_number = 0; int mid = 0; + int rid = 0; + int repaired_rid = 0; }; static ExtensionIds FindExtensionIds( const std::vector& extensions); diff --git a/audio/audio_send_stream_unittest.cc b/audio/audio_send_stream_unittest.cc index 82cc319921..1a173fca80 100644 --- a/audio/audio_send_stream_unittest.cc +++ b/audio/audio_send_stream_unittest.cc @@ -210,6 +210,7 @@ struct ConfigHelper { .Times(1); } EXPECT_CALL(*channel_send_, ResetSenderCongestionControlObjects()).Times(1); + EXPECT_CALL(*channel_send_, SetRid(std::string(), 0, 0)).Times(1); } void SetupMockForSetupSendCodec(bool expect_set_encoder_call) { diff --git a/audio/channel_send.cc b/audio/channel_send.cc index fa211413a8..fa69422beb 100644 --- a/audio/channel_send.cc +++ b/audio/channel_send.cc @@ -133,6 +133,9 @@ class ChannelSend // RTP+RTCP void SetLocalSSRC(uint32_t ssrc) override; + void SetRid(const std::string& rid, + int extension_id, + int repaired_extension_id) override; void SetMid(const std::string& mid, int extension_id) override; void SetExtmapAllowMixed(bool extmap_allow_mixed) override; void SetSendAudioLevelIndicationStatus(bool enable, int id) override; @@ -965,6 +968,23 @@ void ChannelSend::SetLocalSSRC(uint32_t ssrc) { _rtpRtcpModule->SetSSRC(ssrc); } +void ChannelSend::SetRid(const std::string& rid, + int extension_id, + int repaired_extension_id) { + RTC_DCHECK_RUN_ON(&worker_thread_checker_); + if (extension_id != 0) { + int ret = SetSendRtpHeaderExtension(!rid.empty(), kRtpExtensionRtpStreamId, + extension_id); + RTC_DCHECK_EQ(0, ret); + } + if (repaired_extension_id != 0) { + int ret = SetSendRtpHeaderExtension(!rid.empty(), kRtpExtensionRtpStreamId, + repaired_extension_id); + RTC_DCHECK_EQ(0, ret); + } + _rtpRtcpModule->SetRid(rid); +} + void ChannelSend::SetMid(const std::string& mid, int extension_id) { RTC_DCHECK_RUN_ON(&worker_thread_checker_); int ret = SetSendRtpHeaderExtension(true, kRtpExtensionMid, extension_id); diff --git a/audio/channel_send.h b/audio/channel_send.h index 083e9a6029..8bf53e8956 100644 --- a/audio/channel_send.h +++ b/audio/channel_send.h @@ -65,6 +65,10 @@ class ChannelSendInterface { rtc::FunctionView*)> modifier) = 0; virtual void SetLocalSSRC(uint32_t ssrc) = 0; + // Use 0 to indicate that the extension should not be registered. + virtual void SetRid(const std::string& rid, + int extension_id, + int repaired_extension_id) = 0; virtual void SetMid(const std::string& mid, int extension_id) = 0; virtual void SetRTCP_CNAME(absl::string_view c_name) = 0; virtual void SetExtmapAllowMixed(bool extmap_allow_mixed) = 0; diff --git a/audio/mock_voe_channel_proxy.h b/audio/mock_voe_channel_proxy.h index 33b00f9fc2..3aa9bf1eab 100644 --- a/audio/mock_voe_channel_proxy.h +++ b/audio/mock_voe_channel_proxy.h @@ -74,6 +74,10 @@ class MockChannelSend : public voe::ChannelSendInterface { MOCK_METHOD1( ModifyEncoder, void(rtc::FunctionView*)> modifier)); + MOCK_METHOD3(SetRid, + void(const std::string& rid, + int extension_id, + int repaired_extension_id)); MOCK_METHOD2(SetMid, void(const std::string& mid, int extension_id)); MOCK_METHOD1(SetLocalSSRC, void(uint32_t ssrc)); MOCK_METHOD1(SetRTCP_CNAME, void(absl::string_view c_name)); diff --git a/call/audio_send_stream.h b/call/audio_send_stream.h index c996dab83d..f34a51d439 100644 --- a/call/audio_send_stream.h +++ b/call/audio_send_stream.h @@ -79,6 +79,10 @@ class AudioSendStream { // Sender SSRC. uint32_t ssrc = 0; + // The value to send in the RID RTP header extension if the extension is + // included in the list of extensions. + std::string rid; + // The value to send in the MID RTP header extension if the extension is // included in the list of extensions. std::string mid; diff --git a/call/rtp_config.h b/call/rtp_config.h index 97ae951765..06a89f2616 100644 --- a/call/rtp_config.h +++ b/call/rtp_config.h @@ -66,6 +66,14 @@ struct RtpConfig { std::vector ssrcs; + // The Rtp Stream Ids (aka RIDs) to send in the RID RTP header extension + // if the extension is included in the list of extensions. + // If rids are specified, they should correspond to the |ssrcs| vector. + // This means that: + // 1. rids.size() == 0 || rids.size() == ssrcs.size(). + // 2. If rids is not empty, then |rids[i]| should use |ssrcs[i]|. + std::vector rids; + // The value to send in the MID RTP header extension if the extension is // included in the list of extensions. std::string mid; diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc index ba1b992e95..9dcc0f6426 100644 --- a/call/rtp_video_sender.cc +++ b/call/rtp_video_sender.cc @@ -225,6 +225,9 @@ RtpVideoSender::RtpVideoSender( overhead_bytes_per_packet_(0), encoder_target_rate_bps_(0) { RTC_DCHECK_EQ(ssrcs.size(), rtp_modules_.size()); + // The same argument for SSRCs is given to this method twice. + // The SSRCs are also accessed in this method through both variables. + RTC_DCHECK(ssrcs == rtp_config.ssrcs); module_process_thread_checker_.DetachFromThread(); // SSRCs are assumed to be sorted in the same order as |rtp_modules|. for (uint32_t ssrc : ssrcs) { @@ -263,6 +266,7 @@ RtpVideoSender::RtpVideoSender( ConfigureProtection(rtp_config); ConfigureSsrcs(rtp_config); + ConfigureRids(rtp_config); if (!rtp_config.mid.empty()) { for (auto& rtp_rtcp : rtp_modules_) { @@ -532,6 +536,18 @@ void RtpVideoSender::ConfigureSsrcs(const RtpConfig& rtp_config) { } } +void RtpVideoSender::ConfigureRids(const RtpConfig& rtp_config) { + RTC_DCHECK(rtp_config.rids.empty() || + rtp_config.rids.size() == rtp_config.ssrcs.size()); + RTC_DCHECK(rtp_config.rids.empty() || + rtp_config.rids.size() == rtp_modules_.size()); + for (size_t i = 0; i < rtp_config.rids.size(); ++i) { + const std::string& rid = rtp_config.rids[i]; + RtpRtcp* const rtp_rtcp = rtp_modules_[i].get(); + rtp_rtcp->SetRid(rid); + } +} + void RtpVideoSender::OnNetworkAvailability(bool network_available) { for (auto& rtp_rtcp : rtp_modules_) { rtp_rtcp->SetRTCPStatus(network_available ? rtp_config_.rtcp_mode diff --git a/call/rtp_video_sender.h b/call/rtp_video_sender.h index adac46bcd8..aff2067396 100644 --- a/call/rtp_video_sender.h +++ b/call/rtp_video_sender.h @@ -126,6 +126,7 @@ class RtpVideoSender : public RtpVideoSenderInterface, void UpdateModuleSendingState() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_); void ConfigureProtection(const RtpConfig& rtp_config); void ConfigureSsrcs(const RtpConfig& rtp_config); + void ConfigureRids(const RtpConfig& rtp_config); bool FecEnabled() const; bool NackEnabled() const; diff --git a/media/engine/webrtcvideoengine.cc b/media/engine/webrtcvideoengine.cc index b6f788bf58..69fae56382 100644 --- a/media/engine/webrtcvideoengine.cc +++ b/media/engine/webrtcvideoengine.cc @@ -514,10 +514,7 @@ RtpCapabilities WebRtcVideoEngine::GetCapabilities() const { webrtc::RtpExtension::kGenericFrameDescriptorUri, webrtc::RtpExtension::kGenericFrameDescriptorDefaultId)); } - // TODO(bugs.webrtc.org/4050): Add MID header extension as capability once MID - // demuxing is completed. - // capabilities.header_extensions.push_back(webrtc::RtpExtension( - // webrtc::RtpExtension::kMidUri, webrtc::RtpExtension::kMidDefaultId)); + return capabilities; } diff --git a/media/engine/webrtcvoiceengine.cc b/media/engine/webrtcvoiceengine.cc index 1660bd86e1..c7a41692fe 100644 --- a/media/engine/webrtcvoiceengine.cc +++ b/media/engine/webrtcvoiceengine.cc @@ -564,10 +564,7 @@ RtpCapabilities WebRtcVoiceEngine::GetCapabilities() const { webrtc::RtpExtension::kTransportSequenceNumberUri, webrtc::RtpExtension::kTransportSequenceNumberDefaultId)); } - // TODO(bugs.webrtc.org/4050): Add MID header extension as capability once MID - // demuxing is completed. - // capabilities.header_extensions.push_back(webrtc::RtpExtension( - // webrtc::RtpExtension::kMidUri, webrtc::RtpExtension::kMidDefaultId)); + return capabilities; } diff --git a/modules/rtp_rtcp/include/rtp_rtcp.h b/modules/rtp_rtcp/include/rtp_rtcp.h index cccca03c53..a927e41770 100644 --- a/modules/rtp_rtcp/include/rtp_rtcp.h +++ b/modules/rtp_rtcp/include/rtp_rtcp.h @@ -186,6 +186,12 @@ 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 RID (and Repaired) RTP header extension. + // RIDs are used to identify an RTP stream if SSRCs are not negotiated. + // If the RID and Repaired RID extensions are not registered, the RID will + // not be sent. + virtual void SetRid(const std::string& rid) = 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. diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h index 02f258e01a..b9a5d37408 100644 --- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h +++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h @@ -64,6 +64,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(SetRid, void(const std::string& rid)); 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)); diff --git a/modules/rtp_rtcp/source/rtp_packet.cc b/modules/rtp_rtcp/source/rtp_packet.cc index 9d4dce4dea..3fb80e4efd 100644 --- a/modules/rtp_rtcp/source/rtp_packet.cc +++ b/modules/rtp_rtcp/source/rtp_packet.cc @@ -569,4 +569,9 @@ rtc::ArrayView RtpPacket::AllocateExtension(ExtensionType type, return AllocateRawExtension(id, length); } +bool RtpPacket::HasExtension(ExtensionType type) const { + // TODO(webrtc:7990): Add support for empty extensions (length==0). + return !FindExtension(type).empty(); +} + } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_packet.h b/modules/rtp_rtcp/source/rtp_packet.h index 76666b7f12..ddf261a612 100644 --- a/modules/rtp_rtcp/source/rtp_packet.h +++ b/modules/rtp_rtcp/source/rtp_packet.h @@ -95,6 +95,7 @@ class RtpPacket { // Header extensions. template bool HasExtension() const; + bool HasExtension(ExtensionType type) const; template bool GetExtension(FirstValue, Values...) const; @@ -112,6 +113,14 @@ class RtpPacket { template bool ReserveExtension(); + // Find or allocate an extension |type|. Returns view of size |length| + // to write raw extension to or an empty view on failure. + rtc::ArrayView AllocateExtension(ExtensionType type, size_t length); + + // Find an extension |type|. + // Returns view of the raw extension or empty view on failure. + rtc::ArrayView FindExtension(ExtensionType type) const; + // Reserve size_bytes for payload. Returns nullptr on failure. uint8_t* SetPayloadSize(size_t size_bytes); // Same as SetPayloadSize but doesn't guarantee to keep current payload. @@ -145,10 +154,6 @@ class RtpPacket { // with the specified id if not found. ExtensionInfo& FindOrCreateExtensionInfo(int id); - // Find an extension |type|. - // Returns view of the raw extension or empty view on failure. - rtc::ArrayView FindExtension(ExtensionType type) const; - // Allocates and returns place to store rtp header extension. // Returns empty arrayview on failure. rtc::ArrayView AllocateRawExtension(int id, size_t length); @@ -159,10 +164,6 @@ class RtpPacket { uint16_t SetExtensionLengthMaybeAddZeroPadding(size_t extensions_offset); - // Find or allocate an extension |type|. Returns view of size |length| - // to write raw extension to or an empty view on failure. - rtc::ArrayView AllocateExtension(ExtensionType type, size_t length); - uint8_t* WriteAt(size_t offset) { return buffer_.data() + offset; } void WriteAt(size_t offset, uint8_t byte) { buffer_.data()[offset] = byte; } @@ -184,7 +185,7 @@ class RtpPacket { template bool RtpPacket::HasExtension() const { - return !FindExtension(Extension::kId).empty(); + return HasExtension(Extension::kId); } template diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index b854e16ab6..e4fbe71d57 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -335,6 +335,12 @@ void ModuleRtpRtcpImpl::SetSSRC(const uint32_t ssrc) { SetRtcpReceiverSsrcs(ssrc); } +void ModuleRtpRtcpImpl::SetRid(const std::string& rid) { + if (rtp_sender_) { + rtp_sender_->SetRid(rid); + } +} + void ModuleRtpRtcpImpl::SetMid(const std::string& mid) { if (rtp_sender_) { rtp_sender_->SetMid(mid); diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/modules/rtp_rtcp/source/rtp_rtcp_impl.h index 788508c376..2327d6406a 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl.h +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.h @@ -105,6 +105,8 @@ class ModuleRtpRtcpImpl : public RtpRtcp, public RTCPReceiver::ModuleRtpRtcp { // Configure SSRC, default is a random number. void SetSSRC(uint32_t ssrc) override; + void SetRid(const std::string& rid) override; + void SetMid(const std::string& mid) override; void SetCsrcs(const std::vector& csrcs) override; diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc index 3bb5552a54..cbf6b65f72 100644 --- a/modules/rtp_rtcp/source/rtp_sender.cc +++ b/modules/rtp_rtcp/source/rtp_sender.cc @@ -17,6 +17,7 @@ #include "absl/memory/memory.h" #include "absl/strings/match.h" +#include "api/array_view.h" #include "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h" #include "logging/rtc_event_log/rtc_event_log.h" #include "modules/remote_bitrate_estimator/test/bwe_test_logging.h" @@ -57,13 +58,18 @@ constexpr RtpExtensionSize CreateExtensionSize() { return {Extension::kId, Extension::kValueSizeBytes}; } +template +constexpr RtpExtensionSize CreateMaxExtensionSize() { + return {Extension::kId, Extension::kMaxValueSizeBytes}; +} + // Size info for header extensions that might be used in padding or FEC packets. constexpr RtpExtensionSize kFecOrPaddingExtensionSizes[] = { CreateExtensionSize(), CreateExtensionSize(), CreateExtensionSize(), CreateExtensionSize(), - {RtpMid::kId, RtpMid::kMaxValueSizeBytes}, + CreateMaxExtensionSize(), }; // Size info for header extensions that might be used in video packets. @@ -75,7 +81,9 @@ constexpr RtpExtensionSize kVideoExtensionSizes[] = { CreateExtensionSize(), CreateExtensionSize(), CreateExtensionSize(), - {RtpMid::kId, RtpMid::kMaxValueSizeBytes}, + CreateMaxExtensionSize(), + CreateMaxExtensionSize(), + CreateMaxExtensionSize(), {RtpGenericFrameDescriptorExtension::kId, RtpGenericFrameDescriptorExtension::kMaxSizeBytes}, }; @@ -1174,6 +1182,10 @@ std::unique_ptr RTPSender::AllocatePacket() const { // This is a no-op if the MID header extension is not registered. packet->SetExtension(mid_); } + if (!rid_.empty()) { + // This is a no-op if the RID header extension is not registered. + packet->SetExtension(rid_); + } return packet; } @@ -1258,6 +1270,13 @@ uint32_t RTPSender::SSRC() const { return *ssrc_; } +void RTPSender::SetRid(const std::string& rid) { + // RID is used in simulcast scenario when multiple layers share the same mid. + rtc::CritScope lock(&send_critsect_); + RTC_DCHECK_LE(rid.length(), RtpStreamId::kMaxValueSizeBytes); + rid_ = rid; +} + void RTPSender::SetMid(const std::string& mid) { // This is configured via the API. rtc::CritScope lock(&send_critsect_); @@ -1316,14 +1335,69 @@ bool RTPSender::SetFecParameters(const FecProtectionParams& delta_params, return true; } -std::unique_ptr RTPSender::BuildRtxPacket( - const RtpPacketToSend& packet) { +static std::unique_ptr CreateRtxPacket( + const RtpPacketToSend& packet, + RtpHeaderExtensionMap* extension_map) { + RTC_DCHECK(extension_map); // TODO(danilchap): Create rtx packet with extra capacity for SRTP // when transport interface would be updated to take buffer class. - std::unique_ptr rtx_packet(new RtpPacketToSend( - &rtp_header_extension_map_, packet.size() + kRtxHeaderSize)); + size_t packet_size = packet.size() + kRtxHeaderSize; + std::unique_ptr rtx_packet = + absl::make_unique(extension_map, packet_size); + + // Set the relevant fixed packet headers. The following are not set: + // * Payload type - it is replaced in rtx packets. + // * Sequence number - RTX has a separate sequence numbering. + // * SSRC - RTX stream has its own SSRC. + rtx_packet->SetMarker(packet.Marker()); + rtx_packet->SetTimestamp(packet.Timestamp()); + + // Set the variable fields in the packet header: + // * CSRCs - must be set before header extensions. + // * Header extensions - replace Rid header with RepairedRid header. + const std::vector csrcs = packet.Csrcs(); + rtx_packet->SetCsrcs(csrcs); + for (int extension = kRtpExtensionNone + 1; + extension < kRtpExtensionNumberOfExtensions; ++extension) { + RTPExtensionType source_extension = + static_cast(extension); + // Rid header should be replaced with RepairedRid header + RTPExtensionType destination_extension = + source_extension == kRtpExtensionRtpStreamId + ? kRtpExtensionRepairedRtpStreamId + : source_extension; + + // Empty extensions should be supported, so not checking |source.empty()|. + if (!packet.HasExtension(source_extension)) { + continue; + } + + rtc::ArrayView source = + packet.FindExtension(source_extension); + + rtc::ArrayView destination = + rtx_packet->AllocateExtension(destination_extension, source.size()); + + // Could happen if any: + // 1. Extension has 0 length. + // 2. Extension is not registered in destination. + // 3. Allocating extension in destination failed. + if (destination.empty() || source.size() != destination.size()) { + continue; + } + + std::memcpy(destination.begin(), source.begin(), destination.size()); + } + + return rtx_packet; +} + +std::unique_ptr RTPSender::BuildRtxPacket( + const RtpPacketToSend& packet) { + std::unique_ptr rtx_packet = + CreateRtxPacket(packet, &rtp_header_extension_map_); + // Add original RTP header. - rtx_packet->CopyHeaderFrom(packet); { rtc::CritScope lock(&send_critsect_); if (!sending_media_) @@ -1343,11 +1417,20 @@ std::unique_ptr RTPSender::BuildRtxPacket( // Replace SSRC. rtx_packet->SetSsrc(*ssrc_rtx_); - // Possibly include the MID header extension. + // The spec indicates that it is possible for a sender to stop sending mids + // once the SSRCs have been bound on the receiver. As a result the source + // rtp packet might not have the MID header extension set. + // However, the SSRC of the RTX stream might not have been bound on the + // receiver. This means that we should include it here. + // The same argument goes for the Repaired RID extension. if (!mid_.empty()) { // This is a no-op if the MID header extension is not registered. rtx_packet->SetExtension(mid_); } + if (!rid_.empty()) { + // This is a no-op if the Repaired-RID header extension is not registered. + // rtx_packet->SetExtension(rid_); + } } uint8_t* rtx_payload = diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h index b0998e06a5..254a7e5f1a 100644 --- a/modules/rtp_rtcp/source/rtp_sender.h +++ b/modules/rtp_rtcp/source/rtp_sender.h @@ -100,6 +100,8 @@ class RTPSender { void SetSSRC(uint32_t ssrc); + void SetRid(const std::string& rid); + void SetMid(const std::string& mid); uint16_t SequenceNumber() const; @@ -330,6 +332,8 @@ class RTPSender { // Must be explicitly set by the application, use of absl::optional // only to keep track of correct use. absl::optional ssrc_ RTC_GUARDED_BY(send_critsect_); + // RID value to send in the RID or RepairedRID header extension. + std::string rid_ RTC_GUARDED_BY(send_critsect_); // MID value to send in the MID header extension. std::string mid_ RTC_GUARDED_BY(send_critsect_); uint32_t last_rtp_timestamp_ 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 a687bcbb35..acb542e060 100644 --- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc @@ -45,6 +45,11 @@ const int kAbsoluteSendTimeExtensionId = 14; const int kTransportSequenceNumberExtensionId = 13; const int kVideoTimingExtensionId = 12; const int kMidExtensionId = 11; +const int kGenericDescriptorId = 10; +const int kAudioLevelExtensionId = 9; +const int kRidExtensionId = 8; +const int kRepairedRidExtensionId = 7; +const int kVideoRotationExtensionId = 5; const int kPayload = 100; const int kRtxPayload = 98; const uint32_t kTimestamp = 10; @@ -53,15 +58,14 @@ const uint32_t kSsrc = 725242; const int kMaxPacketLength = 1500; const uint8_t kAudioLevel = 0x5a; const uint16_t kTransportSequenceNumber = 0xaabbu; -const uint8_t kAudioLevelExtensionId = 9; const int kAudioPayload = 103; const uint64_t kStartTime = 123456789; const size_t kMaxPaddingSize = 224u; -const int kVideoRotationExtensionId = 5; const size_t kGenericHeaderLength = 1; const uint8_t kPayloadData[] = {47, 11, 32, 93, 89}; const int64_t kDefaultExpectedRetransmissionTimeMs = 125; -const int kGenericDescriptorId = 10; +const char kNoRid[] = ""; +const char kNoMid[] = ""; using ::testing::_; using ::testing::ElementsAre; @@ -91,6 +95,9 @@ class LoopbackTransportTest : public webrtc::Transport { receivers_extensions_.Register(kRtpExtensionMid, kMidExtensionId); receivers_extensions_.Register(kRtpExtensionGenericFrameDescriptor, kGenericDescriptorId); + receivers_extensions_.Register(kRtpExtensionRtpStreamId, kRidExtensionId); + receivers_extensions_.Register(kRtpExtensionRepairedRtpStreamId, + kRepairedRidExtensionId); } bool SendRtp(const uint8_t* data, @@ -1146,7 +1153,6 @@ TEST_P(RtpSenderTest, SendFlexfecPackets) { constexpr int kFlexfecPayloadType = 118; constexpr uint32_t kMediaSsrc = 1234; constexpr uint32_t kFlexfecSsrc = 5678; - const char kNoMid[] = ""; const std::vector kNoRtpExtensions; const std::vector kNoRtpExtensionSizes; FlexfecSender flexfec_sender(kFlexfecPayloadType, kFlexfecSsrc, kMediaSsrc, @@ -1205,7 +1211,6 @@ TEST_P(RtpSenderTest, NoFlexfecForTimingFrames) { constexpr int kFlexfecPayloadType = 118; constexpr uint32_t kMediaSsrc = 1234; constexpr uint32_t kFlexfecSsrc = 5678; - const char kNoMid[] = ""; const std::vector kNoRtpExtensions; const std::vector kNoRtpExtensionSizes; @@ -1305,7 +1310,6 @@ TEST_P(RtpSenderTestWithoutPacer, SendFlexfecPackets) { constexpr int kFlexfecPayloadType = 118; constexpr uint32_t kMediaSsrc = 1234; constexpr uint32_t kFlexfecSsrc = 5678; - const char kNoMid[] = ""; const std::vector kNoRtpExtensions; const std::vector kNoRtpExtensionSizes; FlexfecSender flexfec_sender(kFlexfecPayloadType, kFlexfecSsrc, kMediaSsrc, @@ -1365,11 +1369,64 @@ TEST_P(RtpSenderTestWithoutPacer, MidIncludedOnSentPackets) { } } +TEST_P(RtpSenderTestWithoutPacer, RidIncludedOnSentPackets) { + const char kRid[] = "f"; + + rtp_sender_->SetSendingMediaStatus(false); + rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionRtpStreamId, + kRidExtensionId); + rtp_sender_->SetRid(kRid); + rtp_sender_->SetSendingMediaStatus(true); + + SendGenericPayload(); + + ASSERT_EQ(1u, transport_.sent_packets_.size()); + const RtpPacketReceived& packet = transport_.sent_packets_[0]; + std::string rid; + ASSERT_TRUE(packet.GetExtension(&rid)); + EXPECT_EQ(kRid, rid); +} + +TEST_P(RtpSenderTestWithoutPacer, RidIncludedOnRtxSentPackets) { + const char kRid[] = "f"; + const uint8_t kPayloadType = 127; + + rtp_sender_->SetSendingMediaStatus(false); + rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionRtpStreamId, + kRidExtensionId); + rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionRepairedRtpStreamId, + kRepairedRidExtensionId); + rtp_sender_->SetRid(kRid); + rtp_sender_->SetSendingMediaStatus(true); + + rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); + rtp_sender_->SetRtxSsrc(1234); + rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayloadType); + + rtp_sender_->SetStorePacketsStatus(true, 10); + + SendGenericPayload(); + ASSERT_EQ(1u, transport_.sent_packets_.size()); + const RtpPacketReceived& packet = transport_.sent_packets_[0]; + std::string rid; + ASSERT_TRUE(packet.GetExtension(&rid)); + EXPECT_EQ(kRid, rid); + rid = kNoRid; + EXPECT_FALSE(packet.GetExtension(&rid)); + + uint16_t packet_id = packet.SequenceNumber(); + rtp_sender_->ReSendPacket(packet_id); + ASSERT_EQ(2u, transport_.sent_packets_.size()); + const RtpPacketReceived& rtx_packet = transport_.sent_packets_[1]; + ASSERT_TRUE(rtx_packet.GetExtension(&rid)); + EXPECT_EQ(kRid, rid); + EXPECT_FALSE(rtx_packet.HasExtension()); +} + TEST_P(RtpSenderTest, FecOverheadRate) { constexpr int kFlexfecPayloadType = 118; constexpr uint32_t kMediaSsrc = 1234; constexpr uint32_t kFlexfecSsrc = 5678; - const char kNoMid[] = ""; const std::vector kNoRtpExtensions; const std::vector kNoRtpExtensionSizes; FlexfecSender flexfec_sender(kFlexfecPayloadType, kFlexfecSsrc, kMediaSsrc, diff --git a/pc/mediasession.cc b/pc/mediasession.cc index 27660d898a..4898ed9f03 100644 --- a/pc/mediasession.cc +++ b/pc/mediasession.cc @@ -1274,6 +1274,38 @@ void MediaSessionDescriptionFactory::set_audio_codecs( ComputeAudioCodecsIntersectionAndUnion(); } +static void AddUnifiedPlanExtensions(RtpHeaderExtensions* extensions) { + RTC_DCHECK(extensions); + // Unified Plan also offers the MID and RID header extensions. + extensions->push_back(webrtc::RtpExtension( + webrtc::RtpExtension::kMidUri, webrtc::RtpExtension::kMidDefaultId)); + extensions->push_back(webrtc::RtpExtension( + webrtc::RtpExtension::kRidUri, webrtc::RtpExtension::kRidDefaultId)); + extensions->push_back( + webrtc::RtpExtension(webrtc::RtpExtension::kRepairedRidUri, + webrtc::RtpExtension::kRepairedRidDefaultId)); +} + +RtpHeaderExtensions +MediaSessionDescriptionFactory::audio_rtp_header_extensions() const { + RtpHeaderExtensions extensions = audio_rtp_extensions_; + if (is_unified_plan_) { + AddUnifiedPlanExtensions(&extensions); + } + + return extensions; +} + +RtpHeaderExtensions +MediaSessionDescriptionFactory::video_rtp_header_extensions() const { + RtpHeaderExtensions extensions = video_rtp_extensions_; + if (is_unified_plan_) { + AddUnifiedPlanExtensions(&extensions); + } + + return extensions; +} + std::unique_ptr MediaSessionDescriptionFactory::CreateOffer( const MediaSessionOptions& session_options, const SessionDescription* current_description) const { diff --git a/pc/mediasession.h b/pc/mediasession.h index e7a83cdbfe..d5c26f42ef 100644 --- a/pc/mediasession.h +++ b/pc/mediasession.h @@ -127,29 +127,13 @@ class MediaSessionDescriptionFactory { void set_audio_rtp_header_extensions(const RtpHeaderExtensions& extensions) { audio_rtp_extensions_ = extensions; } - RtpHeaderExtensions audio_rtp_header_extensions() const { - RtpHeaderExtensions extensions = audio_rtp_extensions_; - // If we are Unified Plan, also offer the MID header extension. - if (is_unified_plan_) { - extensions.push_back(webrtc::RtpExtension( - webrtc::RtpExtension::kMidUri, webrtc::RtpExtension::kMidDefaultId)); - } - return extensions; - } + RtpHeaderExtensions audio_rtp_header_extensions() const; const VideoCodecs& video_codecs() const { return video_codecs_; } void set_video_codecs(const VideoCodecs& codecs) { video_codecs_ = codecs; } void set_video_rtp_header_extensions(const RtpHeaderExtensions& extensions) { video_rtp_extensions_ = extensions; } - RtpHeaderExtensions video_rtp_header_extensions() const { - RtpHeaderExtensions extensions = video_rtp_extensions_; - // If we are Unified Plan, also offer the MID header extension. - if (is_unified_plan_) { - extensions.push_back(webrtc::RtpExtension( - webrtc::RtpExtension::kMidUri, webrtc::RtpExtension::kMidDefaultId)); - } - return extensions; - } + RtpHeaderExtensions video_rtp_header_extensions() const; const DataCodecs& data_codecs() const { return data_codecs_; } void set_data_codecs(const DataCodecs& codecs) { data_codecs_ = codecs; } SecurePolicy secure() const { return secure_; } diff --git a/pc/mediasession_unittest.cc b/pc/mediasession_unittest.cc index a2a216b6ed..2af14d2df3 100644 --- a/pc/mediasession_unittest.cc +++ b/pc/mediasession_unittest.cc @@ -8,6 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include #include #include @@ -3686,6 +3687,56 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestSetAudioCodecs) { EXPECT_EQ(no_codecs, sf.audio_sendrecv_codecs()); } +// Checks that the RID extensions are added to the video RTP header extensions. +// Note: This test somewhat shows that |set_video_rtp_header_extensions()| is +// not very well defined, as calling set() and immediately get() will yield +// an object that is not semantically equivalent to the set object. +TEST_F(MediaSessionDescriptionFactoryTest, VideoHasRidExtensionsInUnifiedPlan) { + TransportDescriptionFactory tdf; + MediaSessionDescriptionFactory sf(&tdf); + sf.set_is_unified_plan(true); + cricket::RtpHeaderExtensions extensions; + sf.set_video_rtp_header_extensions(extensions); + cricket::RtpHeaderExtensions result = sf.video_rtp_header_extensions(); + // Check to see that RID extensions were added to the extension list + EXPECT_GE(result.size(), 2u); + auto rid_extension = std::find_if( + result.begin(), result.end(), [](const RtpExtension& extension) { + return extension.uri == webrtc::RtpExtension::kRidUri; + }); + EXPECT_NE(rid_extension, extensions.end()); + auto repaired_rid_extension = std::find_if( + result.begin(), result.end(), [](const RtpExtension& extension) { + return extension.uri == webrtc::RtpExtension::kRepairedRidUri; + }); + EXPECT_NE(repaired_rid_extension, extensions.end()); +} + +// Checks that the RID extensions are added to the audio RTP header extensions. +// Note: This test somewhat shows that |set_audio_rtp_header_extensions()| is +// not very well defined, as calling set() and immediately get() will yield +// an object that is not semantically equivalent to the set object. +TEST_F(MediaSessionDescriptionFactoryTest, AudioHasRidExtensionsInUnifiedPlan) { + TransportDescriptionFactory tdf; + MediaSessionDescriptionFactory sf(&tdf); + sf.set_is_unified_plan(true); + cricket::RtpHeaderExtensions extensions; + sf.set_audio_rtp_header_extensions(extensions); + cricket::RtpHeaderExtensions result = sf.audio_rtp_header_extensions(); + // Check to see that RID extensions were added to the extension list + EXPECT_GE(result.size(), 2u); + auto rid_extension = std::find_if( + result.begin(), result.end(), [](const RtpExtension& extension) { + return extension.uri == webrtc::RtpExtension::kRidUri; + }); + EXPECT_NE(rid_extension, extensions.end()); + auto repaired_rid_extension = std::find_if( + result.begin(), result.end(), [](const RtpExtension& extension) { + return extension.uri == webrtc::RtpExtension::kRepairedRidUri; + }); + EXPECT_NE(repaired_rid_extension, extensions.end()); +} + namespace { // Compare the two vectors of codecs ignoring the payload type. template