michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // Original author: ekr@rtfm.com michael@0: michael@0: #include michael@0: michael@0: #include "sigslot.h" michael@0: michael@0: #include "logging.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsXPCOM.h" michael@0: #include "nss.h" michael@0: #include "ssl.h" michael@0: #include "sslproto.h" michael@0: michael@0: #include "dtlsidentity.h" michael@0: #include "mozilla/RefPtr.h" michael@0: #include "FakeMediaStreams.h" michael@0: #include "FakeMediaStreamsImpl.h" michael@0: #include "MediaConduitErrors.h" michael@0: #include "MediaConduitInterface.h" michael@0: #include "MediaPipeline.h" michael@0: #include "MediaPipelineFilter.h" michael@0: #include "runnable_utils.h" michael@0: #include "transportflow.h" michael@0: #include "transportlayerloopback.h" michael@0: #include "transportlayerdtls.h" michael@0: #include "mozilla/SyncRunnable.h" michael@0: michael@0: michael@0: #include "mtransport_test_utils.h" michael@0: #include "runnable_utils.h" michael@0: michael@0: #include "webrtc/modules/interface/module_common_types.h" michael@0: michael@0: #define GTEST_HAS_RTTI 0 michael@0: #include "gtest/gtest.h" michael@0: #include "gtest_utils.h" michael@0: michael@0: using namespace mozilla; michael@0: MOZ_MTLOG_MODULE("mediapipeline") michael@0: michael@0: MtransportTestUtils *test_utils; michael@0: michael@0: namespace { michael@0: michael@0: class TransportInfo { michael@0: public: michael@0: TransportInfo() : michael@0: flow_(nullptr), michael@0: loopback_(nullptr), michael@0: dtls_(nullptr) {} michael@0: michael@0: static void InitAndConnect(TransportInfo &client, TransportInfo &server) { michael@0: client.Init(true); michael@0: server.Init(false); michael@0: client.PushLayers(); michael@0: server.PushLayers(); michael@0: client.Connect(&server); michael@0: server.Connect(&client); michael@0: } michael@0: michael@0: void Init(bool client) { michael@0: nsresult res; michael@0: michael@0: flow_ = new TransportFlow(); michael@0: loopback_ = new TransportLayerLoopback(); michael@0: dtls_ = new TransportLayerDtls(); michael@0: michael@0: res = loopback_->Init(); michael@0: if (res != NS_OK) { michael@0: FreeLayers(); michael@0: } michael@0: ASSERT_EQ((nsresult)NS_OK, res); michael@0: michael@0: std::vector ciphers; michael@0: ciphers.push_back(SRTP_AES128_CM_HMAC_SHA1_80); michael@0: dtls_->SetSrtpCiphers(ciphers); michael@0: dtls_->SetIdentity(DtlsIdentity::Generate()); michael@0: dtls_->SetRole(client ? TransportLayerDtls::CLIENT : michael@0: TransportLayerDtls::SERVER); michael@0: dtls_->SetVerificationAllowAll(); michael@0: } michael@0: michael@0: void PushLayers() { michael@0: nsresult res; michael@0: michael@0: nsAutoPtr > layers( michael@0: new std::queue); michael@0: layers->push(loopback_); michael@0: layers->push(dtls_); michael@0: res = flow_->PushLayers(layers); michael@0: if (res != NS_OK) { michael@0: FreeLayers(); michael@0: } michael@0: ASSERT_EQ((nsresult)NS_OK, res); michael@0: } michael@0: michael@0: void Connect(TransportInfo* peer) { michael@0: MOZ_ASSERT(loopback_); michael@0: MOZ_ASSERT(peer->loopback_); michael@0: michael@0: loopback_->Connect(peer->loopback_); michael@0: } michael@0: michael@0: // Free the memory allocated at the beginning of Init michael@0: // if failure occurs before layers setup. michael@0: void FreeLayers() { michael@0: delete loopback_; michael@0: loopback_ = nullptr; michael@0: delete dtls_; michael@0: dtls_ = nullptr; michael@0: } michael@0: michael@0: void Shutdown() { michael@0: if (loopback_) { michael@0: loopback_->Disconnect(); michael@0: } michael@0: loopback_ = nullptr; michael@0: dtls_ = nullptr; michael@0: flow_ = nullptr; michael@0: } michael@0: michael@0: mozilla::RefPtr flow_; michael@0: TransportLayerLoopback *loopback_; michael@0: TransportLayerDtls *dtls_; michael@0: }; michael@0: michael@0: class TestAgent { michael@0: public: michael@0: TestAgent() : michael@0: audio_config_(109, "opus", 48000, 960, 2, 64000), michael@0: audio_conduit_(mozilla::AudioSessionConduit::Create(nullptr)), michael@0: audio_(), michael@0: audio_pipeline_() { michael@0: } michael@0: michael@0: static void ConnectRtp(TestAgent *client, TestAgent *server) { michael@0: TransportInfo::InitAndConnect(client->audio_rtp_transport_, michael@0: server->audio_rtp_transport_); michael@0: } michael@0: michael@0: static void ConnectRtcp(TestAgent *client, TestAgent *server) { michael@0: TransportInfo::InitAndConnect(client->audio_rtcp_transport_, michael@0: server->audio_rtcp_transport_); michael@0: } michael@0: michael@0: static void ConnectBundle(TestAgent *client, TestAgent *server) { michael@0: TransportInfo::InitAndConnect(client->bundle_transport_, michael@0: server->bundle_transport_); michael@0: } michael@0: michael@0: virtual void CreatePipelines_s(bool aIsRtcpMux) = 0; michael@0: michael@0: void Start() { michael@0: nsresult ret; michael@0: michael@0: MOZ_MTLOG(ML_DEBUG, "Starting"); michael@0: michael@0: mozilla::SyncRunnable::DispatchToThread( michael@0: test_utils->sts_target(), michael@0: WrapRunnableRet(audio_->GetStream(), &Fake_MediaStream::Start, &ret)); michael@0: michael@0: ASSERT_TRUE(NS_SUCCEEDED(ret)); michael@0: } michael@0: michael@0: void StopInt() { michael@0: audio_->GetStream()->Stop(); michael@0: } michael@0: michael@0: void Stop() { michael@0: MOZ_MTLOG(ML_DEBUG, "Stopping"); michael@0: michael@0: if (audio_pipeline_) michael@0: audio_pipeline_->ShutdownMedia_m(); michael@0: michael@0: mozilla::SyncRunnable::DispatchToThread( michael@0: test_utils->sts_target(), michael@0: WrapRunnable(this, &TestAgent::StopInt)); michael@0: } michael@0: michael@0: void Shutdown_s() { michael@0: audio_rtp_transport_.Shutdown(); michael@0: audio_rtcp_transport_.Shutdown(); michael@0: bundle_transport_.Shutdown(); michael@0: if (audio_pipeline_) michael@0: audio_pipeline_->ShutdownTransport_s(); michael@0: } michael@0: michael@0: void Shutdown() { michael@0: if (audio_pipeline_) michael@0: audio_pipeline_->ShutdownMedia_m(); michael@0: michael@0: mozilla::SyncRunnable::DispatchToThread( michael@0: test_utils->sts_target(), michael@0: WrapRunnable(this, &TestAgent::Shutdown_s)); michael@0: } michael@0: michael@0: uint32_t GetRemoteSSRC() { michael@0: uint32_t res = 0; michael@0: audio_conduit_->GetRemoteSSRC(&res); michael@0: return res; michael@0: } michael@0: michael@0: uint32_t GetLocalSSRC() { michael@0: uint32_t res = 0; michael@0: audio_conduit_->GetLocalSSRC(&res); michael@0: return res; michael@0: } michael@0: michael@0: int GetAudioRtpCountSent() { michael@0: return audio_pipeline_->rtp_packets_sent(); michael@0: } michael@0: michael@0: int GetAudioRtpCountReceived() { michael@0: return audio_pipeline_->rtp_packets_received(); michael@0: } michael@0: michael@0: int GetAudioRtcpCountSent() { michael@0: return audio_pipeline_->rtcp_packets_sent(); michael@0: } michael@0: michael@0: int GetAudioRtcpCountReceived() { michael@0: return audio_pipeline_->rtcp_packets_received(); michael@0: } michael@0: michael@0: protected: michael@0: mozilla::AudioCodecConfig audio_config_; michael@0: mozilla::RefPtr audio_conduit_; michael@0: nsRefPtr audio_; michael@0: // TODO(bcampen@mozilla.com): Right now this does not let us test RTCP in michael@0: // both directions; only the sender's RTCP is sent, but the receiver should michael@0: // be sending it too. michael@0: mozilla::RefPtr audio_pipeline_; michael@0: TransportInfo audio_rtp_transport_; michael@0: TransportInfo audio_rtcp_transport_; michael@0: TransportInfo bundle_transport_; michael@0: }; michael@0: michael@0: class TestAgentSend : public TestAgent { michael@0: public: michael@0: TestAgentSend() : use_bundle_(false) {} michael@0: michael@0: virtual void CreatePipelines_s(bool aIsRtcpMux) { michael@0: audio_ = new Fake_DOMMediaStream(new Fake_AudioStreamSource()); michael@0: michael@0: mozilla::MediaConduitErrorCode err = michael@0: static_cast(audio_conduit_.get())-> michael@0: ConfigureSendMediaCodec(&audio_config_); michael@0: EXPECT_EQ(mozilla::kMediaConduitNoError, err); michael@0: michael@0: std::string test_pc("PC"); michael@0: michael@0: if (aIsRtcpMux) { michael@0: ASSERT_FALSE(audio_rtcp_transport_.flow_); michael@0: } michael@0: michael@0: RefPtr rtp(audio_rtp_transport_.flow_); michael@0: RefPtr rtcp(audio_rtcp_transport_.flow_); michael@0: michael@0: if (use_bundle_) { michael@0: rtp = bundle_transport_.flow_; michael@0: rtcp = nullptr; michael@0: } michael@0: michael@0: audio_pipeline_ = new mozilla::MediaPipelineTransmit( michael@0: test_pc, michael@0: nullptr, michael@0: test_utils->sts_target(), michael@0: audio_, michael@0: 1, michael@0: 1, michael@0: audio_conduit_, michael@0: rtp, michael@0: rtcp); michael@0: michael@0: audio_pipeline_->Init(); michael@0: } michael@0: michael@0: void SetUsingBundle(bool use_bundle) { michael@0: use_bundle_ = use_bundle; michael@0: } michael@0: michael@0: private: michael@0: bool use_bundle_; michael@0: }; michael@0: michael@0: michael@0: class TestAgentReceive : public TestAgent { michael@0: public: michael@0: virtual void CreatePipelines_s(bool aIsRtcpMux) { michael@0: mozilla::SourceMediaStream *audio = new Fake_SourceMediaStream(); michael@0: audio->SetPullEnabled(true); michael@0: michael@0: mozilla::AudioSegment* segment= new mozilla::AudioSegment(); michael@0: audio->AddTrack(0, 100, 0, segment); michael@0: audio->AdvanceKnownTracksTime(mozilla::STREAM_TIME_MAX); michael@0: michael@0: audio_ = new Fake_DOMMediaStream(audio); michael@0: michael@0: std::vector codecs; michael@0: codecs.push_back(&audio_config_); michael@0: michael@0: mozilla::MediaConduitErrorCode err = michael@0: static_cast(audio_conduit_.get())-> michael@0: ConfigureRecvMediaCodecs(codecs); michael@0: EXPECT_EQ(mozilla::kMediaConduitNoError, err); michael@0: michael@0: std::string test_pc("PC"); michael@0: michael@0: if (aIsRtcpMux) { michael@0: ASSERT_FALSE(audio_rtcp_transport_.flow_); michael@0: } michael@0: michael@0: // For now, assume bundle always uses rtcp mux michael@0: RefPtr dummy; michael@0: RefPtr bundle_transport; michael@0: if (bundle_filter_) { michael@0: bundle_transport = bundle_transport_.flow_; michael@0: bundle_filter_->AddLocalSSRC(GetLocalSSRC()); michael@0: } michael@0: michael@0: audio_pipeline_ = new mozilla::MediaPipelineReceiveAudio( michael@0: test_pc, michael@0: nullptr, michael@0: test_utils->sts_target(), michael@0: audio_->GetStream(), 1, 1, michael@0: static_cast(audio_conduit_.get()), michael@0: audio_rtp_transport_.flow_, michael@0: audio_rtcp_transport_.flow_, michael@0: bundle_transport, michael@0: dummy, michael@0: bundle_filter_); michael@0: michael@0: audio_pipeline_->Init(); michael@0: } michael@0: michael@0: void SetBundleFilter(nsAutoPtr filter) { michael@0: bundle_filter_ = filter; michael@0: } michael@0: michael@0: void SetUsingBundle_s(bool decision) { michael@0: audio_pipeline_->SetUsingBundle_s(decision); michael@0: } michael@0: michael@0: void UpdateFilterFromRemoteDescription_s( michael@0: nsAutoPtr filter) { michael@0: audio_pipeline_->UpdateFilterFromRemoteDescription_s(filter); michael@0: } michael@0: michael@0: private: michael@0: nsAutoPtr bundle_filter_; michael@0: }; michael@0: michael@0: michael@0: class MediaPipelineTest : public ::testing::Test { michael@0: public: michael@0: ~MediaPipelineTest() { michael@0: p1_.Stop(); michael@0: p2_.Stop(); michael@0: p1_.Shutdown(); michael@0: p2_.Shutdown(); michael@0: } michael@0: michael@0: // Setup transport. michael@0: void InitTransports(bool aIsRtcpMux) { michael@0: // RTP, p1_ is server, p2_ is client michael@0: mozilla::SyncRunnable::DispatchToThread( michael@0: test_utils->sts_target(), michael@0: WrapRunnableNM(&TestAgent::ConnectRtp, &p2_, &p1_)); michael@0: michael@0: // Create RTCP flows separately if we are not muxing them. michael@0: if(!aIsRtcpMux) { michael@0: // RTCP, p1_ is server, p2_ is client michael@0: mozilla::SyncRunnable::DispatchToThread( michael@0: test_utils->sts_target(), michael@0: WrapRunnableNM(&TestAgent::ConnectRtcp, &p2_, &p1_)); michael@0: } michael@0: michael@0: // BUNDLE, p1_ is server, p2_ is client michael@0: mozilla::SyncRunnable::DispatchToThread( michael@0: test_utils->sts_target(), michael@0: WrapRunnableNM(&TestAgent::ConnectBundle, &p2_, &p1_)); michael@0: } michael@0: michael@0: // Verify RTP and RTCP michael@0: void TestAudioSend(bool aIsRtcpMux, michael@0: bool bundle = false, michael@0: nsAutoPtr localFilter = michael@0: nsAutoPtr(nullptr), michael@0: nsAutoPtr remoteFilter = michael@0: nsAutoPtr(nullptr), michael@0: unsigned int ms_until_answer = 500, michael@0: unsigned int ms_of_traffic_after_answer = 10000) { michael@0: michael@0: // We do not support testing bundle without rtcp mux, since that doesn't michael@0: // make any sense. michael@0: ASSERT_FALSE(!aIsRtcpMux && bundle); michael@0: michael@0: p1_.SetUsingBundle(bundle); michael@0: p2_.SetBundleFilter(localFilter); michael@0: michael@0: // Setup transport flows michael@0: InitTransports(aIsRtcpMux); michael@0: michael@0: mozilla::SyncRunnable::DispatchToThread( michael@0: test_utils->sts_target(), michael@0: WrapRunnable(&p1_, &TestAgent::CreatePipelines_s, aIsRtcpMux)); michael@0: michael@0: mozilla::SyncRunnable::DispatchToThread( michael@0: test_utils->sts_target(), michael@0: WrapRunnable(&p2_, &TestAgent::CreatePipelines_s, aIsRtcpMux)); michael@0: michael@0: p2_.Start(); michael@0: p1_.Start(); michael@0: michael@0: // Simulate pre-answer traffic michael@0: PR_Sleep(ms_until_answer); michael@0: michael@0: mozilla::SyncRunnable::DispatchToThread( michael@0: test_utils->sts_target(), michael@0: WrapRunnable(&p2_, &TestAgentReceive::SetUsingBundle_s, bundle)); michael@0: michael@0: if (bundle) { michael@0: // Leaving remoteFilter not set implies we want to test sunny-day michael@0: if (!remoteFilter) { michael@0: remoteFilter = new MediaPipelineFilter; michael@0: // Might not be safe, strictly speaking. michael@0: remoteFilter->AddRemoteSSRC(p1_.GetLocalSSRC()); michael@0: } michael@0: michael@0: mozilla::SyncRunnable::DispatchToThread( michael@0: test_utils->sts_target(), michael@0: WrapRunnable(&p2_, michael@0: &TestAgentReceive::UpdateFilterFromRemoteDescription_s, michael@0: remoteFilter)); michael@0: } michael@0: michael@0: michael@0: // wait for some RTP/RTCP tx and rx to happen michael@0: PR_Sleep(ms_of_traffic_after_answer); michael@0: michael@0: p1_.Stop(); michael@0: p2_.Stop(); michael@0: michael@0: // wait for any packets in flight to arrive michael@0: PR_Sleep(100); michael@0: michael@0: p1_.Shutdown(); michael@0: p2_.Shutdown(); michael@0: michael@0: if (!bundle) { michael@0: // If we are doing bundle, allow the test-case to do this checking. michael@0: ASSERT_GE(p1_.GetAudioRtpCountSent(), 40); michael@0: ASSERT_EQ(p1_.GetAudioRtpCountReceived(), p2_.GetAudioRtpCountSent()); michael@0: ASSERT_EQ(p1_.GetAudioRtpCountSent(), p2_.GetAudioRtpCountReceived()); michael@0: michael@0: // Calling ShutdownMedia_m on both pipelines does not stop the flow of michael@0: // RTCP. So, we might be off by one here. michael@0: ASSERT_LE(p2_.GetAudioRtcpCountReceived(), p1_.GetAudioRtcpCountSent()); michael@0: ASSERT_GE(p2_.GetAudioRtcpCountReceived() + 1, p1_.GetAudioRtcpCountSent()); michael@0: } michael@0: michael@0: } michael@0: michael@0: void TestAudioReceiverOffersBundle(bool bundle_accepted, michael@0: nsAutoPtr localFilter, michael@0: nsAutoPtr remoteFilter = michael@0: nsAutoPtr(nullptr), michael@0: unsigned int ms_until_answer = 500, michael@0: unsigned int ms_of_traffic_after_answer = 10000) { michael@0: TestAudioSend(true, michael@0: bundle_accepted, michael@0: localFilter, michael@0: remoteFilter, michael@0: ms_until_answer, michael@0: ms_of_traffic_after_answer); michael@0: } michael@0: protected: michael@0: TestAgentSend p1_; michael@0: TestAgentReceive p2_; michael@0: }; michael@0: michael@0: class MediaPipelineFilterTest : public ::testing::Test { michael@0: public: michael@0: bool Filter(MediaPipelineFilter& filter, michael@0: int32_t correlator, michael@0: uint32_t ssrc, michael@0: uint8_t payload_type) { michael@0: michael@0: webrtc::RTPHeader header; michael@0: header.ssrc = ssrc; michael@0: header.payloadType = payload_type; michael@0: return filter.Filter(header, correlator); michael@0: } michael@0: }; michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestConstruct) { michael@0: MediaPipelineFilter filter; michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestDefault) { michael@0: MediaPipelineFilter filter; michael@0: ASSERT_FALSE(Filter(filter, 0, 233, 110)); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestSSRCFilter) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddRemoteSSRC(555); michael@0: ASSERT_TRUE(Filter(filter, 0, 555, 110)); michael@0: ASSERT_FALSE(Filter(filter, 0, 556, 110)); michael@0: } michael@0: michael@0: #define SSRC(ssrc) \ michael@0: ((ssrc >> 24) & 0xFF), \ michael@0: ((ssrc >> 16) & 0xFF), \ michael@0: ((ssrc >> 8 ) & 0xFF), \ michael@0: (ssrc & 0xFF) michael@0: michael@0: #define REPORT_FRAGMENT(ssrc) \ michael@0: SSRC(ssrc), \ michael@0: 0,0,0,0, \ michael@0: 0,0,0,0, \ michael@0: 0,0,0,0, \ michael@0: 0,0,0,0, \ michael@0: 0,0,0,0 michael@0: michael@0: #define RTCP_TYPEINFO(num_rrs, type, size) \ michael@0: 0x80 + num_rrs, type, 0, size michael@0: michael@0: const unsigned char rtcp_rr_s16[] = { michael@0: // zero rrs, size 1 words michael@0: RTCP_TYPEINFO(0, MediaPipelineFilter::RECEIVER_REPORT_T, 1), michael@0: SSRC(16) michael@0: }; michael@0: michael@0: const unsigned char rtcp_rr_s16_r17[] = { michael@0: // one rr, 7 words michael@0: RTCP_TYPEINFO(1, MediaPipelineFilter::RECEIVER_REPORT_T, 7), michael@0: SSRC(16), michael@0: REPORT_FRAGMENT(17) michael@0: }; michael@0: michael@0: const unsigned char rtcp_rr_s16_r17_18[] = { michael@0: // two rrs, size 13 words michael@0: RTCP_TYPEINFO(2, MediaPipelineFilter::RECEIVER_REPORT_T, 13), michael@0: SSRC(16), michael@0: REPORT_FRAGMENT(17), michael@0: REPORT_FRAGMENT(18) michael@0: }; michael@0: michael@0: const unsigned char rtcp_sr_s16[] = { michael@0: // zero rrs, size 6 words michael@0: RTCP_TYPEINFO(0, MediaPipelineFilter::SENDER_REPORT_T, 6), michael@0: REPORT_FRAGMENT(16) michael@0: }; michael@0: michael@0: const unsigned char rtcp_sr_s16_r17[] = { michael@0: // one rr, size 12 words michael@0: RTCP_TYPEINFO(1, MediaPipelineFilter::SENDER_REPORT_T, 12), michael@0: REPORT_FRAGMENT(16), michael@0: REPORT_FRAGMENT(17) michael@0: }; michael@0: michael@0: const unsigned char rtcp_sr_s16_r17_18[] = { michael@0: // two rrs, size 18 words michael@0: RTCP_TYPEINFO(2, MediaPipelineFilter::SENDER_REPORT_T, 18), michael@0: REPORT_FRAGMENT(16), michael@0: REPORT_FRAGMENT(17), michael@0: REPORT_FRAGMENT(18) michael@0: }; michael@0: michael@0: const unsigned char unknown_type[] = { michael@0: RTCP_TYPEINFO(1, 222, 0) michael@0: }; michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestEmptyFilterReport0) { michael@0: MediaPipelineFilter filter; michael@0: ASSERT_EQ(MediaPipelineFilter::FAIL, michael@0: filter.FilterRTCP(rtcp_sr_s16, sizeof(rtcp_sr_s16))); michael@0: ASSERT_EQ(MediaPipelineFilter::FAIL, michael@0: filter.FilterRTCP(rtcp_rr_s16, sizeof(rtcp_rr_s16))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterReport0) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddRemoteSSRC(16); michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rtcp_sr_s16, sizeof(rtcp_sr_s16))); michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rtcp_rr_s16, sizeof(rtcp_rr_s16))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterReport0SSRCTruncated) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddRemoteSSRC(16); michael@0: const unsigned char data[] = { michael@0: RTCP_TYPEINFO(0, MediaPipelineFilter::RECEIVER_REPORT_T, 1), michael@0: 0,0,0 michael@0: }; michael@0: ASSERT_EQ(MediaPipelineFilter::FAIL, michael@0: filter.FilterRTCP(data, sizeof(data))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterReport0PTTruncated) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddRemoteSSRC(16); michael@0: const unsigned char data[] = {0x80}; michael@0: ASSERT_EQ(MediaPipelineFilter::FAIL, michael@0: filter.FilterRTCP(data, sizeof(data))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterReport0CountTruncated) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddRemoteSSRC(16); michael@0: const unsigned char data[] = {}; michael@0: ASSERT_EQ(MediaPipelineFilter::FAIL, michael@0: filter.FilterRTCP(data, sizeof(data))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterReport1BothMatch) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddRemoteSSRC(16); michael@0: filter.AddLocalSSRC(17); michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rtcp_sr_s16_r17, sizeof(rtcp_sr_s16_r17))); michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rtcp_rr_s16_r17, sizeof(rtcp_rr_s16_r17))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterReport1SSRCTruncated) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddRemoteSSRC(16); michael@0: filter.AddLocalSSRC(17); michael@0: const unsigned char rr[] = { michael@0: RTCP_TYPEINFO(1, MediaPipelineFilter::RECEIVER_REPORT_T, 7), michael@0: SSRC(16), michael@0: 0,0,0 michael@0: }; michael@0: ASSERT_EQ(MediaPipelineFilter::FAIL, michael@0: filter.FilterRTCP(rr, sizeof(rr))); michael@0: const unsigned char sr[] = { michael@0: RTCP_TYPEINFO(1, MediaPipelineFilter::RECEIVER_REPORT_T, 12), michael@0: REPORT_FRAGMENT(16), michael@0: 0,0,0 michael@0: }; michael@0: ASSERT_EQ(MediaPipelineFilter::FAIL, michael@0: filter.FilterRTCP(sr, sizeof(rr))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterReport1BigSSRC) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddRemoteSSRC(0x01020304); michael@0: filter.AddLocalSSRC(0x11121314); michael@0: const unsigned char rr[] = { michael@0: RTCP_TYPEINFO(1, MediaPipelineFilter::RECEIVER_REPORT_T, 7), michael@0: SSRC(0x01020304), michael@0: REPORT_FRAGMENT(0x11121314) michael@0: }; michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rr, sizeof(rr))); michael@0: const unsigned char sr[] = { michael@0: RTCP_TYPEINFO(1, MediaPipelineFilter::RECEIVER_REPORT_T, 12), michael@0: SSRC(0x01020304), michael@0: REPORT_FRAGMENT(0x11121314) michael@0: }; michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(sr, sizeof(rr))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterReport1LocalMatch) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddLocalSSRC(17); michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rtcp_sr_s16_r17, sizeof(rtcp_sr_s16_r17))); michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rtcp_rr_s16_r17, sizeof(rtcp_rr_s16_r17))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterReport1Inconsistent) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddRemoteSSRC(16); michael@0: // We assume that the filter is exactly correct in terms of local ssrcs. michael@0: // So, when RTCP shows up with a remote SSRC that matches, and a local michael@0: // ssrc that doesn't, we assume the other end has messed up and put ssrcs michael@0: // from more than one m-line in the packet. michael@0: ASSERT_EQ(MediaPipelineFilter::FAIL, michael@0: filter.FilterRTCP(rtcp_sr_s16_r17, sizeof(rtcp_sr_s16_r17))); michael@0: ASSERT_EQ(MediaPipelineFilter::FAIL, michael@0: filter.FilterRTCP(rtcp_rr_s16_r17, sizeof(rtcp_rr_s16_r17))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterReport1NeitherMatch) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddRemoteSSRC(17); michael@0: filter.AddLocalSSRC(18); michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rtcp_sr_s16_r17, sizeof(rtcp_sr_s16_r17))); michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rtcp_rr_s16_r17, sizeof(rtcp_rr_s16_r17))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterReport2AllMatch) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddRemoteSSRC(16); michael@0: filter.AddLocalSSRC(17); michael@0: filter.AddLocalSSRC(18); michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rtcp_sr_s16_r17_18, michael@0: sizeof(rtcp_sr_s16_r17_18))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterReport2LocalMatch) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddLocalSSRC(17); michael@0: filter.AddLocalSSRC(18); michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rtcp_sr_s16_r17_18, michael@0: sizeof(rtcp_sr_s16_r17_18))); michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rtcp_rr_s16_r17_18, michael@0: sizeof(rtcp_rr_s16_r17_18))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterReport2Inconsistent101) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddRemoteSSRC(16); michael@0: filter.AddLocalSSRC(18); michael@0: ASSERT_EQ(MediaPipelineFilter::FAIL, michael@0: filter.FilterRTCP(rtcp_sr_s16_r17_18, michael@0: sizeof(rtcp_sr_s16_r17_18))); michael@0: ASSERT_EQ(MediaPipelineFilter::FAIL, michael@0: filter.FilterRTCP(rtcp_rr_s16_r17_18, michael@0: sizeof(rtcp_rr_s16_r17_18))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterReport2Inconsistent001) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddLocalSSRC(18); michael@0: ASSERT_EQ(MediaPipelineFilter::FAIL, michael@0: filter.FilterRTCP(rtcp_sr_s16_r17_18, michael@0: sizeof(rtcp_sr_s16_r17_18))); michael@0: ASSERT_EQ(MediaPipelineFilter::FAIL, michael@0: filter.FilterRTCP(rtcp_rr_s16_r17_18, michael@0: sizeof(rtcp_rr_s16_r17_18))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestFilterUnknownRTCPType) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddLocalSSRC(18); michael@0: ASSERT_EQ(MediaPipelineFilter::UNSUPPORTED, michael@0: filter.FilterRTCP(unknown_type, sizeof(unknown_type))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestCorrelatorFilter) { michael@0: MediaPipelineFilter filter; michael@0: filter.SetCorrelator(7777); michael@0: ASSERT_TRUE(Filter(filter, 7777, 16, 110)); michael@0: ASSERT_FALSE(Filter(filter, 7778, 17, 110)); michael@0: // This should also have resulted in the SSRC 16 being added to the filter michael@0: ASSERT_TRUE(Filter(filter, 0, 16, 110)); michael@0: ASSERT_FALSE(Filter(filter, 0, 17, 110)); michael@0: michael@0: // rtcp_sr_s16 has 16 as an SSRC michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rtcp_sr_s16, sizeof(rtcp_sr_s16))); michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rtcp_rr_s16, sizeof(rtcp_rr_s16))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestPayloadTypeFilter) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddUniquePT(110); michael@0: ASSERT_TRUE(Filter(filter, 0, 555, 110)); michael@0: ASSERT_FALSE(Filter(filter, 0, 556, 111)); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestPayloadTypeFilterSSRCUpdate) { michael@0: MediaPipelineFilter filter; michael@0: filter.AddUniquePT(110); michael@0: ASSERT_TRUE(Filter(filter, 0, 16, 110)); michael@0: michael@0: // rtcp_sr_s16 has 16 as an SSRC michael@0: ASSERT_EQ(MediaPipelineFilter::PASS, michael@0: filter.FilterRTCP(rtcp_sr_s16, sizeof(rtcp_sr_s16))); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestAnswerAddsSSRCs) { michael@0: MediaPipelineFilter filter; michael@0: filter.SetCorrelator(7777); michael@0: ASSERT_TRUE(Filter(filter, 7777, 555, 110)); michael@0: ASSERT_FALSE(Filter(filter, 7778, 556, 110)); michael@0: // This should also have resulted in the SSRC 555 being added to the filter michael@0: ASSERT_TRUE(Filter(filter, 0, 555, 110)); michael@0: ASSERT_FALSE(Filter(filter, 0, 556, 110)); michael@0: michael@0: // This sort of thing can happen when getting an answer with SSRC attrs michael@0: // The answer will not contain the correlator. michael@0: MediaPipelineFilter filter2; michael@0: filter2.AddRemoteSSRC(555); michael@0: filter2.AddRemoteSSRC(556); michael@0: filter2.AddRemoteSSRC(557); michael@0: michael@0: filter.IncorporateRemoteDescription(filter2); michael@0: michael@0: // Ensure that the old SSRC still works. michael@0: ASSERT_TRUE(Filter(filter, 0, 555, 110)); michael@0: michael@0: // Ensure that the new SSRCs work. michael@0: ASSERT_TRUE(Filter(filter, 0, 556, 110)); michael@0: ASSERT_TRUE(Filter(filter, 0, 557, 110)); michael@0: michael@0: // Ensure that the correlator continues to work michael@0: ASSERT_TRUE(Filter(filter, 7777, 558, 110)); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestSSRCMovedWithSDP) { michael@0: MediaPipelineFilter filter; michael@0: filter.SetCorrelator(7777); michael@0: filter.AddUniquePT(111); michael@0: ASSERT_TRUE(Filter(filter, 7777, 555, 110)); michael@0: michael@0: MediaPipelineFilter filter2; michael@0: filter2.AddRemoteSSRC(556); michael@0: michael@0: filter.IncorporateRemoteDescription(filter2); michael@0: michael@0: // Ensure that the old SSRC has been removed. michael@0: ASSERT_FALSE(Filter(filter, 0, 555, 110)); michael@0: michael@0: // Ensure that the new SSRC works. michael@0: ASSERT_TRUE(Filter(filter, 0, 556, 110)); michael@0: michael@0: // Ensure that the correlator continues to work michael@0: ASSERT_TRUE(Filter(filter, 7777, 558, 110)); michael@0: michael@0: // Ensure that the payload type mapping continues to work michael@0: ASSERT_TRUE(Filter(filter, 0, 559, 111)); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestSSRCMovedWithCorrelator) { michael@0: MediaPipelineFilter filter; michael@0: filter.SetCorrelator(7777); michael@0: ASSERT_TRUE(Filter(filter, 7777, 555, 110)); michael@0: ASSERT_TRUE(Filter(filter, 0, 555, 110)); michael@0: ASSERT_FALSE(Filter(filter, 7778, 555, 110)); michael@0: ASSERT_FALSE(Filter(filter, 0, 555, 110)); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineFilterTest, TestRemoteSDPNoSSRCs) { michael@0: // If the remote SDP doesn't have SSRCs, right now this is a no-op and michael@0: // there is no point of even incorporating a filter, but we make the michael@0: // behavior consistent to avoid confusion. michael@0: MediaPipelineFilter filter; michael@0: filter.SetCorrelator(7777); michael@0: filter.AddUniquePT(111); michael@0: ASSERT_TRUE(Filter(filter, 7777, 555, 110)); michael@0: michael@0: MediaPipelineFilter filter2; michael@0: michael@0: filter.IncorporateRemoteDescription(filter2); michael@0: michael@0: // Ensure that the old SSRC still works. michael@0: ASSERT_TRUE(Filter(filter, 7777, 555, 110)); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineTest, TestAudioSendNoMux) { michael@0: TestAudioSend(false); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineTest, TestAudioSendMux) { michael@0: TestAudioSend(true); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineTest, TestAudioSendBundleOfferedAndDeclined) { michael@0: nsAutoPtr filter(new MediaPipelineFilter); michael@0: TestAudioReceiverOffersBundle(false, filter); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineTest, TestAudioSendBundleOfferedAndAccepted) { michael@0: nsAutoPtr filter(new MediaPipelineFilter); michael@0: // These durations have to be _extremely_ long to have any assurance that michael@0: // some RTCP will be sent at all. This is because the first RTCP packet michael@0: // is sometimes sent before the transports are ready, which causes it to michael@0: // be dropped. michael@0: TestAudioReceiverOffersBundle(true, michael@0: filter, michael@0: // We do not specify the filter for the remote description, so it will be michael@0: // set to something sane after a short time. michael@0: nsAutoPtr(), michael@0: 10000, michael@0: 10000); michael@0: michael@0: // Some packets should have been dropped, but not all michael@0: ASSERT_GT(p1_.GetAudioRtpCountSent(), p2_.GetAudioRtpCountReceived()); michael@0: ASSERT_GT(p2_.GetAudioRtpCountReceived(), 40); michael@0: ASSERT_GT(p1_.GetAudioRtcpCountSent(), 1); michael@0: ASSERT_GT(p1_.GetAudioRtcpCountSent(), p2_.GetAudioRtcpCountReceived()); michael@0: ASSERT_GT(p2_.GetAudioRtcpCountReceived(), 0); michael@0: } michael@0: michael@0: TEST_F(MediaPipelineTest, TestAudioSendBundleOfferedAndAcceptedEmptyFilter) { michael@0: nsAutoPtr filter(new MediaPipelineFilter); michael@0: nsAutoPtr bad_answer_filter(new MediaPipelineFilter); michael@0: TestAudioReceiverOffersBundle(true, filter, bad_answer_filter); michael@0: // Filter is empty, so should drop everything. michael@0: ASSERT_EQ(0, p2_.GetAudioRtpCountReceived()); michael@0: ASSERT_EQ(0, p2_.GetAudioRtcpCountReceived()); michael@0: } michael@0: michael@0: } // end namespace michael@0: michael@0: michael@0: int main(int argc, char **argv) { michael@0: test_utils = new MtransportTestUtils(); michael@0: // Start the tests michael@0: NSS_NoDB_Init(nullptr); michael@0: NSS_SetDomesticPolicy(); michael@0: ::testing::InitGoogleTest(&argc, argv); michael@0: michael@0: int rv = RUN_ALL_TESTS(); michael@0: delete test_utils; michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: