blob: 7deafbf428b0a1119387a0e5ade63dc7e0ef77a1 [file] [log] [blame]
/*
* Copyright 2022 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.
*/
// Integration tests for PeerConnection.
// These tests exercise a full stack for the SVC extension.
#include <stdint.h>
#include <functional>
#include <vector>
#include "absl/strings/match.h"
#include "api/rtc_error.h"
#include "api/rtp_parameters.h"
#include "api/rtp_transceiver_interface.h"
#include "api/scoped_refptr.h"
#include "pc/test/integration_test_helpers.h"
#include "rtc_base/gunit.h"
#include "rtc_base/helpers.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
class PeerConnectionSVCIntegrationTest
: public PeerConnectionIntegrationBaseTest {
protected:
PeerConnectionSVCIntegrationTest()
: PeerConnectionIntegrationBaseTest(SdpSemantics::kUnifiedPlan) {}
RTCError SetCodecPreferences(
rtc::scoped_refptr<RtpTransceiverInterface> transceiver,
absl::string_view codec_name) {
RtpCapabilities capabilities =
caller()->pc_factory()->GetRtpReceiverCapabilities(
cricket::MEDIA_TYPE_VIDEO);
std::vector<RtpCodecCapability> codecs;
for (const RtpCodecCapability& codec_capability : capabilities.codecs) {
if (codec_capability.name == codec_name)
codecs.push_back(codec_capability);
}
return transceiver->SetCodecPreferences(codecs);
}
};
TEST_F(PeerConnectionSVCIntegrationTest, AddTransceiverAcceptsL1T1) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
RtpTransceiverInit init;
RtpEncodingParameters encoding_parameters;
encoding_parameters.scalability_mode = "L1T1";
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
EXPECT_TRUE(transceiver_or_error.ok());
}
TEST_F(PeerConnectionSVCIntegrationTest, AddTransceiverAcceptsL3T3) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
RtpTransceiverInit init;
RtpEncodingParameters encoding_parameters;
encoding_parameters.scalability_mode = "L3T3";
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
EXPECT_TRUE(transceiver_or_error.ok());
}
TEST_F(PeerConnectionSVCIntegrationTest,
AddTransceiverRejectsUnknownScalabilityMode) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
RtpTransceiverInit init;
RtpEncodingParameters encoding_parameters;
encoding_parameters.scalability_mode = "FOOBAR";
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
EXPECT_FALSE(transceiver_or_error.ok());
EXPECT_EQ(transceiver_or_error.error().type(),
RTCErrorType::UNSUPPORTED_OPERATION);
}
TEST_F(PeerConnectionSVCIntegrationTest, SetParametersAcceptsL1T3WithVP8) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
RtpCapabilities capabilities =
caller()->pc_factory()->GetRtpReceiverCapabilities(
cricket::MEDIA_TYPE_VIDEO);
std::vector<RtpCodecCapability> vp8_codec;
for (const RtpCodecCapability& codec_capability : capabilities.codecs) {
if (codec_capability.name == cricket::kVp8CodecName)
vp8_codec.push_back(codec_capability);
}
RtpTransceiverInit init;
RtpEncodingParameters encoding_parameters;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
ASSERT_TRUE(transceiver_or_error.ok());
auto transceiver = transceiver_or_error.MoveValue();
EXPECT_TRUE(transceiver->SetCodecPreferences(vp8_codec).ok());
RtpParameters parameters = transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 1u);
parameters.encodings[0].scalability_mode = "L1T3";
auto result = transceiver->sender()->SetParameters(parameters);
EXPECT_TRUE(result.ok());
}
TEST_F(PeerConnectionSVCIntegrationTest,
SetParametersAcceptsL1T3WithVP8AfterNegotiation) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
RtpTransceiverInit init;
RtpEncodingParameters encoding_parameters;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
ASSERT_TRUE(transceiver_or_error.ok());
auto transceiver = transceiver_or_error.MoveValue();
EXPECT_TRUE(SetCodecPreferences(transceiver, cricket::kVp8CodecName).ok());
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
RtpParameters parameters = transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 1u);
parameters.encodings[0].scalability_mode = "L1T3";
auto result = transceiver->sender()->SetParameters(parameters);
EXPECT_TRUE(result.ok());
}
TEST_F(PeerConnectionSVCIntegrationTest,
SetParametersAcceptsL3T3WithVP9AfterNegotiation) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
RtpTransceiverInit init;
RtpEncodingParameters encoding_parameters;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
ASSERT_TRUE(transceiver_or_error.ok());
auto transceiver = transceiver_or_error.MoveValue();
EXPECT_TRUE(SetCodecPreferences(transceiver, cricket::kVp9CodecName).ok());
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
RtpParameters parameters = transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 1u);
parameters.encodings[0].scalability_mode = "L3T3";
auto result = transceiver->sender()->SetParameters(parameters);
EXPECT_TRUE(result.ok());
}
TEST_F(PeerConnectionSVCIntegrationTest,
SetParametersRejectsL3T3WithVP8AfterNegotiation) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
RtpTransceiverInit init;
RtpEncodingParameters encoding_parameters;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
ASSERT_TRUE(transceiver_or_error.ok());
auto transceiver = transceiver_or_error.MoveValue();
EXPECT_TRUE(SetCodecPreferences(transceiver, cricket::kVp8CodecName).ok());
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
RtpParameters parameters = transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 1u);
parameters.encodings[0].scalability_mode = "L3T3";
auto result = transceiver->sender()->SetParameters(parameters);
EXPECT_FALSE(result.ok());
EXPECT_EQ(result.type(), RTCErrorType::INVALID_MODIFICATION);
}
TEST_F(PeerConnectionSVCIntegrationTest,
SetParametersRejectsInvalidModeWithVP9AfterNegotiation) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
RtpTransceiverInit init;
RtpEncodingParameters encoding_parameters;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
ASSERT_TRUE(transceiver_or_error.ok());
auto transceiver = transceiver_or_error.MoveValue();
EXPECT_TRUE(SetCodecPreferences(transceiver, cricket::kVp9CodecName).ok());
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
RtpParameters parameters = transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 1u);
parameters.encodings[0].scalability_mode = "FOOBAR";
auto result = transceiver->sender()->SetParameters(parameters);
EXPECT_FALSE(result.ok());
EXPECT_EQ(result.type(), RTCErrorType::INVALID_MODIFICATION);
}
TEST_F(PeerConnectionSVCIntegrationTest, FallbackToL1Tx) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
RtpTransceiverInit init;
RtpEncodingParameters encoding_parameters;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
ASSERT_TRUE(transceiver_or_error.ok());
auto caller_transceiver = transceiver_or_error.MoveValue();
RtpCapabilities capabilities =
caller()->pc_factory()->GetRtpReceiverCapabilities(
cricket::MEDIA_TYPE_VIDEO);
std::vector<RtpCodecCapability> send_codecs = capabilities.codecs;
// Only keep VP9 in the caller
send_codecs.erase(std::partition(send_codecs.begin(), send_codecs.end(),
[](const auto& codec) -> bool {
return codec.name ==
cricket::kVp9CodecName;
}),
send_codecs.end());
ASSERT_FALSE(send_codecs.empty());
caller_transceiver->SetCodecPreferences(send_codecs);
// L3T3 should be supported by VP9
RtpParameters parameters = caller_transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 1u);
parameters.encodings[0].scalability_mode = "L3T3";
auto result = caller_transceiver->sender()->SetParameters(parameters);
EXPECT_TRUE(result.ok());
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
parameters = caller_transceiver->sender()->GetParameters();
ASSERT_TRUE(parameters.encodings[0].scalability_mode.has_value());
EXPECT_TRUE(
absl::StartsWith(*parameters.encodings[0].scalability_mode, "L3T3"));
// Keep only VP8 in the caller
send_codecs = capabilities.codecs;
send_codecs.erase(std::partition(send_codecs.begin(), send_codecs.end(),
[](const auto& codec) -> bool {
return codec.name ==
cricket::kVp8CodecName;
}),
send_codecs.end());
ASSERT_FALSE(send_codecs.empty());
caller_transceiver->SetCodecPreferences(send_codecs);
// Renegotiate to force the new codec list to be used
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
// Fallback should happen and L3T3 is not used anymore
parameters = caller_transceiver->sender()->GetParameters();
ASSERT_TRUE(parameters.encodings[0].scalability_mode.has_value());
EXPECT_TRUE(
absl::StartsWith(*parameters.encodings[0].scalability_mode, "L1T"));
}
} // namespace
} // namespace webrtc