| /* |
| * Copyright 2017 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 <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "api/audio/audio_mixer.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/media_types.h" |
| #include "api/peer_connection_interface.h" |
| #include "api/rtp_transceiver_interface.h" |
| #include "api/scoped_refptr.h" |
| #include "api/video_codecs/builtin_video_decoder_factory.h" |
| #include "api/video_codecs/builtin_video_encoder_factory.h" |
| #include "modules/audio_device/include/audio_device.h" |
| #include "modules/audio_processing/include/audio_processing.h" |
| #include "p2p/base/port_allocator.h" |
| #include "pc/peer_connection_wrapper.h" |
| #include "pc/test/fake_audio_capture_module.h" |
| #include "pc/test/mock_peer_connection_observers.h" |
| #include "rtc_base/rtc_certificate_generator.h" |
| #include "rtc_base/thread.h" |
| #include "system_wrappers/include/metrics.h" |
| #include "test/gtest.h" |
| |
| // This file contains unit tests that relate to the behavior of the |
| // SdpOfferAnswer module. |
| // Tests are writen 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 RTCConfiguration = PeerConnectionInterface::RTCConfiguration; |
| |
| namespace { |
| |
| std::unique_ptr<rtc::Thread> CreateAndStartThread() { |
| auto thread = rtc::Thread::Create(); |
| thread->Start(); |
| return thread; |
| } |
| |
| } // namespace |
| |
| class SdpOfferAnswerTest : public ::testing::Test { |
| public: |
| SdpOfferAnswerTest() |
| // 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(), |
| CreateBuiltinVideoEncoderFactory(), |
| CreateBuiltinVideoDecoderFactory(), |
| nullptr /* audio_mixer */, |
| nullptr /* audio_processing */)) { |
| webrtc::metrics::Reset(); |
| } |
| |
| std::unique_ptr<PeerConnectionWrapper> CreatePeerConnection() { |
| RTCConfiguration config; |
| config.sdp_semantics = SdpSemantics::kUnifiedPlan; |
| return CreatePeerConnection(config); |
| } |
| |
| std::unique_ptr<PeerConnectionWrapper> CreatePeerConnection( |
| const RTCConfiguration& config) { |
| auto observer = std::make_unique<MockPeerConnectionObserver>(); |
| auto result = pc_factory_->CreatePeerConnectionOrError( |
| config, PeerConnectionDependencies(observer.get())); |
| 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<rtc::Thread> signaling_thread_; |
| rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_; |
| |
| private: |
| rtc::AutoThread main_thread_; |
| }; |
| |
| TEST_F(SdpOfferAnswerTest, OnTrackReturnsProxiedObject) { |
| auto caller = CreatePeerConnection(); |
| auto callee = CreatePeerConnection(); |
| |
| auto audio_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); |
| |
| ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); |
| // Verify that caller->observer->OnTrack() has been called with a |
| // proxied transceiver object. |
| ASSERT_EQ(callee->observer()->on_track_transceivers_.size(), 1u); |
| auto transceiver = callee->observer()->on_track_transceivers_[0]; |
| // Since the signaling thread is not the current thread, |
| // this will DCHECK if the transceiver is not proxied. |
| transceiver->stopped(); |
| } |
| |
| TEST_F(SdpOfferAnswerTest, BundleRejectsCodecCollisionsAudioVideo) { |
| auto pc = CreatePeerConnection(); |
| 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=group:BUNDLE 0 1\r\n" |
| "a=fingerprint:sha-1 " |
| "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\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=sendonly\r\n" |
| "a=mid:0\r\n" |
| "a=rtpmap:111 opus/48000/2\r\n" |
| "m=video 9 UDP/TLS/RTP/SAVPF 111\r\n" |
| "c=IN IP4 0.0.0.0\r\n" |
| "a=rtcp-mux\r\n" |
| "a=sendonly\r\n" |
| "a=mid:1\r\n" |
| "a=rtpmap:111 H264/90000\r\n" |
| "a=fmtp:111 " |
| "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" |
| "42e01f\r\n"; |
| |
| auto desc = CreateSessionDescription(SdpType::kOffer, sdp); |
| ASSERT_NE(desc, nullptr); |
| RTCError error; |
| pc->SetRemoteDescription(std::move(desc), &error); |
| EXPECT_TRUE(error.ok()); |
| EXPECT_METRIC_EQ( |
| 1, webrtc::metrics::NumEvents( |
| "WebRTC.PeerConnection.ValidBundledPayloadTypes", false)); |
| } |
| |
| TEST_F(SdpOfferAnswerTest, BundleRejectsCodecCollisionsVideoFmtp) { |
| auto pc = CreatePeerConnection(); |
| 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=group:BUNDLE 0 1\r\n" |
| "a=fingerprint:sha-1 " |
| "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\r\n" |
| "a=setup:actpass\r\n" |
| "a=ice-ufrag:ETEn\r\n" |
| "a=ice-pwd:OtSK0WpNtpUjkY4+86js7Z/l\r\n" |
| "m=video 9 UDP/TLS/RTP/SAVPF 111\r\n" |
| "c=IN IP4 0.0.0.0\r\n" |
| "a=rtcp-mux\r\n" |
| "a=sendonly\r\n" |
| "a=mid:0\r\n" |
| "a=rtpmap:111 H264/90000\r\n" |
| "a=fmtp:111 " |
| "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" |
| "42e01f\r\n" |
| "m=video 9 UDP/TLS/RTP/SAVPF 111\r\n" |
| "c=IN IP4 0.0.0.0\r\n" |
| "a=rtcp-mux\r\n" |
| "a=sendonly\r\n" |
| "a=mid:1\r\n" |
| "a=rtpmap:111 H264/90000\r\n" |
| "a=fmtp:111 " |
| "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" |
| "42e01f\r\n"; |
| |
| auto desc = CreateSessionDescription(SdpType::kOffer, sdp); |
| ASSERT_NE(desc, nullptr); |
| RTCError error; |
| pc->SetRemoteDescription(std::move(desc), &error); |
| EXPECT_TRUE(error.ok()); |
| EXPECT_METRIC_EQ( |
| 1, webrtc::metrics::NumEvents( |
| "WebRTC.PeerConnection.ValidBundledPayloadTypes", false)); |
| } |
| |
| TEST_F(SdpOfferAnswerTest, BundleCodecCollisionInDifferentBundlesAllowed) { |
| auto pc = CreatePeerConnection(); |
| 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=group:BUNDLE 0\r\n" |
| "a=group:BUNDLE 1\r\n" |
| "a=fingerprint:sha-1 " |
| "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\r\n" |
| "a=setup:actpass\r\n" |
| "a=ice-ufrag:ETEn\r\n" |
| "a=ice-pwd:OtSK0WpNtpUjkY4+86js7Z/l\r\n" |
| "m=video 9 UDP/TLS/RTP/SAVPF 111\r\n" |
| "c=IN IP4 0.0.0.0\r\n" |
| "a=rtcp-mux\r\n" |
| "a=sendonly\r\n" |
| "a=mid:0\r\n" |
| "a=rtpmap:111 H264/90000\r\n" |
| "a=fmtp:111 " |
| "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" |
| "42e01f\r\n" |
| "m=video 9 UDP/TLS/RTP/SAVPF 111\r\n" |
| "c=IN IP4 0.0.0.0\r\n" |
| "a=rtcp-mux\r\n" |
| "a=sendonly\r\n" |
| "a=mid:1\r\n" |
| "a=rtpmap:111 H264/90000\r\n" |
| "a=fmtp:111 " |
| "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" |
| "42e01f\r\n"; |
| |
| auto desc = CreateSessionDescription(SdpType::kOffer, sdp); |
| ASSERT_NE(desc, nullptr); |
| RTCError error; |
| pc->SetRemoteDescription(std::move(desc), &error); |
| EXPECT_TRUE(error.ok()); |
| EXPECT_METRIC_EQ( |
| 0, webrtc::metrics::NumEvents( |
| "WebRTC.PeerConnection.ValidBundledPayloadTypes", false)); |
| } |
| |
| TEST_F(SdpOfferAnswerTest, BundleMeasuresHeaderExtensionIdCollision) { |
| auto pc = CreatePeerConnection(); |
| 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=group:BUNDLE 0 1\r\n" |
| "a=fingerprint:sha-1 " |
| "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\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=sendonly\r\n" |
| "a=mid:0\r\n" |
| "a=rtpmap:111 opus/48000/2\r\n" |
| "a=extmap:3 " |
| "http://www.ietf.org/id/" |
| "draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" |
| "m=video 9 UDP/TLS/RTP/SAVPF 112\r\n" |
| "c=IN IP4 0.0.0.0\r\n" |
| "a=rtcp-mux\r\n" |
| "a=sendonly\r\n" |
| "a=mid:1\r\n" |
| "a=rtpmap:112 VP8/90000\r\n" |
| "a=extmap:3 " |
| "http://www.ietf.org/id/" |
| "draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n"; |
| auto desc = CreateSessionDescription(SdpType::kOffer, sdp); |
| ASSERT_NE(desc, nullptr); |
| RTCError error; |
| pc->SetRemoteDescription(std::move(desc), &error); |
| EXPECT_TRUE(error.ok()); |
| EXPECT_METRIC_EQ(1, |
| webrtc::metrics::NumEvents( |
| "WebRTC.PeerConnection.ValidBundledExtensionIds", true)); |
| } |
| |
| // extmap:3 is used with two different URIs which is not allowed. |
| TEST_F(SdpOfferAnswerTest, BundleRejectsHeaderExtensionIdCollision) { |
| auto pc = CreatePeerConnection(); |
| 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=group:BUNDLE 0 1\r\n" |
| "a=fingerprint:sha-1 " |
| "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\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=sendonly\r\n" |
| "a=mid:0\r\n" |
| "a=rtpmap:111 opus/48000/2\r\n" |
| "a=extmap:3 " |
| "http://www.ietf.org/id/" |
| "draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" |
| "m=video 9 UDP/TLS/RTP/SAVPF 112\r\n" |
| "c=IN IP4 0.0.0.0\r\n" |
| "a=rtcp-mux\r\n" |
| "a=sendonly\r\n" |
| "a=mid:1\r\n" |
| "a=rtpmap:112 VP8/90000\r\n" |
| "a=extmap:3 urn:3gpp:video-orientation\r\n"; |
| auto desc = CreateSessionDescription(SdpType::kOffer, sdp); |
| ASSERT_NE(desc, nullptr); |
| RTCError error; |
| pc->SetRemoteDescription(std::move(desc), &error); |
| EXPECT_TRUE(error.ok()); |
| EXPECT_METRIC_EQ( |
| 1, webrtc::metrics::NumEvents( |
| "WebRTC.PeerConnection.ValidBundledExtensionIds", false)); |
| } |
| |
| // transport-wide cc is negotiated with two different ids 3 and 4. |
| // This is not a good idea but tolerable. |
| TEST_F(SdpOfferAnswerTest, BundleAcceptsDifferentIdsForSameExtension) { |
| auto pc = CreatePeerConnection(); |
| 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=group:BUNDLE 0 1\r\n" |
| "a=fingerprint:sha-1 " |
| "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\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=sendonly\r\n" |
| "a=mid:0\r\n" |
| "a=rtpmap:111 opus/48000/2\r\n" |
| "a=extmap:3 " |
| "http://www.ietf.org/id/" |
| "draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" |
| "m=video 9 UDP/TLS/RTP/SAVPF 112\r\n" |
| "c=IN IP4 0.0.0.0\r\n" |
| "a=rtcp-mux\r\n" |
| "a=sendonly\r\n" |
| "a=mid:1\r\n" |
| "a=rtpmap:112 VP8/90000\r\n" |
| "a=extmap:4 " |
| "http://www.ietf.org/id/" |
| "draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n"; |
| auto desc = CreateSessionDescription(SdpType::kOffer, sdp); |
| ASSERT_NE(desc, nullptr); |
| RTCError error; |
| pc->SetRemoteDescription(std::move(desc), &error); |
| EXPECT_TRUE(error.ok()); |
| EXPECT_METRIC_EQ(1, |
| webrtc::metrics::NumEvents( |
| "WebRTC.PeerConnection.ValidBundledExtensionIds", true)); |
| } |
| |
| TEST_F(SdpOfferAnswerTest, LargeMidsAreRejected) { |
| auto pc = CreatePeerConnection(); |
| 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 " |
| "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\r\n" |
| "a=setup:actpass\r\n" |
| "a=ice-ufrag:ETEn\r\n" |
| "a=ice-pwd:OtSK0WpNtpUjkY4+86js7Z/l\r\n" |
| "m=video 9 UDP/TLS/RTP/SAVPF 111\r\n" |
| "c=IN IP4 0.0.0.0\r\n" |
| "a=rtcp-mux\r\n" |
| "a=sendonly\r\n" |
| "a=rtpmap:111 VP8/90000\r\n" |
| "a=mid:01234567890123456\r\n"; |
| auto desc = CreateSessionDescription(SdpType::kOffer, sdp); |
| ASSERT_NE(desc, nullptr); |
| RTCError error; |
| pc->SetRemoteDescription(std::move(desc), &error); |
| EXPECT_FALSE(error.ok()); |
| EXPECT_EQ(error.type(), RTCErrorType::INVALID_PARAMETER); |
| } |
| |
| } // namespace webrtc |