RtpTransceiverInterface: introduce HeaderExtensionsNegotiated.
This changes adds exposure of a new transceiver method for
accessing header extensions that have been negotiated, following
spec details in https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface.
The change contains unit tests testing the functionality.
Note: support for signalling directionality of header extensions
in the SDP isn't implemented yet.
https://chromestatus.com/feature/5680189201711104.
Intent to prototype: https://groups.google.com/a/chromium.org/g/blink-dev/c/65YdUi02yZk
Bug: chromium:1051821
Change-Id: If963beed37e96eed2dff3a2822db4e30caaea4a0
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/198126
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Markus Handell <handellm@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32860}
diff --git a/api/rtp_transceiver_interface.cc b/api/rtp_transceiver_interface.cc
index 1dc0fcc..fd5085c 100644
--- a/api/rtp_transceiver_interface.cc
+++ b/api/rtp_transceiver_interface.cc
@@ -64,6 +64,11 @@
return webrtc::RTCError(webrtc::RTCErrorType::UNSUPPORTED_OPERATION);
}
+std::vector<RtpHeaderExtensionCapability>
+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 fd3555f..9b46846 100644
--- a/api/rtp_transceiver_interface.h
+++ b/api/rtp_transceiver_interface.h
@@ -156,6 +156,12 @@
virtual std::vector<RtpHeaderExtensionCapability> 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<RtpHeaderExtensionCapability> 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 34269a1..661f0e9 100644
--- a/pc/channel.cc
+++ b/pc/channel.cc
@@ -836,6 +836,23 @@
});
}
+void BaseChannel::SetNegotiatedHeaderExtensions_w(
+ const RtpHeaderExtensions& extensions) {
+ TRACE_EVENT0("webrtc", __func__);
+ RtpHeaderExtensions extensions_copy = extensions;
+ invoker_.AsyncInvoke<void>(
+ 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 @@
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 @@
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 @@
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 @@
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 0ba23eb..5e2bd7e 100644
--- a/pc/channel.h
+++ b/pc/channel.h
@@ -223,7 +223,7 @@
// 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 @@
// 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 @@
// 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 4580a2f..1937c8f 100644
--- a/pc/channel_interface.h
+++ b/pc/channel_interface.h
@@ -66,6 +66,9 @@
// * 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 62fda59..8bf6c7a 100644
--- a/pc/peer_connection_header_extension_unittest.cc
+++ b/pc/peer_connection_header_extension_unittest.cc
@@ -166,6 +166,36 @@
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<PeerConnectionWrapper> 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<PeerConnectionWrapper> 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 6b3032e..9e62f95 100644
--- a/pc/rtp_transceiver.cc
+++ b/pc/rtp_transceiver.cc
@@ -12,12 +12,14 @@
#include <string>
#include <utility>
+#include <vector>
#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 @@
return header_extensions_to_offer_;
}
+std::vector<RtpHeaderExtensionCapability>
+RtpTransceiver::HeaderExtensionsNegotiated() const {
+ if (!channel_)
+ return {};
+ std::vector<RtpHeaderExtensionCapability> result;
+ for (const auto& ext : channel_->GetNegotiatedRtpHeaderExtensions()) {
+ result.emplace_back(ext.uri, ext.id, RtpTransceiverDirection::kSendRecv);
+ }
+ return result;
+}
+
RTCError RtpTransceiver::SetOfferedRtpHeaderExtensions(
rtc::ArrayView<const RtpHeaderExtensionCapability>
header_extensions_to_offer) {
diff --git a/pc/rtp_transceiver.h b/pc/rtp_transceiver.h
index 4d9716c..57dbaee 100644
--- a/pc/rtp_transceiver.h
+++ b/pc/rtp_transceiver.h
@@ -207,6 +207,8 @@
}
std::vector<RtpHeaderExtensionCapability> HeaderExtensionsToOffer()
const override;
+ std::vector<RtpHeaderExtensionCapability> HeaderExtensionsNegotiated()
+ const override;
RTCError SetOfferedRtpHeaderExtensions(
rtc::ArrayView<const RtpHeaderExtensionCapability>
header_extensions_to_offer) override;
@@ -264,6 +266,8 @@
PROXY_CONSTMETHOD0(std::vector<RtpCodecCapability>, codec_preferences)
PROXY_CONSTMETHOD0(std::vector<RtpHeaderExtensionCapability>,
HeaderExtensionsToOffer)
+PROXY_CONSTMETHOD0(std::vector<RtpHeaderExtensionCapability>,
+ HeaderExtensionsNegotiated)
PROXY_METHOD1(webrtc::RTCError,
SetOfferedRtpHeaderExtensions,
rtc::ArrayView<const RtpHeaderExtensionCapability>)
diff --git a/pc/rtp_transceiver_unittest.cc b/pc/rtp_transceiver_unittest.cc
index 96e38b0..91e1aa3 100644
--- a/pc/rtp_transceiver_unittest.cc
+++ b/pc/rtp_transceiver_unittest.cc
@@ -14,6 +14,8 @@
#include <memory>
+#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 @@
EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), extensions_);
}
+TEST_F(RtpTransceiverTestForHeaderExtensions,
+ NoNegotiatedHdrExtsWithoutChannel) {
+ EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), ElementsAre());
+}
+
+TEST_F(RtpTransceiverTestForHeaderExtensions,
+ NoNegotiatedHdrExtsWithChannelWithoutNegotiation) {
+ cricket::MockChannelInterface mock_channel;
+ sigslot::signal1<cricket::ChannelInterface*> 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<cricket::ChannelInterface*> 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<cricket::ChannelInterface*> 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 3a73225..52404f1 100644
--- a/pc/test/mock_channel_interface.h
+++ b/pc/test/mock_channel_interface.h
@@ -60,6 +60,10 @@
SetRtpTransport,
(webrtc::RtpTransportInternal*),
(override));
+ MOCK_METHOD(RtpHeaderExtensions,
+ GetNegotiatedRtpHeaderExtensions,
+ (),
+ (const));
};
} // namespace cricket