| /* |
| * Copyright 2025 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <cstdint> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/strings/numbers.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_replace.h" |
| #include "absl/strings/str_split.h" |
| #include "absl/strings/string_view.h" |
| #include "api/audio_codecs/audio_format.h" |
| #include "api/audio_codecs/builtin_audio_decoder_factory.h" |
| #include "api/audio_codecs/builtin_audio_encoder_factory.h" |
| #include "api/create_peerconnection_factory.h" |
| #include "api/field_trials.h" |
| #include "api/jsep.h" |
| #include "api/media_types.h" |
| #include "api/peer_connection_interface.h" |
| #include "api/rtc_error.h" |
| #include "api/rtp_parameters.h" |
| #include "api/rtp_transceiver_direction.h" |
| #include "api/scoped_refptr.h" |
| #include "api/test/rtc_error_matchers.h" |
| #include "api/uma_metrics.h" |
| #include "api/video_codecs/sdp_video_format.h" |
| #include "api/video_codecs/video_decoder_factory_template.h" |
| #include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h" |
| #include "api/video_codecs/video_decoder_factory_template_libvpx_vp8_adapter.h" |
| #include "api/video_codecs/video_decoder_factory_template_libvpx_vp9_adapter.h" |
| #include "api/video_codecs/video_decoder_factory_template_open_h264_adapter.h" |
| #include "api/video_codecs/video_encoder_factory_template.h" |
| #include "api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h" |
| #include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h" |
| #include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h" |
| #include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h" |
| #include "media/base/codec.h" |
| #include "media/base/media_constants.h" |
| #include "media/base/stream_params.h" |
| #include "p2p/base/transport_description.h" |
| #include "pc/peer_connection_wrapper.h" |
| #include "pc/test/fake_audio_capture_module.h" |
| #include "pc/test/fake_rtc_certificate_generator.h" |
| #include "pc/test/integration_test_helpers.h" |
| #include "pc/test/mock_peer_connection_observers.h" |
| #include "rtc_base/strings/string_format.h" |
| #include "rtc_base/thread.h" |
| #include "system_wrappers/include/metrics.h" |
| #include "test/create_test_field_trials.h" |
| #include "test/gmock.h" |
| #include "test/gtest.h" |
| #include "test/wait_until.h" |
| |
| // This file contains unit tests that relate to the behavior of the |
| // SDP munging detector module. |
| // Tests are written as integration tests with PeerConnection, since the |
| // behaviors are still linked so closely that it is hard to test them in |
| // isolation. |
| |
| namespace webrtc { |
| |
| using ::testing::Eq; |
| using ::testing::IsTrue; |
| using ::testing::Pair; |
| |
| namespace { |
| |
| std::unique_ptr<Thread> CreateAndStartThread() { |
| auto thread = Thread::Create(); |
| thread->Start(); |
| return thread; |
| } |
| |
| } // namespace |
| |
| class SdpMungingTest : public ::testing::Test { |
| public: |
| SdpMungingTest() |
| // Note: We use a PeerConnectionFactory with a distinct |
| // signaling thread, so that thread handling can be tested. |
| : signaling_thread_(CreateAndStartThread()), |
| pc_factory_(CreatePeerConnectionFactory( |
| nullptr, |
| nullptr, |
| signaling_thread_.get(), |
| FakeAudioCaptureModule::Create(), |
| CreateBuiltinAudioEncoderFactory(), |
| CreateBuiltinAudioDecoderFactory(), |
| std::make_unique< |
| VideoEncoderFactoryTemplate<LibvpxVp8EncoderTemplateAdapter, |
| LibvpxVp9EncoderTemplateAdapter, |
| OpenH264EncoderTemplateAdapter, |
| LibaomAv1EncoderTemplateAdapter>>(), |
| std::make_unique< |
| VideoDecoderFactoryTemplate<LibvpxVp8DecoderTemplateAdapter, |
| LibvpxVp9DecoderTemplateAdapter, |
| OpenH264DecoderTemplateAdapter, |
| Dav1dDecoderTemplateAdapter>>(), |
| nullptr /* audio_mixer */, |
| nullptr /* audio_processing */, |
| nullptr /* audio_frame_processor */)) { |
| metrics::Reset(); |
| } |
| |
| std::unique_ptr<PeerConnectionWrapper> CreatePeerConnection( |
| absl::string_view field_trials = "") { |
| RTCConfiguration config; |
| config.sdp_semantics = SdpSemantics::kUnifiedPlan; |
| return CreatePeerConnection(config, std::move(field_trials)); |
| } |
| |
| std::unique_ptr<PeerConnectionWrapper> CreatePeerConnection( |
| const RTCConfiguration& config, |
| absl::string_view field_trials) { |
| auto observer = std::make_unique<MockPeerConnectionObserver>(); |
| PeerConnectionDependencies pc_deps(observer.get()); |
| pc_deps.trials = |
| std::make_unique<FieldTrials>(CreateTestFieldTrials(field_trials)); |
| auto result = |
| pc_factory_->CreatePeerConnectionOrError(config, std::move(pc_deps)); |
| EXPECT_TRUE(result.ok()); |
| observer->SetPeerConnectionInterface(result.value().get()); |
| return std::make_unique<PeerConnectionWrapper>( |
| pc_factory_, result.MoveValue(), std::move(observer)); |
| } |
| |
| protected: |
| std::unique_ptr<Thread> signaling_thread_; |
| scoped_refptr<PeerConnectionFactoryInterface> pc_factory_; |
| |
| private: |
| AutoThread main_thread_; |
| }; |
| |
| TEST_F(SdpMungingTest, DISABLED_ReportUMAMetricsWithNoMunging) { |
| auto caller = CreatePeerConnection(); |
| auto callee = CreatePeerConnection(); |
| |
| caller->AddTransceiver(MediaType::AUDIO); |
| caller->AddTransceiver(MediaType::VIDEO); |
| |
| // Negotiate, gather candidates, then exchange ICE candidates. |
| ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kNoModification, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Answer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kNoModification, 1))); |
| |
| EXPECT_THAT(WaitUntil([&] { return caller->IsIceGatheringDone(); }, IsTrue(), |
| {.timeout = kDefaultTimeout}), |
| IsRtcOk()); |
| EXPECT_THAT(WaitUntil([&] { return callee->IsIceGatheringDone(); }, IsTrue(), |
| {.timeout = kDefaultTimeout}), |
| IsRtcOk()); |
| for (const auto& candidate : caller->observer()->GetAllCandidates()) { |
| callee->pc()->AddIceCandidate(candidate); |
| } |
| for (const auto& candidate : callee->observer()->GetAllCandidates()) { |
| caller->pc()->AddIceCandidate(candidate); |
| } |
| EXPECT_THAT( |
| WaitUntil([&] { return caller->pc()->peer_connection_state(); }, |
| Eq(PeerConnectionInterface::PeerConnectionState::kConnected), |
| {.timeout = kDefaultTimeout}), |
| IsRtcOk()); |
| EXPECT_THAT( |
| WaitUntil([&] { return callee->pc()->peer_connection_state(); }, |
| Eq(PeerConnectionInterface::PeerConnectionState::kConnected), |
| {.timeout = kDefaultTimeout}), |
| IsRtcOk()); |
| |
| caller->pc()->Close(); |
| callee->pc()->Close(); |
| |
| EXPECT_THAT( |
| metrics::Samples( |
| "WebRTC.PeerConnection.SdpMunging.Offer.ConnectionEstablished"), |
| ElementsAre(Pair(SdpMungingType::kNoModification, 1))); |
| EXPECT_THAT( |
| metrics::Samples( |
| "WebRTC.PeerConnection.SdpMunging.Answer.ConnectionEstablished"), |
| ElementsAre(Pair(SdpMungingType::kNoModification, 1))); |
| |
| EXPECT_THAT(metrics::Samples( |
| "WebRTC.PeerConnection.SdpMunging.Offer.ConnectionClosed"), |
| ElementsAre(Pair(SdpMungingType::kNoModification, 1))); |
| EXPECT_THAT(metrics::Samples( |
| "WebRTC.PeerConnection.SdpMunging.Answer.ConnectionClosed"), |
| ElementsAre(Pair(SdpMungingType::kNoModification, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, InitialSetLocalDescriptionWithoutCreateOffer) { |
| RTCConfiguration config; |
| config.certificates.push_back( |
| FakeRTCCertificateGenerator::GenerateCertificate()); |
| auto pc = CreatePeerConnection(config, /*field_trials=*/""); |
| std::string sdp = |
| "v=0\r\n" |
| "o=- 0 3 IN IP4 127.0.0.1\r\n" |
| "s=-\r\n" |
| "t=0 0\r\n" |
| "a=fingerprint:sha-1 " |
| "D9:AB:00:AA:12:7B:62:54:CF:AD:3B:55:F7:60:BC:F3:40:A7:0B:5B\r\n" |
| "a=setup:actpass\r\n" |
| "a=ice-ufrag:ETEn\r\n" |
| "a=ice-pwd:OtSK0WpNtpUjkY4+86js7Z/l\r\n"; |
| auto offer = CreateSessionDescription(SdpType::kOffer, sdp); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kWithoutCreateOffer, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, InitialSetLocalDescriptionWithoutCreateAnswer) { |
| RTCConfiguration config; |
| config.certificates.push_back( |
| FakeRTCCertificateGenerator::GenerateCertificate()); |
| auto pc = CreatePeerConnection(config, /*field_trials=*/""); |
| std::string sdp = |
| "v=0\r\n" |
| "o=- 0 3 IN IP4 127.0.0.1\r\n" |
| "s=-\r\n" |
| "t=0 0\r\n" |
| "a=fingerprint:sha-1 " |
| "D9:AB:00:AA:12:7B:62:54:CF:AD:3B:55:F7:60:BC:F3:40:A7:0B:5B\r\n" |
| "a=setup:actpass\r\n" |
| "a=ice-ufrag:ETEn\r\n" |
| "a=ice-pwd:OtSK0WpNtpUjkY4+86js7Z/l\r\n" |
| "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" |
| "c=IN IP4 0.0.0.0\r\n" |
| "a=rtcp-mux\r\n" |
| "a=sendrecv\r\n" |
| "a=mid:0\r\n" |
| "a=rtpmap:111 opus/48000/2\r\n"; |
| auto offer = CreateSessionDescription(SdpType::kOffer, sdp); |
| EXPECT_TRUE(pc->SetRemoteDescription(std::move(offer))); |
| |
| RTCError error; |
| auto answer = CreateSessionDescription(SdpType::kAnswer, sdp); |
| answer->description()->transport_infos()[0].description.connection_role = |
| CONNECTIONROLE_ACTIVE; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(answer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Answer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kWithoutCreateAnswer, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, IceUfrag) { |
| auto pc = CreatePeerConnection("WebRTC-NoSdpMangleUfrag/Enabled/"); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& transport_infos = offer->description()->transport_infos(); |
| ASSERT_EQ(transport_infos.size(), 1u); |
| transport_infos[0].description.ice_ufrag = |
| "amungediceufragthisshouldberejected"; |
| RTCError error; |
| // Ufrag is rejected. |
| EXPECT_FALSE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kIceUfrag, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.SdpOutcome.Rejected"), |
| ElementsAre(Pair(SdpMungingType::kIceUfrag, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Outcome"), |
| ElementsAre(Pair(static_cast<int>(SdpMungingOutcome::kRejected), 1))); |
| } |
| |
| TEST_F(SdpMungingTest, IceUfragCheckDisabledByFieldTrial) { |
| auto pc = CreatePeerConnection("WebRTC-NoSdpMangleUfrag/Disabled/"); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& transport_infos = offer->description()->transport_infos(); |
| ASSERT_EQ(transport_infos.size(), 1u); |
| transport_infos[0].description.ice_ufrag = |
| "amungediceufragthisshouldberejected"; |
| RTCError error; |
| // Ufrag is not rejected. |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kIceUfrag, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.SdpOutcome.Accepted"), |
| ElementsAre(Pair(SdpMungingType::kIceUfrag, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Outcome"), |
| ElementsAre(Pair(static_cast<int>(SdpMungingOutcome::kAccepted), 1))); |
| } |
| |
| TEST_F(SdpMungingTest, IceUfragWithCheckDisabledForTesting) { |
| auto pc = CreatePeerConnection(); |
| pc->GetInternalPeerConnection()->DisableSdpMungingChecksForTesting(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& transport_infos = offer->description()->transport_infos(); |
| ASSERT_EQ(transport_infos.size(), 1u); |
| transport_infos[0].description.ice_ufrag = |
| "amungediceufragthisshouldberejected"; |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kIceUfrag, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, IcePwdCheckDisabledByFieldTrial) { |
| auto pc = CreatePeerConnection("WebRTC-NoSdpMangleUfrag/Disabled/"); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& transport_infos = offer->description()->transport_infos(); |
| ASSERT_EQ(transport_infos.size(), 1u); |
| transport_infos[0].description.ice_pwd = "amungedicepwdthisshouldberejected"; |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kIcePwd, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.SdpOutcome.Accepted"), |
| ElementsAre(Pair(SdpMungingType::kIcePwd, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Outcome"), |
| ElementsAre(Pair(static_cast<int>(SdpMungingOutcome::kAccepted), 1))); |
| } |
| |
| TEST_F(SdpMungingTest, IcePwd) { |
| auto pc = CreatePeerConnection("WebRTC-NoSdpMangleUfrag/Enabled/"); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& transport_infos = offer->description()->transport_infos(); |
| ASSERT_EQ(transport_infos.size(), 1u); |
| transport_infos[0].description.ice_pwd = "amungedicepwdthisshouldberejected"; |
| RTCError error; |
| EXPECT_FALSE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kIcePwd, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.SdpOutcome.Rejected"), |
| ElementsAre(Pair(SdpMungingType::kIcePwd, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Outcome"), |
| ElementsAre(Pair(static_cast<int>(SdpMungingOutcome::kRejected), 1))); |
| } |
| |
| TEST_F(SdpMungingTest, IceUfragRestrictedAddresses) { |
| RTCConfiguration config; |
| config.certificates.push_back( |
| FakeRTCCertificateGenerator::GenerateCertificate()); |
| auto caller = |
| CreatePeerConnection(config, |
| "WebRTC-NoSdpMangleUfragRestrictedAddresses/" |
| "127.0.0.1:12345|127.0.0.*:23456|*:34567/"); |
| auto callee = CreatePeerConnection(); |
| caller->AddAudioTrack("audio_track", {}); |
| auto offer = caller->CreateOffer(); |
| auto& transport_infos = offer->description()->transport_infos(); |
| ASSERT_EQ(transport_infos.size(), 1u); |
| transport_infos[0].description.ice_ufrag = "amungediceufrag"; |
| |
| EXPECT_TRUE(caller->SetLocalDescription(offer->Clone())); |
| EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer))); |
| |
| auto answer = callee->CreateAnswer(); |
| EXPECT_TRUE(callee->SetLocalDescription(answer->Clone())); |
| EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer))); |
| |
| static constexpr const char tmpl[] = |
| "candidate:a0+B/1 1 udp 2130706432 %s typ host"; |
| |
| // Addresses to test. First field is the address in string format, |
| // second field is the expected outcome (success or failure). |
| const std::vector<std::pair<const char*, bool>> address_tests = { |
| {"127.0.0.1:12345", false}, {"127.0.0.2:23456", false}, |
| {"8.8.8.8:34567", false}, {"127.0.0.2:12345", true}, |
| {"127.0.1.1:23456", true}, {"8.8.8.8:3456", true}, |
| }; |
| |
| int num_blocked = 0; |
| for (const auto& address_test : address_tests) { |
| std::optional<RTCError> result; |
| const std::string candidate = StringFormat( |
| tmpl, absl::StrReplaceAll(address_test.first, {{":", " "}}).c_str()); |
| caller->pc()->AddIceCandidate( |
| std::unique_ptr<IceCandidate>( |
| CreateIceCandidate("", 0, candidate, nullptr)), |
| [&result](RTCError error) { result = error; }); |
| |
| ASSERT_THAT( |
| WaitUntil([&] { return result.has_value(); }, ::testing::IsTrue()), |
| IsRtcOk()); |
| if (address_test.second == true) { |
| EXPECT_TRUE(result.value().ok()); |
| } else { |
| std::pair<absl::string_view, absl::string_view> host = |
| absl::StrSplit(address_test.first, ":"); |
| int port; |
| ASSERT_TRUE(absl::SimpleAtoi(host.second, &port)); |
| EXPECT_FALSE(result.value().ok()); |
| EXPECT_EQ(result.value().type(), RTCErrorType::UNSUPPORTED_OPERATION); |
| num_blocked++; |
| EXPECT_THAT( |
| metrics::Samples( |
| "WebRTC.PeerConnection.RestrictedCandidates.SdpMungingType"), |
| ElementsAre(Pair(SdpMungingType::kIceUfrag, num_blocked))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.RestrictedCandidates.Port"), |
| Contains(Pair(port, 1))); |
| } |
| } |
| } |
| |
| TEST_F(SdpMungingTest, IceUfragSdpRejectedAndRestrictedAddresses) { |
| RTCConfiguration config; |
| config.certificates.push_back( |
| FakeRTCCertificateGenerator::GenerateCertificate()); |
| auto caller = |
| CreatePeerConnection(config, |
| "WebRTC-NoSdpMangleUfragRestrictedAddresses/" |
| "127.0.0.1:12345|127.0.0.*:23456|*:34567/" |
| "WebRTC-NoSdpMangleUfrag/Enabled/"); |
| auto callee = CreatePeerConnection(); |
| caller->AddAudioTrack("audio_track", {}); |
| auto offer = caller->CreateOffer(); |
| auto& transport_infos = offer->description()->transport_infos(); |
| ASSERT_EQ(transport_infos.size(), 1u); |
| transport_infos[0].description.ice_ufrag = "amungediceufrag"; |
| |
| EXPECT_FALSE(caller->SetLocalDescription(offer->Clone())); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kIceUfrag, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.SdpOutcome.Rejected"), |
| ElementsAre(Pair(SdpMungingType::kIceUfrag, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Outcome"), |
| ElementsAre(Pair(static_cast<int>(SdpMungingOutcome::kRejected), 1))); |
| } |
| |
| TEST_F(SdpMungingTest, IceMode) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& transport_infos = offer->description()->transport_infos(); |
| ASSERT_EQ(transport_infos.size(), 1u); |
| transport_infos[0].description.ice_mode = ICEMODE_LITE; |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kIceMode, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, IceOptions) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& transport_infos = offer->description()->transport_infos(); |
| ASSERT_EQ(transport_infos.size(), 1u); |
| transport_infos[0].description.transport_options.push_back( |
| "something-unsupported"); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kIceOptions, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, IceOptionsRenomination) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& transport_infos = offer->description()->transport_infos(); |
| ASSERT_EQ(transport_infos.size(), 1u); |
| transport_infos[0].description.transport_options.push_back( |
| ICE_OPTION_RENOMINATION); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kIceOptionsRenomination, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, DtlsRole) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& transport_infos = offer->description()->transport_infos(); |
| ASSERT_EQ(transport_infos.size(), 1u); |
| transport_infos[0].description.connection_role = CONNECTIONROLE_PASSIVE; |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kDtlsSetup, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, RemoveContentDefault) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto name = contents[0].mid(); |
| EXPECT_TRUE(offer->description()->RemoveContentByName(contents[0].mid())); |
| std::string sdp; |
| offer->ToString(&sdp); |
| auto modified_offer = CreateSessionDescription( |
| SdpType::kOffer, |
| absl::StrReplaceAll(sdp, {{"a=group:BUNDLE " + name, "a=group:BUNDLE"}})); |
| |
| RTCError error; |
| EXPECT_FALSE(pc->SetLocalDescription(std::move(modified_offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kNumberOfContents, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.SdpOutcome.Rejected"), |
| ElementsAre(Pair(SdpMungingType::kNumberOfContents, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Outcome"), |
| ElementsAre(Pair(static_cast<int>(SdpMungingOutcome::kRejected), 1))); |
| } |
| |
| TEST_F(SdpMungingTest, RemoveContentKillswitch) { |
| auto pc = |
| CreatePeerConnection("WebRTC-NoSdpMangleNumberOfContents/Disabled/"); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto name = contents[0].mid(); |
| EXPECT_TRUE(offer->description()->RemoveContentByName(contents[0].mid())); |
| std::string sdp; |
| offer->ToString(&sdp); |
| auto modified_offer = CreateSessionDescription( |
| SdpType::kOffer, |
| absl::StrReplaceAll(sdp, {{"a=group:BUNDLE " + name, "a=group:BUNDLE"}})); |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(modified_offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kNumberOfContents, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.SdpOutcome.Accepted"), |
| ElementsAre(Pair(SdpMungingType::kNumberOfContents, 1))); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Outcome"), |
| ElementsAre(Pair(static_cast<int>(SdpMungingOutcome::kAccepted), 1))); |
| } |
| |
| TEST_F(SdpMungingTest, TransceiverDirection) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| auto direction = media_description->direction(); |
| if (direction == RtpTransceiverDirection::kInactive) { |
| media_description->set_direction(RtpTransceiverDirection::kSendRecv); |
| } else { |
| media_description->set_direction(RtpTransceiverDirection::kInactive); |
| } |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kDirection, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, Mid) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| std::string name(contents[0].mid()); |
| contents[0].set_mid("amungedmid"); |
| |
| auto& transport_infos = offer->description()->transport_infos(); |
| ASSERT_EQ(transport_infos.size(), 1u); |
| transport_infos[0].content_name = "amungedmid"; |
| std::string sdp; |
| offer->ToString(&sdp); |
| auto modified_offer = CreateSessionDescription( |
| SdpType::kOffer, |
| absl::StrReplaceAll( |
| sdp, {{"a=group:BUNDLE " + name, "a=group:BUNDLE amungedmid"}})); |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(modified_offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kMid, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, LegacySimulcast) { |
| auto pc = CreatePeerConnection(); |
| pc->AddVideoTrack("video_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| uint32_t ssrc = media_description->first_ssrc(); |
| ASSERT_EQ(media_description->streams().size(), 1u); |
| const std::string& cname = media_description->streams()[0].cname; |
| |
| std::string sdp; |
| offer->ToString(&sdp); |
| sdp += "a=ssrc-group:SIM " + absl::StrCat(ssrc) + " " + |
| absl::StrCat(ssrc + 1) + "\r\n" + // |
| "a=ssrc-group:FID " + absl::StrCat(ssrc + 1) + " " + |
| absl::StrCat(ssrc + 2) + "\r\n" + // |
| "a=ssrc:" + absl::StrCat(ssrc + 1) + " msid:- video_track\r\n" + // |
| "a=ssrc:" + absl::StrCat(ssrc + 1) + " cname:" + cname + "\r\n" + // |
| "a=ssrc:" + absl::StrCat(ssrc + 2) + " msid:- video_track\r\n" + // |
| "a=ssrc:" + absl::StrCat(ssrc + 2) + " cname:" + cname + "\r\n"; |
| auto modified_offer = CreateSessionDescription(SdpType::kOffer, sdp); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(modified_offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kVideoCodecsLegacySimulcast, 1))); |
| } |
| |
| #ifdef WEBRTC_USE_H264 |
| TEST_F(SdpMungingTest, H264SpsPpsIdrInKeyFrame) { |
| auto pc = CreatePeerConnection(); |
| pc->AddVideoTrack("video_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| std::vector<Codec> codecs = media_description->codecs(); |
| for (auto& codec : codecs) { |
| if (codec.name == webrtc::kH264CodecName) { |
| codec.SetParam(webrtc::kH264FmtpSpsPpsIdrInKeyframe, |
| webrtc::kParamValueTrue); |
| } |
| } |
| media_description->set_codecs(codecs); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre( |
| Pair(SdpMungingType::kVideoCodecsFmtpH264SpsPpsIdrInKeyframe, 1))); |
| } |
| #endif // WEBRTC_USE_H264 |
| |
| TEST_F(SdpMungingTest, OpusStereo) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| std::vector<Codec> codecs = media_description->codecs(); |
| for (auto& codec : codecs) { |
| if (codec.name == kOpusCodecName) { |
| codec.SetParam(kCodecParamStereo, kParamValueTrue); |
| } |
| } |
| media_description->set_codecs(codecs); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kAudioCodecsFmtpOpusStereo, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, OpusFec) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| std::vector<Codec> codecs = media_description->codecs(); |
| for (auto& codec : codecs) { |
| if (codec.name == kOpusCodecName) { |
| // Enabled by default so we need to remove the parameter. |
| EXPECT_TRUE(codec.RemoveParam(kCodecParamUseInbandFec)); |
| } |
| } |
| media_description->set_codecs(codecs); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kAudioCodecsFmtpOpusFec, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, OpusDtx) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| std::vector<Codec> codecs = media_description->codecs(); |
| for (auto& codec : codecs) { |
| if (codec.name == kOpusCodecName) { |
| codec.SetParam(kCodecParamUseDtx, kParamValueTrue); |
| } |
| } |
| media_description->set_codecs(codecs); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kAudioCodecsFmtpOpusDtx, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, OpusCbr) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| std::vector<Codec> codecs = media_description->codecs(); |
| for (auto& codec : codecs) { |
| if (codec.name == kOpusCodecName) { |
| codec.SetParam(kCodecParamCbr, kParamValueTrue); |
| } |
| } |
| media_description->set_codecs(codecs); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kAudioCodecsFmtpOpusCbr, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, AudioCodecsRemoved) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| std::vector<Codec> codecs = media_description->codecs(); |
| codecs.pop_back(); |
| media_description->set_codecs(codecs); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kAudioCodecsRemoved, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, AudioCodecsAdded) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| std::vector<Codec> codecs = media_description->codecs(); |
| auto codec = CreateAudioCodec(SdpAudioFormat("pcmu", 8000, 1, {})); |
| codec.id = 19; // IANA reserved payload type, should not conflict. |
| codecs.push_back(codec); |
| media_description->set_codecs(codecs); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kAudioCodecsAdded, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, VideoCodecsRemoved) { |
| auto pc = CreatePeerConnection(); |
| pc->AddVideoTrack("video_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| std::vector<Codec> codecs = media_description->codecs(); |
| codecs.pop_back(); |
| media_description->set_codecs(codecs); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kVideoCodecsRemoved, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, VideoCodecsAdded) { |
| auto pc = CreatePeerConnection(); |
| pc->AddVideoTrack("video_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| std::vector<Codec> codecs = media_description->codecs(); |
| auto codec = CreateVideoCodec(SdpVideoFormat("VP8", {})); |
| codec.id = 19; // IANA reserved payload type, should not conflict. |
| codecs.push_back(codec); |
| media_description->set_codecs(codecs); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kVideoCodecsAdded, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, VideoCodecsAddedWithRawPacketization) { |
| auto pc = CreatePeerConnection(); |
| pc->AddVideoTrack("video_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| std::vector<Codec> codecs = media_description->codecs(); |
| auto codec = CreateVideoCodec(SdpVideoFormat("VP8", {})); |
| codec.id = 19; // IANA reserved payload type, should not conflict. |
| codec.packetization = "raw"; |
| codecs.push_back(codec); |
| media_description->set_codecs(codecs); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre( |
| Pair(SdpMungingType::kVideoCodecsAddedWithRawPacketization, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, VideoCodecsModifiedWithRawPacketization) { |
| auto pc = CreatePeerConnection(); |
| pc->AddVideoTrack("video_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| std::vector<Codec> codecs = media_description->codecs(); |
| ASSERT_TRUE(!codecs.empty()); |
| codecs[0].packetization = "raw"; |
| media_description->set_codecs(codecs); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre( |
| Pair(SdpMungingType::kVideoCodecsModifiedWithRawPacketization, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, MultiOpus) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| std::vector<Codec> codecs = media_description->codecs(); |
| auto multiopus = |
| CreateAudioCodec(SdpAudioFormat("multiopus", 48000, 4, |
| {{"channel_mapping", "0,1,2,3"}, |
| {"coupled_streams", "2"}, |
| {"num_streams", "2"}})); |
| multiopus.id = 19; // IANA reserved payload type, should not conflict. |
| codecs.push_back(multiopus); |
| media_description->set_codecs(codecs); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kAudioCodecsAddedMultiOpus, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, L16) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| std::vector<Codec> codecs = media_description->codecs(); |
| auto l16 = CreateAudioCodec(SdpAudioFormat("L16", 48000, 2, {})); |
| l16.id = 19; // IANA reserved payload type, should not conflict. |
| codecs.push_back(l16); |
| media_description->set_codecs(codecs); |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kAudioCodecsAddedL16, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, AudioSsrc) { |
| // Note: same applies to video but is harder to write since one needs to |
| // modify the ssrc-group too. |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| ASSERT_EQ(media_description->streams().size(), 1u); |
| media_description->mutable_streams()[0].ssrcs[0] = 4404; |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kSsrcs, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, HeaderExtensionAdded) { |
| auto pc = CreatePeerConnection(); |
| pc->AddVideoTrack("video_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| // VLA is off by default, id=42 should be unused. |
| media_description->AddRtpHeaderExtension( |
| {RtpExtension::kVideoLayersAllocationUri, 42}); |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kRtpHeaderExtensionAdded, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, HeaderExtensionRemoved) { |
| auto pc = CreatePeerConnection(); |
| pc->AddVideoTrack("video_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| media_description->ClearRtpHeaderExtensions(); |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kRtpHeaderExtensionRemoved, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, HeaderExtensionModified) { |
| auto pc = CreatePeerConnection(); |
| pc->AddVideoTrack("video_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| auto extensions = media_description->rtp_header_extensions(); |
| ASSERT_GT(extensions.size(), 0u); |
| extensions[0].id = 42; // id=42 should be unused. |
| media_description->set_rtp_header_extensions(extensions); |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kRtpHeaderExtensionModified, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, PayloadTypeChanged) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| auto codecs = media_description->codecs(); |
| ASSERT_GT(codecs.size(), 0u); |
| codecs[0].id = 19; // IANA reserved payload type, should not conflict. |
| media_description->set_codecs(codecs); |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kPayloadTypes, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, AudioCodecsReordered) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| auto codecs = media_description->codecs(); |
| ASSERT_GT(codecs.size(), 1u); |
| std::swap(codecs[0], codecs[1]); |
| media_description->set_codecs(codecs); |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kAudioCodecsReordered, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, VideoCodecsReordered) { |
| auto pc = CreatePeerConnection(); |
| pc->AddVideoTrack("video_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| auto codecs = media_description->codecs(); |
| ASSERT_GT(codecs.size(), 1u); |
| std::swap(codecs[0], codecs[1]); |
| media_description->set_codecs(codecs); |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kVideoCodecsReordered, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, AudioCodecsFmtp) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| auto codecs = media_description->codecs(); |
| ASSERT_GT(codecs.size(), 0u); |
| codecs[0].params["dont"] = "munge"; |
| media_description->set_codecs(codecs); |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kAudioCodecsFmtp, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, VideoCodecsFmtp) { |
| auto pc = CreatePeerConnection(); |
| pc->AddVideoTrack("video_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| auto codecs = media_description->codecs(); |
| ASSERT_GT(codecs.size(), 0u); |
| codecs[0].params["dont"] = "munge"; |
| media_description->set_codecs(codecs); |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kVideoCodecsFmtp, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, AudioCodecsRtcpFb) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| auto codecs = media_description->codecs(); |
| ASSERT_GT(codecs.size(), 0u); |
| codecs[0].feedback_params.Add({"dont", "munge"}); |
| media_description->set_codecs(codecs); |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kAudioCodecsRtcpFb, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, AudioCodecsRtcpFbNack) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| auto codecs = media_description->codecs(); |
| ASSERT_GT(codecs.size(), 0u); |
| codecs[0].feedback_params.Add(FeedbackParam("nack")); |
| media_description->set_codecs(codecs); |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kAudioCodecsRtcpFbAudioNack, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, AudioCodecsRtcpFbRrtr) { |
| auto pc = CreatePeerConnection(); |
| pc->AddAudioTrack("audio_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| auto codecs = media_description->codecs(); |
| ASSERT_GT(codecs.size(), 0u); |
| codecs[0].feedback_params.Add(FeedbackParam("rrtr")); |
| media_description->set_codecs(codecs); |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kAudioCodecsRtcpFbRrtr, 1))); |
| } |
| |
| TEST_F(SdpMungingTest, VideoCodecsRtcpFb) { |
| auto pc = CreatePeerConnection(); |
| pc->AddVideoTrack("video_track", {}); |
| |
| auto offer = pc->CreateOffer(); |
| auto& contents = offer->description()->contents(); |
| ASSERT_EQ(contents.size(), 1u); |
| auto* media_description = contents[0].media_description(); |
| ASSERT_TRUE(media_description); |
| auto codecs = media_description->codecs(); |
| ASSERT_GT(codecs.size(), 0u); |
| codecs[0].feedback_params.Add({"dont", "munge"}); |
| media_description->set_codecs(codecs); |
| |
| RTCError error; |
| EXPECT_TRUE(pc->SetLocalDescription(std::move(offer), &error)); |
| EXPECT_THAT( |
| metrics::Samples("WebRTC.PeerConnection.SdpMunging.Offer.Initial"), |
| ElementsAre(Pair(SdpMungingType::kVideoCodecsRtcpFb, 1))); |
| } |
| |
| } // namespace webrtc |