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 <orphis@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38433}
This commit is contained in:
Florent Castelli 2022-10-18 17:05:58 +02:00 committed by WebRTC LUCI CQ
parent d806705623
commit 725ee24060
11 changed files with 418 additions and 10 deletions

View File

@ -65,8 +65,46 @@ std::vector<webrtc::RtpExtension> GetDefaultEnabledRtpHeaderExtensions(
return extensions;
}
webrtc::RTCError CheckScalabilityModeValues(
const webrtc::RtpParameters& rtp_parameters,
rtc::ArrayView<cricket::VideoCodec> 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<cricket::VideoCodec> 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<cricket::VideoCodec> 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(

View File

@ -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<cricket::VideoCodec> codecs);
// Checks the parameters have valid and supported values, and checks parameters
// with CheckScalabilityModeValues().
webrtc::RTCError CheckRtpParametersValues(
const webrtc::RtpParameters& new_parameters,
rtc::ArrayView<cricket::VideoCodec> 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<cricket::VideoCodec> 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);

View File

@ -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;
}

View File

@ -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",

View File

@ -1075,7 +1075,14 @@ PeerConnection::AddTransceiver(
"Attempted to set an unimplemented parameter of RtpParameters.");
}
auto result = cricket::CheckRtpParametersValues(parameters);
std::vector<cricket::VideoCodec> 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());
}

View File

@ -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 <stdint.h>
#include <vector>
#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<webrtc::RtpTransceiverInterface> transceiver,
absl::string_view codec_name) {
webrtc::RtpCapabilities capabilities =
caller()->pc_factory()->GetRtpSenderCapabilities(
cricket::MEDIA_TYPE_VIDEO);
std::vector<RtpCodecCapability> 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<RtpCodecCapability> 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

View File

@ -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<cricket::VideoCodec> 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

View File

@ -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<std::string>& 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<cricket::VideoCodec> 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<cricket::VideoCodec> 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<std::string> stream_ids_;
RtpParameters init_parameters_;
std::vector<cricket::VideoCodec> 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<DtmfSenderInterface> GetDtmfSender() const override;
RTCError CheckSVCParameters(const RtpParameters& parameters) override;
protected:
VideoRtpSender(rtc::Thread* worker_thread,
const std::string& id,

View File

@ -107,6 +107,25 @@ RTCError VerifyCodecPreferences(const std::vector<RtpCodecCapability>& 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<cricket::VideoCodec> MatchCodecPreferences(
const std::vector<RtpCodecCapability>& codecs,
const std::vector<cricket::VideoCodec>& send_codecs) {
std::vector<cricket::VideoCodec> 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<cricket::VideoCodec> 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()) {

View File

@ -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<cricket::FakeMediaEngine>();
return dependencies;
}

View File

@ -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<cricket::VideoCodec>),
(override));
MOCK_METHOD(rtc::scoped_refptr<DtmfSenderInterface>,
GetDtmfSender,
(),