diff --git a/api/rtp_transceiver_interface.cc b/api/rtp_transceiver_interface.cc index 1dc0fcc79e..fd5085c336 100644 --- a/api/rtp_transceiver_interface.cc +++ b/api/rtp_transceiver_interface.cc @@ -64,6 +64,11 @@ webrtc::RTCError RtpTransceiverInterface::SetOfferedRtpHeaderExtensions( return webrtc::RTCError(webrtc::RTCErrorType::UNSUPPORTED_OPERATION); } +std::vector +RtpTransceiverInterface::HeaderExtensionsNegotiated() const { + return {}; +} + // TODO(bugs.webrtc.org/11839) Remove default implementations when clients // are updated. void RtpTransceiverInterface::SetDirection( diff --git a/api/rtp_transceiver_interface.h b/api/rtp_transceiver_interface.h index fd3555fb40..9b46846597 100644 --- a/api/rtp_transceiver_interface.h +++ b/api/rtp_transceiver_interface.h @@ -156,6 +156,12 @@ class RTC_EXPORT RtpTransceiverInterface : public rtc::RefCountInterface { virtual std::vector HeaderExtensionsToOffer() const; + // Readonly attribute which is either empty if negotation has not yet + // happened, or a vector of the negotiated header extensions. + // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface + virtual std::vector HeaderExtensionsNegotiated() + const; + // The SetOfferedRtpHeaderExtensions method modifies the next SDP negotiation // so that it negotiates use of header extensions which are not kStopped. // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface diff --git a/pc/channel.cc b/pc/channel.cc index 34269a13b0..661f0e908c 100644 --- a/pc/channel.cc +++ b/pc/channel.cc @@ -836,6 +836,23 @@ void BaseChannel::SignalSentPacket_n(const rtc::SentPacket& sent_packet) { }); } +void BaseChannel::SetNegotiatedHeaderExtensions_w( + const RtpHeaderExtensions& extensions) { + TRACE_EVENT0("webrtc", __func__); + RtpHeaderExtensions extensions_copy = extensions; + invoker_.AsyncInvoke( + RTC_FROM_HERE, signaling_thread(), + [this, extensions_copy = std::move(extensions_copy)] { + RTC_DCHECK_RUN_ON(signaling_thread()); + negotiated_header_extensions_ = std::move(extensions_copy); + }); +} + +RtpHeaderExtensions BaseChannel::GetNegotiatedRtpHeaderExtensions() const { + RTC_DCHECK_RUN_ON(signaling_thread()); + return negotiated_header_extensions_; +} + VoiceChannel::VoiceChannel(rtc::Thread* worker_thread, rtc::Thread* network_thread, rtc::Thread* signaling_thread, @@ -895,6 +912,9 @@ bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content, const AudioContentDescription* audio = content->as_audio(); + if (type == SdpType::kAnswer) + SetNegotiatedHeaderExtensions_w(audio->rtp_header_extensions()); + RtpHeaderExtensions rtp_header_extensions = GetFilteredRtpHeaderExtensions(audio->rtp_header_extensions()); SetReceiveExtensions(rtp_header_extensions); @@ -954,6 +974,9 @@ bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content, const AudioContentDescription* audio = content->as_audio(); + if (type == SdpType::kAnswer) + SetNegotiatedHeaderExtensions_w(audio->rtp_header_extensions()); + RtpHeaderExtensions rtp_header_extensions = GetFilteredRtpHeaderExtensions(audio->rtp_header_extensions()); @@ -1057,6 +1080,9 @@ bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, const VideoContentDescription* video = content->as_video(); + if (type == SdpType::kAnswer) + SetNegotiatedHeaderExtensions_w(video->rtp_header_extensions()); + RtpHeaderExtensions rtp_header_extensions = GetFilteredRtpHeaderExtensions(video->rtp_header_extensions()); SetReceiveExtensions(rtp_header_extensions); @@ -1149,6 +1175,9 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content, const VideoContentDescription* video = content->as_video(); + if (type == SdpType::kAnswer) + SetNegotiatedHeaderExtensions_w(video->rtp_header_extensions()); + RtpHeaderExtensions rtp_header_extensions = GetFilteredRtpHeaderExtensions(video->rtp_header_extensions()); diff --git a/pc/channel.h b/pc/channel.h index 0ba23eb425..5e2bd7e651 100644 --- a/pc/channel.h +++ b/pc/channel.h @@ -223,7 +223,7 @@ class BaseChannel : public ChannelInterface, // called. bool IsReadyToReceiveMedia_w() const RTC_RUN_ON(worker_thread()); bool IsReadyToSendMedia_w() const RTC_RUN_ON(worker_thread()); - rtc::Thread* signaling_thread() { return signaling_thread_; } + rtc::Thread* signaling_thread() const { return signaling_thread_; } void FlushRtcpMessages_n() RTC_RUN_ON(network_thread()); @@ -309,6 +309,11 @@ class BaseChannel : public ChannelInterface, // Return description of media channel to facilitate logging std::string ToString() const; + void SetNegotiatedHeaderExtensions_w(const RtpHeaderExtensions& extensions); + + // ChannelInterface overrides + RtpHeaderExtensions GetNegotiatedRtpHeaderExtensions() const override; + bool has_received_packet_ = false; private: @@ -375,6 +380,9 @@ class BaseChannel : public ChannelInterface, // like in Simulcast. // This object is not owned by the channel so it must outlive it. rtc::UniqueRandomIdGenerator* const ssrc_generator_; + + RtpHeaderExtensions negotiated_header_extensions_ + RTC_GUARDED_BY(signaling_thread()); }; // VoiceChannel is a specialization that adds support for early media, DTMF, diff --git a/pc/channel_interface.h b/pc/channel_interface.h index 4580a2fd60..1937c8f9f6 100644 --- a/pc/channel_interface.h +++ b/pc/channel_interface.h @@ -66,6 +66,9 @@ class ChannelInterface { // * A DtlsSrtpTransport for DTLS-SRTP. virtual bool SetRtpTransport(webrtc::RtpTransportInternal* rtp_transport) = 0; + // Returns the last negotiated header extensions. + virtual RtpHeaderExtensions GetNegotiatedRtpHeaderExtensions() const = 0; + protected: virtual ~ChannelInterface() = default; }; diff --git a/pc/peer_connection_header_extension_unittest.cc b/pc/peer_connection_header_extension_unittest.cc index 62fda59212..8bf6c7ab44 100644 --- a/pc/peer_connection_header_extension_unittest.cc +++ b/pc/peer_connection_header_extension_unittest.cc @@ -166,6 +166,36 @@ TEST_P(PeerConnectionHeaderExtensionTest, OffersUnstoppedModifiedExtensions) { Field(&RtpExtension::uri, "uri3"))); } +TEST_P(PeerConnectionHeaderExtensionTest, NegotiatedExtensionsAreAccessible) { + cricket::MediaType media_type; + SdpSemantics semantics; + std::tie(media_type, semantics) = GetParam(); + if (semantics != SdpSemantics::kUnifiedPlan) + return; + std::unique_ptr pc1 = + CreatePeerConnection(media_type, semantics); + auto transceiver1 = pc1->AddTransceiver(media_type); + auto modified_extensions = transceiver1->HeaderExtensionsToOffer(); + modified_extensions[3].direction = RtpTransceiverDirection::kStopped; + transceiver1->SetOfferedRtpHeaderExtensions(modified_extensions); + auto offer = pc1->CreateOfferAndSetAsLocal( + PeerConnectionInterface::RTCOfferAnswerOptions()); + + std::unique_ptr pc2 = + CreatePeerConnection(media_type, semantics); + auto transceiver2 = pc2->AddTransceiver(media_type); + pc2->SetRemoteDescription(std::move(offer)); + auto answer = pc2->CreateAnswerAndSetAsLocal( + PeerConnectionInterface::RTCOfferAnswerOptions()); + pc1->SetRemoteDescription(std::move(answer)); + + // PC1 has exts 2-4 unstopped and PC2 has exts 1-3 unstopped -> ext 2, 3 + // survives. + EXPECT_THAT(transceiver1->HeaderExtensionsNegotiated(), + ElementsAre(Field(&RtpHeaderExtensionCapability::uri, "uri2"), + Field(&RtpHeaderExtensionCapability::uri, "uri3"))); +} + INSTANTIATE_TEST_SUITE_P( , PeerConnectionHeaderExtensionTest, diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc index 6b3032e27f..9e62f95dd8 100644 --- a/pc/rtp_transceiver.cc +++ b/pc/rtp_transceiver.cc @@ -12,12 +12,14 @@ #include #include +#include #include "absl/algorithm/container.h" #include "api/rtp_parameters.h" #include "pc/channel_manager.h" #include "pc/rtp_media_utils.h" #include "pc/rtp_parameters_conversion.h" +#include "pc/session_description.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -455,6 +457,17 @@ RtpTransceiver::HeaderExtensionsToOffer() const { return header_extensions_to_offer_; } +std::vector +RtpTransceiver::HeaderExtensionsNegotiated() const { + if (!channel_) + return {}; + std::vector result; + for (const auto& ext : channel_->GetNegotiatedRtpHeaderExtensions()) { + result.emplace_back(ext.uri, ext.id, RtpTransceiverDirection::kSendRecv); + } + return result; +} + RTCError RtpTransceiver::SetOfferedRtpHeaderExtensions( rtc::ArrayView header_extensions_to_offer) { diff --git a/pc/rtp_transceiver.h b/pc/rtp_transceiver.h index 4d9716c89b..57dbaeea85 100644 --- a/pc/rtp_transceiver.h +++ b/pc/rtp_transceiver.h @@ -207,6 +207,8 @@ class RtpTransceiver final } std::vector HeaderExtensionsToOffer() const override; + std::vector HeaderExtensionsNegotiated() + const override; RTCError SetOfferedRtpHeaderExtensions( rtc::ArrayView header_extensions_to_offer) override; @@ -264,6 +266,8 @@ PROXY_METHOD1(webrtc::RTCError, PROXY_CONSTMETHOD0(std::vector, codec_preferences) PROXY_CONSTMETHOD0(std::vector, HeaderExtensionsToOffer) +PROXY_CONSTMETHOD0(std::vector, + HeaderExtensionsNegotiated) PROXY_METHOD1(webrtc::RTCError, SetOfferedRtpHeaderExtensions, rtc::ArrayView) diff --git a/pc/rtp_transceiver_unittest.cc b/pc/rtp_transceiver_unittest.cc index 96e38b0b23..91e1aa3cee 100644 --- a/pc/rtp_transceiver_unittest.cc +++ b/pc/rtp_transceiver_unittest.cc @@ -14,6 +14,8 @@ #include +#include "absl/types/optional.h" +#include "api/rtp_parameters.h" #include "media/base/fake_media_engine.h" #include "pc/test/mock_channel_interface.h" #include "pc/test/mock_rtp_receiver_internal.h" @@ -22,9 +24,7 @@ #include "test/gtest.h" using ::testing::ElementsAre; -using ::testing::Eq; -using ::testing::Field; -using ::testing::Not; +using ::testing::Optional; using ::testing::Property; using ::testing::Return; using ::testing::ReturnRef; @@ -206,4 +206,62 @@ TEST_F(RtpTransceiverTestForHeaderExtensions, EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), extensions_); } +TEST_F(RtpTransceiverTestForHeaderExtensions, + NoNegotiatedHdrExtsWithoutChannel) { + EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), ElementsAre()); +} + +TEST_F(RtpTransceiverTestForHeaderExtensions, + NoNegotiatedHdrExtsWithChannelWithoutNegotiation) { + cricket::MockChannelInterface mock_channel; + sigslot::signal1 signal; + ON_CALL(mock_channel, SignalFirstPacketReceived) + .WillByDefault(ReturnRef(signal)); + transceiver_.SetChannel(&mock_channel); + EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), ElementsAre()); +} + +TEST_F(RtpTransceiverTestForHeaderExtensions, ReturnsNegotiatedHdrExts) { + cricket::MockChannelInterface mock_channel; + sigslot::signal1 signal; + ON_CALL(mock_channel, SignalFirstPacketReceived) + .WillByDefault(ReturnRef(signal)); + cricket::RtpHeaderExtensions extensions = {webrtc::RtpExtension("uri1", 1), + webrtc::RtpExtension("uri2", 2)}; + EXPECT_CALL(mock_channel, GetNegotiatedRtpHeaderExtensions) + .WillOnce(Return(extensions)); + transceiver_.SetChannel(&mock_channel); + EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), + ElementsAre(RtpHeaderExtensionCapability( + "uri1", 1, RtpTransceiverDirection::kSendRecv), + RtpHeaderExtensionCapability( + "uri2", 2, RtpTransceiverDirection::kSendRecv))); +} + +TEST_F(RtpTransceiverTestForHeaderExtensions, + ReturnsNegotiatedHdrExtsSecondTime) { + cricket::MockChannelInterface mock_channel; + sigslot::signal1 signal; + ON_CALL(mock_channel, SignalFirstPacketReceived) + .WillByDefault(ReturnRef(signal)); + cricket::RtpHeaderExtensions extensions = {webrtc::RtpExtension("uri1", 1), + webrtc::RtpExtension("uri2", 2)}; + + EXPECT_CALL(mock_channel, GetNegotiatedRtpHeaderExtensions) + .WillOnce(Return(extensions)); + transceiver_.SetChannel(&mock_channel); + transceiver_.HeaderExtensionsNegotiated(); + testing::Mock::VerifyAndClearExpectations(&mock_channel); + + extensions = {webrtc::RtpExtension("uri3", 4), + webrtc::RtpExtension("uri5", 6)}; + EXPECT_CALL(mock_channel, GetNegotiatedRtpHeaderExtensions) + .WillOnce(Return(extensions)); + EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), + ElementsAre(RtpHeaderExtensionCapability( + "uri3", 4, RtpTransceiverDirection::kSendRecv), + RtpHeaderExtensionCapability( + "uri5", 6, RtpTransceiverDirection::kSendRecv))); +} + } // namespace webrtc diff --git a/pc/test/mock_channel_interface.h b/pc/test/mock_channel_interface.h index 3a73225239..52404f1dea 100644 --- a/pc/test/mock_channel_interface.h +++ b/pc/test/mock_channel_interface.h @@ -60,6 +60,10 @@ class MockChannelInterface : public cricket::ChannelInterface { SetRtpTransport, (webrtc::RtpTransportInternal*), (override)); + MOCK_METHOD(RtpHeaderExtensions, + GetNegotiatedRtpHeaderExtensions, + (), + (const)); }; } // namespace cricket