diff --git a/media/base/media_engine.cc b/media/base/media_engine.cc index c551a58cf9..7304ab03d7 100644 --- a/media/base/media_engine.cc +++ b/media/base/media_engine.cc @@ -67,11 +67,11 @@ std::vector GetDefaultEnabledRtpHeaderExtensions( webrtc::RTCError CheckScalabilityModeValues( const webrtc::RtpParameters& rtp_parameters, - rtc::ArrayView send_codecs, + rtc::ArrayView codec_preferences, absl::optional send_codec) { using webrtc::RTCErrorType; - if (send_codecs.empty()) { + if (codec_preferences.empty()) { // This is an audio sender or an extra check in the stack where the codec // list is not available and we can't check the scalability_mode values. return webrtc::RTCError::OK(); @@ -80,7 +80,7 @@ webrtc::RTCError CheckScalabilityModeValues( for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) { if (rtp_parameters.encodings[i].codec) { bool codecFound = false; - for (const cricket::Codec& codec : send_codecs) { + for (const cricket::VideoCodec& codec : codec_preferences) { if (codec.MatchesRtpCodec(*rtp_parameters.encodings[i].codec)) { codecFound = true; send_codec = codec; @@ -97,7 +97,7 @@ webrtc::RTCError CheckScalabilityModeValues( if (rtp_parameters.encodings[i].scalability_mode) { if (!send_codec) { bool scalabilityModeFound = false; - for (const cricket::Codec& codec : send_codecs) { + for (const cricket::VideoCodec& codec : codec_preferences) { for (const auto& scalability_mode : codec.scalability_modes) { if (ScalabilityModeToString(scalability_mode) == *rtp_parameters.encodings[i].scalability_mode) { @@ -139,7 +139,7 @@ webrtc::RTCError CheckScalabilityModeValues( webrtc::RTCError CheckRtpParametersValues( const webrtc::RtpParameters& rtp_parameters, - rtc::ArrayView send_codecs, + rtc::ArrayView codec_preferences, absl::optional send_codec) { using webrtc::RTCErrorType; @@ -196,7 +196,8 @@ webrtc::RTCError CheckRtpParametersValues( } } - return CheckScalabilityModeValues(rtp_parameters, send_codecs, send_codec); + return CheckScalabilityModeValues(rtp_parameters, codec_preferences, + send_codec); } webrtc::RTCError CheckRtpParametersInvalidModificationAndValues( @@ -209,7 +210,7 @@ webrtc::RTCError CheckRtpParametersInvalidModificationAndValues( webrtc::RTCError CheckRtpParametersInvalidModificationAndValues( const webrtc::RtpParameters& old_rtp_parameters, const webrtc::RtpParameters& rtp_parameters, - rtc::ArrayView send_codecs, + rtc::ArrayView codec_preferences, absl::optional send_codec) { using webrtc::RTCErrorType; if (rtp_parameters.encodings.size() != old_rtp_parameters.encodings.size()) { @@ -245,7 +246,8 @@ webrtc::RTCError CheckRtpParametersInvalidModificationAndValues( "Attempted to set RtpParameters with modified SSRC"); } - return CheckRtpParametersValues(rtp_parameters, send_codecs, send_codec); + return CheckRtpParametersValues(rtp_parameters, codec_preferences, + send_codec); } CompositeMediaEngine::CompositeMediaEngine( diff --git a/media/base/media_engine.h b/media/base/media_engine.h index b054893163..428123516f 100644 --- a/media/base/media_engine.h +++ b/media/base/media_engine.h @@ -42,14 +42,14 @@ namespace cricket { // least one video codec of the list. If the list is empty, no check is done. webrtc::RTCError CheckScalabilityModeValues( const webrtc::RtpParameters& new_parameters, - rtc::ArrayView send_codecs, + rtc::ArrayView codec_preferences, absl::optional send_codec); // Checks the parameters have valid and supported values, and checks parameters // with CheckScalabilityModeValues(). webrtc::RTCError CheckRtpParametersValues( const webrtc::RtpParameters& new_parameters, - rtc::ArrayView send_codecs, + rtc::ArrayView codec_preferences, absl::optional send_codec); // Checks that the immutable values have not changed in new_parameters and @@ -57,7 +57,7 @@ webrtc::RTCError CheckRtpParametersValues( webrtc::RTCError CheckRtpParametersInvalidModificationAndValues( const webrtc::RtpParameters& old_parameters, const webrtc::RtpParameters& new_parameters, - rtc::ArrayView send_codecs, + rtc::ArrayView codec_preferences, absl::optional send_codec); // Checks that the immutable values have not changed in new_parameters and diff --git a/pc/peer_connection_encodings_integrationtest.cc b/pc/peer_connection_encodings_integrationtest.cc index d6c4499210..06299d7029 100644 --- a/pc/peer_connection_encodings_integrationtest.cc +++ b/pc/peer_connection_encodings_integrationtest.cc @@ -134,12 +134,12 @@ class PeerConnectionEncodingsIntegrationTest : public ::testing::Test { return transceiver_or_error.value(); } - bool HasReceiverVideoCodecCapability( + bool HasSenderVideoCodecCapability( rtc::scoped_refptr pc_wrapper, absl::string_view codec_name) { std::vector codecs = pc_wrapper->pc_factory() - ->GetRtpReceiverCapabilities(cricket::MEDIA_TYPE_VIDEO) + ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO) .codecs; return std::find_if(codecs.begin(), codecs.end(), [&codec_name](const RtpCodecCapability& codec) { @@ -152,7 +152,7 @@ class PeerConnectionEncodingsIntegrationTest : public ::testing::Test { absl::string_view codec_name) { std::vector codecs = pc_wrapper->pc_factory() - ->GetRtpReceiverCapabilities(cricket::MEDIA_TYPE_VIDEO) + ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO) .codecs; codecs.erase(std::remove_if(codecs.begin(), codecs.end(), [&codec_name](const RtpCodecCapability& codec) { @@ -431,7 +431,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, layers); std::vector codecs = - GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, "VP8"); + GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP8"); transceiver->SetCodecPreferences(codecs); NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); @@ -464,14 +464,10 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, rtc::scoped_refptr transceiver = AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, layers); - // Restricting the local receive codecs will restrict what we offer and - // hence the answer if it is a subset of our offer. + // Restricting codecs restricts what SetParameters() will accept or reject. std::vector codecs = GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP8"); transceiver->SetCodecPreferences(codecs); - - NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); - // Attempt SVC (L3T3_KEY). This is not possible because only VP8 is up for // negotiation and VP8 does not support it. rtc::scoped_refptr sender = transceiver->sender(); @@ -485,6 +481,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, ASSERT_EQ(parameters.encodings.size(), 1u); EXPECT_THAT(parameters.encodings[0].scalability_mode, Eq(absl::nullopt)); + NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); local_pc_wrapper->WaitForConnection(); remote_pc_wrapper->WaitForConnection(); @@ -505,60 +502,6 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, EXPECT_THAT(parameters.encodings[0].scalability_mode, Eq(absl::nullopt)); } -TEST_F(PeerConnectionEncodingsIntegrationTest, - SetParametersWithScalabilityModeNotSupportedBySubsequentNegotiation) { - rtc::scoped_refptr local_pc_wrapper = CreatePc(); - rtc::scoped_refptr remote_pc_wrapper = CreatePc(); - ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); - - std::vector layers = - CreateLayers({"f"}, /*active=*/true); - rtc::scoped_refptr transceiver = - AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, - layers); - // Restricting the local receive codecs will restrict what we offer and - // hence the answer if it is a subset of our offer. - std::vector codecs = - GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP8"); - transceiver->SetCodecPreferences(codecs); - - // Attempt SVC (L3T3_KEY). This is still possible because VP9 might be - // available from the remote end. - rtc::scoped_refptr sender = transceiver->sender(); - RtpParameters parameters = sender->GetParameters(); - ASSERT_EQ(parameters.encodings.size(), 1u); - parameters.encodings[0].scalability_mode = "L3T3_KEY"; - parameters.encodings[0].scale_resolution_down_by = 1; - EXPECT_TRUE(sender->SetParameters(parameters).ok()); - - NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); - - // `scalability_mode` is set to the VP8 default since that is what was - // negotiated. - parameters = sender->GetParameters(); - ASSERT_EQ(parameters.encodings.size(), 1u); - EXPECT_THAT(parameters.encodings[0].scalability_mode, Eq("L1T2")); - - local_pc_wrapper->WaitForConnection(); - remote_pc_wrapper->WaitForConnection(); - - // Wait until media is flowing. - ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 1u), - kDefaultTimeout.ms()); - // When `scalability_mode` is not set, VP8 defaults to L1T1. - rtc::scoped_refptr report = GetStats(local_pc_wrapper); - std::vector outbound_rtps = - report->GetStatsOfType(); - ASSERT_THAT(outbound_rtps, SizeIs(1u)); - EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[0]), - StrCaseEq("video/VP8")); - EXPECT_THAT(*outbound_rtps[0]->scalability_mode, StrEq("L1T2")); - // GetParameters() confirms `scalability_mode` is still not set. - parameters = sender->GetParameters(); - ASSERT_EQ(parameters.encodings.size(), 1u); - EXPECT_THAT(parameters.encodings[0].scalability_mode, Eq("L1T2")); -} - TEST_F(PeerConnectionEncodingsIntegrationTest, VP8_FallbackFromSvcResultsInL1T2) { rtc::scoped_refptr local_pc_wrapper = CreatePc(); @@ -615,13 +558,6 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[0]), StrCaseEq("video/VP8")); EXPECT_THAT(*outbound_rtps[0]->scalability_mode, StrEq("L1T2")); - - // Now that we know VP8 is used, try setting L3T3 which should fail. - parameters = sender->GetParameters(); - ASSERT_EQ(parameters.encodings.size(), 1u); - parameters.encodings[0].scalability_mode = "L3T3_KEY"; - parameters.encodings[0].scale_resolution_down_by = 1; - EXPECT_FALSE(sender->SetParameters(parameters).ok()); } // The legacy SVC path is triggered when VP9 us used, but `scalability_mode` has @@ -641,7 +577,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, layers); std::vector codecs = - GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, "VP9"); + GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9"); transceiver->SetCodecPreferences(codecs); NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); @@ -686,7 +622,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, layers); std::vector codecs = - GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, "VP9"); + GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9"); transceiver->SetCodecPreferences(codecs); // Configure SVC, a.k.a. "L3T3_KEY". rtc::scoped_refptr sender = transceiver->sender(); @@ -739,7 +675,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, layers); std::vector codecs = - GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, "VP9"); + GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9"); transceiver->SetCodecPreferences(codecs); // Configure SVC, a.k.a. "L3T3_KEY". rtc::scoped_refptr sender = transceiver->sender(); @@ -789,7 +725,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, layers); std::vector codecs = - GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, "VP9"); + GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9"); transceiver->SetCodecPreferences(codecs); // The original negotiation triggers legacy SVC because we didn't specify @@ -844,7 +780,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, layers); std::vector codecs = - GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, "VP9"); + GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9"); transceiver->SetCodecPreferences(codecs); // Legacy SVC mode and all layers inactive. @@ -881,7 +817,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, layers); std::vector codecs = - GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, "VP9"); + GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9"); transceiver->SetCodecPreferences(codecs); // Standard mode and all layers inactive. @@ -921,7 +857,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, VP9_TargetBitrate_LegacyL1T3) { AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, layers); std::vector codecs = - GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, "VP9"); + GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9"); transceiver->SetCodecPreferences(codecs); // In legacy SVC, disabling the bottom two layers encodings is interpreted as @@ -968,7 +904,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, VP9_TargetBitrate_StandardL1T3) { AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, layers); std::vector codecs = - GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, "VP9"); + GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9"); transceiver->SetCodecPreferences(codecs); // With standard APIs, L1T3 is explicitly specified and the encodings refers @@ -1019,7 +955,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, layers); std::vector codecs = - GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, "VP8"); + GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP8"); transceiver->SetCodecPreferences(codecs); NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); @@ -1428,6 +1364,72 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION); } +TEST_F(PeerConnectionEncodingsIntegrationTest, + SetParametersRejectsNonPreferredCodecParameterAudio) { + rtc::scoped_refptr local_pc_wrapper = CreatePc(); + + absl::optional opus = + local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO, + "opus"); + ASSERT_TRUE(opus); + + std::vector not_opus_codecs = + local_pc_wrapper->pc_factory() + ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO) + .codecs; + not_opus_codecs.erase( + std::remove_if(not_opus_codecs.begin(), not_opus_codecs.end(), + [&](const auto& codec) { + return absl::EqualsIgnoreCase(codec.name, opus->name); + }), + not_opus_codecs.end()); + + auto transceiver_or_error = + local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); + ASSERT_TRUE(transceiver_or_error.ok()); + rtc::scoped_refptr audio_transceiver = + transceiver_or_error.MoveValue(); + ASSERT_TRUE(audio_transceiver->SetCodecPreferences(not_opus_codecs).ok()); + + RtpParameters parameters = audio_transceiver->sender()->GetParameters(); + parameters.encodings[0].codec = opus; + RTCError error = audio_transceiver->sender()->SetParameters(parameters); + EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION); +} + +TEST_F(PeerConnectionEncodingsIntegrationTest, + SetParametersRejectsNonPreferredCodecParameterVideo) { + rtc::scoped_refptr local_pc_wrapper = CreatePc(); + + absl::optional vp8 = + local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO, + "vp8"); + ASSERT_TRUE(vp8); + + std::vector not_vp8_codecs = + local_pc_wrapper->pc_factory() + ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO) + .codecs; + not_vp8_codecs.erase( + std::remove_if(not_vp8_codecs.begin(), not_vp8_codecs.end(), + [&](const auto& codec) { + return absl::EqualsIgnoreCase(codec.name, vp8->name); + }), + not_vp8_codecs.end()); + + auto transceiver_or_error = + local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO); + ASSERT_TRUE(transceiver_or_error.ok()); + rtc::scoped_refptr video_transceiver = + transceiver_or_error.MoveValue(); + ASSERT_TRUE(video_transceiver->SetCodecPreferences(not_vp8_codecs).ok()); + + RtpParameters parameters = video_transceiver->sender()->GetParameters(); + parameters.encodings[0].codec = vp8; + RTCError error = video_transceiver->sender()->SetParameters(parameters); + EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION); +} + TEST_F(PeerConnectionEncodingsIntegrationTest, SetParametersRejectsNonNegotiatedCodecParameterAudio) { rtc::scoped_refptr local_pc_wrapper = CreatePc(); @@ -1873,7 +1875,7 @@ class PeerConnectionEncodingsIntegrationParameterizedTest bool SkipTestDueToAv1Missing( rtc::scoped_refptr local_pc_wrapper) { if (codec_name_ == "AV1" && - !HasReceiverVideoCodecCapability(local_pc_wrapper, "AV1")) { + !HasSenderVideoCodecCapability(local_pc_wrapper, "AV1")) { RTC_LOG(LS_WARNING) << "\n***\nAV1 is not available, skipping test.\n***"; return true; } @@ -1899,7 +1901,7 @@ TEST_P(PeerConnectionEncodingsIntegrationParameterizedTest, AllLayersInactive) { AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, layers); std::vector codecs = - GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, codec_name_); + GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, codec_name_); transceiver->SetCodecPreferences(codecs); // Standard mode and all layers inactive. @@ -1942,7 +1944,7 @@ TEST_P(PeerConnectionEncodingsIntegrationParameterizedTest, Simulcast) { AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, layers); std::vector codecs = - GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, codec_name_); + GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, codec_name_); transceiver->SetCodecPreferences(codecs); rtc::scoped_refptr sender = transceiver->sender(); diff --git a/pc/peer_connection_media_unittest.cc b/pc/peer_connection_media_unittest.cc index 04fb9c9e26..b892eacb78 100644 --- a/pc/peer_connection_media_unittest.cc +++ b/pc/peer_connection_media_unittest.cc @@ -1547,6 +1547,29 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan, EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type()); } +TEST_F(PeerConnectionMediaTestUnifiedPlan, + SetCodecPreferencesAudioMissingSendCodec) { + auto fake_engine = std::make_unique(); + auto recv_codecs = fake_engine->voice().recv_codecs(); + recv_codecs.push_back(cricket::CreateAudioCodec(recv_codecs.back().id + 1, + "recv_only_codec", 0, 1)); + fake_engine->SetAudioRecvCodecs(recv_codecs); + auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine)); + + auto transceiver = caller->pc()->GetTransceivers().front(); + auto capabilities = caller->pc_factory()->GetRtpReceiverCapabilities( + cricket::MediaType::MEDIA_TYPE_AUDIO); + + std::vector codecs; + absl::c_copy_if(capabilities.codecs, std::back_inserter(codecs), + [](const RtpCodecCapability& codec) { + return codec.name.find("_only_") != std::string::npos; + }); + + auto result = transceiver->SetCodecPreferences(codecs); + EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type()); +} + TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesAudioRejectsVideoCodec) { auto caller = CreatePeerConnectionWithAudio(); diff --git a/pc/peer_connection_svc_integrationtest.cc b/pc/peer_connection_svc_integrationtest.cc index 7deafbf428..32ca451866 100644 --- a/pc/peer_connection_svc_integrationtest.cc +++ b/pc/peer_connection_svc_integrationtest.cc @@ -40,7 +40,7 @@ class PeerConnectionSVCIntegrationTest rtc::scoped_refptr transceiver, absl::string_view codec_name) { RtpCapabilities capabilities = - caller()->pc_factory()->GetRtpReceiverCapabilities( + caller()->pc_factory()->GetRtpSenderCapabilities( cricket::MEDIA_TYPE_VIDEO); std::vector codecs; for (const RtpCodecCapability& codec_capability : capabilities.codecs) { @@ -95,7 +95,7 @@ TEST_F(PeerConnectionSVCIntegrationTest, SetParametersAcceptsL1T3WithVP8) { ConnectFakeSignaling(); RtpCapabilities capabilities = - caller()->pc_factory()->GetRtpReceiverCapabilities( + caller()->pc_factory()->GetRtpSenderCapabilities( cricket::MEDIA_TYPE_VIDEO); std::vector vp8_codec; for (const RtpCodecCapability& codec_capability : capabilities.codecs) { @@ -119,6 +119,27 @@ TEST_F(PeerConnectionSVCIntegrationTest, SetParametersAcceptsL1T3WithVP8) { EXPECT_TRUE(result.ok()); } +TEST_F(PeerConnectionSVCIntegrationTest, SetParametersRejectsL3T3WithVP8) { + 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()); + + 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, SetParametersAcceptsL1T3WithVP8AfterNegotiation) { ASSERT_TRUE(CreatePeerConnectionWrappers()); @@ -230,7 +251,7 @@ TEST_F(PeerConnectionSVCIntegrationTest, FallbackToL1Tx) { auto caller_transceiver = transceiver_or_error.MoveValue(); RtpCapabilities capabilities = - caller()->pc_factory()->GetRtpReceiverCapabilities( + caller()->pc_factory()->GetRtpSenderCapabilities( cricket::MEDIA_TYPE_VIDEO); std::vector send_codecs = capabilities.codecs; // Only keep VP9 in the caller diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc index be0975778f..b0c32eff85 100644 --- a/pc/rtp_sender.cc +++ b/pc/rtp_sender.cc @@ -248,7 +248,7 @@ void RtpSenderBase::SetParametersInternal(const RtpParameters& parameters, } if (!media_channel_ || !ssrc_) { auto result = cricket::CheckRtpParametersInvalidModificationAndValues( - init_parameters_, parameters, send_codecs_, absl::nullopt); + init_parameters_, parameters, codec_preferences_, absl::nullopt); if (result.ok()) { init_parameters_ = parameters; } @@ -299,7 +299,7 @@ RTCError RtpSenderBase::SetParametersInternalWithAllLayers( } if (!media_channel_ || !ssrc_) { auto result = cricket::CheckRtpParametersInvalidModificationAndValues( - init_parameters_, parameters, send_codecs_, absl::nullopt); + init_parameters_, parameters, codec_preferences_, absl::nullopt); if (result.ok()) { init_parameters_ = parameters; } @@ -345,14 +345,16 @@ RTCError RtpSenderBase::CheckCodecParameters(const RtpParameters& parameters) { // the SVC capabilities. absl::optional send_codec_with_svc_info; if (send_codec && send_codec->type == cricket::Codec::Type::kVideo) { - auto codec_match = absl::c_find_if( - send_codecs_, [&](auto& codec) { return send_codec->Matches(codec); }); - if (codec_match != send_codecs_.end()) { + auto codec_match = + absl::c_find_if(codec_preferences_, [&](auto& codec_preference) { + return send_codec->Matches(codec_preference); + }); + if (codec_match != codec_preferences_.end()) { send_codec_with_svc_info = *codec_match; } } - return cricket::CheckScalabilityModeValues(parameters, send_codecs_, + return cricket::CheckScalabilityModeValues(parameters, codec_preferences_, send_codec_with_svc_info); } diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h index 90d33aaffc..8925230636 100644 --- a/pc/rtp_sender.h +++ b/pc/rtp_sender.h @@ -102,7 +102,8 @@ class RtpSenderInternal : public RtpSenderInterface { // Used by the owning transceiver to inform the sender on the currently // selected codecs. - virtual void SetSendCodecs(std::vector send_codecs) = 0; + virtual void SetCodecPreferences( + std::vector codec_preferences) = 0; }; // Shared implementation for RtpSenderInternal interface. @@ -220,8 +221,9 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { is_transceiver_stopped_ = true; } - void SetSendCodecs(std::vector send_codecs) override { - send_codecs_ = send_codecs; + void SetCodecPreferences( + std::vector codec_preferences) override { + codec_preferences_ = codec_preferences; } protected: @@ -259,7 +261,7 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { std::vector stream_ids_; RtpParameters init_parameters_; - std::vector send_codecs_; + std::vector codec_preferences_; // TODO(tommi): `media_channel_` and several other member variables in this // class (ssrc_, stopped_, etc) are accessed from more than one thread without diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc index 55a0f7d584..de31b2e36a 100644 --- a/pc/rtp_transceiver.cc +++ b/pc/rtp_transceiver.cc @@ -41,12 +41,12 @@ namespace { RTCError VerifyCodecPreferences( const std::vector& codecs, + const std::vector& send_codecs, const std::vector& recv_codecs) { // If the intersection between codecs and // RTCRtpSender.getCapabilities(kind).codecs or the intersection between - // codecs and RTCRtpReceiver.getCapabilities(kind).codecs contains only - // contains RTX, RED, FEC or CN codecs or is an empty set, throw - // InvalidModificationError. + // codecs and RTCRtpReceiver.getCapabilities(kind).codecs only contains RTX, + // RED or FEC codecs or is an empty set, throw InvalidModificationError. // This ensures that we always have something to offer, regardless of // transceiver.direction. @@ -62,15 +62,34 @@ RTCError VerifyCodecPreferences( "codec capabilities."); } - // Let codecCapabilities RTCRtpReceiver.getCapabilities(kind).codecs. - // For each codec in codecs, If + if (!absl::c_any_of(codecs, [&send_codecs](const RtpCodecCapability& codec) { + return codec.IsMediaCodec() && + absl::c_any_of(send_codecs, + [&codec](const cricket::Codec& send_codec) { + return send_codec.MatchesRtpCodec(codec); + }); + })) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Invalid codec preferences: Missing codec from send " + "codec capabilities."); + } + + // Let codecCapabilities be the union of + // RTCRtpSender.getCapabilities(kind).codecs and + // RTCRtpReceiver.getCapabilities(kind).codecs. For each codec in codecs, If // codec is not in codecCapabilities, throw InvalidModificationError. for (const auto& codec_preference : codecs) { bool is_recv_codec = absl::c_any_of( recv_codecs, [&codec_preference](const cricket::Codec& codec) { return codec.MatchesRtpCodec(codec_preference); }); - if (!is_recv_codec) { + + bool is_send_codec = absl::c_any_of( + send_codecs, [&codec_preference](const cricket::Codec& codec) { + return codec.MatchesRtpCodec(codec_preference); + }); + + if (!is_recv_codec && !is_send_codec) { LOG_AND_RETURN_ERROR( RTCErrorType::INVALID_MODIFICATION, std::string("Invalid codec preferences: invalid codec with name \"") + @@ -91,6 +110,25 @@ RTCError VerifyCodecPreferences( return RTCError::OK(); } +// Matches the list of codecs as capabilities (potentially without SVC related +// information) to the list of send codecs and returns the list of codecs with +// all the SVC related information. +std::vector MatchCodecPreferences( + const std::vector& codecs, + const std::vector& send_codecs) { + std::vector result; + + for (const auto& codec_preference : codecs) { + for (const cricket::VideoCodec& send_codec : send_codecs) { + if (send_codec.MatchesRtpCodec(codec_preference)) { + result.push_back(send_codec); + } + } + } + + return result; +} + TaskQueueBase* GetCurrentTaskQueueOrThread() { TaskQueueBase* current = TaskQueueBase::Current(); if (!current) @@ -127,7 +165,7 @@ RtpTransceiver::RtpTransceiver( RTC_DCHECK(media_type_ == cricket::MEDIA_TYPE_AUDIO || media_type_ == cricket::MEDIA_TYPE_VIDEO); RTC_DCHECK_EQ(sender->media_type(), receiver->media_type()); - sender->internal()->SetSendCodecs( + sender->internal()->SetCodecPreferences( sender->media_type() == cricket::MEDIA_TYPE_VIDEO ? media_engine()->video().send_codecs(false) : media_engine()->voice().send_codecs()); @@ -403,7 +441,10 @@ void RtpTransceiver::AddSender( media_type() == cricket::MEDIA_TYPE_VIDEO ? media_engine()->video().send_codecs(false) : media_engine()->voice().send_codecs(); - sender->internal()->SetSendCodecs(send_codecs); + sender->internal()->SetCodecPreferences( + codec_preferences_.empty() + ? send_codecs + : MatchCodecPreferences(codec_preferences_, send_codecs)); senders_.push_back(sender); } @@ -652,6 +693,10 @@ RTCError RtpTransceiver::SetCodecPreferences( // to codecs and abort these steps. if (codec_capabilities.empty()) { codec_preferences_.clear(); + senders_.front()->internal()->SetCodecPreferences( + media_type() == cricket::MEDIA_TYPE_VIDEO + ? media_engine()->video().send_codecs(false) + : media_engine()->voice().send_codecs()); return RTCError::OK(); } @@ -664,15 +709,19 @@ RTCError RtpTransceiver::SetCodecPreferences( // 6. to 8. RTCError result; - std::vector recv_codecs; + std::vector recv_codecs, send_codecs; if (media_type_ == cricket::MEDIA_TYPE_AUDIO) { + send_codecs = media_engine()->voice().send_codecs(); recv_codecs = media_engine()->voice().recv_codecs(); } else if (media_type_ == cricket::MEDIA_TYPE_VIDEO) { + send_codecs = media_engine()->video().send_codecs(context()->use_rtx()); recv_codecs = media_engine()->video().recv_codecs(context()->use_rtx()); } - result = VerifyCodecPreferences(codecs, recv_codecs); + result = VerifyCodecPreferences(codecs, send_codecs, recv_codecs); if (result.ok()) { + senders_.front()->internal()->SetCodecPreferences( + MatchCodecPreferences(codecs, send_codecs)); codec_preferences_ = codecs; } diff --git a/pc/test/mock_rtp_sender_internal.h b/pc/test/mock_rtp_sender_internal.h index 9a01aecabb..4cfb2cfeaf 100644 --- a/pc/test/mock_rtp_sender_internal.h +++ b/pc/test/mock_rtp_sender_internal.h @@ -69,7 +69,7 @@ class MockRtpSenderInternal : public RtpSenderInternal { (const RtpParameters&), (override)); MOCK_METHOD(void, - SetSendCodecs, + SetCodecPreferences, (std::vector), (override)); MOCK_METHOD(rtc::scoped_refptr,