Implement codec selection api

The implementation covers the latest specification, but does not
support mixed-codec simulcast at the moment.
Changing codec for audio and video is supported.

Bug: webrtc:15064
Change-Id: I09082f39e2a7d54dd4a663a8a57bf9df5a851690
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/311663
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Florent Castelli <orphis@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40616}
This commit is contained in:
Florent Castelli 2023-04-12 10:45:07 +00:00 committed by WebRTC LUCI CQ
parent f45c2ceae0
commit 43a5dd86c2
21 changed files with 1109 additions and 131 deletions

View File

@ -520,6 +520,9 @@ struct RTC_EXPORT RtpEncodingParameters {
// https://w3c.github.io/webrtc-extensions/#dom-rtcrtpencodingparameters-adaptiveptime
bool adaptive_ptime = false;
// Allow changing the used codec for this encoding.
absl::optional<RtpCodec> codec;
bool operator==(const RtpEncodingParameters& o) const {
return ssrc == o.ssrc && bitrate_priority == o.bitrate_priority &&
network_priority == o.network_priority &&
@ -530,7 +533,7 @@ struct RTC_EXPORT RtpEncodingParameters {
scale_resolution_down_by == o.scale_resolution_down_by &&
active == o.active && rid == o.rid &&
adaptive_ptime == o.adaptive_ptime &&
requested_resolution == o.requested_resolution;
requested_resolution == o.requested_resolution && codec == o.codec;
}
bool operator!=(const RtpEncodingParameters& o) const {
return !(*this == o);

View File

@ -211,8 +211,7 @@ bool Codec::Matches(const Codec& codec,
return matches_id && matches_type_specific();
}
bool Codec::MatchesCapability(
const webrtc::RtpCodecCapability& codec_capability) const {
bool Codec::MatchesRtpCodec(const webrtc::RtpCodec& codec_capability) const {
webrtc::RtpCodecParameters codec_parameters = ToCodecParameters();
return codec_parameters.name == codec_capability.name &&

View File

@ -114,7 +114,7 @@ struct RTC_EXPORT Codec {
// H264 levels are not compared.
bool Matches(const Codec& codec,
const webrtc::FieldTrialsView* field_trials = nullptr) const;
bool MatchesCapability(const webrtc::RtpCodecCapability& capability) const;
bool MatchesRtpCodec(const webrtc::RtpCodec& capability) const;
// Find the parameter for `name` and write the value to `out`.
bool GetParam(const std::string& name, std::string* out) const;

View File

@ -67,34 +67,69 @@ std::vector<webrtc::RtpExtension> GetDefaultEnabledRtpHeaderExtensions(
webrtc::RTCError CheckScalabilityModeValues(
const webrtc::RtpParameters& rtp_parameters,
rtc::ArrayView<cricket::VideoCodec> codecs) {
rtc::ArrayView<cricket::Codec> codec_preferences,
absl::optional<cricket::Codec> send_codec) {
using webrtc::RTCErrorType;
if (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();
}
for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) {
if (rtp_parameters.encodings[i].codec) {
bool codecFound = false;
for (const cricket::VideoCodec& codec : codec_preferences) {
if (codec.MatchesRtpCodec(*rtp_parameters.encodings[i].codec)) {
codecFound = true;
send_codec = codec;
break;
}
}
if (!codecFound) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to use an unsupported codec for layer " +
std::to_string(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 (!send_codec) {
bool scalabilityModeFound = false;
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) {
scalabilityModeFound = true;
break;
}
}
if (scalabilityModeFound)
break;
}
if (!scalabilityModeFound) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters scalabilityMode "
"to an unsupported value for the current codecs.");
}
} else {
bool scalabilityModeFound = false;
for (const auto& scalability_mode : send_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.");
if (!scalabilityModeFound) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters scalabilityMode "
"to an unsupported value for the current codecs.");
}
}
}
}
@ -104,7 +139,8 @@ webrtc::RTCError CheckScalabilityModeValues(
webrtc::RTCError CheckRtpParametersValues(
const webrtc::RtpParameters& rtp_parameters,
rtc::ArrayView<cricket::VideoCodec> codecs) {
rtc::ArrayView<cricket::Codec> codec_preferences,
absl::optional<cricket::Codec> send_codec) {
using webrtc::RTCErrorType;
for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) {
@ -151,22 +187,31 @@ webrtc::RTCError CheckRtpParametersValues(
"Attempted to set scale_resolution_down_by and "
"requested_resolution simultaniously.");
}
if (i > 0 && rtp_parameters.encodings[i - 1].codec !=
rtp_parameters.encodings[i].codec) {
LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
"Attempted to use different codec values for "
"different encodings.");
}
}
return CheckScalabilityModeValues(rtp_parameters, codecs);
return CheckScalabilityModeValues(rtp_parameters, codec_preferences,
send_codec);
}
webrtc::RTCError CheckRtpParametersInvalidModificationAndValues(
const webrtc::RtpParameters& old_rtp_parameters,
const webrtc::RtpParameters& rtp_parameters) {
return CheckRtpParametersInvalidModificationAndValues(old_rtp_parameters,
rtp_parameters, {});
return CheckRtpParametersInvalidModificationAndValues(
old_rtp_parameters, rtp_parameters, {}, absl::nullopt);
}
webrtc::RTCError CheckRtpParametersInvalidModificationAndValues(
const webrtc::RtpParameters& old_rtp_parameters,
const webrtc::RtpParameters& rtp_parameters,
rtc::ArrayView<cricket::VideoCodec> codecs) {
rtc::ArrayView<cricket::Codec> codec_preferences,
absl::optional<cricket::Codec> send_codec) {
using webrtc::RTCErrorType;
if (rtp_parameters.encodings.size() != old_rtp_parameters.encodings.size()) {
LOG_AND_RETURN_ERROR(
@ -201,7 +246,8 @@ webrtc::RTCError CheckRtpParametersInvalidModificationAndValues(
"Attempted to set RtpParameters with modified SSRC");
}
return CheckRtpParametersValues(rtp_parameters, codecs);
return CheckRtpParametersValues(rtp_parameters, codec_preferences,
send_codec);
}
CompositeMediaEngine::CompositeMediaEngine(

View File

@ -42,20 +42,23 @@ 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<cricket::VideoCodec> codecs);
rtc::ArrayView<cricket::Codec> codec_preferences,
absl::optional<cricket::Codec> 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<cricket::VideoCodec> codecs);
rtc::ArrayView<cricket::Codec> codec_preferences,
absl::optional<cricket::Codec> send_codec);
// 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);
rtc::ArrayView<cricket::Codec> codec_preferences,
absl::optional<cricket::Codec> send_codec);
// Checks that the immutable values have not changed in new_parameters and
// checks parameters (except SVC) with CheckRtpParametersValues(). It should

View File

@ -994,9 +994,36 @@ bool WebRtcVideoSendChannel::GetChangedSendParameters(
codec.flexfec_payload_type = -1;
}
absl::optional<VideoCodecSettings> force_codec;
if (!send_streams_.empty()) {
// Since we do not support mixed-codec simulcast yet,
// all send streams must have the same codec value.
auto rtp_parameters = send_streams_.begin()->second->GetRtpParameters();
if (rtp_parameters.encodings[0].codec) {
auto matched_codec =
absl::c_find_if(negotiated_codecs, [&](auto negotiated_codec) {
return negotiated_codec.codec.MatchesRtpCodec(
*rtp_parameters.encodings[0].codec);
});
if (matched_codec != negotiated_codecs.end()) {
force_codec = *matched_codec;
} else {
// The requested codec has been negotiated away, we clear it from the
// parameters.
for (auto& encoding : rtp_parameters.encodings) {
encoding.codec.reset();
}
send_streams_.begin()->second->SetRtpParameters(rtp_parameters,
nullptr);
}
}
}
if (negotiated_codecs_ != negotiated_codecs) {
if (negotiated_codecs.empty()) {
changed_params->send_codec = absl::nullopt;
} else if (force_codec) {
changed_params->send_codec = force_codec;
} else if (send_codec() != negotiated_codecs.front()) {
changed_params->send_codec = negotiated_codecs.front();
}
@ -1264,6 +1291,24 @@ webrtc::RTCError WebRtcVideoSendChannel::SetRtpSendParameters(
new_dscp = rtc::DSCP_AF41;
break;
}
// TODO(orphis): Support mixed-codec simulcast
if (parameters.encodings[0].codec && send_codec_ &&
!send_codec_->codec.MatchesRtpCodec(*parameters.encodings[0].codec)) {
RTC_LOG(LS_ERROR) << "Trying to change codec to "
<< parameters.encodings[0].codec->name;
auto matched_codec =
absl::c_find_if(negotiated_codecs_, [&](auto negotiated_codec) {
return negotiated_codec.codec.MatchesRtpCodec(
*parameters.encodings[0].codec);
});
RTC_CHECK(matched_codec != negotiated_codecs_.end());
ChangedSendParameters params;
params.send_codec = *matched_codec;
ApplyChangedParams(params);
}
SetPreferredDscp(new_dscp);
}

View File

@ -4836,6 +4836,39 @@ TEST_F(WebRtcVideoChannelTest, SetSendCodecsRejectsMaxLessThanMinBitrate) {
EXPECT_FALSE(send_channel_->SetSendParameters(send_parameters_));
}
TEST_F(WebRtcVideoChannelTest,
SetSendParametersRemovesSelectedCodecFromRtpParameters) {
EXPECT_TRUE(AddSendStream());
cricket::VideoSenderParameters parameters;
parameters.codecs.push_back(cricket::CreateVideoCodec(100, "VP8"));
parameters.codecs.push_back(cricket::CreateVideoCodec(100, "VP9"));
send_channel_->SetSendParameters(parameters);
webrtc::RtpParameters initial_params =
send_channel_->GetRtpSendParameters(last_ssrc_);
webrtc::RtpCodec vp9_rtp_codec;
vp9_rtp_codec.name = "VP9";
vp9_rtp_codec.kind = cricket::MEDIA_TYPE_VIDEO;
vp9_rtp_codec.clock_rate = 90000;
initial_params.encodings[0].codec = vp9_rtp_codec;
// We should be able to set the params with the VP9 codec that has been
// negotiated.
EXPECT_TRUE(
send_channel_->SetRtpSendParameters(last_ssrc_, initial_params).ok());
parameters.codecs.clear();
parameters.codecs.push_back(cricket::CreateVideoCodec(100, "VP8"));
send_channel_->SetSendParameters(parameters);
// Since VP9 is no longer negotiated, the RTP parameters should not have a
// forced codec anymore.
webrtc::RtpParameters new_params =
send_channel_->GetRtpSendParameters(last_ssrc_);
EXPECT_EQ(new_params.encodings[0].codec, absl::nullopt);
}
// Test that when both the codec-specific bitrate params and max_bandwidth_bps
// are present in the same send parameters, the settings are combined correctly.
TEST_F(WebRtcVideoChannelTest, SetSendCodecsWithBitratesAndMaxSendBandwidth) {

View File

@ -1271,7 +1271,34 @@ bool WebRtcVoiceSendChannel::SetSendParameters(
// TODO(pthatcher): Refactor this to be more clean now that we have
// all the information at once.
if (!SetSendCodecs(params.codecs)) {
// Finding if the RtpParameters force a specific codec
absl::optional<Codec> force_codec;
if (send_streams_.size() == 1) {
// Since audio simulcast is not supported, currently, only PlanB
// has multiple tracks and we don't care about getting the
// functionality working there properly.
auto rtp_parameters = send_streams_.begin()->second->rtp_parameters();
if (rtp_parameters.encodings[0].codec) {
auto matched_codec =
absl::c_find_if(params.codecs, [&](auto negotiated_codec) {
return negotiated_codec.MatchesRtpCodec(
*rtp_parameters.encodings[0].codec);
});
if (matched_codec != params.codecs.end()) {
force_codec = *matched_codec;
} else {
// The requested codec has been negotiated away, we clear it from the
// parameters.
for (auto& encoding : rtp_parameters.encodings) {
encoding.codec.reset();
}
send_streams_.begin()->second->SetRtpParameters(rtp_parameters,
nullptr);
}
}
}
if (!SetSendCodecs(params.codecs, force_codec)) {
return false;
}
@ -1319,13 +1346,14 @@ absl::optional<Codec> WebRtcVoiceSendChannel::GetSendCodec() const {
// codec settings from the given list of codecs (originally from SDP). Both send
// and receive streams may be reconfigured based on the new settings.
bool WebRtcVoiceSendChannel::SetSendCodecs(
const std::vector<AudioCodec>& codecs) {
const std::vector<Codec>& codecs,
absl::optional<Codec> preferred_codec) {
RTC_DCHECK_RUN_ON(worker_thread_);
dtmf_payload_type_ = absl::nullopt;
dtmf_payload_freq_ = -1;
// Validate supplied codecs list.
for (const AudioCodec& codec : codecs) {
for (const Codec& codec : codecs) {
// TODO(solenberg): Validate more aspects of input - that payload types
// don't overlap, remove redundant/unsupported codecs etc -
// the same way it is done for RtpHeaderExtensions.
@ -1339,8 +1367,8 @@ bool WebRtcVoiceSendChannel::SetSendCodecs(
// Find PT of telephone-event codec with lowest clockrate, as a fallback, in
// case we don't have a DTMF codec with a rate matching the send codec's, or
// if this function returns early.
std::vector<AudioCodec> dtmf_codecs;
for (const AudioCodec& codec : codecs) {
std::vector<Codec> dtmf_codecs;
for (const Codec& codec : codecs) {
if (IsCodec(codec, kDtmfCodecName)) {
dtmf_codecs.push_back(codec);
if (!dtmf_payload_type_ || codec.clockrate < dtmf_payload_freq_) {
@ -1356,10 +1384,11 @@ bool WebRtcVoiceSendChannel::SetSendCodecs(
webrtc::BitrateConstraints bitrate_config;
absl::optional<webrtc::AudioCodecInfo> voice_codec_info;
size_t send_codec_position = 0;
for (const AudioCodec& voice_codec : codecs) {
for (const Codec& voice_codec : codecs) {
if (!(IsCodec(voice_codec, kCnCodecName) ||
IsCodec(voice_codec, kDtmfCodecName) ||
IsCodec(voice_codec, kRedCodecName))) {
IsCodec(voice_codec, kRedCodecName)) &&
(!preferred_codec || preferred_codec->Matches(voice_codec))) {
webrtc::SdpAudioFormat format(voice_codec.name, voice_codec.clockrate,
voice_codec.channels, voice_codec.params);
@ -1391,7 +1420,7 @@ bool WebRtcVoiceSendChannel::SetSendCodecs(
if (voice_codec_info->allow_comfort_noise) {
// Loop through the codecs list again to find the CN codec.
// TODO(solenberg): Break out into a separate function?
for (const AudioCodec& cn_codec : codecs) {
for (const Codec& cn_codec : codecs) {
if (IsCodec(cn_codec, kCnCodecName) &&
cn_codec.clockrate == send_codec_spec->format.clockrate_hz &&
cn_codec.channels == voice_codec_info->num_channels) {
@ -1410,7 +1439,7 @@ bool WebRtcVoiceSendChannel::SetSendCodecs(
}
// Find the telephone-event PT exactly matching the preferred send codec.
for (const AudioCodec& dtmf_codec : dtmf_codecs) {
for (const Codec& dtmf_codec : dtmf_codecs) {
if (dtmf_codec.clockrate == send_codec_spec->format.clockrate_hz) {
dtmf_payload_type_ = dtmf_codec.id;
dtmf_payload_freq_ = dtmf_codec.clockrate;
@ -1421,15 +1450,15 @@ bool WebRtcVoiceSendChannel::SetSendCodecs(
// Loop through the codecs to find the RED codec that matches opus
// with respect to clockrate and number of channels.
size_t red_codec_position = 0;
for (const AudioCodec& red_codec : codecs) {
if (red_codec_position < send_codec_position &&
IsCodec(red_codec, kRedCodecName) &&
// RED codec needs to be negotiated before the actual codec they
// reference.
for (size_t i = 0; i < send_codec_position; ++i) {
const Codec& red_codec = codecs[i];
if (IsCodec(red_codec, kRedCodecName) &&
CheckRedParameters(red_codec, *send_codec_spec)) {
send_codec_spec->red_payload_type = red_codec.id;
break;
}
red_codec_position++;
}
if (send_codec_spec_ != send_codec_spec) {
@ -1839,6 +1868,22 @@ webrtc::RTCError WebRtcVoiceSendChannel::SetRtpSendParameters(
break;
}
SetPreferredDscp(new_dscp);
absl::optional<cricket::Codec> send_codec = GetSendCodec();
// TODO(orphis): Support mixed-codec simulcast
if (parameters.encodings[0].codec && send_codec &&
!send_codec->MatchesRtpCodec(*parameters.encodings[0].codec)) {
RTC_LOG(LS_ERROR) << "Trying to change codec to "
<< parameters.encodings[0].codec->name;
auto matched_codec =
absl::c_find_if(send_codecs_, [&](auto negotiated_codec) {
return negotiated_codec.MatchesRtpCodec(
*parameters.encodings[0].codec);
});
RTC_DCHECK(matched_codec != send_codecs_.end());
SetSendCodecs(send_codecs_, *matched_codec);
}
}
// TODO(minyue): The following legacy actions go into

View File

@ -290,7 +290,8 @@ class WebRtcVoiceSendChannel final : public MediaChannelUtil,
private:
bool SetOptions(const AudioOptions& options);
bool SetSendCodecs(const std::vector<AudioCodec>& codecs);
bool SetSendCodecs(const std::vector<Codec>& codecs,
absl::optional<Codec> preferred_codec);
bool SetLocalSource(uint32_t ssrc, AudioSource* source);
bool MuteStream(uint32_t ssrc, bool mute);

View File

@ -15,8 +15,10 @@
#include "absl/memory/memory.h"
#include "absl/strings/match.h"
#include "absl/types/optional.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "api/media_types.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/rtp_parameters.h"
#include "api/scoped_refptr.h"
@ -1370,6 +1372,41 @@ TEST_P(WebRtcVoiceEngineTestFake, SetAndGetRtpSendParameters) {
EXPECT_EQ(initial_params, send_channel_->GetRtpSendParameters(kSsrcX));
}
// Test that we remove the codec from RTP parameters if it's not negotiated
// anymore.
TEST_P(WebRtcVoiceEngineTestFake,
SetSendParametersRemovesSelectedCodecFromRtpParameters) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSenderParameter parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs.push_back(kPcmuCodec);
SetSendParameters(parameters);
webrtc::RtpParameters initial_params =
send_channel_->GetRtpSendParameters(kSsrcX);
webrtc::RtpCodec opus_rtp_codec;
opus_rtp_codec.name = "opus";
opus_rtp_codec.kind = cricket::MEDIA_TYPE_AUDIO;
opus_rtp_codec.num_channels = 2;
opus_rtp_codec.clock_rate = 48000;
initial_params.encodings[0].codec = opus_rtp_codec;
// We should be able to set the params with the opus codec that has been
// negotiated.
EXPECT_TRUE(send_channel_->SetRtpSendParameters(kSsrcX, initial_params).ok());
parameters.codecs.clear();
parameters.codecs.push_back(kPcmuCodec);
SetSendParameters(parameters);
// Since Opus is no longer negotiated, the RTP parameters should not have a
// forced codec anymore.
webrtc::RtpParameters new_params =
send_channel_->GetRtpSendParameters(kSsrcX);
EXPECT_EQ(new_params.encodings[0].codec, absl::nullopt);
}
// Test that max_bitrate_bps in send stream config gets updated correctly when
// SetRtpSendParameters is called.
TEST_P(WebRtcVoiceEngineTestFake, SetRtpSendParameterUpdatesMaxBitrate) {

View File

@ -2770,6 +2770,7 @@ if (rtc_include_tests && !build_with_chromium) {
"../api:media_stream_interface",
"../api:rtc_error",
"../api:rtc_stats_api",
"../api:rtp_parameters",
"../api:scoped_refptr",
"../api:sequence_checker",
"../api/audio:audio_mixer_api",
@ -2827,6 +2828,7 @@ if (rtc_include_tests && !build_with_chromium) {
]
absl_deps = [
"//third_party/abseil-cpp/absl/algorithm:container",
"//third_party/abseil-cpp/absl/strings",
"//third_party/abseil-cpp/absl/types:optional",
]
}

View File

@ -1106,14 +1106,20 @@ PeerConnection::AddTransceiver(
}
std::vector<cricket::VideoCodec> codecs;
// Gather the current codec capabilities to allow checking scalabilityMode and
// codec selection against supported values.
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);
} else {
codecs = context_->media_engine()->voice().send_codecs();
}
auto result = cricket::CheckRtpParametersValues(parameters, codecs);
auto result =
cricket::CheckRtpParametersValues(parameters, codecs, absl::nullopt);
if (!result.ok()) {
if (result.type() == RTCErrorType::INVALID_MODIFICATION) {
result.set_type(RTCErrorType::UNSUPPORTED_OPERATION);
}
LOG_AND_RETURN_ERROR(result.type(), result.message());
}

View File

@ -11,11 +11,17 @@
#include <string>
#include <vector>
#include "absl/strings/match.h"
#include "absl/types/optional.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "api/audio_codecs/opus_audio_decoder_factory.h"
#include "api/audio_codecs/opus_audio_encoder_factory.h"
#include "api/media_types.h"
#include "api/rtc_error.h"
#include "api/rtp_parameters.h"
#include "api/rtp_transceiver_direction.h"
#include "api/rtp_transceiver_interface.h"
#include "api/stats/rtcstats_objects.h"
#include "api/units/data_rate.h"
#include "api/video_codecs/video_decoder_factory_template.h"
@ -115,8 +121,8 @@ class PeerConnectionEncodingsIntegrationTest : public ::testing::Test {
rtc::scoped_refptr<PeerConnectionTestWrapper> CreatePc() {
auto pc_wrapper = rtc::make_ref_counted<PeerConnectionTestWrapper>(
"pc", &pss_, background_thread_.get(), background_thread_.get());
pc_wrapper->CreatePc({}, webrtc::CreateOpusAudioEncoderFactory(),
webrtc::CreateOpusAudioDecoderFactory());
pc_wrapper->CreatePc({}, webrtc::CreateBuiltinAudioEncoderFactory(),
webrtc::CreateBuiltinAudioDecoderFactory());
return pc_wrapper;
}
@ -183,8 +189,7 @@ class PeerConnectionEncodingsIntegrationTest : public ::testing::Test {
void NegotiateWithSimulcastTweaks(
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper,
std::vector<cricket::SimulcastLayer> init_layers) {
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper) {
// Create and set offer for `local_pc_wrapper`.
std::unique_ptr<SessionDescriptionInterface> offer =
CreateOffer(local_pc_wrapper);
@ -226,6 +231,29 @@ class PeerConnectionEncodingsIntegrationTest : public ::testing::Test {
return callback->report();
}
bool IsCodecIdDifferent(
rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
size_t index,
const std::string& codec_id) {
return IsCodecIdDifferentWithScalabilityMode(pc_wrapper, index, codec_id,
absl::nullopt);
}
bool IsCodecIdDifferentWithScalabilityMode(
rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
size_t index,
const std::string& codec_id,
absl::optional<std::string> wanted_scalability_mode) {
rtc::scoped_refptr<const RTCStatsReport> report = GetStats(pc_wrapper);
std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
report->GetStatsOfType<RTCOutboundRtpStreamStats>();
return outbound_rtps[index]->codec_id.value() != codec_id &&
(!wanted_scalability_mode ||
(outbound_rtps[index]->scalability_mode.has_value() &&
outbound_rtps[index]->scalability_mode.value() ==
wanted_scalability_mode));
}
bool HasOutboundRtpBytesSent(
rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
size_t num_layers) {
@ -387,7 +415,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP8");
transceiver->SetCodecPreferences(codecs);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
@ -434,7 +462,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, layers);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
@ -488,7 +516,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
Optional(std::string("L3T3_KEY")));
// Negotiate, this results in VP8 being picked and fallback happening.
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
// `scalaiblity_mode` is assigned the fallback value "L1T2" which is different
@ -533,7 +561,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
transceiver->SetCodecPreferences(codecs);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
@ -585,7 +613,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
parameters.encodings[0].scale_resolution_down_by = 1;
EXPECT_TRUE(sender->SetParameters(parameters).ok());
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
@ -640,7 +668,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
parameters.encodings[2].active = false;
EXPECT_TRUE(sender->SetParameters(parameters).ok());
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
@ -683,7 +711,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
// The original negotiation triggers legacy SVC because we didn't specify
// any scalability mode.
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
@ -745,7 +773,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
parameters.encodings[2].active = false;
sender->SetParameters(parameters);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
@ -784,7 +812,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
parameters.encodings[2].active = false;
sender->SetParameters(parameters);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
@ -822,7 +850,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, VP9_TargetBitrate_LegacyL1T3) {
parameters.encodings[2].active = true;
sender->SetParameters(parameters);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
@ -872,7 +900,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, VP9_TargetBitrate_StandardL1T3) {
parameters.encodings[2].active = false;
sender->SetParameters(parameters);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
@ -896,6 +924,714 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, VP9_TargetBitrate_StandardL1T3) {
EXPECT_LE(target_bitrate.kbps(), kVp9ExpectedMaxBitrateForL1T3.kbps());
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
EncodingParameterCodecIsEmptyWhenCreatedAudio) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
auto transceiver_or_error =
local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
transceiver_or_error.MoveValue();
webrtc::RtpParameters parameters =
audio_transceiver->sender()->GetParameters();
EXPECT_FALSE(parameters.encodings[0].codec.has_value());
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
EncodingParameterCodecIsEmptyWhenCreatedVideo) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
auto transceiver_or_error =
local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
transceiver_or_error.MoveValue();
webrtc::RtpParameters parameters =
video_transceiver->sender()->GetParameters();
EXPECT_FALSE(parameters.encodings[0].codec.has_value());
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
EncodingParameterCodecIsSetByAddTransceiverAudio) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
local_pc_wrapper->GetUserMedia(
/*audio=*/true, {}, /*video=*/false, {});
rtc::scoped_refptr<AudioTrackInterface> track = stream->GetAudioTracks()[0];
absl::optional<webrtc::RtpCodecCapability> pcmu =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"pcmu");
ASSERT_TRUE(pcmu);
webrtc::RtpTransceiverInit init;
init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
webrtc::RtpEncodingParameters encoding_parameters;
encoding_parameters.codec = pcmu;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
local_pc_wrapper->pc()->AddTransceiver(track, init);
rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
transceiver_or_error.MoveValue();
webrtc::RtpParameters parameters =
audio_transceiver->sender()->GetParameters();
EXPECT_EQ(*parameters.encodings[0].codec, *pcmu);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
report->GetStatsOfType<RTCOutboundRtpStreamStats>();
ASSERT_EQ(outbound_rtps.size(), 1u);
std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
EXPECT_STRCASEEQ(("audio/" + pcmu->name).c_str(), codec_name.c_str());
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
EncodingParameterCodecIsSetByAddTransceiverVideo) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
local_pc_wrapper->GetUserMedia(
/*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720});
rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
absl::optional<webrtc::RtpCodecCapability> vp9 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp9");
ASSERT_TRUE(vp9);
webrtc::RtpTransceiverInit init;
init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
webrtc::RtpEncodingParameters encoding_parameters;
encoding_parameters.codec = vp9;
encoding_parameters.scalability_mode = "L3T3";
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
local_pc_wrapper->pc()->AddTransceiver(track, init);
rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
transceiver_or_error.MoveValue();
webrtc::RtpParameters parameters =
audio_transceiver->sender()->GetParameters();
EXPECT_EQ(*parameters.encodings[0].codec, *vp9);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
EXPECT_TRUE_WAIT(
IsCodecIdDifferentWithScalabilityMode(local_pc_wrapper, 0, "", "L3T3"),
kDefaultTimeout.ms());
rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
report->GetStatsOfType<RTCOutboundRtpStreamStats>();
ASSERT_EQ(outbound_rtps.size(), 1u);
std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
EXPECT_STRCASEEQ(("video/" + vp9->name).c_str(), codec_name.c_str());
EXPECT_EQ(outbound_rtps[0]->scalability_mode.value(), "L3T3");
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
EncodingParameterCodecIsSetBySetParametersBeforeNegotiationAudio) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
local_pc_wrapper->GetUserMedia(
/*audio=*/true, {}, /*video=*/false, {});
rtc::scoped_refptr<AudioTrackInterface> track = stream->GetAudioTracks()[0];
absl::optional<webrtc::RtpCodecCapability> pcmu =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"pcmu");
auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track);
rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
transceiver_or_error.MoveValue();
webrtc::RtpParameters parameters =
audio_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = pcmu;
EXPECT_TRUE(audio_transceiver->sender()->SetParameters(parameters).ok());
parameters = audio_transceiver->sender()->GetParameters();
EXPECT_EQ(parameters.encodings[0].codec, pcmu);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
report->GetStatsOfType<RTCOutboundRtpStreamStats>();
ASSERT_EQ(outbound_rtps.size(), 1u);
std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
EXPECT_STRCASEEQ(("audio/" + pcmu->name).c_str(), codec_name.c_str());
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
EncodingParameterCodecIsSetBySetParametersAfterNegotiationAudio) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
local_pc_wrapper->GetUserMedia(
/*audio=*/true, {}, /*video=*/false, {});
rtc::scoped_refptr<AudioTrackInterface> track = stream->GetAudioTracks()[0];
absl::optional<webrtc::RtpCodecCapability> pcmu =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"pcmu");
auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track);
rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
transceiver_or_error.MoveValue();
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
report->GetStatsOfType<RTCOutboundRtpStreamStats>();
ASSERT_EQ(outbound_rtps.size(), 1u);
std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
EXPECT_STRCASENE(("audio/" + pcmu->name).c_str(), codec_name.c_str());
std::string last_codec_id = outbound_rtps[0]->codec_id.value();
webrtc::RtpParameters parameters =
audio_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = pcmu;
EXPECT_TRUE(audio_transceiver->sender()->SetParameters(parameters).ok());
parameters = audio_transceiver->sender()->GetParameters();
EXPECT_EQ(parameters.encodings[0].codec, pcmu);
EXPECT_TRUE_WAIT(IsCodecIdDifferent(local_pc_wrapper, 0, last_codec_id),
kDefaultTimeout.ms());
report = GetStats(local_pc_wrapper);
outbound_rtps = report->GetStatsOfType<RTCOutboundRtpStreamStats>();
ASSERT_EQ(outbound_rtps.size(), 1u);
codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
EXPECT_STRCASEEQ(("audio/" + pcmu->name).c_str(), codec_name.c_str());
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
EncodingParameterCodecIsSetBySetParametersBeforeNegotiationVideo) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
local_pc_wrapper->GetUserMedia(
/*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720});
rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
absl::optional<webrtc::RtpCodecCapability> vp9 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp9");
auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track);
rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
transceiver_or_error.MoveValue();
webrtc::RtpParameters parameters =
video_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = vp9;
parameters.encodings[0].scalability_mode = "L3T3";
EXPECT_TRUE(video_transceiver->sender()->SetParameters(parameters).ok());
parameters = video_transceiver->sender()->GetParameters();
EXPECT_EQ(parameters.encodings[0].codec, vp9);
EXPECT_EQ(parameters.encodings[0].scalability_mode, "L3T3");
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
EXPECT_TRUE_WAIT(
IsCodecIdDifferentWithScalabilityMode(local_pc_wrapper, 0, "", "L3T3"),
kDefaultTimeout.ms());
rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
report->GetStatsOfType<RTCOutboundRtpStreamStats>();
ASSERT_EQ(outbound_rtps.size(), 1u);
std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
EXPECT_STRCASEEQ(("video/" + vp9->name).c_str(), codec_name.c_str());
EXPECT_EQ(outbound_rtps[0]->scalability_mode.ValueOrDefault(""), "L3T3");
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
EncodingParameterCodecIsSetBySetParametersAfterNegotiationVideo) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
local_pc_wrapper->GetUserMedia(
/*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720});
rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
absl::optional<webrtc::RtpCodecCapability> vp9 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp9");
auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track);
rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
transceiver_or_error.MoveValue();
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
report->GetStatsOfType<RTCOutboundRtpStreamStats>();
ASSERT_EQ(outbound_rtps.size(), 1u);
std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
EXPECT_STRCASENE(("audio/" + vp9->name).c_str(), codec_name.c_str());
std::string last_codec_id = outbound_rtps[0]->codec_id.value();
webrtc::RtpParameters parameters =
video_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = vp9;
parameters.encodings[0].scalability_mode = "L3T3";
EXPECT_TRUE(video_transceiver->sender()->SetParameters(parameters).ok());
parameters = video_transceiver->sender()->GetParameters();
EXPECT_EQ(parameters.encodings[0].codec, vp9);
EXPECT_EQ(parameters.encodings[0].scalability_mode, "L3T3");
EXPECT_TRUE_WAIT(IsCodecIdDifferentWithScalabilityMode(local_pc_wrapper, 0,
last_codec_id, "L3T3"),
kDefaultTimeout.ms());
report = GetStats(local_pc_wrapper);
outbound_rtps = report->GetStatsOfType<RTCOutboundRtpStreamStats>();
ASSERT_EQ(outbound_rtps.size(), 1u);
codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
EXPECT_STRCASEEQ(("video/" + vp9->name).c_str(), codec_name.c_str());
EXPECT_EQ(outbound_rtps[0]->scalability_mode.value(), "L3T3");
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
AddTransceiverRejectsUnknownCodecParameterAudio) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
webrtc::RtpCodec dummy_codec;
dummy_codec.kind = cricket::MEDIA_TYPE_AUDIO;
dummy_codec.name = "FOOBAR";
dummy_codec.clock_rate = 90000;
dummy_codec.num_channels = 2;
webrtc::RtpTransceiverInit init;
init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
webrtc::RtpEncodingParameters encoding_parameters;
encoding_parameters.codec = dummy_codec;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
EXPECT_FALSE(transceiver_or_error.ok());
EXPECT_EQ(transceiver_or_error.error().type(),
RTCErrorType::UNSUPPORTED_OPERATION);
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
AddTransceiverRejectsUnknownCodecParameterVideo) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
webrtc::RtpCodec dummy_codec;
dummy_codec.kind = cricket::MEDIA_TYPE_VIDEO;
dummy_codec.name = "FOOBAR";
dummy_codec.clock_rate = 90000;
webrtc::RtpTransceiverInit init;
init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
webrtc::RtpEncodingParameters encoding_parameters;
encoding_parameters.codec = dummy_codec;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
EXPECT_FALSE(transceiver_or_error.ok());
EXPECT_EQ(transceiver_or_error.error().type(),
RTCErrorType::UNSUPPORTED_OPERATION);
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
SetParametersRejectsUnknownCodecParameterAudio) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
webrtc::RtpCodec dummy_codec;
dummy_codec.kind = cricket::MEDIA_TYPE_AUDIO;
dummy_codec.name = "FOOBAR";
dummy_codec.clock_rate = 90000;
dummy_codec.num_channels = 2;
auto transceiver_or_error =
local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
ASSERT_TRUE(transceiver_or_error.ok());
rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
transceiver_or_error.MoveValue();
webrtc::RtpParameters parameters =
audio_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = dummy_codec;
RTCError error = audio_transceiver->sender()->SetParameters(parameters);
EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
SetParametersRejectsUnknownCodecParameterVideo) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
webrtc::RtpCodec dummy_codec;
dummy_codec.kind = cricket::MEDIA_TYPE_VIDEO;
dummy_codec.name = "FOOBAR";
dummy_codec.clock_rate = 90000;
auto transceiver_or_error =
local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
ASSERT_TRUE(transceiver_or_error.ok());
rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
transceiver_or_error.MoveValue();
webrtc::RtpParameters parameters =
video_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = dummy_codec;
RTCError error = video_transceiver->sender()->SetParameters(parameters);
EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
SetParametersRejectsNonPreferredCodecParameterAudio) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
absl::optional<webrtc::RtpCodecCapability> opus =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"opus");
ASSERT_TRUE(opus);
std::vector<webrtc::RtpCodecCapability> 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<RtpTransceiverInterface> audio_transceiver =
transceiver_or_error.MoveValue();
ASSERT_TRUE(audio_transceiver->SetCodecPreferences(not_opus_codecs).ok());
webrtc::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<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
absl::optional<webrtc::RtpCodecCapability> vp8 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp8");
ASSERT_TRUE(vp8);
std::vector<webrtc::RtpCodecCapability> 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<RtpTransceiverInterface> video_transceiver =
transceiver_or_error.MoveValue();
ASSERT_TRUE(video_transceiver->SetCodecPreferences(not_vp8_codecs).ok());
webrtc::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<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
absl::optional<webrtc::RtpCodecCapability> opus =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"opus");
ASSERT_TRUE(opus);
std::vector<webrtc::RtpCodecCapability> 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<RtpTransceiverInterface> audio_transceiver =
transceiver_or_error.MoveValue();
ASSERT_TRUE(audio_transceiver->SetCodecPreferences(not_opus_codecs).ok());
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
webrtc::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,
SetParametersRejectsNonNegotiatedCodecParameterVideo) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
absl::optional<webrtc::RtpCodecCapability> vp8 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp8");
ASSERT_TRUE(vp8);
std::vector<webrtc::RtpCodecCapability> 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<RtpTransceiverInterface> video_transceiver =
transceiver_or_error.MoveValue();
ASSERT_TRUE(video_transceiver->SetCodecPreferences(not_vp8_codecs).ok());
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
webrtc::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,
EncodingParametersCodecRemovedAfterNegotiationAudio) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
absl::optional<webrtc::RtpCodecCapability> opus =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"opus");
ASSERT_TRUE(opus);
std::vector<webrtc::RtpCodecCapability> 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());
webrtc::RtpTransceiverInit init;
init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
webrtc::RtpEncodingParameters encoding_parameters;
encoding_parameters.codec = opus;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
ASSERT_TRUE(transceiver_or_error.ok());
rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
transceiver_or_error.MoveValue();
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
webrtc::RtpParameters parameters =
audio_transceiver->sender()->GetParameters();
EXPECT_EQ(parameters.encodings[0].codec, opus);
ASSERT_TRUE(audio_transceiver->SetCodecPreferences(not_opus_codecs).ok());
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
parameters = audio_transceiver->sender()->GetParameters();
EXPECT_FALSE(parameters.encodings[0].codec);
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
SetParametersRejectsScalabilityModeForSelectedCodec) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
absl::optional<webrtc::RtpCodecCapability> vp8 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp8");
ASSERT_TRUE(vp8);
webrtc::RtpTransceiverInit init;
init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
webrtc::RtpEncodingParameters encoding_parameters;
encoding_parameters.codec = vp8;
encoding_parameters.scalability_mode = "L1T3";
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
ASSERT_TRUE(transceiver_or_error.ok());
rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
transceiver_or_error.MoveValue();
webrtc::RtpParameters parameters =
video_transceiver->sender()->GetParameters();
parameters.encodings[0].scalability_mode = "L3T3";
RTCError error = video_transceiver->sender()->SetParameters(parameters);
EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
EncodingParametersCodecRemovedByNegotiationVideo) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
absl::optional<webrtc::RtpCodecCapability> vp8 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp8");
ASSERT_TRUE(vp8);
std::vector<webrtc::RtpCodecCapability> 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());
webrtc::RtpTransceiverInit init;
init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
webrtc::RtpEncodingParameters encoding_parameters;
encoding_parameters.rid = "h";
encoding_parameters.codec = vp8;
encoding_parameters.scale_resolution_down_by = 2;
init.send_encodings.push_back(encoding_parameters);
encoding_parameters.rid = "f";
encoding_parameters.scale_resolution_down_by = 1;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
ASSERT_TRUE(transceiver_or_error.ok());
rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
transceiver_or_error.MoveValue();
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
webrtc::RtpParameters parameters =
video_transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 2u);
EXPECT_EQ(parameters.encodings[0].codec, vp8);
EXPECT_EQ(parameters.encodings[1].codec, vp8);
ASSERT_TRUE(video_transceiver->SetCodecPreferences(not_vp8_codecs).ok());
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
parameters = video_transceiver->sender()->GetParameters();
EXPECT_FALSE(parameters.encodings[0].codec);
EXPECT_FALSE(parameters.encodings[1].codec);
}
TEST_F(PeerConnectionEncodingsIntegrationTest,
AddTransceiverRejectsMixedCodecSimulcast) {
// Mixed Codec Simulcast is not yet supported, so we ensure that we reject
// such parameters.
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
absl::optional<webrtc::RtpCodecCapability> vp8 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp8");
ASSERT_TRUE(vp8);
absl::optional<webrtc::RtpCodecCapability> vp9 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp9");
webrtc::RtpTransceiverInit init;
init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
webrtc::RtpEncodingParameters encoding_parameters;
encoding_parameters.rid = "h";
encoding_parameters.codec = vp8;
encoding_parameters.scale_resolution_down_by = 2;
init.send_encodings.push_back(encoding_parameters);
encoding_parameters.rid = "f";
encoding_parameters.codec = vp9;
encoding_parameters.scale_resolution_down_by = 1;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
ASSERT_FALSE(transceiver_or_error.ok());
EXPECT_EQ(transceiver_or_error.error().type(),
RTCErrorType::UNSUPPORTED_OPERATION);
}
// Tests that use the standard path (specifying both `scalability_mode` and
// `scale_resolution_down_by`) should pass for all codecs.
class PeerConnectionEncodingsIntegrationParameterizedTest
@ -952,7 +1688,7 @@ TEST_P(PeerConnectionEncodingsIntegrationParameterizedTest, AllLayersInactive) {
parameters.encodings[2].active = false;
sender->SetParameters(parameters);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
@ -995,7 +1731,7 @@ TEST_P(PeerConnectionEncodingsIntegrationParameterizedTest, Simulcast) {
parameters.encodings[2].scale_resolution_down_by = 1;
sender->SetParameters(parameters);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();

View File

@ -1521,7 +1521,7 @@ bool CompareCodecs(const std::vector<webrtc::RtpCodecCapability>& capabilities,
absl::c_equal(
capabilities_no_rtx, codecs_no_rtx,
[](const webrtc::RtpCodecCapability& capability, const C& codec) {
return codec.MatchesCapability(capability);
return codec.MatchesRtpCodec(capability);
});
}

View File

@ -139,7 +139,7 @@ TEST_F(PeerConnectionSVCIntegrationTest, SetParametersRejectsL3T3WithVP8) {
parameters.encodings[0].scalability_mode = "L3T3";
auto result = transceiver->sender()->SetParameters(parameters);
EXPECT_FALSE(result.ok());
EXPECT_EQ(result.type(), webrtc::RTCErrorType::UNSUPPORTED_OPERATION);
EXPECT_EQ(result.type(), webrtc::RTCErrorType::INVALID_MODIFICATION);
}
TEST_F(PeerConnectionSVCIntegrationTest,
@ -212,7 +212,7 @@ TEST_F(PeerConnectionSVCIntegrationTest,
parameters.encodings[0].scalability_mode = "L3T3";
auto result = transceiver->sender()->SetParameters(parameters);
EXPECT_FALSE(result.ok());
EXPECT_EQ(result.type(), webrtc::RTCErrorType::UNSUPPORTED_OPERATION);
EXPECT_EQ(result.type(), webrtc::RTCErrorType::INVALID_MODIFICATION);
}
TEST_F(PeerConnectionSVCIntegrationTest,
@ -237,7 +237,7 @@ TEST_F(PeerConnectionSVCIntegrationTest,
parameters.encodings[0].scalability_mode = "FOOBAR";
auto result = transceiver->sender()->SetParameters(parameters);
EXPECT_FALSE(result.ok());
EXPECT_EQ(result.type(), webrtc::RTCErrorType::UNSUPPORTED_OPERATION);
EXPECT_EQ(result.type(), webrtc::RTCErrorType::INVALID_MODIFICATION);
}
TEST_F(PeerConnectionSVCIntegrationTest, FallbackToL1Tx) {

View File

@ -248,7 +248,7 @@ void RtpSenderBase::SetParametersInternal(const RtpParameters& parameters,
}
if (!media_channel_ || !ssrc_) {
auto result = cricket::CheckRtpParametersInvalidModificationAndValues(
init_parameters_, parameters, video_codec_preferences_);
init_parameters_, parameters, codec_preferences_, absl::nullopt);
if (result.ok()) {
init_parameters_ = parameters;
}
@ -272,7 +272,7 @@ void RtpSenderBase::SetParametersInternal(const RtpParameters& parameters,
return;
}
result = CheckSVCParameters(rtp_parameters);
result = CheckCodecParameters(rtp_parameters);
if (!result.ok()) {
webrtc::InvokeSetParametersCallback(callback, result);
return;
@ -299,7 +299,7 @@ RTCError RtpSenderBase::SetParametersInternalWithAllLayers(
}
if (!media_channel_ || !ssrc_) {
auto result = cricket::CheckRtpParametersInvalidModificationAndValues(
init_parameters_, parameters, video_codec_preferences_);
init_parameters_, parameters, codec_preferences_, absl::nullopt);
if (result.ok()) {
init_parameters_ = parameters;
}
@ -338,6 +338,26 @@ RTCError RtpSenderBase::CheckSetParameters(const RtpParameters& parameters) {
return RTCError::OK();
}
RTCError RtpSenderBase::CheckCodecParameters(const RtpParameters& parameters) {
absl::optional<cricket::Codec> send_codec = media_channel_->GetSendCodec();
// Match the currently used codec against the codec preferences to gather
// the SVC capabilities.
absl::optional<cricket::Codec> send_codec_with_svc_info;
if (send_codec && send_codec->type == cricket::Codec::Type::kVideo) {
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, codec_preferences_,
send_codec_with_svc_info);
}
RTCError RtpSenderBase::SetParameters(const RtpParameters& parameters) {
RTC_DCHECK_RUN_ON(signaling_thread_);
TRACE_EVENT0("webrtc", "RtpSenderBase::SetParameters");
@ -876,23 +896,4 @@ void VideoRtpSender::ClearSend() {
[&] { video_media_channel()->SetVideoSend(ssrc_, nullptr, nullptr); });
}
RTCError VideoRtpSender::CheckSVCParameters(const RtpParameters& parameters) {
absl::optional<cricket::VideoCodec> send_codec =
video_media_channel()->GetSendCodec();
// Match the currently used codec against the codec preferences to gather
// the SVC capabilities.
std::vector<cricket::VideoCodec> codecs;
if (send_codec) {
for (const auto& codec_preference : video_codec_preferences_) {
if (send_codec->Matches(codec_preference)) {
codecs.push_back(codec_preference);
break;
}
}
}
return cricket::CheckScalabilityModeValues(parameters, codecs);
}
} // namespace webrtc

View File

@ -85,8 +85,8 @@ 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) {
// Additional checks that are specific to the current codec settings
virtual RTCError CheckCodecParameters(const RtpParameters& parameters) {
return webrtc::RTCError::OK();
}
@ -104,8 +104,8 @@ class RtpSenderInternal : public RtpSenderInterface {
// Used by the owning transceiver to inform the sender on the currently
// selected codecs.
virtual void SetVideoCodecPreferences(
std::vector<cricket::VideoCodec> codec_preferences) = 0;
virtual void SetCodecPreferences(
std::vector<cricket::Codec> codec_preferences) = 0;
};
// Shared implementation for RtpSenderInternal interface.
@ -144,6 +144,7 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface {
SetParametersCallback callback = nullptr,
bool blocking = true) override;
RTCError CheckSetParameters(const RtpParameters& parameters);
RTCError CheckCodecParameters(const RtpParameters& parameters) override;
RtpParameters GetParametersInternalWithAllLayers() const override;
RTCError SetParametersInternalWithAllLayers(
const RtpParameters& parameters) override;
@ -222,9 +223,9 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface {
is_transceiver_stopped_ = true;
}
void SetVideoCodecPreferences(
std::vector<cricket::VideoCodec> codec_preferences) override {
video_codec_preferences_ = codec_preferences;
void SetCodecPreferences(
std::vector<cricket::Codec> codec_preferences) override {
codec_preferences_ = codec_preferences;
}
protected:
@ -262,7 +263,7 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface {
std::vector<std::string> stream_ids_;
RtpParameters init_parameters_;
std::vector<cricket::VideoCodec> video_codec_preferences_;
std::vector<cricket::Codec> 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
@ -422,8 +423,6 @@ class VideoRtpSender : public RtpSenderBase {
rtc::scoped_refptr<DtmfSenderInterface> GetDtmfSender() const override;
RTCError GenerateKeyFrame(const std::vector<std::string>& rids) override;
RTCError CheckSVCParameters(const RtpParameters& parameters) override;
protected:
VideoRtpSender(rtc::Thread* worker_thread,
const std::string& id,

View File

@ -56,7 +56,7 @@ RTCError VerifyCodecPreferences(
codec.name != cricket::kFlexfecCodecName &&
absl::c_any_of(recv_codecs,
[&codec](const cricket::Codec& recv_codec) {
return recv_codec.MatchesCapability(codec);
return recv_codec.MatchesRtpCodec(codec);
});
})) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
@ -70,7 +70,7 @@ RTCError VerifyCodecPreferences(
codec.name != cricket::kFlexfecCodecName &&
absl::c_any_of(send_codecs,
[&codec](const cricket::Codec& send_codec) {
return send_codec.MatchesCapability(codec);
return send_codec.MatchesRtpCodec(codec);
});
})) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
@ -85,12 +85,12 @@ RTCError VerifyCodecPreferences(
for (const auto& codec_preference : codecs) {
bool is_recv_codec = absl::c_any_of(
recv_codecs, [&codec_preference](const cricket::Codec& codec) {
return codec.MatchesCapability(codec_preference);
return codec.MatchesRtpCodec(codec_preference);
});
bool is_send_codec = absl::c_any_of(
send_codecs, [&codec_preference](const cricket::Codec& codec) {
return codec.MatchesCapability(codec_preference);
return codec.MatchesRtpCodec(codec_preference);
});
if (!is_recv_codec && !is_send_codec) {
@ -126,7 +126,7 @@ std::vector<cricket::VideoCodec> MatchCodecPreferences(
for (const auto& codec_preference : codecs) {
for (const cricket::VideoCodec& send_codec : send_codecs) {
if (send_codec.MatchesCapability(codec_preference)) {
if (send_codec.MatchesRtpCodec(codec_preference)) {
result.push_back(send_codec);
}
}
@ -171,9 +171,10 @@ 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));
sender->internal()->SetCodecPreferences(
sender->media_type() == cricket::MEDIA_TYPE_VIDEO
? media_engine()->video().send_codecs(false)
: media_engine()->voice().send_codecs());
senders_.push_back(sender);
receivers_.push_back(receiver);
}
@ -412,14 +413,15 @@ 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));
}
std::vector<cricket::Codec> send_codecs =
media_type() == cricket::MEDIA_TYPE_VIDEO
? media_engine()->video().send_codecs(false)
: media_engine()->voice().send_codecs();
sender->internal()->SetCodecPreferences(
codec_preferences_.empty()
? send_codecs
: MatchCodecPreferences(codec_preferences_, send_codecs));
senders_.push_back(sender);
}
@ -668,9 +670,10 @@ 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));
senders_.front()->internal()->SetCodecPreferences(
media_type() == cricket::MEDIA_TYPE_VIDEO
? media_engine()->video().send_codecs(false)
: media_engine()->voice().send_codecs());
return RTCError::OK();
}
@ -683,24 +686,19 @@ RTCError RtpTransceiver::SetCodecPreferences(
// 6. to 8.
RTCError result;
std::vector<cricket::Codec> recv_codecs, send_codecs;
if (media_type_ == cricket::MEDIA_TYPE_AUDIO) {
result =
VerifyCodecPreferences(codecs, media_engine()->voice().send_codecs(),
media_engine()->voice().recv_codecs());
send_codecs = media_engine()->voice().send_codecs();
recv_codecs = media_engine()->voice().recv_codecs();
} else if (media_type_ == cricket::MEDIA_TYPE_VIDEO) {
std::vector<cricket::Codec> send_codecs =
media_engine()->video().send_codecs(context()->use_rtx());
result = VerifyCodecPreferences(
codecs, send_codecs,
media_engine()->video().recv_codecs(context()->use_rtx()));
if (result.ok()) {
senders_.front()->internal()->SetVideoCodecPreferences(
MatchCodecPreferences(codecs, send_codecs));
}
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()->SetCodecPreferences(
MatchCodecPreferences(codecs, send_codecs));
codec_preferences_ = codecs;
}

View File

@ -64,9 +64,12 @@ class MockRtpSenderInternal : public RtpSenderInternal {
SetParametersInternalWithAllLayers,
(const RtpParameters&),
(override));
MOCK_METHOD(RTCError, CheckSVCParameters, (const RtpParameters&), (override));
MOCK_METHOD(RTCError,
CheckCodecParameters,
(const RtpParameters&),
(override));
MOCK_METHOD(void,
SetVideoCodecPreferences,
SetCodecPreferences,
(std::vector<cricket::VideoCodec>),
(override));
MOCK_METHOD(rtc::scoped_refptr<DtmfSenderInterface>,

View File

@ -17,9 +17,11 @@
#include <utility>
#include <vector>
#include "absl/strings/match.h"
#include "absl/types/optional.h"
#include "api/audio/audio_mixer.h"
#include "api/create_peerconnection_factory.h"
#include "api/media_types.h"
#include "api/sequence_checker.h"
#include "api/video_codecs/video_decoder_factory.h"
#include "api/video_codecs/video_decoder_factory_template.h"
@ -201,6 +203,20 @@ PeerConnectionTestWrapper::CreateDataChannel(
return result.MoveValue();
}
absl::optional<webrtc::RtpCodecCapability>
PeerConnectionTestWrapper::FindFirstSendCodecWithName(
cricket::MediaType media_type,
const std::string& name) const {
std::vector<webrtc::RtpCodecCapability> codecs =
peer_connection_factory_->GetRtpSenderCapabilities(media_type).codecs;
for (const auto& codec : codecs) {
if (absl::EqualsIgnoreCase(codec.name, name)) {
return codec;
}
}
return absl::nullopt;
}
void PeerConnectionTestWrapper::WaitForNegotiation() {
EXPECT_TRUE_WAIT(!pending_negotiation_, kMaxWait);
}

View File

@ -23,6 +23,7 @@
#include "api/media_stream_interface.h"
#include "api/peer_connection_interface.h"
#include "api/rtc_error.h"
#include "api/rtp_parameters.h"
#include "api/rtp_receiver_interface.h"
#include "api/scoped_refptr.h"
#include "api/sequence_checker.h"
@ -64,6 +65,10 @@ class PeerConnectionTestWrapper
const std::string& label,
const webrtc::DataChannelInit& init);
absl::optional<webrtc::RtpCodecCapability> FindFirstSendCodecWithName(
cricket::MediaType media_type,
const std::string& name) const;
void WaitForNegotiation();
// Implements PeerConnectionObserver.