diff --git a/pc/media_session.cc b/pc/media_session.cc index 71a6d0a19c..5d87a4f721 100644 --- a/pc/media_session.cc +++ b/pc/media_session.cc @@ -645,6 +645,16 @@ static bool IsFlexfecCodec(const C& codec) { return absl::EqualsIgnoreCase(codec.name, kFlexfecCodecName); } +template +static bool IsUlpfecCodec(const C& codec) { + return absl::EqualsIgnoreCase(codec.name, kUlpfecCodecName); +} + +template +static bool IsComfortNoiseCodec(const C& codec) { + return absl::EqualsIgnoreCase(codec.name, kComfortNoiseCodecName); +} + // Create a media content to be offered for the given `sender_options`, // according to the given options.rtcp_mux, session_options.is_muc, codecs, // secure_transport, crypto, and current_streams. If we don't currently have @@ -1347,8 +1357,7 @@ static void NegotiateRtpHeaderExtensions( static void StripCNCodecs(AudioCodecs* audio_codecs) { audio_codecs->erase(std::remove_if(audio_codecs->begin(), audio_codecs->end(), [](const AudioCodec& codec) { - return absl::EqualsIgnoreCase( - codec.name, kComfortNoiseCodecName); + return IsComfortNoiseCodec(codec); }), audio_codecs->end()); } @@ -2646,6 +2655,13 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer( StripCNCodecs(&filtered_codecs); } + // Determine if we have media codecs in common. + bool has_common_media_codecs = + std::find_if(filtered_codecs.begin(), filtered_codecs.end(), + [](const AudioCodec& c) { + return !(IsRedCodec(c) || IsComfortNoiseCodec(c)); + }) != filtered_codecs.end(); + bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) && session_options.bundle_enabled; auto audio_answer = std::make_unique(); @@ -2670,7 +2686,7 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer( bool secure = bundle_transport ? bundle_transport->description.secure() : audio_transport->secure(); bool rejected = media_description_options.stopped || - offer_content->rejected || + offer_content->rejected || !has_common_media_codecs || !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO, audio_answer->protocol(), secure); if (!AddTransportAnswer(media_description_options.mid, @@ -2766,6 +2782,13 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer( filtered_codecs = ComputeCodecsUnion( filtered_codecs, other_video_codecs, field_trials); } + // Determine if we have media codecs in common. + bool has_common_media_codecs = + std::find_if( + filtered_codecs.begin(), filtered_codecs.end(), + [](const VideoCodec& c) { + return !(IsRedCodec(c) || IsUlpfecCodec(c) || IsFlexfecCodec(c)); + }) != filtered_codecs.end(); if (session_options.raw_packetization_for_video) { for (VideoCodec& codec : filtered_codecs) { @@ -2793,12 +2816,12 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer( filtered_rtp_header_extensions(default_video_rtp_header_extensions), ssrc_generator_, enable_encrypted_rtp_header_extensions_, current_streams, bundle_enabled, video_answer.get())) { - return false; // Failed the sessin setup. + return false; // Failed the session setup. } bool secure = bundle_transport ? bundle_transport->description.secure() : video_transport->secure(); bool rejected = media_description_options.stopped || - offer_content->rejected || + offer_content->rejected || !has_common_media_codecs || !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO, video_answer->protocol(), secure); if (!AddTransportAnswer(media_description_options.mid, diff --git a/pc/media_session_unittest.cc b/pc/media_session_unittest.cc index 3ca6c8580f..8f38f6a1b4 100644 --- a/pc/media_session_unittest.cc +++ b/pc/media_session_unittest.cc @@ -1305,6 +1305,27 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswerGcm) { EXPECT_EQ(cricket::kMediaProtocolSavpf, acd->protocol()); } +// Create an audio answer with no common codecs, and ensure it is rejected. +TEST_F(MediaSessionDescriptionFactoryTest, + TestCreateAudioAnswerWithNoCommonCodecs) { + MediaSessionOptions opts; + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + std::vector f1_codecs = {AudioCodec(96, "opus", 48000, -1, 1)}; + f1_.set_audio_codecs(f1_codecs, f1_codecs); + + std::vector f2_codecs = {AudioCodec(0, "PCMU", 8000, -1, 1)}; + f2_.set_audio_codecs(f2_codecs, f2_codecs); + + std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); + std::unique_ptr answer = + f2_.CreateAnswer(offer.get(), opts, NULL); + const ContentInfo* ac = answer->GetContentByName("audio"); + ASSERT_TRUE(ac != NULL); + EXPECT_TRUE(ac->rejected); +} + // Create a typical video answer, and ensure it matches what we expect. TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswer) { MediaSessionOptions opts; @@ -1355,6 +1376,51 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswerGcmAnswer) { TestVideoGcmCipher(false, true); } +// Create a video answer with no common codecs, and ensure it is rejected. +TEST_F(MediaSessionDescriptionFactoryTest, + TestCreateVideoAnswerWithNoCommonCodecs) { + MediaSessionOptions opts; + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + std::vector f1_codecs = {VideoCodec(96, "H264")}; + f1_.set_video_codecs(f1_codecs, f1_codecs); + + std::vector f2_codecs = {VideoCodec(97, "VP8")}; + f2_.set_video_codecs(f2_codecs, f2_codecs); + + std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); + std::unique_ptr answer = + f2_.CreateAnswer(offer.get(), opts, NULL); + const ContentInfo* vc = answer->GetContentByName("video"); + ASSERT_TRUE(vc != NULL); + EXPECT_TRUE(vc->rejected); +} + +// Create a video answer with no common codecs (but a common FEC codec), and +// ensure it is rejected. +TEST_F(MediaSessionDescriptionFactoryTest, + TestCreateVideoAnswerWithOnlyFecCodecsCommon) { + MediaSessionOptions opts; + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + std::vector f1_codecs = {VideoCodec(96, "H264"), + VideoCodec(118, "flexfec-03")}; + f1_.set_video_codecs(f1_codecs, f1_codecs); + + std::vector f2_codecs = {VideoCodec(97, "VP8"), + VideoCodec(118, "flexfec-03")}; + f2_.set_video_codecs(f2_codecs, f2_codecs); + + std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); + std::unique_ptr answer = + f2_.CreateAnswer(offer.get(), opts, NULL); + const ContentInfo* vc = answer->GetContentByName("video"); + ASSERT_TRUE(vc != NULL); + EXPECT_TRUE(vc->rejected); +} + // The use_sctpmap flag should be set in an Sctp DataContentDescription by // default. The answer's use_sctpmap flag should match the offer's. TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataAnswerUsesSctpmap) {