/src/mozilla-central/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
3 | | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | // Original author: ekr@rtfm.com |
6 | | |
7 | | #include <iostream> |
8 | | |
9 | | #include "logging.h" |
10 | | #include "nss.h" |
11 | | |
12 | | #include "AudioSegment.h" |
13 | | #include "AudioStreamTrack.h" |
14 | | #include "DOMMediaStream.h" |
15 | | #include "mozilla/Mutex.h" |
16 | | #include "mozilla/RefPtr.h" |
17 | | #include "MediaPipeline.h" |
18 | | #include "MediaPipelineFilter.h" |
19 | | #include "MediaStreamGraph.h" |
20 | | #include "MediaStreamListener.h" |
21 | | #include "MediaStreamTrack.h" |
22 | | #include "transportflow.h" |
23 | | #include "transportlayerloopback.h" |
24 | | #include "transportlayerdtls.h" |
25 | | #include "transportlayersrtp.h" |
26 | | #include "mozilla/SyncRunnable.h" |
27 | | #include "mtransport_test_utils.h" |
28 | | #include "SharedBuffer.h" |
29 | | |
30 | | #define GTEST_HAS_RTTI 0 |
31 | | #include "gtest/gtest.h" |
32 | | |
33 | | using namespace mozilla; |
34 | | MOZ_MTLOG_MODULE("mediapipeline") |
35 | | |
36 | | static MtransportTestUtils *test_utils; |
37 | | |
38 | | namespace { |
39 | | |
40 | | class FakeSourceMediaStream : public mozilla::SourceMediaStream { |
41 | | |
42 | | public: |
43 | | |
44 | | FakeSourceMediaStream() |
45 | | : SourceMediaStream() |
46 | 0 | { |
47 | 0 | } |
48 | | |
49 | | virtual ~FakeSourceMediaStream() override |
50 | 0 | { |
51 | 0 | mMainThreadDestroyed = true; |
52 | 0 | } |
53 | | |
54 | | virtual bool AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegment *aRawSegment = nullptr) override |
55 | 0 | { |
56 | 0 | return true; |
57 | 0 | } |
58 | | }; |
59 | | |
60 | | class FakeMediaStreamTrackSource : public mozilla::dom::MediaStreamTrackSource { |
61 | | |
62 | | public: |
63 | | |
64 | | FakeMediaStreamTrackSource() |
65 | | : MediaStreamTrackSource(nullptr, nsString()) |
66 | 0 | { |
67 | 0 | } |
68 | | |
69 | | virtual mozilla::dom::MediaSourceEnum GetMediaSource() const override |
70 | 0 | { |
71 | 0 | return mozilla::dom::MediaSourceEnum::Microphone; |
72 | 0 | } |
73 | | |
74 | | |
75 | | virtual void Disable() override |
76 | 0 | { |
77 | 0 | } |
78 | | |
79 | | virtual void Enable() override |
80 | 0 | { |
81 | 0 | } |
82 | | |
83 | | virtual void Stop() override |
84 | 0 | { |
85 | 0 | } |
86 | | |
87 | | }; |
88 | | |
89 | | class FakeAudioStreamTrack : public mozilla::dom::AudioStreamTrack { |
90 | | |
91 | | public: |
92 | | |
93 | | FakeAudioStreamTrack() |
94 | | : AudioStreamTrack(new DOMMediaStream(nullptr, nullptr), 0, 1, |
95 | | new FakeMediaStreamTrackSource()) |
96 | | , mMutex("Fake AudioStreamTrack") |
97 | | , mStop(false) |
98 | | , mCount(0) |
99 | 0 | { |
100 | 0 | NS_NewTimerWithFuncCallback(getter_AddRefs(mTimer), |
101 | 0 | FakeAudioStreamTrackGenerateData, this, 20, |
102 | 0 | nsITimer::TYPE_REPEATING_SLACK, |
103 | 0 | "FakeAudioStreamTrack::FakeAudioStreamTrackGenerateData", |
104 | 0 | test_utils->sts_target()); |
105 | 0 |
|
106 | 0 | } |
107 | | |
108 | | void Stop() |
109 | 0 | { |
110 | 0 | mozilla::MutexAutoLock lock(mMutex); |
111 | 0 | mStop = true; |
112 | 0 | mTimer->Cancel(); |
113 | 0 | } |
114 | | |
115 | | virtual void AddListener(MediaStreamTrackListener* aListener) override |
116 | 0 | { |
117 | 0 | mozilla::MutexAutoLock lock(mMutex); |
118 | 0 | mListeners.push_back(aListener); |
119 | 0 | } |
120 | | |
121 | | virtual already_AddRefed<mozilla::dom::MediaStreamTrack> CloneInternal(DOMMediaStream* aOwningStream, TrackID aTrackID) override |
122 | 0 | { |
123 | 0 | return RefPtr<MediaStreamTrack>(new FakeAudioStreamTrack).forget(); |
124 | 0 | } |
125 | | |
126 | | private: |
127 | | std::vector<MediaStreamTrackListener*> mListeners; |
128 | | mozilla::Mutex mMutex; |
129 | | bool mStop; |
130 | | nsCOMPtr<nsITimer> mTimer; |
131 | | int mCount; |
132 | | |
133 | | static void FakeAudioStreamTrackGenerateData(nsITimer* timer, void* closure) |
134 | 0 | { |
135 | 0 | auto mst = static_cast<FakeAudioStreamTrack*>(closure); |
136 | 0 | const int AUDIO_BUFFER_SIZE = 1600; |
137 | 0 | const int NUM_CHANNELS = 2; |
138 | 0 |
|
139 | 0 | mozilla::MutexAutoLock lock(mst->mMutex); |
140 | 0 | if (mst->mStop) { |
141 | 0 | return; |
142 | 0 | } |
143 | 0 | |
144 | 0 | RefPtr<mozilla::SharedBuffer> samples = |
145 | 0 | mozilla::SharedBuffer::Create(AUDIO_BUFFER_SIZE * NUM_CHANNELS * sizeof(int16_t)); |
146 | 0 | int16_t* data = reinterpret_cast<int16_t *>(samples->Data()); |
147 | 0 | for(int i=0; i<(AUDIO_BUFFER_SIZE * NUM_CHANNELS); i++) { |
148 | 0 | //saw tooth audio sample |
149 | 0 | data[i] = ((mst->mCount % 8) * 4000) - (7*4000)/2; |
150 | 0 | mst->mCount++; |
151 | 0 | } |
152 | 0 |
|
153 | 0 | mozilla::AudioSegment segment; |
154 | 0 | AutoTArray<const int16_t *,1> channels; |
155 | 0 | channels.AppendElement(data); |
156 | 0 | segment.AppendFrames(samples.forget(), |
157 | 0 | channels, |
158 | 0 | AUDIO_BUFFER_SIZE, |
159 | 0 | PRINCIPAL_HANDLE_NONE); |
160 | 0 |
|
161 | 0 | for (auto& listener: mst->mListeners) { |
162 | 0 | listener->NotifyQueuedChanges(nullptr, 0, segment); |
163 | 0 | } |
164 | 0 | } |
165 | | }; |
166 | | |
167 | | class TransportInfo { |
168 | | public: |
169 | | TransportInfo() : |
170 | | flow_(nullptr), |
171 | 0 | loopback_(nullptr) {} |
172 | | |
173 | 0 | static void InitAndConnect(TransportInfo &client, TransportInfo &server) { |
174 | 0 | client.Init(true); |
175 | 0 | server.Init(false); |
176 | 0 | client.Connect(&server); |
177 | 0 | server.Connect(&client); |
178 | 0 | } |
179 | | |
180 | 0 | void Init(bool client) { |
181 | 0 | UniquePtr<TransportLayerLoopback> loopback(new TransportLayerLoopback); |
182 | 0 | UniquePtr<TransportLayerDtls> dtls(new TransportLayerDtls); |
183 | 0 | UniquePtr<TransportLayerSrtp> srtp(new TransportLayerSrtp(*dtls)); |
184 | 0 |
|
185 | 0 | std::vector<uint16_t> ciphers; |
186 | 0 | ciphers.push_back(kDtlsSrtpAeadAes256Gcm); |
187 | 0 | dtls->SetSrtpCiphers(ciphers); |
188 | 0 | dtls->SetIdentity(DtlsIdentity::Generate()); |
189 | 0 | dtls->SetRole(client ? TransportLayerDtls::CLIENT : |
190 | 0 | TransportLayerDtls::SERVER); |
191 | 0 | dtls->SetVerificationAllowAll(); |
192 | 0 |
|
193 | 0 | ASSERT_EQ(NS_OK, loopback->Init()); |
194 | 0 | ASSERT_EQ(NS_OK, dtls->Init()); |
195 | 0 | ASSERT_EQ(NS_OK, srtp->Init()); |
196 | 0 |
|
197 | 0 | dtls->Chain(loopback.get()); |
198 | 0 | srtp->Chain(loopback.get()); |
199 | 0 |
|
200 | 0 | flow_ = new TransportFlow(); |
201 | 0 | loopback_ = loopback.release(); |
202 | 0 | flow_->PushLayer(loopback_); |
203 | 0 | flow_->PushLayer(dtls.release()); |
204 | 0 | flow_->PushLayer(srtp.release()); |
205 | 0 | } |
206 | | |
207 | 0 | void Connect(TransportInfo* peer) { |
208 | 0 | MOZ_ASSERT(loopback_); |
209 | 0 | MOZ_ASSERT(peer->loopback_); |
210 | 0 |
|
211 | 0 | loopback_->Connect(peer->loopback_); |
212 | 0 | } |
213 | | |
214 | 0 | void Shutdown() { |
215 | 0 | if (loopback_) { |
216 | 0 | loopback_->Disconnect(); |
217 | 0 | } |
218 | 0 | loopback_ = nullptr; |
219 | 0 | flow_ = nullptr; |
220 | 0 | } |
221 | | |
222 | | RefPtr<TransportFlow> flow_; |
223 | | TransportLayerLoopback *loopback_; |
224 | | }; |
225 | | |
226 | | class TestAgent { |
227 | | public: |
228 | | TestAgent() : |
229 | | audio_config_(109, "opus", 48000, 960, 2, 64000, false), |
230 | | audio_conduit_(mozilla::AudioSessionConduit::Create()), |
231 | | audio_pipeline_(), |
232 | 0 | use_bundle_(false) { |
233 | 0 | } |
234 | | |
235 | 0 | static void ConnectRtp(TestAgent *client, TestAgent *server) { |
236 | 0 | TransportInfo::InitAndConnect(client->audio_rtp_transport_, |
237 | 0 | server->audio_rtp_transport_); |
238 | 0 | } |
239 | | |
240 | 0 | static void ConnectRtcp(TestAgent *client, TestAgent *server) { |
241 | 0 | TransportInfo::InitAndConnect(client->audio_rtcp_transport_, |
242 | 0 | server->audio_rtcp_transport_); |
243 | 0 | } |
244 | | |
245 | 0 | static void ConnectBundle(TestAgent *client, TestAgent *server) { |
246 | 0 | TransportInfo::InitAndConnect(client->bundle_transport_, |
247 | 0 | server->bundle_transport_); |
248 | 0 | } |
249 | | |
250 | | virtual void CreatePipeline(bool aIsRtcpMux) = 0; |
251 | | |
252 | 0 | void Stop() { |
253 | 0 | MOZ_MTLOG(ML_DEBUG, "Stopping"); |
254 | 0 |
|
255 | 0 | if (audio_pipeline_) |
256 | 0 | audio_pipeline_->Stop(); |
257 | 0 | } |
258 | | |
259 | 0 | void Shutdown_s() { |
260 | 0 | audio_rtp_transport_.Shutdown(); |
261 | 0 | audio_rtcp_transport_.Shutdown(); |
262 | 0 | bundle_transport_.Shutdown(); |
263 | 0 | } |
264 | | |
265 | 0 | void Shutdown() { |
266 | 0 | if (audio_pipeline_) |
267 | 0 | audio_pipeline_->Shutdown_m(); |
268 | 0 | if (audio_stream_track_) |
269 | 0 | audio_stream_track_->Stop(); |
270 | 0 |
|
271 | 0 | mozilla::SyncRunnable::DispatchToThread( |
272 | 0 | test_utils->sts_target(), |
273 | 0 | WrapRunnable(this, &TestAgent::Shutdown_s)); |
274 | 0 | } |
275 | | |
276 | 0 | uint32_t GetRemoteSSRC() { |
277 | 0 | uint32_t res = 0; |
278 | 0 | audio_conduit_->GetRemoteSSRC(&res); |
279 | 0 | return res; |
280 | 0 | } |
281 | | |
282 | 0 | uint32_t GetLocalSSRC() { |
283 | 0 | std::vector<uint32_t> res; |
284 | 0 | res = audio_conduit_->GetLocalSSRCs(); |
285 | 0 | return res.empty() ? 0 : res[0]; |
286 | 0 | } |
287 | | |
288 | 0 | int GetAudioRtpCountSent() { |
289 | 0 | return audio_pipeline_->RtpPacketsSent(); |
290 | 0 | } |
291 | | |
292 | 0 | int GetAudioRtpCountReceived() { |
293 | 0 | return audio_pipeline_->RtpPacketsReceived(); |
294 | 0 | } |
295 | | |
296 | 0 | int GetAudioRtcpCountSent() { |
297 | 0 | return audio_pipeline_->RtcpPacketsSent(); |
298 | 0 | } |
299 | | |
300 | 0 | int GetAudioRtcpCountReceived() { |
301 | 0 | return audio_pipeline_->RtcpPacketsReceived(); |
302 | 0 | } |
303 | | |
304 | | |
305 | 0 | void SetUsingBundle(bool use_bundle) { |
306 | 0 | use_bundle_ = use_bundle; |
307 | 0 | } |
308 | | |
309 | | protected: |
310 | | mozilla::AudioCodecConfig audio_config_; |
311 | | RefPtr<mozilla::MediaSessionConduit> audio_conduit_; |
312 | | RefPtr<FakeAudioStreamTrack> audio_stream_track_; |
313 | | // TODO(bcampen@mozilla.com): Right now this does not let us test RTCP in |
314 | | // both directions; only the sender's RTCP is sent, but the receiver should |
315 | | // be sending it too. |
316 | | RefPtr<mozilla::MediaPipeline> audio_pipeline_; |
317 | | TransportInfo audio_rtp_transport_; |
318 | | TransportInfo audio_rtcp_transport_; |
319 | | TransportInfo bundle_transport_; |
320 | | bool use_bundle_; |
321 | | }; |
322 | | |
323 | | class TestAgentSend : public TestAgent { |
324 | | public: |
325 | 0 | TestAgentSend() { |
326 | 0 | mozilla::MediaConduitErrorCode err = |
327 | 0 | static_cast<mozilla::AudioSessionConduit *>(audio_conduit_.get())-> |
328 | 0 | ConfigureSendMediaCodec(&audio_config_); |
329 | 0 | EXPECT_EQ(mozilla::kMediaConduitNoError, err); |
330 | 0 |
|
331 | 0 | audio_stream_track_ = new FakeAudioStreamTrack(); |
332 | 0 | } |
333 | | |
334 | 0 | virtual void CreatePipeline(bool aIsRtcpMux) { |
335 | 0 |
|
336 | 0 | std::string test_pc; |
337 | 0 |
|
338 | 0 | if (aIsRtcpMux) { |
339 | 0 | ASSERT_FALSE(audio_rtcp_transport_.flow_); |
340 | 0 | } |
341 | 0 |
|
342 | 0 | RefPtr<MediaPipelineTransmit> audio_pipeline = |
343 | 0 | new mozilla::MediaPipelineTransmit( |
344 | 0 | test_pc, |
345 | 0 | nullptr, |
346 | 0 | test_utils->sts_target(), |
347 | 0 | false, |
348 | 0 | audio_conduit_); |
349 | 0 |
|
350 | 0 | audio_pipeline->SetTrack(audio_stream_track_.get()); |
351 | 0 | audio_pipeline->Start(); |
352 | 0 |
|
353 | 0 | audio_pipeline_ = audio_pipeline; |
354 | 0 |
|
355 | 0 | RefPtr<TransportFlow> rtp(audio_rtp_transport_.flow_); |
356 | 0 | RefPtr<TransportFlow> rtcp(audio_rtcp_transport_.flow_); |
357 | 0 |
|
358 | 0 | if (use_bundle_) { |
359 | 0 | rtp = bundle_transport_.flow_; |
360 | 0 | rtcp = nullptr; |
361 | 0 | } |
362 | 0 |
|
363 | 0 | audio_pipeline_->UpdateTransport_m( |
364 | 0 | rtp, rtcp, nsAutoPtr<MediaPipelineFilter>(nullptr)); |
365 | 0 | } |
366 | | }; |
367 | | |
368 | | |
369 | | class TestAgentReceive : public TestAgent { |
370 | | public: |
371 | | |
372 | 0 | TestAgentReceive() { |
373 | 0 | std::vector<mozilla::AudioCodecConfig *> codecs; |
374 | 0 | codecs.push_back(&audio_config_); |
375 | 0 |
|
376 | 0 | mozilla::MediaConduitErrorCode err = |
377 | 0 | static_cast<mozilla::AudioSessionConduit *>(audio_conduit_.get())-> |
378 | 0 | ConfigureRecvMediaCodecs(codecs); |
379 | 0 | EXPECT_EQ(mozilla::kMediaConduitNoError, err); |
380 | 0 | } |
381 | | |
382 | 0 | virtual void CreatePipeline(bool aIsRtcpMux) { |
383 | 0 | std::string test_pc; |
384 | 0 |
|
385 | 0 | if (aIsRtcpMux) { |
386 | 0 | ASSERT_FALSE(audio_rtcp_transport_.flow_); |
387 | 0 | } |
388 | 0 |
|
389 | 0 | audio_pipeline_ = new mozilla::MediaPipelineReceiveAudio( |
390 | 0 | test_pc, |
391 | 0 | nullptr, |
392 | 0 | test_utils->sts_target(), |
393 | 0 | static_cast<mozilla::AudioSessionConduit *>(audio_conduit_.get()), |
394 | 0 | nullptr); |
395 | 0 |
|
396 | 0 | audio_pipeline_->Start(); |
397 | 0 |
|
398 | 0 | RefPtr<TransportFlow> rtp(audio_rtp_transport_.flow_); |
399 | 0 | RefPtr<TransportFlow> rtcp(audio_rtcp_transport_.flow_); |
400 | 0 |
|
401 | 0 | if (use_bundle_) { |
402 | 0 | rtp = bundle_transport_.flow_; |
403 | 0 | rtcp = nullptr; |
404 | 0 | } |
405 | 0 |
|
406 | 0 | audio_pipeline_->UpdateTransport_m(rtp, rtcp, bundle_filter_); |
407 | 0 | } |
408 | | |
409 | 0 | void SetBundleFilter(nsAutoPtr<MediaPipelineFilter> filter) { |
410 | 0 | bundle_filter_ = filter; |
411 | 0 | } |
412 | | |
413 | | void UpdateFilter_s( |
414 | 0 | nsAutoPtr<MediaPipelineFilter> filter) { |
415 | 0 | audio_pipeline_->UpdateTransport_s(audio_rtp_transport_.flow_, |
416 | 0 | audio_rtcp_transport_.flow_, |
417 | 0 | filter); |
418 | 0 | } |
419 | | |
420 | | private: |
421 | | nsAutoPtr<MediaPipelineFilter> bundle_filter_; |
422 | | }; |
423 | | |
424 | | |
425 | | class MediaPipelineTest : public ::testing::Test { |
426 | | public: |
427 | 0 | ~MediaPipelineTest() { |
428 | 0 | p1_.Shutdown(); |
429 | 0 | p2_.Shutdown(); |
430 | 0 | } |
431 | | |
432 | 0 | static void SetUpTestCase() { |
433 | 0 | test_utils = new MtransportTestUtils(); |
434 | 0 | NSS_NoDB_Init(nullptr); |
435 | 0 | NSS_SetDomesticPolicy(); |
436 | 0 | } |
437 | | |
438 | | // Setup transport. |
439 | 0 | void InitTransports(bool aIsRtcpMux) { |
440 | 0 | // RTP, p1_ is server, p2_ is client |
441 | 0 | mozilla::SyncRunnable::DispatchToThread( |
442 | 0 | test_utils->sts_target(), |
443 | 0 | WrapRunnableNM(&TestAgent::ConnectRtp, &p2_, &p1_)); |
444 | 0 |
|
445 | 0 | // Create RTCP flows separately if we are not muxing them. |
446 | 0 | if(!aIsRtcpMux) { |
447 | 0 | // RTCP, p1_ is server, p2_ is client |
448 | 0 | mozilla::SyncRunnable::DispatchToThread( |
449 | 0 | test_utils->sts_target(), |
450 | 0 | WrapRunnableNM(&TestAgent::ConnectRtcp, &p2_, &p1_)); |
451 | 0 | } |
452 | 0 |
|
453 | 0 | // BUNDLE, p1_ is server, p2_ is client |
454 | 0 | mozilla::SyncRunnable::DispatchToThread( |
455 | 0 | test_utils->sts_target(), |
456 | 0 | WrapRunnableNM(&TestAgent::ConnectBundle, &p2_, &p1_)); |
457 | 0 | } |
458 | | |
459 | | // Verify RTP and RTCP |
460 | | void TestAudioSend(bool aIsRtcpMux, |
461 | | nsAutoPtr<MediaPipelineFilter> initialFilter = |
462 | | nsAutoPtr<MediaPipelineFilter>(nullptr), |
463 | | nsAutoPtr<MediaPipelineFilter> refinedFilter = |
464 | | nsAutoPtr<MediaPipelineFilter>(nullptr), |
465 | | unsigned int ms_until_filter_update = 500, |
466 | 0 | unsigned int ms_of_traffic_after_answer = 10000) { |
467 | 0 |
|
468 | 0 | bool bundle = !!(initialFilter); |
469 | 0 | // We do not support testing bundle without rtcp mux, since that doesn't |
470 | 0 | // make any sense. |
471 | 0 | ASSERT_FALSE(!aIsRtcpMux && bundle); |
472 | 0 |
|
473 | 0 | p2_.SetBundleFilter(initialFilter); |
474 | 0 |
|
475 | 0 | // Setup transport flows |
476 | 0 | InitTransports(aIsRtcpMux); |
477 | 0 |
|
478 | 0 | p1_.CreatePipeline(aIsRtcpMux); |
479 | 0 | p2_.CreatePipeline(aIsRtcpMux); |
480 | 0 |
|
481 | 0 | if (bundle) { |
482 | 0 | PR_Sleep(ms_until_filter_update); |
483 | 0 |
|
484 | 0 | // Leaving refinedFilter not set implies we want to just update with |
485 | 0 | // the other side's SSRC |
486 | 0 | if (!refinedFilter) { |
487 | 0 | refinedFilter = new MediaPipelineFilter; |
488 | 0 | // Might not be safe, strictly speaking. |
489 | 0 | refinedFilter->AddRemoteSSRC(p1_.GetLocalSSRC()); |
490 | 0 | } |
491 | 0 |
|
492 | 0 | mozilla::SyncRunnable::DispatchToThread( |
493 | 0 | test_utils->sts_target(), |
494 | 0 | WrapRunnable(&p2_, |
495 | 0 | &TestAgentReceive::UpdateFilter_s, |
496 | 0 | refinedFilter)); |
497 | 0 | } |
498 | 0 |
|
499 | 0 | // wait for some RTP/RTCP tx and rx to happen |
500 | 0 | PR_Sleep(ms_of_traffic_after_answer); |
501 | 0 |
|
502 | 0 | p1_.Stop(); |
503 | 0 | p2_.Stop(); |
504 | 0 |
|
505 | 0 | // wait for any packets in flight to arrive |
506 | 0 | PR_Sleep(100); |
507 | 0 |
|
508 | 0 | p1_.Shutdown(); |
509 | 0 | p2_.Shutdown(); |
510 | 0 |
|
511 | 0 | if (!bundle) { |
512 | 0 | // If we are filtering, allow the test-case to do this checking. |
513 | 0 | ASSERT_GE(p1_.GetAudioRtpCountSent(), 40); |
514 | 0 | ASSERT_EQ(p1_.GetAudioRtpCountReceived(), p2_.GetAudioRtpCountSent()); |
515 | 0 | ASSERT_EQ(p1_.GetAudioRtpCountSent(), p2_.GetAudioRtpCountReceived()); |
516 | 0 | } |
517 | 0 |
|
518 | 0 | // No RTCP packets should have been dropped, because we do not filter them. |
519 | 0 | // Calling ShutdownMedia_m on both pipelines does not stop the flow of |
520 | 0 | // RTCP. So, we might be off by one here. |
521 | 0 | ASSERT_LE(p2_.GetAudioRtcpCountReceived(), p1_.GetAudioRtcpCountSent()); |
522 | 0 | ASSERT_GE(p2_.GetAudioRtcpCountReceived() + 1, p1_.GetAudioRtcpCountSent()); |
523 | 0 | } |
524 | | |
525 | | void TestAudioReceiverBundle(bool bundle_accepted, |
526 | | nsAutoPtr<MediaPipelineFilter> initialFilter, |
527 | | nsAutoPtr<MediaPipelineFilter> refinedFilter = |
528 | | nsAutoPtr<MediaPipelineFilter>(nullptr), |
529 | | unsigned int ms_until_answer = 500, |
530 | 0 | unsigned int ms_of_traffic_after_answer = 10000) { |
531 | 0 | TestAudioSend(true, |
532 | 0 | initialFilter, |
533 | 0 | refinedFilter, |
534 | 0 | ms_until_answer, |
535 | 0 | ms_of_traffic_after_answer); |
536 | 0 | } |
537 | | protected: |
538 | | TestAgentSend p1_; |
539 | | TestAgentReceive p2_; |
540 | | }; |
541 | | |
542 | | class MediaPipelineFilterTest : public ::testing::Test { |
543 | | public: |
544 | | bool Filter(MediaPipelineFilter& filter, |
545 | | int32_t correlator, |
546 | | uint32_t ssrc, |
547 | 0 | uint8_t payload_type) { |
548 | 0 |
|
549 | 0 | webrtc::RTPHeader header; |
550 | 0 | header.ssrc = ssrc; |
551 | 0 | header.payloadType = payload_type; |
552 | 0 | return filter.Filter(header, correlator); |
553 | 0 | } |
554 | | }; |
555 | | |
556 | 0 | TEST_F(MediaPipelineFilterTest, TestConstruct) { |
557 | 0 | MediaPipelineFilter filter; |
558 | 0 | } |
559 | | |
560 | 0 | TEST_F(MediaPipelineFilterTest, TestDefault) { |
561 | 0 | MediaPipelineFilter filter; |
562 | 0 | ASSERT_FALSE(Filter(filter, 0, 233, 110)); |
563 | 0 | } |
564 | | |
565 | 0 | TEST_F(MediaPipelineFilterTest, TestSSRCFilter) { |
566 | 0 | MediaPipelineFilter filter; |
567 | 0 | filter.AddRemoteSSRC(555); |
568 | 0 | ASSERT_TRUE(Filter(filter, 0, 555, 110)); |
569 | 0 | ASSERT_FALSE(Filter(filter, 0, 556, 110)); |
570 | 0 | } |
571 | | |
572 | | #define SSRC(ssrc) \ |
573 | | ((ssrc >> 24) & 0xFF), \ |
574 | | ((ssrc >> 16) & 0xFF), \ |
575 | | ((ssrc >> 8 ) & 0xFF), \ |
576 | | (ssrc & 0xFF) |
577 | | |
578 | | #define REPORT_FRAGMENT(ssrc) \ |
579 | | SSRC(ssrc), \ |
580 | | 0,0,0,0, \ |
581 | | 0,0,0,0, \ |
582 | | 0,0,0,0, \ |
583 | | 0,0,0,0, \ |
584 | | 0,0,0,0 |
585 | | |
586 | | #define RTCP_TYPEINFO(num_rrs, type, size) \ |
587 | | 0x80 + num_rrs, type, 0, size |
588 | | |
589 | 0 | TEST_F(MediaPipelineFilterTest, TestCorrelatorFilter) { |
590 | 0 | MediaPipelineFilter filter; |
591 | 0 | filter.SetCorrelator(7777); |
592 | 0 | ASSERT_TRUE(Filter(filter, 7777, 16, 110)); |
593 | 0 | ASSERT_FALSE(Filter(filter, 7778, 17, 110)); |
594 | 0 | // This should also have resulted in the SSRC 16 being added to the filter |
595 | 0 | ASSERT_TRUE(Filter(filter, 0, 16, 110)); |
596 | 0 | ASSERT_FALSE(Filter(filter, 0, 17, 110)); |
597 | 0 | } |
598 | | |
599 | 0 | TEST_F(MediaPipelineFilterTest, TestPayloadTypeFilter) { |
600 | 0 | MediaPipelineFilter filter; |
601 | 0 | filter.AddUniquePT(110); |
602 | 0 | ASSERT_TRUE(Filter(filter, 0, 555, 110)); |
603 | 0 | ASSERT_FALSE(Filter(filter, 0, 556, 111)); |
604 | 0 | } |
605 | | |
606 | 0 | TEST_F(MediaPipelineFilterTest, TestSSRCMovedWithCorrelator) { |
607 | 0 | MediaPipelineFilter filter; |
608 | 0 | filter.SetCorrelator(7777); |
609 | 0 | ASSERT_TRUE(Filter(filter, 7777, 555, 110)); |
610 | 0 | ASSERT_TRUE(Filter(filter, 0, 555, 110)); |
611 | 0 | ASSERT_FALSE(Filter(filter, 7778, 555, 110)); |
612 | 0 | ASSERT_FALSE(Filter(filter, 0, 555, 110)); |
613 | 0 | } |
614 | | |
615 | 0 | TEST_F(MediaPipelineFilterTest, TestRemoteSDPNoSSRCs) { |
616 | 0 | // If the remote SDP doesn't have SSRCs, right now this is a no-op and |
617 | 0 | // there is no point of even incorporating a filter, but we make the |
618 | 0 | // behavior consistent to avoid confusion. |
619 | 0 | MediaPipelineFilter filter; |
620 | 0 | filter.SetCorrelator(7777); |
621 | 0 | filter.AddUniquePT(111); |
622 | 0 | ASSERT_TRUE(Filter(filter, 7777, 555, 110)); |
623 | 0 |
|
624 | 0 | MediaPipelineFilter filter2; |
625 | 0 |
|
626 | 0 | filter.Update(filter2); |
627 | 0 |
|
628 | 0 | // Ensure that the old SSRC still works. |
629 | 0 | ASSERT_TRUE(Filter(filter, 0, 555, 110)); |
630 | 0 | } |
631 | | |
632 | 0 | TEST_F(MediaPipelineTest, TestAudioSendNoMux) { |
633 | 0 | TestAudioSend(false); |
634 | 0 | } |
635 | | |
636 | 0 | TEST_F(MediaPipelineTest, TestAudioSendMux) { |
637 | 0 | TestAudioSend(true); |
638 | 0 | } |
639 | | |
640 | 0 | TEST_F(MediaPipelineTest, TestAudioSendBundle) { |
641 | 0 | nsAutoPtr<MediaPipelineFilter> filter(new MediaPipelineFilter); |
642 | 0 | // These durations have to be _extremely_ long to have any assurance that |
643 | 0 | // some RTCP will be sent at all. This is because the first RTCP packet |
644 | 0 | // is sometimes sent before the transports are ready, which causes it to |
645 | 0 | // be dropped. |
646 | 0 | TestAudioReceiverBundle(true, |
647 | 0 | filter, |
648 | 0 | // We do not specify the filter for the remote description, so it will be |
649 | 0 | // set to something sane after a short time. |
650 | 0 | nsAutoPtr<MediaPipelineFilter>(), |
651 | 0 | 10000, |
652 | 0 | 10000); |
653 | 0 |
|
654 | 0 | // Some packets should have been dropped, but not all |
655 | 0 | ASSERT_GT(p1_.GetAudioRtpCountSent(), p2_.GetAudioRtpCountReceived()); |
656 | 0 | ASSERT_GT(p2_.GetAudioRtpCountReceived(), 40); |
657 | 0 | ASSERT_GT(p1_.GetAudioRtcpCountSent(), 1); |
658 | 0 | } |
659 | | |
660 | 0 | TEST_F(MediaPipelineTest, TestAudioSendEmptyBundleFilter) { |
661 | 0 | nsAutoPtr<MediaPipelineFilter> filter(new MediaPipelineFilter); |
662 | 0 | nsAutoPtr<MediaPipelineFilter> bad_answer_filter(new MediaPipelineFilter); |
663 | 0 | TestAudioReceiverBundle(true, filter, bad_answer_filter); |
664 | 0 | // Filter is empty, so should drop everything. |
665 | 0 | ASSERT_EQ(0, p2_.GetAudioRtpCountReceived()); |
666 | 0 | } |
667 | | |
668 | | } // end namespace |