| /* | |
| * 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 <algorithm> | |
| #include <vector> | |
| #include "absl/strings/match.h" | |
| #include "absl/strings/string_view.h" | |
| #include "api/media_types.h" | |
| #include "api/peer_connection_interface.h" | |
| #include "api/rtc_error.h" | |
| #include "api/rtp_parameters.h" | |
| #include "api/rtp_transceiver_interface.h" | |
| #include "api/scoped_refptr.h" | |
| #include "api/test/rtc_error_matchers.h" | |
| #include "media/base/media_constants.h" | |
| #include "pc/test/integration_test_helpers.h" | |
| #include "test/gmock.h" | |
| #include "test/gtest.h" | |
| #include "test/wait_until.h" | |
| namespace webrtc { | |
| namespace { | |
| class PeerConnectionSVCIntegrationTest | |
| : public PeerConnectionIntegrationBaseTest { | |
| protected: | |
| PeerConnectionSVCIntegrationTest() | |
| : PeerConnectionIntegrationBaseTest(SdpSemantics::kUnifiedPlan) {} | |
| RTCError SetCodecPreferences( | |
| scoped_refptr<RtpTransceiverInterface> transceiver, | |
| absl::string_view codec_name) { | |
| RtpCapabilities capabilities = | |
| caller()->pc_factory()->GetRtpReceiverCapabilities(MediaType::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(MediaType::VIDEO); | |
| std::vector<RtpCodecCapability> vp8_codec; | |
| for (const RtpCodecCapability& codec_capability : capabilities.codecs) { | |
| if (codec_capability.name == 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, kVp8CodecName).ok()); | |
| caller()->CreateAndSetAndSignalOffer(); | |
| ASSERT_THAT( | |
| WaitUntil([&] { return SignalingStateStable(); }, ::testing::IsTrue()), | |
| IsRtcOk()); | |
| 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, kVp9CodecName).ok()); | |
| caller()->CreateAndSetAndSignalOffer(); | |
| ASSERT_THAT( | |
| WaitUntil([&] { return SignalingStateStable(); }, ::testing::IsTrue()), | |
| IsRtcOk()); | |
| 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, kVp8CodecName).ok()); | |
| caller()->CreateAndSetAndSignalOffer(); | |
| ASSERT_THAT( | |
| WaitUntil([&] { return SignalingStateStable(); }, ::testing::IsTrue()), | |
| IsRtcOk()); | |
| 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, kVp9CodecName).ok()); | |
| caller()->CreateAndSetAndSignalOffer(); | |
| ASSERT_THAT( | |
| WaitUntil([&] { return SignalingStateStable(); }, ::testing::IsTrue()), | |
| IsRtcOk()); | |
| 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(MediaType::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 == 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_THAT( | |
| WaitUntil([&] { return SignalingStateStable(); }, ::testing::IsTrue()), | |
| IsRtcOk()); | |
| 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 == 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_THAT( | |
| WaitUntil([&] { return SignalingStateStable(); }, ::testing::IsTrue()), | |
| IsRtcOk()); | |
| // 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 |