881 lines
39 KiB
C++
881 lines
39 KiB
C++
/*
|
|
* 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 <memory>
|
|
#include <optional>
|
|
#include <utility>
|
|
|
|
#include "absl/strings/string_view.h"
|
|
#include "api/environment/environment_factory.h"
|
|
#include "api/peer_connection_interface.h"
|
|
#include "api/rtp_parameters.h"
|
|
#include "api/test/rtc_error_matchers.h"
|
|
#include "media/base/codec_comparators.h"
|
|
#include "media/base/fake_media_engine.h"
|
|
#include "pc/rtp_parameters_conversion.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::NiceMock;
|
|
using ::testing::Optional;
|
|
using ::testing::Property;
|
|
using ::testing::Return;
|
|
using ::testing::ReturnRef;
|
|
using ::testing::SizeIs;
|
|
|
|
namespace webrtc {
|
|
|
|
namespace {
|
|
|
|
class RtpTransceiverTest : public testing::Test {
|
|
public:
|
|
RtpTransceiverTest()
|
|
: dependencies_(MakeDependencies()),
|
|
context_(
|
|
ConnectionContext::Create(CreateEnvironment(), &dependencies_)) {}
|
|
|
|
protected:
|
|
cricket::FakeMediaEngine* media_engine() {
|
|
// We know this cast is safe because we supplied the fake implementation
|
|
// in MakeDependencies().
|
|
return static_cast<cricket::FakeMediaEngine*>(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, std::make_unique<cricket::FakeMediaEngine>());
|
|
return d;
|
|
}
|
|
|
|
PeerConnectionFactoryDependencies dependencies_;
|
|
rtc::scoped_refptr<ConnectionContext> 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<RtpTransceiver>(
|
|
cricket::MediaType::MEDIA_TYPE_AUDIO, context());
|
|
auto channel1 = std::make_unique<NiceMock<cricket::MockChannelInterface>>();
|
|
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<NiceMock<cricket::MockChannelInterface>>();
|
|
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();
|
|
ASSERT_EQ(nullptr, transceiver->channel());
|
|
// 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<RtpTransceiver>(
|
|
cricket::MediaType::MEDIA_TYPE_VIDEO, context());
|
|
auto channel = std::make_unique<NiceMock<cricket::MockChannelInterface>>();
|
|
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:
|
|
static rtc::scoped_refptr<MockRtpReceiverInternal> MockReceiver(
|
|
cricket::MediaType media_type) {
|
|
auto receiver = rtc::make_ref_counted<NiceMock<MockRtpReceiverInternal>>();
|
|
EXPECT_CALL(*receiver.get(), media_type())
|
|
.WillRepeatedly(Return(media_type));
|
|
return receiver;
|
|
}
|
|
|
|
static rtc::scoped_refptr<MockRtpSenderInternal> MockSender(
|
|
cricket::MediaType media_type) {
|
|
auto sender = rtc::make_ref_counted<NiceMock<MockRtpSenderInternal>>();
|
|
EXPECT_CALL(*sender.get(), media_type()).WillRepeatedly(Return(media_type));
|
|
return sender;
|
|
}
|
|
|
|
rtc::scoped_refptr<RtpTransceiver> CreateTransceiver(
|
|
rtc::scoped_refptr<RtpSenderInternal> sender,
|
|
rtc::scoped_refptr<RtpReceiverInternal> receiver) {
|
|
return rtc::make_ref_counted<RtpTransceiver>(
|
|
RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
|
|
rtc::Thread::Current(), std::move(sender)),
|
|
RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
|
|
rtc::Thread::Current(), rtc::Thread::Current(),
|
|
std::move(receiver)),
|
|
context(), media_engine()->voice().GetRtpHeaderExtensions(),
|
|
/* on_negotiation_needed= */ [] {});
|
|
}
|
|
|
|
protected:
|
|
rtc::AutoThread main_thread_;
|
|
};
|
|
|
|
// Basic tests for Stop()
|
|
TEST_F(RtpTransceiverUnifiedPlanTest, StopSetsDirection) {
|
|
rtc::scoped_refptr<MockRtpReceiverInternal> receiver =
|
|
MockReceiver(cricket::MediaType::MEDIA_TYPE_AUDIO);
|
|
rtc::scoped_refptr<MockRtpSenderInternal> sender =
|
|
MockSender(cricket::MediaType::MEDIA_TYPE_AUDIO);
|
|
rtc::scoped_refptr<RtpTransceiver> transceiver =
|
|
CreateTransceiver(sender, receiver);
|
|
|
|
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 RtpTransceiverFilteredCodecPreferencesTest
|
|
: public RtpTransceiverUnifiedPlanTest {
|
|
public:
|
|
RtpTransceiverFilteredCodecPreferencesTest()
|
|
: transceiver_(CreateTransceiver(
|
|
MockSender(cricket::MediaType::MEDIA_TYPE_VIDEO),
|
|
MockReceiver(cricket::MediaType::MEDIA_TYPE_VIDEO))) {}
|
|
|
|
struct H264CodecCapabilities {
|
|
cricket::Codec cricket_sendrecv_codec;
|
|
RtpCodecCapability sendrecv_codec;
|
|
cricket::Codec cricket_sendonly_codec;
|
|
RtpCodecCapability sendonly_codec;
|
|
cricket::Codec cricket_recvonly_codec;
|
|
RtpCodecCapability recvonly_codec;
|
|
cricket::Codec cricket_rtx_codec;
|
|
RtpCodecCapability rtx_codec;
|
|
};
|
|
|
|
// For H264, the profile and level IDs are entangled. This function uses
|
|
// profile-level-id values that are not equal even when levels are ignored.
|
|
H264CodecCapabilities ConfigureH264CodecCapabilities() {
|
|
cricket::Codec cricket_sendrecv_codec = cricket::CreateVideoCodec(
|
|
SdpVideoFormat("H264",
|
|
{{"level-asymmetry-allowed", "1"},
|
|
{"packetization-mode", "1"},
|
|
{"profile-level-id", "42f00b"}},
|
|
{ScalabilityMode::kL1T1}));
|
|
cricket::Codec cricket_sendonly_codec = cricket::CreateVideoCodec(
|
|
SdpVideoFormat("H264",
|
|
{{"level-asymmetry-allowed", "1"},
|
|
{"packetization-mode", "1"},
|
|
{"profile-level-id", "640034"}},
|
|
{ScalabilityMode::kL1T1}));
|
|
cricket::Codec cricket_recvonly_codec = cricket::CreateVideoCodec(
|
|
SdpVideoFormat("H264",
|
|
{{"level-asymmetry-allowed", "1"},
|
|
{"packetization-mode", "1"},
|
|
{"profile-level-id", "f4001f"}},
|
|
{ScalabilityMode::kL1T1}));
|
|
cricket::Codec cricket_rtx_codec = cricket::CreateVideoRtxCodec(
|
|
cricket::Codec::kIdNotSet, cricket::Codec::kIdNotSet);
|
|
media_engine()->SetVideoSendCodecs(
|
|
{cricket_sendrecv_codec, cricket_sendonly_codec, cricket_rtx_codec});
|
|
media_engine()->SetVideoRecvCodecs(
|
|
{cricket_sendrecv_codec, cricket_recvonly_codec, cricket_rtx_codec});
|
|
H264CodecCapabilities capabilities = {
|
|
.cricket_sendrecv_codec = cricket_sendrecv_codec,
|
|
.sendrecv_codec = ToRtpCodecCapability(cricket_sendrecv_codec),
|
|
.cricket_sendonly_codec = cricket_sendonly_codec,
|
|
.sendonly_codec = ToRtpCodecCapability(cricket_sendonly_codec),
|
|
.cricket_recvonly_codec = cricket_recvonly_codec,
|
|
.recvonly_codec = ToRtpCodecCapability(cricket_recvonly_codec),
|
|
.cricket_rtx_codec = cricket_rtx_codec,
|
|
.rtx_codec = ToRtpCodecCapability(cricket_rtx_codec),
|
|
};
|
|
EXPECT_FALSE(IsSameRtpCodecIgnoringLevel(
|
|
capabilities.cricket_sendrecv_codec, capabilities.sendonly_codec));
|
|
EXPECT_FALSE(IsSameRtpCodecIgnoringLevel(
|
|
capabilities.cricket_sendrecv_codec, capabilities.recvonly_codec));
|
|
EXPECT_FALSE(IsSameRtpCodecIgnoringLevel(
|
|
capabilities.cricket_sendonly_codec, capabilities.recvonly_codec));
|
|
return capabilities;
|
|
}
|
|
|
|
#ifdef RTC_ENABLE_H265
|
|
struct H265CodecCapabilities {
|
|
// The level-id from sender getCapabilities() or receiver getCapabilities().
|
|
static constexpr const char* kSendOnlyLevel = "180";
|
|
static constexpr const char* kRecvOnlyLevel = "156";
|
|
// A valid H265 level-id, but one not present in either getCapabilities().
|
|
static constexpr const char* kLevelNotInCapabilities = "135";
|
|
|
|
cricket::Codec cricket_sendonly_codec;
|
|
RtpCodecCapability sendonly_codec;
|
|
cricket::Codec cricket_recvonly_codec;
|
|
RtpCodecCapability recvonly_codec;
|
|
};
|
|
|
|
// For H265, the profile and level IDs are separate and are ignored by
|
|
// IsSameRtpCodecIgnoringLevel().
|
|
H265CodecCapabilities ConfigureH265CodecCapabilities() {
|
|
cricket::Codec cricket_sendonly_codec = cricket::CreateVideoCodec(
|
|
SdpVideoFormat("H265",
|
|
{{"profile-id", "1"},
|
|
{"tier-flag", "0"},
|
|
{"level-id", H265CodecCapabilities::kSendOnlyLevel},
|
|
{"tx-mode", "SRST"}},
|
|
{ScalabilityMode::kL1T1}));
|
|
cricket::Codec cricket_recvonly_codec = cricket::CreateVideoCodec(
|
|
SdpVideoFormat("H265",
|
|
{{"profile-id", "1"},
|
|
{"tier-flag", "0"},
|
|
{"level-id", H265CodecCapabilities::kRecvOnlyLevel},
|
|
{"tx-mode", "SRST"}},
|
|
{ScalabilityMode::kL1T1}));
|
|
media_engine()->SetVideoSendCodecs({cricket_sendonly_codec});
|
|
media_engine()->SetVideoRecvCodecs({cricket_recvonly_codec});
|
|
return {
|
|
.cricket_sendonly_codec = cricket_sendonly_codec,
|
|
.sendonly_codec = ToRtpCodecCapability(cricket_sendonly_codec),
|
|
.cricket_recvonly_codec = cricket_recvonly_codec,
|
|
.recvonly_codec = ToRtpCodecCapability(cricket_recvonly_codec),
|
|
};
|
|
}
|
|
#endif // RTC_ENABLE_H265
|
|
|
|
protected:
|
|
rtc::scoped_refptr<RtpTransceiver> transceiver_;
|
|
};
|
|
|
|
TEST_F(RtpTransceiverFilteredCodecPreferencesTest, EmptyByDefault) {
|
|
ConfigureH264CodecCapabilities();
|
|
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendRecv),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(), SizeIs(0));
|
|
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendOnly),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(), SizeIs(0));
|
|
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(), SizeIs(0));
|
|
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kInactive),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(), SizeIs(0));
|
|
}
|
|
|
|
TEST_F(RtpTransceiverFilteredCodecPreferencesTest, OrderIsMaintained) {
|
|
const auto codecs = ConfigureH264CodecCapabilities();
|
|
std::vector<RtpCodecCapability> codec_capabilities = {codecs.sendrecv_codec,
|
|
codecs.rtx_codec};
|
|
EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(),
|
|
ElementsAre(codec_capabilities[0], codec_capabilities[1]));
|
|
// Reverse order.
|
|
codec_capabilities = {codecs.rtx_codec, codecs.sendrecv_codec};
|
|
EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(),
|
|
ElementsAre(codec_capabilities[0], codec_capabilities[1]));
|
|
}
|
|
|
|
TEST_F(RtpTransceiverFilteredCodecPreferencesTest,
|
|
FiltersCodecsBasedOnDirection) {
|
|
const auto codecs = ConfigureH264CodecCapabilities();
|
|
std::vector<RtpCodecCapability> codec_capabilities = {
|
|
codecs.sendonly_codec, codecs.sendrecv_codec, codecs.recvonly_codec};
|
|
EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());
|
|
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendRecv),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(),
|
|
ElementsAre(codecs.sendrecv_codec));
|
|
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendOnly),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(),
|
|
ElementsAre(codecs.sendonly_codec, codecs.sendrecv_codec));
|
|
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(),
|
|
ElementsAre(codecs.sendrecv_codec, codecs.recvonly_codec));
|
|
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kInactive),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(),
|
|
ElementsAre(codecs.sendrecv_codec));
|
|
}
|
|
|
|
TEST_F(RtpTransceiverFilteredCodecPreferencesTest,
|
|
RtxIsIncludedAfterFiltering) {
|
|
const auto codecs = ConfigureH264CodecCapabilities();
|
|
std::vector<RtpCodecCapability> codec_capabilities = {codecs.recvonly_codec,
|
|
codecs.rtx_codec};
|
|
EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());
|
|
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(),
|
|
ElementsAre(codecs.recvonly_codec, codecs.rtx_codec));
|
|
}
|
|
|
|
TEST_F(RtpTransceiverFilteredCodecPreferencesTest,
|
|
NoMediaIsTheSameAsNoPreference) {
|
|
const auto codecs = ConfigureH264CodecCapabilities();
|
|
std::vector<RtpCodecCapability> codec_capabilities = {codecs.recvonly_codec,
|
|
codecs.rtx_codec};
|
|
EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());
|
|
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendOnly),
|
|
IsRtcOk());
|
|
// After filtering the only codec that remains is RTX which is not a media
|
|
// codec, this is the same as not having any preferences.
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(), SizeIs(0));
|
|
|
|
// But the preferences are remembered in case the direction changes such that
|
|
// we do have a media codec.
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(),
|
|
ElementsAre(codecs.recvonly_codec, codecs.rtx_codec));
|
|
}
|
|
|
|
TEST_F(RtpTransceiverFilteredCodecPreferencesTest,
|
|
H264LevelIdsIgnoredByFilter) {
|
|
// Baseline 3.1 and 5.2 are compatible when ignoring level IDs.
|
|
cricket::Codec baseline_3_1 = cricket::CreateVideoCodec(
|
|
SdpVideoFormat("H264",
|
|
{{"level-asymmetry-allowed", "1"},
|
|
{"packetization-mode", "1"},
|
|
{"profile-level-id", "42001f"}},
|
|
{ScalabilityMode::kL1T1}));
|
|
cricket::Codec baseline_5_2 = cricket::CreateVideoCodec(
|
|
SdpVideoFormat("H264",
|
|
{{"level-asymmetry-allowed", "1"},
|
|
{"packetization-mode", "1"},
|
|
{"profile-level-id", "420034"}},
|
|
{ScalabilityMode::kL1T1}));
|
|
// High is NOT compatible with baseline.
|
|
cricket::Codec high_3_1 = cricket::CreateVideoCodec(
|
|
SdpVideoFormat("H264",
|
|
{{"level-asymmetry-allowed", "1"},
|
|
{"packetization-mode", "1"},
|
|
{"profile-level-id", "64001f"}},
|
|
{ScalabilityMode::kL1T1}));
|
|
// Configure being able to both send and receive Baseline but using different
|
|
// level IDs in either direction, while the High profile is "truly" recvonly.
|
|
media_engine()->SetVideoSendCodecs({baseline_3_1});
|
|
media_engine()->SetVideoRecvCodecs({baseline_5_2, high_3_1});
|
|
|
|
// Prefer to "sendrecv" Baseline 5.2. Even though we can only send 3.1 this
|
|
// codec is not filtered out due to 5.2 and 3.1 being compatible when ignoring
|
|
// level IDs.
|
|
std::vector<RtpCodecCapability> codec_capabilities = {
|
|
ToRtpCodecCapability(baseline_5_2)};
|
|
EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendRecv),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(),
|
|
ElementsAre(codec_capabilities[0]));
|
|
// Prefer to "sendrecv" High 3.1. This gets filtered out because we cannot
|
|
// send it (Baseline 3.1 is not compatible with it).
|
|
codec_capabilities = {ToRtpCodecCapability(high_3_1)};
|
|
EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(), SizeIs(0));
|
|
// Change direction to "recvonly" to avoid High 3.1 being filtered out.
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(),
|
|
ElementsAre(codec_capabilities[0]));
|
|
}
|
|
|
|
#ifdef RTC_ENABLE_H265
|
|
TEST_F(RtpTransceiverFilteredCodecPreferencesTest,
|
|
H265LevelIdIsIgnoredByFilter) {
|
|
const auto codecs = ConfigureH265CodecCapabilities();
|
|
std::vector<RtpCodecCapability> codec_capabilities = {codecs.sendonly_codec,
|
|
codecs.recvonly_codec};
|
|
EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());
|
|
// Regardless of direction, both codecs are preferred due to ignoring levels.
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendOnly),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(),
|
|
ElementsAre(codec_capabilities[0], codec_capabilities[1]));
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(),
|
|
ElementsAre(codec_capabilities[0], codec_capabilities[1]));
|
|
EXPECT_THAT(
|
|
transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendRecv),
|
|
IsRtcOk());
|
|
EXPECT_THAT(transceiver_->filtered_codec_preferences(),
|
|
ElementsAre(codec_capabilities[0], codec_capabilities[1]));
|
|
}
|
|
|
|
TEST_F(RtpTransceiverFilteredCodecPreferencesTest,
|
|
H265LevelIdHasToBeFromSenderOrReceiverCapabilities) {
|
|
ConfigureH265CodecCapabilities();
|
|
cricket::Codec cricket_codec = cricket::CreateVideoCodec(SdpVideoFormat(
|
|
"H265",
|
|
{{"profile-id", "1"},
|
|
{"tier-flag", "0"},
|
|
{"level-id", H265CodecCapabilities::kLevelNotInCapabilities},
|
|
{"tx-mode", "SRST"}},
|
|
{ScalabilityMode::kL1T1}));
|
|
|
|
std::vector<RtpCodecCapability> codec_capabilities = {
|
|
ToRtpCodecCapability(cricket_codec)};
|
|
EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities),
|
|
IsRtcErrorWithTypeAndMessage(
|
|
RTCErrorType::INVALID_MODIFICATION,
|
|
"Invalid codec preferences: Missing codec from codec "
|
|
"capabilities."));
|
|
}
|
|
#endif // RTC_ENABLE_H265
|
|
|
|
class RtpTransceiverTestForHeaderExtensions
|
|
: public RtpTransceiverUnifiedPlanTest {
|
|
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<RtpTransceiver>(
|
|
RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
|
|
rtc::Thread::Current(),
|
|
sender_),
|
|
RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
|
|
rtc::Thread::Current(),
|
|
rtc::Thread::Current(),
|
|
receiver_),
|
|
context(),
|
|
extensions_,
|
|
/* on_negotiation_needed= */ [] {})) {}
|
|
|
|
void ClearChannel() {
|
|
EXPECT_CALL(*sender_.get(), SetMediaChannel(_));
|
|
transceiver_->ClearChannel();
|
|
}
|
|
|
|
rtc::scoped_refptr<MockRtpReceiverInternal> receiver_ =
|
|
MockReceiver(cricket::MediaType::MEDIA_TYPE_AUDIO);
|
|
rtc::scoped_refptr<MockRtpSenderInternal> sender_ =
|
|
MockSender(cricket::MediaType::MEDIA_TYPE_AUDIO);
|
|
|
|
std::vector<RtpHeaderExtensionCapability> extensions_;
|
|
rtc::scoped_refptr<RtpTransceiver> 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<RtpHeaderExtensionCapability> 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<NiceMock<cricket::MockChannelInterface>>();
|
|
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<NiceMock<cricket::MockChannelInterface>>();
|
|
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<RtpHeaderExtensionCapability> extensions = {
|
|
{RtpExtension::kDependencyDescriptorUri, 1,
|
|
RtpTransceiverDirection::kStopped},
|
|
{RtpExtension::kVideoLayersAllocationUri, 2,
|
|
RtpTransceiverDirection::kStopped},
|
|
};
|
|
|
|
// Default is stopped.
|
|
auto sender = rtc::make_ref_counted<NiceMock<MockRtpSenderInternal>>();
|
|
auto transceiver = rtc::make_ref_counted<RtpTransceiver>(
|
|
RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
|
|
rtc::Thread::Current(), sender),
|
|
RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
|
|
rtc::Thread::Current(), rtc::Thread::Current(), receiver_),
|
|
context(), extensions,
|
|
/* on_negotiation_needed= */ [] {});
|
|
std::vector<webrtc::RtpHeaderExtensionCapability> 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<NiceMock<MockRtpSenderInternal>>();
|
|
EXPECT_CALL(*simulcast_sender, GetParametersInternal())
|
|
.WillRepeatedly(Return(simulcast_parameters));
|
|
auto simulcast_transceiver = rtc::make_ref_counted<RtpTransceiver>(
|
|
RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
|
|
rtc::Thread::Current(), simulcast_sender),
|
|
RtpReceiverProxyWithInternal<RtpReceiverInternal>::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<NiceMock<MockRtpSenderInternal>>();
|
|
EXPECT_CALL(*svc_sender, GetParametersInternal())
|
|
.WillRepeatedly(Return(svc_parameters));
|
|
auto svc_transceiver = rtc::make_ref_counted<RtpTransceiver>(
|
|
RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
|
|
rtc::Thread::Current(), svc_sender),
|
|
RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
|
|
rtc::Thread::Current(), rtc::Thread::Current(), receiver_),
|
|
context(), extensions,
|
|
/* on_negotiation_needed= */ [] {});
|
|
std::vector<webrtc::RtpHeaderExtensionCapability> 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
|