diff --git a/api/rtp_parameters.h b/api/rtp_parameters.h index e277e7d1ac..91455e2912 100644 --- a/api/rtp_parameters.h +++ b/api/rtp_parameters.h @@ -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 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); diff --git a/media/base/codec.cc b/media/base/codec.cc index 70a8d90e25..7ecf383d9f 100644 --- a/media/base/codec.cc +++ b/media/base/codec.cc @@ -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 && diff --git a/media/base/codec.h b/media/base/codec.h index 74c5cc2add..5595708cfa 100644 --- a/media/base/codec.h +++ b/media/base/codec.h @@ -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; diff --git a/media/base/media_engine.cc b/media/base/media_engine.cc index 0efbd71bf7..7304ab03d7 100644 --- a/media/base/media_engine.cc +++ b/media/base/media_engine.cc @@ -67,34 +67,69 @@ std::vector GetDefaultEnabledRtpHeaderExtensions( webrtc::RTCError CheckScalabilityModeValues( const webrtc::RtpParameters& rtp_parameters, - rtc::ArrayView codecs) { + rtc::ArrayView codec_preferences, + absl::optional 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 codecs) { + rtc::ArrayView codec_preferences, + absl::optional 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 codecs) { + rtc::ArrayView codec_preferences, + absl::optional 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( diff --git a/media/base/media_engine.h b/media/base/media_engine.h index b3d7491165..428123516f 100644 --- a/media/base/media_engine.h +++ b/media/base/media_engine.h @@ -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 codecs); + rtc::ArrayView codec_preferences, + absl::optional send_codec); // Checks the parameters have valid and supported values, and checks parameters // with CheckScalabilityModeValues(). webrtc::RTCError CheckRtpParametersValues( const webrtc::RtpParameters& new_parameters, - rtc::ArrayView codecs); + rtc::ArrayView codec_preferences, + absl::optional 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 codecs); + rtc::ArrayView codec_preferences, + absl::optional 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 f041dce5ef..504aad9002 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -994,9 +994,36 @@ bool WebRtcVideoSendChannel::GetChangedSendParameters( codec.flexfec_payload_type = -1; } + absl::optional 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); } diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc index bf1e3968e3..7719dc293e 100644 --- a/media/engine/webrtc_video_engine_unittest.cc +++ b/media/engine/webrtc_video_engine_unittest.cc @@ -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) { diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc index 25fdbdc811..65363c286e 100644 --- a/media/engine/webrtc_voice_engine.cc +++ b/media/engine/webrtc_voice_engine.cc @@ -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 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 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& codecs) { + const std::vector& codecs, + absl::optional 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 dtmf_codecs; - for (const AudioCodec& codec : codecs) { + std::vector 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 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 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 925d086fde..868c78b30b 100644 --- a/media/engine/webrtc_voice_engine.h +++ b/media/engine/webrtc_voice_engine.h @@ -290,7 +290,8 @@ class WebRtcVoiceSendChannel final : public MediaChannelUtil, private: bool SetOptions(const AudioOptions& options); - bool SetSendCodecs(const std::vector& codecs); + bool SetSendCodecs(const std::vector& codecs, + absl::optional 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 aa3d4266b4..d3902e4db0 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 @@ 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) { diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 8b2e1282f8..60006c4981 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -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", ] } diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index b20e536ac5..767e5a4145 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc @@ -1106,14 +1106,20 @@ PeerConnection::AddTransceiver( } std::vector 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 4ebcc0a17e..5b25e293cd 100644 --- a/pc/peer_connection_encodings_integrationtest.cc +++ b/pc/peer_connection_encodings_integrationtest.cc @@ -11,11 +11,17 @@ #include #include +#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 CreatePc() { auto pc_wrapper = rtc::make_ref_counted( "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 local_pc_wrapper, - rtc::scoped_refptr remote_pc_wrapper, - std::vector init_layers) { + rtc::scoped_refptr remote_pc_wrapper) { // Create and set offer for `local_pc_wrapper`. std::unique_ptr offer = CreateOffer(local_pc_wrapper); @@ -226,6 +231,29 @@ class PeerConnectionEncodingsIntegrationTest : public ::testing::Test { return callback->report(); } + bool IsCodecIdDifferent( + rtc::scoped_refptr pc_wrapper, + size_t index, + const std::string& codec_id) { + return IsCodecIdDifferentWithScalabilityMode(pc_wrapper, index, codec_id, + absl::nullopt); + } + + bool IsCodecIdDifferentWithScalabilityMode( + rtc::scoped_refptr pc_wrapper, + size_t index, + const std::string& codec_id, + absl::optional wanted_scalability_mode) { + rtc::scoped_refptr report = GetStats(pc_wrapper); + std::vector outbound_rtps = + report->GetStatsOfType(); + 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 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 local_pc_wrapper = CreatePc(); + + auto transceiver_or_error = + local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); + rtc::scoped_refptr 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 local_pc_wrapper = CreatePc(); + + auto transceiver_or_error = + local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO); + rtc::scoped_refptr 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 local_pc_wrapper = CreatePc(); + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + rtc::scoped_refptr stream = + local_pc_wrapper->GetUserMedia( + /*audio=*/true, {}, /*video=*/false, {}); + rtc::scoped_refptr track = stream->GetAudioTracks()[0]; + + absl::optional 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 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 report = GetStats(local_pc_wrapper); + std::vector outbound_rtps = + report->GetStatsOfType(); + 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 local_pc_wrapper = CreatePc(); + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + rtc::scoped_refptr stream = + local_pc_wrapper->GetUserMedia( + /*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720}); + rtc::scoped_refptr track = stream->GetVideoTracks()[0]; + + absl::optional 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 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 report = GetStats(local_pc_wrapper); + std::vector outbound_rtps = + report->GetStatsOfType(); + 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 local_pc_wrapper = CreatePc(); + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + rtc::scoped_refptr stream = + local_pc_wrapper->GetUserMedia( + /*audio=*/true, {}, /*video=*/false, {}); + rtc::scoped_refptr track = stream->GetAudioTracks()[0]; + + absl::optional pcmu = + local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO, + "pcmu"); + + auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track); + rtc::scoped_refptr 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 report = GetStats(local_pc_wrapper); + std::vector outbound_rtps = + report->GetStatsOfType(); + 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 local_pc_wrapper = CreatePc(); + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + rtc::scoped_refptr stream = + local_pc_wrapper->GetUserMedia( + /*audio=*/true, {}, /*video=*/false, {}); + rtc::scoped_refptr track = stream->GetAudioTracks()[0]; + + absl::optional pcmu = + local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO, + "pcmu"); + + auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track); + rtc::scoped_refptr audio_transceiver = + transceiver_or_error.MoveValue(); + + NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); + local_pc_wrapper->WaitForConnection(); + remote_pc_wrapper->WaitForConnection(); + + rtc::scoped_refptr report = GetStats(local_pc_wrapper); + std::vector outbound_rtps = + report->GetStatsOfType(); + 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(); + 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 local_pc_wrapper = CreatePc(); + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + rtc::scoped_refptr stream = + local_pc_wrapper->GetUserMedia( + /*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720}); + rtc::scoped_refptr track = stream->GetVideoTracks()[0]; + + absl::optional vp9 = + local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO, + "vp9"); + + auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track); + rtc::scoped_refptr 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 report = GetStats(local_pc_wrapper); + std::vector outbound_rtps = + report->GetStatsOfType(); + 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 local_pc_wrapper = CreatePc(); + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + rtc::scoped_refptr stream = + local_pc_wrapper->GetUserMedia( + /*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720}); + rtc::scoped_refptr track = stream->GetVideoTracks()[0]; + + absl::optional vp9 = + local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO, + "vp9"); + + auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track); + rtc::scoped_refptr video_transceiver = + transceiver_or_error.MoveValue(); + + NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); + local_pc_wrapper->WaitForConnection(); + remote_pc_wrapper->WaitForConnection(); + + rtc::scoped_refptr report = GetStats(local_pc_wrapper); + std::vector outbound_rtps = + report->GetStatsOfType(); + 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(); + 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 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 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 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 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 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 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 local_pc_wrapper = CreatePc(); + + absl::optional opus = + local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO, + "opus"); + ASSERT_TRUE(opus); + + std::vector not_opus_codecs = + local_pc_wrapper->pc_factory() + ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO) + .codecs; + not_opus_codecs.erase( + std::remove_if(not_opus_codecs.begin(), not_opus_codecs.end(), + [&](const auto& codec) { + return absl::EqualsIgnoreCase(codec.name, opus->name); + }), + not_opus_codecs.end()); + + auto transceiver_or_error = + local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); + ASSERT_TRUE(transceiver_or_error.ok()); + rtc::scoped_refptr audio_transceiver = + transceiver_or_error.MoveValue(); + ASSERT_TRUE(audio_transceiver->SetCodecPreferences(not_opus_codecs).ok()); + + 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 local_pc_wrapper = CreatePc(); + + absl::optional vp8 = + local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO, + "vp8"); + ASSERT_TRUE(vp8); + + std::vector not_vp8_codecs = + local_pc_wrapper->pc_factory() + ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO) + .codecs; + not_vp8_codecs.erase( + std::remove_if(not_vp8_codecs.begin(), not_vp8_codecs.end(), + [&](const auto& codec) { + return absl::EqualsIgnoreCase(codec.name, vp8->name); + }), + not_vp8_codecs.end()); + + auto transceiver_or_error = + local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO); + ASSERT_TRUE(transceiver_or_error.ok()); + rtc::scoped_refptr video_transceiver = + transceiver_or_error.MoveValue(); + ASSERT_TRUE(video_transceiver->SetCodecPreferences(not_vp8_codecs).ok()); + + 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 local_pc_wrapper = CreatePc(); + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + absl::optional opus = + local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO, + "opus"); + ASSERT_TRUE(opus); + + std::vector not_opus_codecs = + local_pc_wrapper->pc_factory() + ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO) + .codecs; + not_opus_codecs.erase( + std::remove_if(not_opus_codecs.begin(), not_opus_codecs.end(), + [&](const auto& codec) { + return absl::EqualsIgnoreCase(codec.name, opus->name); + }), + not_opus_codecs.end()); + + auto transceiver_or_error = + local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); + ASSERT_TRUE(transceiver_or_error.ok()); + rtc::scoped_refptr audio_transceiver = + transceiver_or_error.MoveValue(); + ASSERT_TRUE(audio_transceiver->SetCodecPreferences(not_opus_codecs).ok()); + + 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 local_pc_wrapper = CreatePc(); + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + absl::optional vp8 = + local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO, + "vp8"); + ASSERT_TRUE(vp8); + + std::vector not_vp8_codecs = + local_pc_wrapper->pc_factory() + ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO) + .codecs; + not_vp8_codecs.erase( + std::remove_if(not_vp8_codecs.begin(), not_vp8_codecs.end(), + [&](const auto& codec) { + return absl::EqualsIgnoreCase(codec.name, vp8->name); + }), + not_vp8_codecs.end()); + + auto transceiver_or_error = + local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO); + ASSERT_TRUE(transceiver_or_error.ok()); + rtc::scoped_refptr video_transceiver = + transceiver_or_error.MoveValue(); + ASSERT_TRUE(video_transceiver->SetCodecPreferences(not_vp8_codecs).ok()); + + 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 local_pc_wrapper = CreatePc(); + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + absl::optional opus = + local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO, + "opus"); + ASSERT_TRUE(opus); + + std::vector not_opus_codecs = + local_pc_wrapper->pc_factory() + ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO) + .codecs; + not_opus_codecs.erase( + std::remove_if(not_opus_codecs.begin(), not_opus_codecs.end(), + [&](const auto& codec) { + return absl::EqualsIgnoreCase(codec.name, opus->name); + }), + not_opus_codecs.end()); + + 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 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 local_pc_wrapper = CreatePc(); + + absl::optional 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 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 local_pc_wrapper = CreatePc(); + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + absl::optional vp8 = + local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO, + "vp8"); + ASSERT_TRUE(vp8); + + std::vector not_vp8_codecs = + local_pc_wrapper->pc_factory() + ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO) + .codecs; + not_vp8_codecs.erase( + std::remove_if(not_vp8_codecs.begin(), not_vp8_codecs.end(), + [&](const auto& codec) { + return absl::EqualsIgnoreCase(codec.name, vp8->name); + }), + not_vp8_codecs.end()); + + 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 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 local_pc_wrapper = CreatePc(); + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + absl::optional vp8 = + local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO, + "vp8"); + ASSERT_TRUE(vp8); + absl::optional 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(); diff --git a/pc/peer_connection_media_unittest.cc b/pc/peer_connection_media_unittest.cc index 87e018b83e..485541981e 100644 --- a/pc/peer_connection_media_unittest.cc +++ b/pc/peer_connection_media_unittest.cc @@ -1521,7 +1521,7 @@ bool CompareCodecs(const std::vector& 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); }); } diff --git a/pc/peer_connection_svc_integrationtest.cc b/pc/peer_connection_svc_integrationtest.cc index 6b579b10e6..672f3eef99 100644 --- a/pc/peer_connection_svc_integrationtest.cc +++ b/pc/peer_connection_svc_integrationtest.cc @@ -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) { diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc index 6c5acacd7d..cdae1595b3 100644 --- a/pc/rtp_sender.cc +++ b/pc/rtp_sender.cc @@ -248,7 +248,7 @@ void RtpSenderBase::SetParametersInternal(const RtpParameters& parameters, } if (!media_channel_ || !ssrc_) { auto result = cricket::CheckRtpParametersInvalidModificationAndValues( - init_parameters_, parameters, 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 send_codec = media_channel_->GetSendCodec(); + + // Match the currently used codec against the codec preferences to gather + // the SVC capabilities. + absl::optional send_codec_with_svc_info; + if (send_codec && send_codec->type == cricket::Codec::Type::kVideo) { + auto codec_match = + absl::c_find_if(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 send_codec = - video_media_channel()->GetSendCodec(); - - // Match the currently used codec against the codec preferences to gather - // the SVC capabilities. - std::vector 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 fdeedd5e5a..232f7473e9 100644 --- a/pc/rtp_sender.h +++ b/pc/rtp_sender.h @@ -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 codec_preferences) = 0; + virtual void SetCodecPreferences( + std::vector 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 codec_preferences) override { - video_codec_preferences_ = codec_preferences; + void SetCodecPreferences( + std::vector codec_preferences) override { + codec_preferences_ = codec_preferences; } protected: @@ -262,7 +263,7 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { std::vector stream_ids_; RtpParameters init_parameters_; - std::vector video_codec_preferences_; + std::vector codec_preferences_; // TODO(tommi): `media_channel_` and several other member variables in this // class (ssrc_, stopped_, etc) are accessed from more than one thread without @@ -422,8 +423,6 @@ class VideoRtpSender : public RtpSenderBase { rtc::scoped_refptr GetDtmfSender() const override; RTCError GenerateKeyFrame(const std::vector& 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 e705b0f372..815ec9dece 100644 --- a/pc/rtp_transceiver.cc +++ b/pc/rtp_transceiver.cc @@ -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 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 send_codecs = - media_engine()->video().send_codecs(false); - sender->internal()->SetVideoCodecPreferences( - codec_preferences_.empty() - ? send_codecs - : MatchCodecPreferences(codec_preferences_, send_codecs)); - } + + std::vector 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 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 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 8ed0ede21b..4cfb2cfeaf 100644 --- a/pc/test/mock_rtp_sender_internal.h +++ b/pc/test/mock_rtp_sender_internal.h @@ -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), (override)); MOCK_METHOD(rtc::scoped_refptr, diff --git a/pc/test/peer_connection_test_wrapper.cc b/pc/test/peer_connection_test_wrapper.cc index 112f04d66b..1a3dd3109a 100644 --- a/pc/test/peer_connection_test_wrapper.cc +++ b/pc/test/peer_connection_test_wrapper.cc @@ -17,9 +17,11 @@ #include #include +#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 +PeerConnectionTestWrapper::FindFirstSendCodecWithName( + cricket::MediaType media_type, + const std::string& name) const { + std::vector 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 cfc423f81a..751c9462d3 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 @@ class PeerConnectionTestWrapper const std::string& label, const webrtc::DataChannelInit& init); + absl::optional FindFirstSendCodecWithName( + cricket::MediaType media_type, + const std::string& name) const; + void WaitForNegotiation(); // Implements PeerConnectionObserver.