/* * Copyright 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. */ // This file contains tests for `RtpTransceiver`. #include "pc/rtp_transceiver.h" #include #include #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/environment/environment_factory.h" #include "api/peer_connection_interface.h" #include "api/rtp_parameters.h" #include "media/base/media_engine.h" #include "pc/test/enable_fake_media.h" #include "pc/test/mock_channel_interface.h" #include "pc/test/mock_rtp_receiver_internal.h" #include "pc/test/mock_rtp_sender_internal.h" #include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" using ::testing::_; using ::testing::ElementsAre; using ::testing::Field; using ::testing::Optional; using ::testing::Property; using ::testing::Return; using ::testing::ReturnRef; namespace webrtc { namespace { class RtpTransceiverTest : public testing::Test { public: RtpTransceiverTest() : dependencies_(MakeDependencies()), context_( ConnectionContext::Create(CreateEnvironment(), &dependencies_)) {} protected: cricket::MediaEngineInterface* media_engine() { return context_->media_engine(); } ConnectionContext* context() { return context_.get(); } private: rtc::AutoThread main_thread_; static PeerConnectionFactoryDependencies MakeDependencies() { PeerConnectionFactoryDependencies d; d.network_thread = rtc::Thread::Current(); d.worker_thread = rtc::Thread::Current(); d.signaling_thread = rtc::Thread::Current(); EnableFakeMedia(d); return d; } PeerConnectionFactoryDependencies dependencies_; rtc::scoped_refptr context_; }; // Checks that a channel cannot be set on a stopped `RtpTransceiver`. TEST_F(RtpTransceiverTest, CannotSetChannelOnStoppedTransceiver) { const std::string content_name("my_mid"); auto transceiver = rtc::make_ref_counted( cricket::MediaType::MEDIA_TYPE_AUDIO, context()); auto channel1 = std::make_unique(); EXPECT_CALL(*channel1, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); EXPECT_CALL(*channel1, mid()).WillRepeatedly(ReturnRef(content_name)); EXPECT_CALL(*channel1, SetFirstPacketReceivedCallback(_)); EXPECT_CALL(*channel1, SetRtpTransport(_)).WillRepeatedly(Return(true)); auto channel1_ptr = channel1.get(); transceiver->SetChannel(std::move(channel1), [&](const std::string& mid) { EXPECT_EQ(mid, content_name); return nullptr; }); EXPECT_EQ(channel1_ptr, transceiver->channel()); // Stop the transceiver. transceiver->StopInternal(); EXPECT_EQ(channel1_ptr, transceiver->channel()); auto channel2 = std::make_unique(); EXPECT_CALL(*channel2, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); // Clear the current channel - required to allow SetChannel() EXPECT_CALL(*channel1_ptr, SetFirstPacketReceivedCallback(_)); transceiver->ClearChannel(); // Channel can no longer be set, so this call should be a no-op. transceiver->SetChannel(std::move(channel2), [](const std::string&) { return nullptr; }); EXPECT_EQ(nullptr, transceiver->channel()); } // Checks that a channel can be unset on a stopped `RtpTransceiver` TEST_F(RtpTransceiverTest, CanUnsetChannelOnStoppedTransceiver) { const std::string content_name("my_mid"); auto transceiver = rtc::make_ref_counted( cricket::MediaType::MEDIA_TYPE_VIDEO, context()); auto channel = std::make_unique(); EXPECT_CALL(*channel, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_VIDEO)); EXPECT_CALL(*channel, mid()).WillRepeatedly(ReturnRef(content_name)); EXPECT_CALL(*channel, SetFirstPacketReceivedCallback(_)) .WillRepeatedly(testing::Return()); EXPECT_CALL(*channel, SetRtpTransport(_)).WillRepeatedly(Return(true)); auto channel_ptr = channel.get(); transceiver->SetChannel(std::move(channel), [&](const std::string& mid) { EXPECT_EQ(mid, content_name); return nullptr; }); EXPECT_EQ(channel_ptr, transceiver->channel()); // Stop the transceiver. transceiver->StopInternal(); EXPECT_EQ(channel_ptr, transceiver->channel()); // Set the channel to `nullptr`. transceiver->ClearChannel(); EXPECT_EQ(nullptr, transceiver->channel()); } class RtpTransceiverUnifiedPlanTest : public RtpTransceiverTest { public: RtpTransceiverUnifiedPlanTest() : transceiver_(rtc::make_ref_counted( RtpSenderProxyWithInternal::Create( rtc::Thread::Current(), sender_), RtpReceiverProxyWithInternal::Create( rtc::Thread::Current(), rtc::Thread::Current(), receiver_), context(), media_engine()->voice().GetRtpHeaderExtensions(), /* on_negotiation_needed= */ [] {})) {} static rtc::scoped_refptr MockReceiver() { auto receiver = rtc::make_ref_counted(); EXPECT_CALL(*receiver.get(), media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); return receiver; } static rtc::scoped_refptr MockSender() { auto sender = rtc::make_ref_counted(); EXPECT_CALL(*sender.get(), media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); return sender; } rtc::AutoThread main_thread_; rtc::scoped_refptr receiver_ = MockReceiver(); rtc::scoped_refptr sender_ = MockSender(); rtc::scoped_refptr transceiver_; }; // Basic tests for Stop() TEST_F(RtpTransceiverUnifiedPlanTest, StopSetsDirection) { EXPECT_CALL(*receiver_.get(), Stop()); EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); EXPECT_EQ(RtpTransceiverDirection::kInactive, transceiver_->direction()); EXPECT_FALSE(transceiver_->current_direction()); transceiver_->StopStandard(); EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver_->direction()); EXPECT_FALSE(transceiver_->current_direction()); transceiver_->StopTransceiverProcedure(); EXPECT_TRUE(transceiver_->current_direction()); EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver_->direction()); EXPECT_EQ(RtpTransceiverDirection::kStopped, *transceiver_->current_direction()); } class RtpTransceiverTestForHeaderExtensions : public RtpTransceiverTest { public: RtpTransceiverTestForHeaderExtensions() : extensions_( {RtpHeaderExtensionCapability("uri1", 1, RtpTransceiverDirection::kSendOnly), RtpHeaderExtensionCapability("uri2", 2, RtpTransceiverDirection::kRecvOnly), RtpHeaderExtensionCapability(RtpExtension::kMidUri, 3, RtpTransceiverDirection::kSendRecv), RtpHeaderExtensionCapability(RtpExtension::kVideoRotationUri, 4, RtpTransceiverDirection::kSendRecv)}), transceiver_(rtc::make_ref_counted( RtpSenderProxyWithInternal::Create( rtc::Thread::Current(), sender_), RtpReceiverProxyWithInternal::Create( rtc::Thread::Current(), rtc::Thread::Current(), receiver_), context(), extensions_, /* on_negotiation_needed= */ [] {})) {} static rtc::scoped_refptr MockReceiver() { auto receiver = rtc::make_ref_counted(); EXPECT_CALL(*receiver.get(), media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); return receiver; } static rtc::scoped_refptr MockSender() { auto sender = rtc::make_ref_counted(); EXPECT_CALL(*sender.get(), media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); return sender; } void ClearChannel() { EXPECT_CALL(*sender_.get(), SetMediaChannel(_)); transceiver_->ClearChannel(); } rtc::AutoThread main_thread_; rtc::scoped_refptr receiver_ = MockReceiver(); rtc::scoped_refptr sender_ = MockSender(); std::vector extensions_; rtc::scoped_refptr transceiver_; }; TEST_F(RtpTransceiverTestForHeaderExtensions, OffersChannelManagerList) { EXPECT_CALL(*receiver_.get(), Stop()); EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), extensions_); } TEST_F(RtpTransceiverTestForHeaderExtensions, ModifiesDirection) { EXPECT_CALL(*receiver_.get(), Stop()); EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); auto modified_extensions = extensions_; modified_extensions[0].direction = RtpTransceiverDirection::kSendOnly; EXPECT_TRUE( transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions).ok()); EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), modified_extensions); modified_extensions[0].direction = RtpTransceiverDirection::kRecvOnly; EXPECT_TRUE( transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions).ok()); EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), modified_extensions); modified_extensions[0].direction = RtpTransceiverDirection::kSendRecv; EXPECT_TRUE( transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions).ok()); EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), modified_extensions); modified_extensions[0].direction = RtpTransceiverDirection::kInactive; EXPECT_TRUE( transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions).ok()); EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), modified_extensions); } TEST_F(RtpTransceiverTestForHeaderExtensions, AcceptsStoppedExtension) { EXPECT_CALL(*receiver_.get(), Stop()); EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); auto modified_extensions = extensions_; modified_extensions[0].direction = RtpTransceiverDirection::kStopped; EXPECT_TRUE( transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions).ok()); EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), modified_extensions); } TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsDifferentSize) { EXPECT_CALL(*receiver_.get(), Stop()); EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); auto modified_extensions = extensions_; modified_extensions.pop_back(); EXPECT_THAT(transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions), Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION)); EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), extensions_); } TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsChangedUri) { EXPECT_CALL(*receiver_.get(), Stop()); EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); auto modified_extensions = extensions_; ASSERT_TRUE(!modified_extensions.empty()); modified_extensions[0].uri = "http://webrtc.org"; EXPECT_THAT(transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions), Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION)); EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), extensions_); } TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsReorder) { EXPECT_CALL(*receiver_.get(), Stop()); EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); auto modified_extensions = extensions_; ASSERT_GE(modified_extensions.size(), 2u); std::swap(modified_extensions[0], modified_extensions[1]); EXPECT_THAT(transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions), Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION)); EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), extensions_); } TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsStoppedMandatoryExtensions) { EXPECT_CALL(*receiver_.get(), Stop()); EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); std::vector modified_extensions = extensions_; // Attempting to stop the mandatory MID extension. modified_extensions[2].direction = RtpTransceiverDirection::kStopped; EXPECT_THAT(transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions), Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION)); EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), extensions_); } TEST_F(RtpTransceiverTestForHeaderExtensions, NoNegotiatedHdrExtsWithoutChannel) { EXPECT_CALL(*receiver_.get(), Stop()); EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); EXPECT_THAT(transceiver_->GetNegotiatedHeaderExtensions(), ElementsAre(Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped))); } TEST_F(RtpTransceiverTestForHeaderExtensions, NoNegotiatedHdrExtsWithChannelWithoutNegotiation) { const std::string content_name("my_mid"); EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)).WillRepeatedly(Return()); EXPECT_CALL(*receiver_.get(), Stop()).WillRepeatedly(Return()); EXPECT_CALL(*sender_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); auto mock_channel = std::make_unique(); auto mock_channel_ptr = mock_channel.get(); EXPECT_CALL(*mock_channel, SetFirstPacketReceivedCallback(_)); EXPECT_CALL(*mock_channel, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); EXPECT_CALL(*mock_channel, voice_media_send_channel()) .WillRepeatedly(Return(nullptr)); EXPECT_CALL(*mock_channel, mid()).WillRepeatedly(ReturnRef(content_name)); EXPECT_CALL(*mock_channel, SetRtpTransport(_)).WillRepeatedly(Return(true)); transceiver_->SetChannel(std::move(mock_channel), [](const std::string&) { return nullptr; }); EXPECT_THAT(transceiver_->GetNegotiatedHeaderExtensions(), ElementsAre(Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped))); EXPECT_CALL(*mock_channel_ptr, SetFirstPacketReceivedCallback(_)); ClearChannel(); } TEST_F(RtpTransceiverTestForHeaderExtensions, ReturnsNegotiatedHdrExts) { const std::string content_name("my_mid"); EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)).WillRepeatedly(Return()); EXPECT_CALL(*receiver_.get(), Stop()).WillRepeatedly(Return()); EXPECT_CALL(*sender_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); auto mock_channel = std::make_unique(); auto mock_channel_ptr = mock_channel.get(); EXPECT_CALL(*mock_channel, SetFirstPacketReceivedCallback(_)); EXPECT_CALL(*mock_channel, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); EXPECT_CALL(*mock_channel, voice_media_send_channel()) .WillRepeatedly(Return(nullptr)); EXPECT_CALL(*mock_channel, mid()).WillRepeatedly(ReturnRef(content_name)); EXPECT_CALL(*mock_channel, SetRtpTransport(_)).WillRepeatedly(Return(true)); cricket::RtpHeaderExtensions extensions = {RtpExtension("uri1", 1), RtpExtension("uri2", 2)}; cricket::AudioContentDescription description; description.set_rtp_header_extensions(extensions); transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description); transceiver_->SetChannel(std::move(mock_channel), [](const std::string&) { return nullptr; }); EXPECT_THAT(transceiver_->GetNegotiatedHeaderExtensions(), ElementsAre(Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kSendRecv), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kSendRecv), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped))); EXPECT_CALL(*mock_channel_ptr, SetFirstPacketReceivedCallback(_)); ClearChannel(); } TEST_F(RtpTransceiverTestForHeaderExtensions, ReturnsNegotiatedHdrExtsSecondTime) { EXPECT_CALL(*receiver_.get(), Stop()); EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); cricket::RtpHeaderExtensions extensions = {RtpExtension("uri1", 1), RtpExtension("uri2", 2)}; cricket::AudioContentDescription description; description.set_rtp_header_extensions(extensions); transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description); EXPECT_THAT(transceiver_->GetNegotiatedHeaderExtensions(), ElementsAre(Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kSendRecv), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kSendRecv), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped))); extensions = {RtpExtension("uri3", 4), RtpExtension("uri5", 6)}; description.set_rtp_header_extensions(extensions); transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description); EXPECT_THAT(transceiver_->GetNegotiatedHeaderExtensions(), ElementsAre(Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped), Field(&RtpHeaderExtensionCapability::direction, RtpTransceiverDirection::kStopped))); } TEST_F(RtpTransceiverTestForHeaderExtensions, SimulcastOrSvcEnablesExtensionsByDefault) { std::vector extensions = { {RtpExtension::kDependencyDescriptorUri, 1, RtpTransceiverDirection::kStopped}, {RtpExtension::kVideoLayersAllocationUri, 2, RtpTransceiverDirection::kStopped}, }; // Default is stopped. auto sender = rtc::make_ref_counted(); auto transceiver = rtc::make_ref_counted( RtpSenderProxyWithInternal::Create( rtc::Thread::Current(), sender), RtpReceiverProxyWithInternal::Create( rtc::Thread::Current(), rtc::Thread::Current(), receiver_), context(), extensions, /* on_negotiation_needed= */ [] {}); std::vector header_extensions = transceiver->GetHeaderExtensionsToNegotiate(); ASSERT_EQ(header_extensions.size(), 2u); EXPECT_EQ(header_extensions[0].uri, RtpExtension::kDependencyDescriptorUri); EXPECT_EQ(header_extensions[0].direction, RtpTransceiverDirection::kStopped); EXPECT_EQ(header_extensions[1].uri, RtpExtension::kVideoLayersAllocationUri); EXPECT_EQ(header_extensions[1].direction, RtpTransceiverDirection::kStopped); // Simulcast, i.e. more than one encoding. RtpParameters simulcast_parameters; simulcast_parameters.encodings.resize(2); auto simulcast_sender = rtc::make_ref_counted(); EXPECT_CALL(*simulcast_sender, GetParametersInternal()) .WillRepeatedly(Return(simulcast_parameters)); auto simulcast_transceiver = rtc::make_ref_counted( RtpSenderProxyWithInternal::Create( rtc::Thread::Current(), simulcast_sender), RtpReceiverProxyWithInternal::Create( rtc::Thread::Current(), rtc::Thread::Current(), receiver_), context(), extensions, /* on_negotiation_needed= */ [] {}); auto simulcast_extensions = simulcast_transceiver->GetHeaderExtensionsToNegotiate(); ASSERT_EQ(simulcast_extensions.size(), 2u); EXPECT_EQ(simulcast_extensions[0].uri, RtpExtension::kDependencyDescriptorUri); EXPECT_EQ(simulcast_extensions[0].direction, RtpTransceiverDirection::kSendRecv); EXPECT_EQ(simulcast_extensions[1].uri, RtpExtension::kVideoLayersAllocationUri); EXPECT_EQ(simulcast_extensions[1].direction, RtpTransceiverDirection::kSendRecv); // SVC, a single encoding with a scalabilityMode other than L1T1. webrtc::RtpParameters svc_parameters; svc_parameters.encodings.resize(1); svc_parameters.encodings[0].scalability_mode = "L3T3"; auto svc_sender = rtc::make_ref_counted(); EXPECT_CALL(*svc_sender, GetParametersInternal()) .WillRepeatedly(Return(svc_parameters)); auto svc_transceiver = rtc::make_ref_counted( RtpSenderProxyWithInternal::Create( rtc::Thread::Current(), svc_sender), RtpReceiverProxyWithInternal::Create( rtc::Thread::Current(), rtc::Thread::Current(), receiver_), context(), extensions, /* on_negotiation_needed= */ [] {}); std::vector svc_extensions = svc_transceiver->GetHeaderExtensionsToNegotiate(); ASSERT_EQ(svc_extensions.size(), 2u); EXPECT_EQ(svc_extensions[0].uri, RtpExtension::kDependencyDescriptorUri); EXPECT_EQ(svc_extensions[0].direction, RtpTransceiverDirection::kSendRecv); EXPECT_EQ(svc_extensions[1].uri, RtpExtension::kVideoLayersAllocationUri); EXPECT_EQ(svc_extensions[1].direction, RtpTransceiverDirection::kSendRecv); } } // namespace } // namespace webrtc