From 725ee2406005dfea3d891137b26b2d5e887edf85 Mon Sep 17 00:00:00 2001 From: Florent Castelli Date: Tue, 18 Oct 2022 17:05:58 +0200 Subject: [PATCH] SVC: Check scalability in AddTransceiver and SetParameters ScalabilityMode should be validated against the currently allowed codecs or the currently used codec. Bug: webrtc:11607 Change-Id: Id2e6cbfad4f089de450150e1203657ed316e2f29 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/277403 Commit-Queue: Florent Castelli Reviewed-by: Harald Alvestrand Cr-Commit-Position: refs/heads/main@{#38433} --- media/base/media_engine.cc | 52 ++++- media/base/media_engine.h | 23 +- media/engine/webrtc_video_engine.cc | 5 + pc/BUILD.gn | 1 + pc/peer_connection.cc | 9 +- pc/peer_connection_svc_integrationtest.cc | 243 ++++++++++++++++++++++ pc/rtp_sender.cc | 33 ++- pc/rtp_sender.h | 18 ++ pc/rtp_transceiver.cc | 38 ++++ pc/test/fake_peer_connection_for_stats.h | 1 + pc/test/mock_rtp_sender_internal.h | 5 + 11 files changed, 418 insertions(+), 10 deletions(-) create mode 100644 pc/peer_connection_svc_integrationtest.cc diff --git a/media/base/media_engine.cc b/media/base/media_engine.cc index 694e690056..0efbd71bf7 100644 --- a/media/base/media_engine.cc +++ b/media/base/media_engine.cc @@ -65,8 +65,46 @@ std::vector GetDefaultEnabledRtpHeaderExtensions( return extensions; } +webrtc::RTCError CheckScalabilityModeValues( + const webrtc::RtpParameters& rtp_parameters, + rtc::ArrayView codecs) { + using webrtc::RTCErrorType; + + if (codecs.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(); + } + + for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) { + if (rtp_parameters.encodings[i].scalability_mode) { + bool scalabilityModeFound = false; + for (const cricket::VideoCodec& codec : codecs) { + for (const auto& scalability_mode : codec.scalability_modes) { + if (ScalabilityModeToString(scalability_mode) == + *rtp_parameters.encodings[i].scalability_mode) { + scalabilityModeFound = true; + break; + } + } + if (scalabilityModeFound) + break; + } + + if (!scalabilityModeFound) { + LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, + "Attempted to set RtpParameters scalabilityMode " + "to an unsupported value for the current codecs."); + } + } + } + + return webrtc::RTCError::OK(); +} + webrtc::RTCError CheckRtpParametersValues( - const webrtc::RtpParameters& rtp_parameters) { + const webrtc::RtpParameters& rtp_parameters, + rtc::ArrayView codecs) { using webrtc::RTCErrorType; for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) { @@ -115,12 +153,20 @@ webrtc::RTCError CheckRtpParametersValues( } } - return webrtc::RTCError::OK(); + return CheckScalabilityModeValues(rtp_parameters, codecs); } webrtc::RTCError CheckRtpParametersInvalidModificationAndValues( const webrtc::RtpParameters& old_rtp_parameters, const webrtc::RtpParameters& rtp_parameters) { + return CheckRtpParametersInvalidModificationAndValues(old_rtp_parameters, + rtp_parameters, {}); +} + +webrtc::RTCError CheckRtpParametersInvalidModificationAndValues( + const webrtc::RtpParameters& old_rtp_parameters, + const webrtc::RtpParameters& rtp_parameters, + rtc::ArrayView codecs) { using webrtc::RTCErrorType; if (rtp_parameters.encodings.size() != old_rtp_parameters.encodings.size()) { LOG_AND_RETURN_ERROR( @@ -155,7 +201,7 @@ webrtc::RTCError CheckRtpParametersInvalidModificationAndValues( "Attempted to set RtpParameters with modified SSRC"); } - return CheckRtpParametersValues(rtp_parameters); + return CheckRtpParametersValues(rtp_parameters, codecs); } CompositeMediaEngine::CompositeMediaEngine( diff --git a/media/base/media_engine.h b/media/base/media_engine.h index 2b08b3951b..e533691751 100644 --- a/media/base/media_engine.h +++ b/media/base/media_engine.h @@ -37,9 +37,28 @@ class Call; namespace cricket { -webrtc::RTCError CheckRtpParametersValues( - const webrtc::RtpParameters& new_parameters); +// Checks that the scalability_mode value of each encoding is supported by at +// 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 codecs); +// Checks the parameters have valid and supported values, and checks parameters +// with CheckScalabilityModeValues(). +webrtc::RTCError CheckRtpParametersValues( + const webrtc::RtpParameters& new_parameters, + rtc::ArrayView codecs); + +// Checks that the immutable values have not changed in new_parameters and +// checks all parameters with CheckRtpParametersValues(). +webrtc::RTCError CheckRtpParametersInvalidModificationAndValues( + const webrtc::RtpParameters& old_parameters, + const webrtc::RtpParameters& new_parameters, + rtc::ArrayView codecs); + +// Checks that the immutable values have not changed in new_parameters and +// checks parameters (except SVC) with CheckRtpParametersValues(). It should +// usually be paired with a call to CheckScalabilityModeValues(). webrtc::RTCError CheckRtpParametersInvalidModificationAndValues( const webrtc::RtpParameters& old_parameters, const webrtc::RtpParameters& new_parameters); diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index e76f044eed..c080266f87 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -2285,9 +2285,14 @@ void WebRtcVideoChannel::WebRtcVideoSendStream::SetSendParameters( webrtc::RTCError WebRtcVideoChannel::WebRtcVideoSendStream::SetRtpParameters( const webrtc::RtpParameters& new_parameters) { RTC_DCHECK_RUN_ON(&thread_checker_); + // This is checked higher in the stack (RtpSender), so this is only checking + // for users accessing the private APIs or tests, not specification + // conformance. + // TODO(orphis): Migrate tests to later make this a DCHECK only webrtc::RTCError error = CheckRtpParametersInvalidModificationAndValues( rtp_parameters_, new_parameters); if (!error.ok()) { + // Error is propagated to the callback at a higher level return error; } diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 26a044b615..2621d57ea4 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -2241,6 +2241,7 @@ if (rtc_include_tests && !build_with_chromium) { "peer_connection_rtp_unittest.cc", "peer_connection_signaling_unittest.cc", "peer_connection_simulcast_unittest.cc", + "peer_connection_svc_integrationtest.cc", "peer_connection_wrapper.cc", "peer_connection_wrapper.h", "proxy_unittest.cc", diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index 0c10d33d75..ed6cd99913 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc @@ -1075,7 +1075,14 @@ PeerConnection::AddTransceiver( "Attempted to set an unimplemented parameter of RtpParameters."); } - auto result = cricket::CheckRtpParametersValues(parameters); + std::vector codecs; + if (media_type == cricket::MEDIA_TYPE_VIDEO) { + // Gather the current codec capabilities to allow checking scalabilityMode + // against supported values. + codecs = context_->media_engine()->video().send_codecs(false); + } + + auto result = cricket::CheckRtpParametersValues(parameters, codecs); if (!result.ok()) { LOG_AND_RETURN_ERROR(result.type(), result.message()); } diff --git a/pc/peer_connection_svc_integrationtest.cc b/pc/peer_connection_svc_integrationtest.cc new file mode 100644 index 0000000000..f53205ee32 --- /dev/null +++ b/pc/peer_connection_svc_integrationtest.cc @@ -0,0 +1,243 @@ +/* + * 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 + +#include + +#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 transceiver, + absl::string_view codec_name) { + webrtc::RtpCapabilities capabilities = + caller()->pc_factory()->GetRtpSenderCapabilities( + cricket::MEDIA_TYPE_VIDEO); + std::vector codecs; + for (const webrtc::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(); + webrtc::RtpTransceiverInit init; + webrtc::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(); + webrtc::RtpTransceiverInit init; + webrtc::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(); + webrtc::RtpTransceiverInit init; + webrtc::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(), + webrtc::RTCErrorType::UNSUPPORTED_OPERATION); +} + +TEST_F(PeerConnectionSVCIntegrationTest, SetParametersAcceptsL1T3WithVP8) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + + webrtc::RtpCapabilities capabilities = + caller()->pc_factory()->GetRtpSenderCapabilities( + cricket::MEDIA_TYPE_VIDEO); + std::vector vp8_codec; + for (const webrtc::RtpCodecCapability& codec_capability : + capabilities.codecs) { + if (codec_capability.name == cricket::kVp8CodecName) + vp8_codec.push_back(codec_capability); + } + + webrtc::RtpTransceiverInit init; + webrtc::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()); + + webrtc::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, SetParametersRejectsL3T3WithVP8) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + + webrtc::RtpTransceiverInit init; + webrtc::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()); + + webrtc::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(), webrtc::RTCErrorType::UNSUPPORTED_OPERATION); +} + +TEST_F(PeerConnectionSVCIntegrationTest, + SetParametersAcceptsL1T3WithVP8AfterNegotiation) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + + webrtc::RtpTransceiverInit init; + webrtc::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); + + webrtc::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(); + + webrtc::RtpTransceiverInit init; + webrtc::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); + + webrtc::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(); + + webrtc::RtpTransceiverInit init; + webrtc::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); + + webrtc::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(), webrtc::RTCErrorType::UNSUPPORTED_OPERATION); +} + +TEST_F(PeerConnectionSVCIntegrationTest, + SetParametersRejectsInvalidModeWithVP9AfterNegotiation) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + + webrtc::RtpTransceiverInit init; + webrtc::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); + + webrtc::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(), webrtc::RTCErrorType::UNSUPPORTED_OPERATION); +} + +} // namespace + +} // namespace webrtc diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc index b4c773003b..753ac2f130 100644 --- a/pc/rtp_sender.cc +++ b/pc/rtp_sender.cc @@ -200,7 +200,7 @@ RTCError RtpSenderBase::SetParametersInternal(const RtpParameters& parameters) { } if (!media_channel_ || !ssrc_) { auto result = cricket::CheckRtpParametersInvalidModificationAndValues( - init_parameters_, parameters); + init_parameters_, parameters, video_codec_preferences_); if (result.ok()) { init_parameters_ = parameters; } @@ -208,13 +208,22 @@ RTCError RtpSenderBase::SetParametersInternal(const RtpParameters& parameters) { } return worker_thread_->BlockingCall([&] { RtpParameters rtp_parameters = parameters; + RtpParameters old_parameters = media_channel_->GetRtpSendParameters(ssrc_); if (!disabled_rids_.empty()) { // Need to add the inactive layers. - RtpParameters old_parameters = - media_channel_->GetRtpSendParameters(ssrc_); rtp_parameters = RestoreEncodingLayers(parameters, disabled_rids_, old_parameters.encodings); } + + auto result = cricket::CheckRtpParametersInvalidModificationAndValues( + old_parameters, rtp_parameters); + if (!result.ok()) + return result; + + result = CheckSVCParameters(rtp_parameters); + if (!result.ok()) + return result; + return media_channel_->SetRtpSendParameters(ssrc_, rtp_parameters); }); } @@ -231,7 +240,7 @@ RTCError RtpSenderBase::SetParametersInternalWithAllLayers( } if (!media_channel_ || !ssrc_) { auto result = cricket::CheckRtpParametersInvalidModificationAndValues( - init_parameters_, parameters); + init_parameters_, parameters, video_codec_preferences_); if (result.ok()) { init_parameters_ = parameters; } @@ -725,4 +734,20 @@ void VideoRtpSender::ClearSend() { [&] { video_media_channel()->SetVideoSend(ssrc_, nullptr, nullptr); }); } +RTCError VideoRtpSender::CheckSVCParameters(const RtpParameters& parameters) { + cricket::VideoCodec codec; + video_media_channel()->GetSendCodec(&codec); + + // Match the currently used codec against the codec preferences to gather + // the SVC capabilities. + std::vector codecs; + for (const auto& codec_preference : video_codec_preferences_) { + if (codec.Matches(codec_preference)) { + codecs.push_back(codec_preference); + } + } + + return cricket::CheckScalabilityModeValues(parameters, codecs); +} + } // namespace webrtc diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h index 33d613905b..e3cd12fed4 100644 --- a/pc/rtp_sender.h +++ b/pc/rtp_sender.h @@ -82,6 +82,11 @@ class RtpSenderInternal : public RtpSenderInterface { virtual RTCError SetParametersInternalWithAllLayers( const RtpParameters& parameters) = 0; + // Additional checks that are specific to the Sender type + virtual RTCError CheckSVCParameters(const RtpParameters& parameters) { + return webrtc::RTCError::OK(); + } + // Returns an ID that changes every time SetTrack() is called, but // otherwise remains constant. Used to generate IDs for stats. // The special value zero means that no track is attached. @@ -93,6 +98,11 @@ class RtpSenderInternal : public RtpSenderInterface { const std::vector& rid) = 0; virtual void SetTransceiverAsStopped() = 0; + + // Used by the owning transceiver to inform the sender on the currently + // selected codecs. + virtual void SetVideoCodecPreferences( + std::vector codec_preferences) = 0; }; // Shared implementation for RtpSenderInternal interface. @@ -203,6 +213,11 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { is_transceiver_stopped_ = true; } + void SetVideoCodecPreferences( + std::vector codec_preferences) override { + video_codec_preferences_ = codec_preferences; + } + protected: // If `set_streams_observer` is not null, it is invoked when SetStreams() // is called. `set_streams_observer` is not owned by this object. If not @@ -238,6 +253,7 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { std::vector stream_ids_; RtpParameters init_parameters_; + std::vector video_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 @@ -395,6 +411,8 @@ class VideoRtpSender : public RtpSenderBase { rtc::scoped_refptr GetDtmfSender() const override; + RTCError CheckSVCParameters(const RtpParameters& parameters) override; + protected: VideoRtpSender(rtc::Thread* worker_thread, const std::string& id, diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc index db49468c07..8b65dbf4fe 100644 --- a/pc/rtp_transceiver.cc +++ b/pc/rtp_transceiver.cc @@ -107,6 +107,25 @@ RTCError VerifyCodecPreferences(const std::vector& codecs, 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.MatchesCapability(codec_preference)) { + result.push_back(send_codec); + } + } + } + + return result; +} + TaskQueueBase* GetCurrentTaskQueueOrThread() { TaskQueueBase* current = TaskQueueBase::Current(); if (!current) @@ -142,6 +161,9 @@ 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()); + if (sender->media_type() == cricket::MEDIA_TYPE_VIDEO) + sender->internal()->SetVideoCodecPreferences( + media_engine()->video().send_codecs(false)); senders_.push_back(sender); receivers_.push_back(receiver); } @@ -342,6 +364,14 @@ void RtpTransceiver::AddSender( RTC_DCHECK(sender); RTC_DCHECK_EQ(media_type(), sender->media_type()); RTC_DCHECK(!absl::c_linear_search(senders_, sender)); + if (media_type() == cricket::MEDIA_TYPE_VIDEO) { + std::vector send_codecs = + media_engine()->video().send_codecs(false); + sender->internal()->SetVideoCodecPreferences( + codec_preferences_.empty() + ? send_codecs + : MatchCodecPreferences(codec_preferences_, send_codecs)); + } senders_.push_back(sender); } @@ -590,6 +620,9 @@ RTCError RtpTransceiver::SetCodecPreferences( // to codecs and abort these steps. if (codec_capabilities.empty()) { codec_preferences_.clear(); + if (media_type() == cricket::MEDIA_TYPE_VIDEO) + senders_.front()->internal()->SetVideoCodecPreferences( + media_engine()->video().send_codecs(false)); return RTCError::OK(); } @@ -612,6 +645,11 @@ RTCError RtpTransceiver::SetCodecPreferences( send_codecs = media_engine()->video().send_codecs(context()->use_rtx()); recv_codecs = media_engine()->video().recv_codecs(context()->use_rtx()); result = VerifyCodecPreferences(codecs, send_codecs, recv_codecs); + + if (result.ok()) { + senders_.front()->internal()->SetVideoCodecPreferences( + MatchCodecPreferences(codecs, send_codecs)); + } } if (result.ok()) { diff --git a/pc/test/fake_peer_connection_for_stats.h b/pc/test/fake_peer_connection_for_stats.h index d4ccfa30bd..ca7bf0bebc 100644 --- a/pc/test/fake_peer_connection_for_stats.h +++ b/pc/test/fake_peer_connection_for_stats.h @@ -167,6 +167,7 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { dependencies.network_thread = rtc::Thread::Current(); dependencies.worker_thread = rtc::Thread::Current(); dependencies.signaling_thread = rtc::Thread::Current(); + dependencies.media_engine = std::make_unique(); return dependencies; } diff --git a/pc/test/mock_rtp_sender_internal.h b/pc/test/mock_rtp_sender_internal.h index 5261d47b82..8b9e75a7fb 100644 --- a/pc/test/mock_rtp_sender_internal.h +++ b/pc/test/mock_rtp_sender_internal.h @@ -60,6 +60,11 @@ class MockRtpSenderInternal : public RtpSenderInternal { SetParametersInternalWithAllLayers, (const RtpParameters&), (override)); + MOCK_METHOD(RTCError, CheckSVCParameters, (const RtpParameters&), (override)); + MOCK_METHOD(void, + SetVideoCodecPreferences, + (std::vector), + (override)); MOCK_METHOD(rtc::scoped_refptr, GetDtmfSender, (),