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}
This commit is contained in:
Markus Handell 2020-12-17 22:19:40 +01:00 committed by Commit Bot
parent d95a47d08f
commit 5932fe1392
10 changed files with 164 additions and 4 deletions

View file

@ -64,6 +64,11 @@ webrtc::RTCError RtpTransceiverInterface::SetOfferedRtpHeaderExtensions(
return webrtc::RTCError(webrtc::RTCErrorType::UNSUPPORTED_OPERATION); return webrtc::RTCError(webrtc::RTCErrorType::UNSUPPORTED_OPERATION);
} }
std::vector<RtpHeaderExtensionCapability>
RtpTransceiverInterface::HeaderExtensionsNegotiated() const {
return {};
}
// TODO(bugs.webrtc.org/11839) Remove default implementations when clients // TODO(bugs.webrtc.org/11839) Remove default implementations when clients
// are updated. // are updated.
void RtpTransceiverInterface::SetDirection( void RtpTransceiverInterface::SetDirection(

View file

@ -156,6 +156,12 @@ class RTC_EXPORT RtpTransceiverInterface : public rtc::RefCountInterface {
virtual std::vector<RtpHeaderExtensionCapability> HeaderExtensionsToOffer() virtual std::vector<RtpHeaderExtensionCapability> HeaderExtensionsToOffer()
const; 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 // The SetOfferedRtpHeaderExtensions method modifies the next SDP negotiation
// so that it negotiates use of header extensions which are not kStopped. // so that it negotiates use of header extensions which are not kStopped.
// https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface

View file

@ -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<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, VoiceChannel::VoiceChannel(rtc::Thread* worker_thread,
rtc::Thread* network_thread, rtc::Thread* network_thread,
rtc::Thread* signaling_thread, rtc::Thread* signaling_thread,
@ -895,6 +912,9 @@ bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content,
const AudioContentDescription* audio = content->as_audio(); const AudioContentDescription* audio = content->as_audio();
if (type == SdpType::kAnswer)
SetNegotiatedHeaderExtensions_w(audio->rtp_header_extensions());
RtpHeaderExtensions rtp_header_extensions = RtpHeaderExtensions rtp_header_extensions =
GetFilteredRtpHeaderExtensions(audio->rtp_header_extensions()); GetFilteredRtpHeaderExtensions(audio->rtp_header_extensions());
SetReceiveExtensions(rtp_header_extensions); SetReceiveExtensions(rtp_header_extensions);
@ -954,6 +974,9 @@ bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content,
const AudioContentDescription* audio = content->as_audio(); const AudioContentDescription* audio = content->as_audio();
if (type == SdpType::kAnswer)
SetNegotiatedHeaderExtensions_w(audio->rtp_header_extensions());
RtpHeaderExtensions rtp_header_extensions = RtpHeaderExtensions rtp_header_extensions =
GetFilteredRtpHeaderExtensions(audio->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(); const VideoContentDescription* video = content->as_video();
if (type == SdpType::kAnswer)
SetNegotiatedHeaderExtensions_w(video->rtp_header_extensions());
RtpHeaderExtensions rtp_header_extensions = RtpHeaderExtensions rtp_header_extensions =
GetFilteredRtpHeaderExtensions(video->rtp_header_extensions()); GetFilteredRtpHeaderExtensions(video->rtp_header_extensions());
SetReceiveExtensions(rtp_header_extensions); SetReceiveExtensions(rtp_header_extensions);
@ -1149,6 +1175,9 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content,
const VideoContentDescription* video = content->as_video(); const VideoContentDescription* video = content->as_video();
if (type == SdpType::kAnswer)
SetNegotiatedHeaderExtensions_w(video->rtp_header_extensions());
RtpHeaderExtensions rtp_header_extensions = RtpHeaderExtensions rtp_header_extensions =
GetFilteredRtpHeaderExtensions(video->rtp_header_extensions()); GetFilteredRtpHeaderExtensions(video->rtp_header_extensions());

View file

@ -223,7 +223,7 @@ class BaseChannel : public ChannelInterface,
// called. // called.
bool IsReadyToReceiveMedia_w() const RTC_RUN_ON(worker_thread()); bool IsReadyToReceiveMedia_w() const RTC_RUN_ON(worker_thread());
bool IsReadyToSendMedia_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()); void FlushRtcpMessages_n() RTC_RUN_ON(network_thread());
@ -309,6 +309,11 @@ class BaseChannel : public ChannelInterface,
// Return description of media channel to facilitate logging // Return description of media channel to facilitate logging
std::string ToString() const; std::string ToString() const;
void SetNegotiatedHeaderExtensions_w(const RtpHeaderExtensions& extensions);
// ChannelInterface overrides
RtpHeaderExtensions GetNegotiatedRtpHeaderExtensions() const override;
bool has_received_packet_ = false; bool has_received_packet_ = false;
private: private:
@ -375,6 +380,9 @@ class BaseChannel : public ChannelInterface,
// like in Simulcast. // like in Simulcast.
// This object is not owned by the channel so it must outlive it. // This object is not owned by the channel so it must outlive it.
rtc::UniqueRandomIdGenerator* const ssrc_generator_; 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, // VoiceChannel is a specialization that adds support for early media, DTMF,

View file

@ -66,6 +66,9 @@ class ChannelInterface {
// * A DtlsSrtpTransport for DTLS-SRTP. // * A DtlsSrtpTransport for DTLS-SRTP.
virtual bool SetRtpTransport(webrtc::RtpTransportInternal* rtp_transport) = 0; virtual bool SetRtpTransport(webrtc::RtpTransportInternal* rtp_transport) = 0;
// Returns the last negotiated header extensions.
virtual RtpHeaderExtensions GetNegotiatedRtpHeaderExtensions() const = 0;
protected: protected:
virtual ~ChannelInterface() = default; virtual ~ChannelInterface() = default;
}; };

View file

@ -166,6 +166,36 @@ TEST_P(PeerConnectionHeaderExtensionTest, OffersUnstoppedModifiedExtensions) {
Field(&RtpExtension::uri, "uri3"))); 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( INSTANTIATE_TEST_SUITE_P(
, ,
PeerConnectionHeaderExtensionTest, PeerConnectionHeaderExtensionTest,

View file

@ -12,12 +12,14 @@
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector>
#include "absl/algorithm/container.h" #include "absl/algorithm/container.h"
#include "api/rtp_parameters.h" #include "api/rtp_parameters.h"
#include "pc/channel_manager.h" #include "pc/channel_manager.h"
#include "pc/rtp_media_utils.h" #include "pc/rtp_media_utils.h"
#include "pc/rtp_parameters_conversion.h" #include "pc/rtp_parameters_conversion.h"
#include "pc/session_description.h"
#include "rtc_base/checks.h" #include "rtc_base/checks.h"
#include "rtc_base/logging.h" #include "rtc_base/logging.h"
@ -455,6 +457,17 @@ RtpTransceiver::HeaderExtensionsToOffer() const {
return header_extensions_to_offer_; 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( RTCError RtpTransceiver::SetOfferedRtpHeaderExtensions(
rtc::ArrayView<const RtpHeaderExtensionCapability> rtc::ArrayView<const RtpHeaderExtensionCapability>
header_extensions_to_offer) { header_extensions_to_offer) {

View file

@ -207,6 +207,8 @@ class RtpTransceiver final
} }
std::vector<RtpHeaderExtensionCapability> HeaderExtensionsToOffer() std::vector<RtpHeaderExtensionCapability> HeaderExtensionsToOffer()
const override; const override;
std::vector<RtpHeaderExtensionCapability> HeaderExtensionsNegotiated()
const override;
RTCError SetOfferedRtpHeaderExtensions( RTCError SetOfferedRtpHeaderExtensions(
rtc::ArrayView<const RtpHeaderExtensionCapability> rtc::ArrayView<const RtpHeaderExtensionCapability>
header_extensions_to_offer) override; header_extensions_to_offer) override;
@ -264,6 +266,8 @@ PROXY_METHOD1(webrtc::RTCError,
PROXY_CONSTMETHOD0(std::vector<RtpCodecCapability>, codec_preferences) PROXY_CONSTMETHOD0(std::vector<RtpCodecCapability>, codec_preferences)
PROXY_CONSTMETHOD0(std::vector<RtpHeaderExtensionCapability>, PROXY_CONSTMETHOD0(std::vector<RtpHeaderExtensionCapability>,
HeaderExtensionsToOffer) HeaderExtensionsToOffer)
PROXY_CONSTMETHOD0(std::vector<RtpHeaderExtensionCapability>,
HeaderExtensionsNegotiated)
PROXY_METHOD1(webrtc::RTCError, PROXY_METHOD1(webrtc::RTCError,
SetOfferedRtpHeaderExtensions, SetOfferedRtpHeaderExtensions,
rtc::ArrayView<const RtpHeaderExtensionCapability>) rtc::ArrayView<const RtpHeaderExtensionCapability>)

View file

@ -14,6 +14,8 @@
#include <memory> #include <memory>
#include "absl/types/optional.h"
#include "api/rtp_parameters.h"
#include "media/base/fake_media_engine.h" #include "media/base/fake_media_engine.h"
#include "pc/test/mock_channel_interface.h" #include "pc/test/mock_channel_interface.h"
#include "pc/test/mock_rtp_receiver_internal.h" #include "pc/test/mock_rtp_receiver_internal.h"
@ -22,9 +24,7 @@
#include "test/gtest.h" #include "test/gtest.h"
using ::testing::ElementsAre; using ::testing::ElementsAre;
using ::testing::Eq; using ::testing::Optional;
using ::testing::Field;
using ::testing::Not;
using ::testing::Property; using ::testing::Property;
using ::testing::Return; using ::testing::Return;
using ::testing::ReturnRef; using ::testing::ReturnRef;
@ -206,4 +206,62 @@ TEST_F(RtpTransceiverTestForHeaderExtensions,
EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), extensions_); 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 } // namespace webrtc

View file

@ -60,6 +60,10 @@ class MockChannelInterface : public cricket::ChannelInterface {
SetRtpTransport, SetRtpTransport,
(webrtc::RtpTransportInternal*), (webrtc::RtpTransportInternal*),
(override)); (override));
MOCK_METHOD(RtpHeaderExtensions,
GetNegotiatedRtpHeaderExtensions,
(),
(const));
}; };
} // namespace cricket } // namespace cricket