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}
diff --git a/api/rtp_parameters.h b/api/rtp_parameters.h
index e277e7d..91455e2 100644
--- a/api/rtp_parameters.h
+++ b/api/rtp_parameters.h
@@ -520,6 +520,9 @@
// 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 @@
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);
diff --git a/media/base/codec.cc b/media/base/codec.cc
index 70a8d90..7ecf383 100644
--- a/media/base/codec.cc
+++ b/media/base/codec.cc
@@ -211,8 +211,7 @@
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 &&
diff --git a/media/base/codec.h b/media/base/codec.h
index 74c5cc2..5595708 100644
--- a/media/base/codec.h
+++ b/media/base/codec.h
@@ -114,7 +114,7 @@
// 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;
diff --git a/media/base/media_engine.cc b/media/base/media_engine.cc
index 0efbd71..7304ab0 100644
--- a/media/base/media_engine.cc
+++ b/media/base/media_engine.cc
@@ -67,34 +67,69 @@
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 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 @@
"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 @@
"Attempted to set RtpParameters with modified SSRC");
}
- return CheckRtpParametersValues(rtp_parameters, codecs);
+ return CheckRtpParametersValues(rtp_parameters, codec_preferences,
+ send_codec);
}
CompositeMediaEngine::CompositeMediaEngine(
diff --git a/media/base/media_engine.h b/media/base/media_engine.h
index b3d7491..4281235 100644
--- a/media/base/media_engine.h
+++ b/media/base/media_engine.h
@@ -42,20 +42,23 @@
// 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
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index f041dce..504aad9 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -994,9 +994,36 @@
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 @@
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);
}
diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc
index bf1e396..7719dc2 100644
--- a/media/engine/webrtc_video_engine_unittest.cc
+++ b/media/engine/webrtc_video_engine_unittest.cc
@@ -4836,6 +4836,39 @@
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) {
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index 25fdbdc..65363c2 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -1271,7 +1271,34 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
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 @@
}
// 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 @@
// 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 @@
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
diff --git a/media/engine/webrtc_voice_engine.h b/media/engine/webrtc_voice_engine.h
index 925d086..868c78b 100644
--- a/media/engine/webrtc_voice_engine.h
+++ b/media/engine/webrtc_voice_engine.h
@@ -290,7 +290,8 @@
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);
diff --git a/media/engine/webrtc_voice_engine_unittest.cc b/media/engine/webrtc_voice_engine_unittest.cc
index aa3d426..d3902e4 100644
--- a/media/engine/webrtc_voice_engine_unittest.cc
+++ b/media/engine/webrtc_voice_engine_unittest.cc
@@ -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 @@
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) {
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index 8b2e128..60006c4 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -2770,6 +2770,7 @@
"../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 @@
]
absl_deps = [
"//third_party/abseil-cpp/absl/algorithm:container",
+ "//third_party/abseil-cpp/absl/strings",
"//third_party/abseil-cpp/absl/types:optional",
]
}
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index b20e536..767e5a4 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -1106,14 +1106,20 @@
}
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());
}
diff --git a/pc/peer_connection_encodings_integrationtest.cc b/pc/peer_connection_encodings_integrationtest.cc
index 4ebcc0a..5b25e29 100644
--- a/pc/peer_connection_encodings_integrationtest.cc
+++ b/pc/peer_connection_encodings_integrationtest.cc
@@ -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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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();
diff --git a/pc/peer_connection_media_unittest.cc b/pc/peer_connection_media_unittest.cc
index 87e018b..4855419 100644
--- a/pc/peer_connection_media_unittest.cc
+++ b/pc/peer_connection_media_unittest.cc
@@ -1521,7 +1521,7 @@
absl::c_equal(
capabilities_no_rtx, codecs_no_rtx,
[](const webrtc::RtpCodecCapability& capability, const C& codec) {
- return codec.MatchesCapability(capability);
+ return codec.MatchesRtpCodec(capability);
});
}
diff --git a/pc/peer_connection_svc_integrationtest.cc b/pc/peer_connection_svc_integrationtest.cc
index 6b579b1..672f3ee 100644
--- a/pc/peer_connection_svc_integrationtest.cc
+++ b/pc/peer_connection_svc_integrationtest.cc
@@ -139,7 +139,7 @@
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 @@
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 @@
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) {
diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc
index 6c5acac..cdae159 100644
--- a/pc/rtp_sender.cc
+++ b/pc/rtp_sender.cc
@@ -248,7 +248,7 @@
}
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 @@
return;
}
- result = CheckSVCParameters(rtp_parameters);
+ result = CheckCodecParameters(rtp_parameters);
if (!result.ok()) {
webrtc::InvokeSetParametersCallback(callback, result);
return;
@@ -299,7 +299,7 @@
}
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 @@
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 @@
[&] { 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
diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h
index fdeedd5..232f747 100644
--- a/pc/rtp_sender.h
+++ b/pc/rtp_sender.h
@@ -85,8 +85,8 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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,
diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc
index e705b0f..815ec9d 100644
--- a/pc/rtp_transceiver.cc
+++ b/pc/rtp_transceiver.cc
@@ -56,7 +56,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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;
}
diff --git a/pc/test/mock_rtp_sender_internal.h b/pc/test/mock_rtp_sender_internal.h
index 8ed0ede..4cfb2cf 100644
--- a/pc/test/mock_rtp_sender_internal.h
+++ b/pc/test/mock_rtp_sender_internal.h
@@ -64,9 +64,12 @@
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>,
diff --git a/pc/test/peer_connection_test_wrapper.cc b/pc/test/peer_connection_test_wrapper.cc
index 112f04d..1a3dd31 100644
--- a/pc/test/peer_connection_test_wrapper.cc
+++ b/pc/test/peer_connection_test_wrapper.cc
@@ -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 @@
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);
}
diff --git a/pc/test/peer_connection_test_wrapper.h b/pc/test/peer_connection_test_wrapper.h
index cfc423f..751c946 100644
--- a/pc/test/peer_connection_test_wrapper.h
+++ b/pc/test/peer_connection_test_wrapper.h
@@ -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 @@
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.