1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 #include "base/message_loop/message_loop.h"
9 #include "base/stl_util.h"
10 #include "base/test/simple_test_tick_clock.h"
11 #include "base/threading/simple_thread.h"
12 #include "base/time/clock.h"
13 #include "media/base/clock.h"
14 #include "media/base/fake_text_track_stream.h"
15 #include "media/base/gmock_callback_support.h"
16 #include "media/base/media_log.h"
17 #include "media/base/mock_filters.h"
18 #include "media/base/pipeline.h"
19 #include "media/base/test_helpers.h"
20 #include "media/base/text_renderer.h"
21 #include "media/base/text_track_config.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "ui/gfx/size.h"
26 using ::testing::DeleteArg
;
27 using ::testing::DoAll
;
28 // TODO(scherkus): Remove InSequence after refactoring Pipeline.
29 using ::testing::InSequence
;
30 using ::testing::Invoke
;
31 using ::testing::InvokeWithoutArgs
;
32 using ::testing::Mock
;
33 using ::testing::NotNull
;
34 using ::testing::Return
;
35 using ::testing::SaveArg
;
36 using ::testing::StrictMock
;
37 using ::testing::WithArg
;
41 ACTION_P(SetDemuxerProperties
, duration
) {
42 arg0
->SetDuration(duration
);
45 ACTION_P2(Stop
, pipeline
, stop_cb
) {
46 pipeline
->Stop(stop_cb
);
49 ACTION_P2(SetError
, pipeline
, status
) {
50 pipeline
->SetErrorForTesting(status
);
53 // Used for setting expectations on pipeline callbacks. Using a StrictMock
54 // also lets us test for missing callbacks.
55 class CallbackHelper
{
58 virtual ~CallbackHelper() {}
60 MOCK_METHOD1(OnStart
, void(PipelineStatus
));
61 MOCK_METHOD1(OnSeek
, void(PipelineStatus
));
62 MOCK_METHOD0(OnStop
, void());
63 MOCK_METHOD0(OnEnded
, void());
64 MOCK_METHOD1(OnError
, void(PipelineStatus
));
65 MOCK_METHOD1(OnMetadata
, void(PipelineMetadata
));
66 MOCK_METHOD0(OnPrerollCompleted
, void());
67 MOCK_METHOD0(OnDurationChange
, void());
70 DISALLOW_COPY_AND_ASSIGN(CallbackHelper
);
73 // TODO(scherkus): even though some filters are initialized on separate
74 // threads these test aren't flaky... why? It's because filters' Initialize()
75 // is executed on |message_loop_| and the mock filters instantly call
76 // InitializationComplete(), which keeps the pipeline humming along. If
77 // either filters don't call InitializationComplete() immediately or filter
78 // initialization is moved to a separate thread this test will become flaky.
79 class PipelineTest
: public ::testing::Test
{
82 : pipeline_(new Pipeline(message_loop_
.message_loop_proxy(),
84 filter_collection_(new FilterCollection()),
85 demuxer_(new MockDemuxer()) {
86 filter_collection_
->SetDemuxer(demuxer_
.get());
88 video_renderer_
= new MockVideoRenderer();
89 scoped_ptr
<VideoRenderer
> video_renderer(video_renderer_
);
90 filter_collection_
->SetVideoRenderer(video_renderer
.Pass());
92 audio_renderer_
= new MockAudioRenderer();
93 scoped_ptr
<AudioRenderer
> audio_renderer(audio_renderer_
);
94 filter_collection_
->SetAudioRenderer(audio_renderer
.Pass());
96 text_renderer_
= new TextRenderer(
97 message_loop_
.message_loop_proxy(),
98 base::Bind(&PipelineTest::OnAddTextTrack
,
99 base::Unretained(this)));
100 scoped_ptr
<TextRenderer
> text_renderer(text_renderer_
);
101 filter_collection_
->SetTextRenderer(text_renderer
.Pass());
103 // InitializeDemuxer() adds overriding expectations for expected non-NULL
105 DemuxerStream
* null_pointer
= NULL
;
106 EXPECT_CALL(*demuxer_
, GetStream(_
))
107 .WillRepeatedly(Return(null_pointer
));
109 EXPECT_CALL(*demuxer_
, GetStartTime())
110 .WillRepeatedly(Return(base::TimeDelta()));
112 EXPECT_CALL(*demuxer_
, GetTimelineOffset())
113 .WillRepeatedly(Return(base::Time()));
115 EXPECT_CALL(*demuxer_
, GetLiveness())
116 .WillRepeatedly(Return(Demuxer::LIVENESS_UNKNOWN
));
119 virtual ~PipelineTest() {
120 if (!pipeline_
|| !pipeline_
->IsRunning())
125 // The mock demuxer doesn't stop the fake text track stream,
126 // so just stop it manually.
128 text_stream_
->Stop();
129 message_loop_
.RunUntilIdle();
132 // Expect a stop callback if we were started.
133 EXPECT_CALL(callbacks_
, OnStop());
134 pipeline_
->Stop(base::Bind(&CallbackHelper::OnStop
,
135 base::Unretained(&callbacks_
)));
136 message_loop_
.RunUntilIdle();
140 // Sets up expectations to allow the demuxer to initialize.
141 typedef std::vector
<MockDemuxerStream
*> MockDemuxerStreamVector
;
142 void InitializeDemuxer(MockDemuxerStreamVector
* streams
,
143 const base::TimeDelta
& duration
) {
144 EXPECT_CALL(callbacks_
, OnDurationChange());
145 EXPECT_CALL(*demuxer_
, Initialize(_
, _
, _
))
146 .WillOnce(DoAll(SetDemuxerProperties(duration
),
147 RunCallback
<1>(PIPELINE_OK
)));
149 // Configure the demuxer to return the streams.
150 for (size_t i
= 0; i
< streams
->size(); ++i
) {
151 DemuxerStream
* stream
= (*streams
)[i
];
152 EXPECT_CALL(*demuxer_
, GetStream(stream
->type()))
153 .WillRepeatedly(Return(stream
));
157 void InitializeDemuxer(MockDemuxerStreamVector
* streams
) {
158 // Initialize with a default non-zero duration.
159 InitializeDemuxer(streams
, base::TimeDelta::FromSeconds(10));
162 scoped_ptr
<StrictMock
<MockDemuxerStream
> > CreateStream(
163 DemuxerStream::Type type
) {
164 scoped_ptr
<StrictMock
<MockDemuxerStream
> > stream(
165 new StrictMock
<MockDemuxerStream
>(type
));
166 return stream
.Pass();
169 // Sets up expectations to allow the video renderer to initialize.
170 void InitializeVideoRenderer(DemuxerStream
* stream
) {
171 EXPECT_CALL(*video_renderer_
, Initialize(stream
, _
, _
, _
, _
, _
, _
, _
, _
))
172 .WillOnce(RunCallback
<2>(PIPELINE_OK
));
173 EXPECT_CALL(*video_renderer_
, SetPlaybackRate(0.0f
));
176 EXPECT_CALL(*video_renderer_
, Preroll(demuxer_
->GetStartTime(), _
))
177 .WillOnce(RunCallback
<1>(PIPELINE_OK
));
178 EXPECT_CALL(*video_renderer_
, Play(_
))
179 .WillOnce(RunClosure
<0>());
182 // Sets up expectations to allow the audio renderer to initialize.
183 void InitializeAudioRenderer(DemuxerStream
* stream
) {
184 EXPECT_CALL(*audio_renderer_
, Initialize(stream
, _
, _
, _
, _
, _
, _
))
185 .WillOnce(DoAll(SaveArg
<4>(&audio_time_cb_
),
186 RunCallback
<1>(PIPELINE_OK
)));
189 void AddTextStream() {
190 EXPECT_CALL(*this, OnAddTextTrack(_
,_
))
191 .WillOnce(Invoke(this, &PipelineTest::DoOnAddTextTrack
));
192 static_cast<DemuxerHost
*>(pipeline_
.get())->AddTextStream(text_stream(),
193 TextTrackConfig(kTextSubtitles
, "", "", ""));
196 // Sets up expectations on the callback and initializes the pipeline. Called
197 // after tests have set expectations any filters they wish to use.
198 void InitializePipeline(PipelineStatus start_status
) {
199 EXPECT_CALL(callbacks_
, OnStart(start_status
));
201 if (start_status
== PIPELINE_OK
) {
202 EXPECT_CALL(callbacks_
, OnMetadata(_
)).WillOnce(SaveArg
<0>(&metadata_
));
205 EXPECT_CALL(*audio_renderer_
, SetPlaybackRate(0.0f
));
206 EXPECT_CALL(*audio_renderer_
, SetVolume(1.0f
));
209 EXPECT_CALL(*audio_renderer_
, Preroll(base::TimeDelta(), _
))
210 .WillOnce(RunCallback
<1>(PIPELINE_OK
));
211 EXPECT_CALL(*audio_renderer_
, Play());
213 EXPECT_CALL(callbacks_
, OnPrerollCompleted());
217 filter_collection_
.Pass(),
218 base::Bind(&CallbackHelper::OnEnded
, base::Unretained(&callbacks_
)),
219 base::Bind(&CallbackHelper::OnError
, base::Unretained(&callbacks_
)),
220 base::Bind(&CallbackHelper::OnStart
, base::Unretained(&callbacks_
)),
221 base::Bind(&CallbackHelper::OnMetadata
, base::Unretained(&callbacks_
)),
222 base::Bind(&CallbackHelper::OnPrerollCompleted
,
223 base::Unretained(&callbacks_
)),
224 base::Bind(&CallbackHelper::OnDurationChange
,
225 base::Unretained(&callbacks_
)));
226 message_loop_
.RunUntilIdle();
229 void CreateAudioStream() {
230 audio_stream_
= CreateStream(DemuxerStream::AUDIO
);
233 void CreateVideoStream() {
234 video_stream_
= CreateStream(DemuxerStream::VIDEO
);
235 video_stream_
->set_video_decoder_config(video_decoder_config_
);
238 void CreateTextStream() {
239 scoped_ptr
<FakeTextTrackStream
> text_stream(new FakeTextTrackStream
);
240 text_stream_
= text_stream
.Pass();
243 MockDemuxerStream
* audio_stream() {
244 return audio_stream_
.get();
247 MockDemuxerStream
* video_stream() {
248 return video_stream_
.get();
251 FakeTextTrackStream
* text_stream() {
252 return text_stream_
.get();
255 void ExpectSeek(const base::TimeDelta
& seek_time
) {
256 // Every filter should receive a call to Seek().
257 EXPECT_CALL(*demuxer_
, Seek(seek_time
, _
))
258 .WillOnce(RunCallback
<1>(PIPELINE_OK
));
261 EXPECT_CALL(*audio_renderer_
, Pause());
262 EXPECT_CALL(*audio_renderer_
, Flush(_
))
263 .WillOnce(RunClosure
<0>());
264 EXPECT_CALL(*audio_renderer_
, Preroll(seek_time
, _
))
265 .WillOnce(RunCallback
<1>(PIPELINE_OK
));
266 EXPECT_CALL(*audio_renderer_
, SetPlaybackRate(_
));
267 EXPECT_CALL(*audio_renderer_
, SetVolume(_
));
268 EXPECT_CALL(*audio_renderer_
, Play());
272 EXPECT_CALL(*video_renderer_
, Pause(_
))
273 .WillOnce(RunClosure
<0>());
274 EXPECT_CALL(*video_renderer_
, Flush(_
))
275 .WillOnce(RunClosure
<0>());
276 EXPECT_CALL(*video_renderer_
, Preroll(seek_time
, _
))
277 .WillOnce(RunCallback
<1>(PIPELINE_OK
));
278 EXPECT_CALL(*video_renderer_
, SetPlaybackRate(_
));
279 EXPECT_CALL(*video_renderer_
, Play(_
))
280 .WillOnce(RunClosure
<0>());
283 EXPECT_CALL(callbacks_
, OnPrerollCompleted());
285 // We expect a successful seek callback.
286 EXPECT_CALL(callbacks_
, OnSeek(PIPELINE_OK
));
289 void DoSeek(const base::TimeDelta
& seek_time
) {
290 pipeline_
->Seek(seek_time
,
291 base::Bind(&CallbackHelper::OnSeek
,
292 base::Unretained(&callbacks_
)));
294 // We expect the time to be updated only after the seek has completed.
295 EXPECT_NE(seek_time
, pipeline_
->GetMediaTime());
296 message_loop_
.RunUntilIdle();
297 EXPECT_EQ(seek_time
, pipeline_
->GetMediaTime());
302 EXPECT_CALL(*demuxer_
, Stop(_
)).WillOnce(RunClosure
<0>());
305 EXPECT_CALL(*audio_renderer_
, Stop(_
)).WillOnce(RunClosure
<0>());
308 EXPECT_CALL(*video_renderer_
, Stop(_
)).WillOnce(RunClosure
<0>());
311 MOCK_METHOD2(OnAddTextTrack
, void(const TextTrackConfig
&,
312 const AddTextTrackDoneCB
&));
314 void DoOnAddTextTrack(const TextTrackConfig
& config
,
315 const AddTextTrackDoneCB
& done_cb
) {
316 scoped_ptr
<TextTrack
> text_track(new MockTextTrack
);
317 done_cb
.Run(text_track
.Pass());
321 StrictMock
<CallbackHelper
> callbacks_
;
322 base::SimpleTestTickClock test_tick_clock_
;
323 base::MessageLoop message_loop_
;
324 scoped_ptr
<Pipeline
> pipeline_
;
326 scoped_ptr
<FilterCollection
> filter_collection_
;
327 scoped_ptr
<MockDemuxer
> demuxer_
;
328 MockVideoRenderer
* video_renderer_
;
329 MockAudioRenderer
* audio_renderer_
;
330 StrictMock
<CallbackHelper
> text_renderer_callbacks_
;
331 TextRenderer
* text_renderer_
;
332 scoped_ptr
<StrictMock
<MockDemuxerStream
> > audio_stream_
;
333 scoped_ptr
<StrictMock
<MockDemuxerStream
> > video_stream_
;
334 scoped_ptr
<FakeTextTrackStream
> text_stream_
;
335 AudioRenderer::TimeCB audio_time_cb_
;
336 VideoDecoderConfig video_decoder_config_
;
337 PipelineMetadata metadata_
;
340 DISALLOW_COPY_AND_ASSIGN(PipelineTest
);
343 // Test that playback controls methods no-op when the pipeline hasn't been
345 TEST_F(PipelineTest
, NotStarted
) {
346 const base::TimeDelta kZero
;
348 EXPECT_FALSE(pipeline_
->IsRunning());
350 // Setting should still work.
351 EXPECT_EQ(0.0f
, pipeline_
->GetPlaybackRate());
352 pipeline_
->SetPlaybackRate(-1.0f
);
353 EXPECT_EQ(0.0f
, pipeline_
->GetPlaybackRate());
354 pipeline_
->SetPlaybackRate(1.0f
);
355 EXPECT_EQ(1.0f
, pipeline_
->GetPlaybackRate());
357 // Setting should still work.
358 EXPECT_EQ(1.0f
, pipeline_
->GetVolume());
359 pipeline_
->SetVolume(-1.0f
);
360 EXPECT_EQ(1.0f
, pipeline_
->GetVolume());
361 pipeline_
->SetVolume(0.0f
);
362 EXPECT_EQ(0.0f
, pipeline_
->GetVolume());
364 EXPECT_TRUE(kZero
== pipeline_
->GetMediaTime());
365 EXPECT_EQ(0u, pipeline_
->GetBufferedTimeRanges().size());
366 EXPECT_TRUE(kZero
== pipeline_
->GetMediaDuration());
369 TEST_F(PipelineTest
, NeverInitializes
) {
370 // Don't execute the callback passed into Initialize().
371 EXPECT_CALL(*demuxer_
, Initialize(_
, _
, _
));
373 // This test hangs during initialization by never calling
374 // InitializationComplete(). StrictMock<> will ensure that the callback is
377 filter_collection_
.Pass(),
378 base::Bind(&CallbackHelper::OnEnded
, base::Unretained(&callbacks_
)),
379 base::Bind(&CallbackHelper::OnError
, base::Unretained(&callbacks_
)),
380 base::Bind(&CallbackHelper::OnStart
, base::Unretained(&callbacks_
)),
381 base::Bind(&CallbackHelper::OnMetadata
, base::Unretained(&callbacks_
)),
382 base::Bind(&CallbackHelper::OnPrerollCompleted
,
383 base::Unretained(&callbacks_
)),
384 base::Bind(&CallbackHelper::OnDurationChange
,
385 base::Unretained(&callbacks_
)));
386 message_loop_
.RunUntilIdle();
389 // Because our callback will get executed when the test tears down, we'll
390 // verify that nothing has been called, then set our expectation for the call
391 // made during tear down.
392 Mock::VerifyAndClear(&callbacks_
);
393 EXPECT_CALL(callbacks_
, OnStart(PIPELINE_OK
));
396 TEST_F(PipelineTest
, URLNotFound
) {
397 EXPECT_CALL(*demuxer_
, Initialize(_
, _
, _
))
398 .WillOnce(RunCallback
<1>(PIPELINE_ERROR_URL_NOT_FOUND
));
399 EXPECT_CALL(*demuxer_
, Stop(_
))
400 .WillOnce(RunClosure
<0>());
402 InitializePipeline(PIPELINE_ERROR_URL_NOT_FOUND
);
405 TEST_F(PipelineTest
, NoStreams
) {
406 EXPECT_CALL(*demuxer_
, Initialize(_
, _
, _
))
407 .WillOnce(RunCallback
<1>(PIPELINE_OK
));
408 EXPECT_CALL(*demuxer_
, Stop(_
))
409 .WillOnce(RunClosure
<0>());
411 InitializePipeline(PIPELINE_ERROR_COULD_NOT_RENDER
);
414 TEST_F(PipelineTest
, AudioStream
) {
416 MockDemuxerStreamVector streams
;
417 streams
.push_back(audio_stream());
419 InitializeDemuxer(&streams
);
420 InitializeAudioRenderer(audio_stream());
422 InitializePipeline(PIPELINE_OK
);
423 EXPECT_TRUE(metadata_
.has_audio
);
424 EXPECT_FALSE(metadata_
.has_video
);
427 TEST_F(PipelineTest
, VideoStream
) {
429 MockDemuxerStreamVector streams
;
430 streams
.push_back(video_stream());
432 InitializeDemuxer(&streams
);
433 InitializeVideoRenderer(video_stream());
435 InitializePipeline(PIPELINE_OK
);
436 EXPECT_FALSE(metadata_
.has_audio
);
437 EXPECT_TRUE(metadata_
.has_video
);
440 TEST_F(PipelineTest
, AudioVideoStream
) {
443 MockDemuxerStreamVector streams
;
444 streams
.push_back(audio_stream());
445 streams
.push_back(video_stream());
447 InitializeDemuxer(&streams
);
448 InitializeAudioRenderer(audio_stream());
449 InitializeVideoRenderer(video_stream());
451 InitializePipeline(PIPELINE_OK
);
452 EXPECT_TRUE(metadata_
.has_audio
);
453 EXPECT_TRUE(metadata_
.has_video
);
456 TEST_F(PipelineTest
, VideoTextStream
) {
459 MockDemuxerStreamVector streams
;
460 streams
.push_back(video_stream());
462 InitializeDemuxer(&streams
);
463 InitializeVideoRenderer(video_stream());
465 InitializePipeline(PIPELINE_OK
);
466 EXPECT_FALSE(metadata_
.has_audio
);
467 EXPECT_TRUE(metadata_
.has_video
);
470 message_loop_
.RunUntilIdle();
473 TEST_F(PipelineTest
, VideoAudioTextStream
) {
477 MockDemuxerStreamVector streams
;
478 streams
.push_back(video_stream());
479 streams
.push_back(audio_stream());
481 InitializeDemuxer(&streams
);
482 InitializeVideoRenderer(video_stream());
483 InitializeAudioRenderer(audio_stream());
485 InitializePipeline(PIPELINE_OK
);
486 EXPECT_TRUE(metadata_
.has_audio
);
487 EXPECT_TRUE(metadata_
.has_video
);
490 message_loop_
.RunUntilIdle();
493 TEST_F(PipelineTest
, Seek
) {
497 MockDemuxerStreamVector streams
;
498 streams
.push_back(audio_stream());
499 streams
.push_back(video_stream());
501 InitializeDemuxer(&streams
, base::TimeDelta::FromSeconds(3000));
502 InitializeAudioRenderer(audio_stream());
503 InitializeVideoRenderer(video_stream());
505 // Initialize then seek!
506 InitializePipeline(PIPELINE_OK
);
509 message_loop_
.RunUntilIdle();
511 // Every filter should receive a call to Seek().
512 base::TimeDelta expected
= base::TimeDelta::FromSeconds(2000);
513 ExpectSeek(expected
);
517 TEST_F(PipelineTest
, SetVolume
) {
519 MockDemuxerStreamVector streams
;
520 streams
.push_back(audio_stream());
522 InitializeDemuxer(&streams
);
523 InitializeAudioRenderer(audio_stream());
525 // The audio renderer should receive a call to SetVolume().
526 float expected
= 0.5f
;
527 EXPECT_CALL(*audio_renderer_
, SetVolume(expected
));
529 // Initialize then set volume!
530 InitializePipeline(PIPELINE_OK
);
531 pipeline_
->SetVolume(expected
);
534 TEST_F(PipelineTest
, Properties
) {
536 MockDemuxerStreamVector streams
;
537 streams
.push_back(video_stream());
539 const base::TimeDelta kDuration
= base::TimeDelta::FromSeconds(100);
540 InitializeDemuxer(&streams
, kDuration
);
541 InitializeVideoRenderer(video_stream());
543 InitializePipeline(PIPELINE_OK
);
544 EXPECT_EQ(kDuration
.ToInternalValue(),
545 pipeline_
->GetMediaDuration().ToInternalValue());
546 EXPECT_FALSE(pipeline_
->DidLoadingProgress());
549 TEST_F(PipelineTest
, GetBufferedTimeRanges
) {
551 MockDemuxerStreamVector streams
;
552 streams
.push_back(video_stream());
554 const base::TimeDelta kDuration
= base::TimeDelta::FromSeconds(100);
555 InitializeDemuxer(&streams
, kDuration
);
556 InitializeVideoRenderer(video_stream());
558 InitializePipeline(PIPELINE_OK
);
560 EXPECT_EQ(0u, pipeline_
->GetBufferedTimeRanges().size());
562 EXPECT_FALSE(pipeline_
->DidLoadingProgress());
563 pipeline_
->AddBufferedTimeRange(base::TimeDelta(), kDuration
/ 8);
564 EXPECT_TRUE(pipeline_
->DidLoadingProgress());
565 EXPECT_FALSE(pipeline_
->DidLoadingProgress());
566 EXPECT_EQ(1u, pipeline_
->GetBufferedTimeRanges().size());
567 EXPECT_EQ(base::TimeDelta(), pipeline_
->GetBufferedTimeRanges().start(0));
568 EXPECT_EQ(kDuration
/ 8, pipeline_
->GetBufferedTimeRanges().end(0));
570 base::TimeDelta kSeekTime
= kDuration
/ 2;
571 ExpectSeek(kSeekTime
);
574 EXPECT_FALSE(pipeline_
->DidLoadingProgress());
577 TEST_F(PipelineTest
, EndedCallback
) {
581 MockDemuxerStreamVector streams
;
582 streams
.push_back(audio_stream());
583 streams
.push_back(video_stream());
585 InitializeDemuxer(&streams
);
586 InitializeAudioRenderer(audio_stream());
587 InitializeVideoRenderer(video_stream());
588 InitializePipeline(PIPELINE_OK
);
592 // The ended callback shouldn't run until all renderers have ended.
593 pipeline_
->OnAudioRendererEnded();
594 message_loop_
.RunUntilIdle();
596 pipeline_
->OnVideoRendererEnded();
597 message_loop_
.RunUntilIdle();
599 EXPECT_CALL(callbacks_
, OnEnded());
600 text_stream()->SendEosNotification();
601 message_loop_
.RunUntilIdle();
604 TEST_F(PipelineTest
, AudioStreamShorterThanVideo
) {
605 base::TimeDelta duration
= base::TimeDelta::FromSeconds(10);
609 MockDemuxerStreamVector streams
;
610 streams
.push_back(audio_stream());
611 streams
.push_back(video_stream());
613 // Replace the clock so we can simulate wall clock time advancing w/o using
615 pipeline_
->SetClockForTesting(new Clock(&test_tick_clock_
));
617 InitializeDemuxer(&streams
, duration
);
618 InitializeAudioRenderer(audio_stream());
619 InitializeVideoRenderer(video_stream());
620 InitializePipeline(PIPELINE_OK
);
622 EXPECT_EQ(0, pipeline_
->GetMediaTime().ToInternalValue());
624 float playback_rate
= 1.0f
;
625 EXPECT_CALL(*video_renderer_
, SetPlaybackRate(playback_rate
));
626 EXPECT_CALL(*audio_renderer_
, SetPlaybackRate(playback_rate
));
627 pipeline_
->SetPlaybackRate(playback_rate
);
628 message_loop_
.RunUntilIdle();
632 // Verify that the clock doesn't advance since it hasn't been started by
633 // a time update from the audio stream.
634 int64 start_time
= pipeline_
->GetMediaTime().ToInternalValue();
635 test_tick_clock_
.Advance(base::TimeDelta::FromMilliseconds(100));
636 EXPECT_EQ(pipeline_
->GetMediaTime().ToInternalValue(), start_time
);
638 // Signal end of audio stream.
639 pipeline_
->OnAudioRendererEnded();
640 message_loop_
.RunUntilIdle();
642 // Verify that the clock advances.
643 start_time
= pipeline_
->GetMediaTime().ToInternalValue();
644 test_tick_clock_
.Advance(base::TimeDelta::FromMilliseconds(100));
645 EXPECT_GT(pipeline_
->GetMediaTime().ToInternalValue(), start_time
);
647 // Signal end of video stream and make sure OnEnded() callback occurs.
648 EXPECT_CALL(callbacks_
, OnEnded());
649 pipeline_
->OnVideoRendererEnded();
652 TEST_F(PipelineTest
, ErrorDuringSeek
) {
654 MockDemuxerStreamVector streams
;
655 streams
.push_back(audio_stream());
657 InitializeDemuxer(&streams
);
658 InitializeAudioRenderer(audio_stream());
659 InitializePipeline(PIPELINE_OK
);
661 float playback_rate
= 1.0f
;
662 EXPECT_CALL(*audio_renderer_
, SetPlaybackRate(playback_rate
));
663 pipeline_
->SetPlaybackRate(playback_rate
);
664 message_loop_
.RunUntilIdle();
666 base::TimeDelta seek_time
= base::TimeDelta::FromSeconds(5);
668 // Preroll() isn't called as the demuxer errors out first.
669 EXPECT_CALL(*audio_renderer_
, Pause());
670 EXPECT_CALL(*audio_renderer_
, Flush(_
))
671 .WillOnce(RunClosure
<0>());
672 EXPECT_CALL(*audio_renderer_
, Stop(_
))
673 .WillOnce(RunClosure
<0>());
675 EXPECT_CALL(*demuxer_
, Seek(seek_time
, _
))
676 .WillOnce(RunCallback
<1>(PIPELINE_ERROR_READ
));
677 EXPECT_CALL(*demuxer_
, Stop(_
))
678 .WillOnce(RunClosure
<0>());
680 pipeline_
->Seek(seek_time
, base::Bind(&CallbackHelper::OnSeek
,
681 base::Unretained(&callbacks_
)));
682 EXPECT_CALL(callbacks_
, OnSeek(PIPELINE_ERROR_READ
));
683 message_loop_
.RunUntilIdle();
686 // Invoked function OnError. This asserts that the pipeline does not enqueue
687 // non-teardown related tasks while tearing down.
688 static void TestNoCallsAfterError(
689 Pipeline
* pipeline
, base::MessageLoop
* message_loop
,
690 PipelineStatus
/* status */) {
694 // When we get to this stage, the message loop should be empty.
695 EXPECT_TRUE(message_loop
->IsIdleForTesting());
697 // Make calls on pipeline after error has occurred.
698 pipeline
->SetPlaybackRate(0.5f
);
699 pipeline
->SetVolume(0.5f
);
701 // No additional tasks should be queued as a result of these calls.
702 EXPECT_TRUE(message_loop
->IsIdleForTesting());
705 TEST_F(PipelineTest
, NoMessageDuringTearDownFromError
) {
707 MockDemuxerStreamVector streams
;
708 streams
.push_back(audio_stream());
710 InitializeDemuxer(&streams
);
711 InitializeAudioRenderer(audio_stream());
712 InitializePipeline(PIPELINE_OK
);
714 // Trigger additional requests on the pipeline during tear down from error.
715 base::Callback
<void(PipelineStatus
)> cb
= base::Bind(
716 &TestNoCallsAfterError
, pipeline_
.get(), &message_loop_
);
717 ON_CALL(callbacks_
, OnError(_
))
718 .WillByDefault(Invoke(&cb
, &base::Callback
<void(PipelineStatus
)>::Run
));
720 base::TimeDelta seek_time
= base::TimeDelta::FromSeconds(5);
722 // Seek() isn't called as the demuxer errors out first.
723 EXPECT_CALL(*audio_renderer_
, Pause());
724 EXPECT_CALL(*audio_renderer_
, Flush(_
))
725 .WillOnce(RunClosure
<0>());
726 EXPECT_CALL(*audio_renderer_
, Stop(_
))
727 .WillOnce(RunClosure
<0>());
729 EXPECT_CALL(*demuxer_
, Seek(seek_time
, _
))
730 .WillOnce(RunCallback
<1>(PIPELINE_ERROR_READ
));
731 EXPECT_CALL(*demuxer_
, Stop(_
))
732 .WillOnce(RunClosure
<0>());
734 pipeline_
->Seek(seek_time
, base::Bind(&CallbackHelper::OnSeek
,
735 base::Unretained(&callbacks_
)));
736 EXPECT_CALL(callbacks_
, OnSeek(PIPELINE_ERROR_READ
));
737 message_loop_
.RunUntilIdle();
740 TEST_F(PipelineTest
, StartTimeIsZero
) {
742 MockDemuxerStreamVector streams
;
743 streams
.push_back(video_stream());
745 const base::TimeDelta kDuration
= base::TimeDelta::FromSeconds(100);
746 InitializeDemuxer(&streams
, kDuration
);
747 InitializeVideoRenderer(video_stream());
749 InitializePipeline(PIPELINE_OK
);
750 EXPECT_FALSE(metadata_
.has_audio
);
751 EXPECT_TRUE(metadata_
.has_video
);
753 EXPECT_EQ(base::TimeDelta(), pipeline_
->GetMediaTime());
756 TEST_F(PipelineTest
, StartTimeIsNonZero
) {
757 const base::TimeDelta kStartTime
= base::TimeDelta::FromSeconds(4);
758 const base::TimeDelta kDuration
= base::TimeDelta::FromSeconds(100);
760 EXPECT_CALL(*demuxer_
, GetStartTime())
761 .WillRepeatedly(Return(kStartTime
));
764 MockDemuxerStreamVector streams
;
765 streams
.push_back(video_stream());
767 InitializeDemuxer(&streams
, kDuration
);
768 InitializeVideoRenderer(video_stream());
770 InitializePipeline(PIPELINE_OK
);
771 EXPECT_FALSE(metadata_
.has_audio
);
772 EXPECT_TRUE(metadata_
.has_video
);
774 EXPECT_EQ(kStartTime
, pipeline_
->GetMediaTime());
777 static void RunTimeCB(const AudioRenderer::TimeCB
& time_cb
,
779 int max_time_in_ms
) {
780 time_cb
.Run(base::TimeDelta::FromMilliseconds(time_in_ms
),
781 base::TimeDelta::FromMilliseconds(max_time_in_ms
));
784 TEST_F(PipelineTest
, AudioTimeUpdateDuringSeek
) {
786 MockDemuxerStreamVector streams
;
787 streams
.push_back(audio_stream());
789 InitializeDemuxer(&streams
);
790 InitializeAudioRenderer(audio_stream());
791 InitializePipeline(PIPELINE_OK
);
793 float playback_rate
= 1.0f
;
794 EXPECT_CALL(*audio_renderer_
, SetPlaybackRate(playback_rate
));
795 pipeline_
->SetPlaybackRate(playback_rate
);
796 message_loop_
.RunUntilIdle();
798 // Provide an initial time update so that the pipeline transitions out of the
799 // "waiting for time update" state.
800 audio_time_cb_
.Run(base::TimeDelta::FromMilliseconds(100),
801 base::TimeDelta::FromMilliseconds(500));
803 base::TimeDelta seek_time
= base::TimeDelta::FromSeconds(5);
805 // Arrange to trigger a time update while the demuxer is in the middle of
806 // seeking. This update should be ignored by the pipeline and the clock should
808 base::Closure closure
= base::Bind(&RunTimeCB
, audio_time_cb_
, 300, 700);
809 EXPECT_CALL(*demuxer_
, Seek(seek_time
, _
))
810 .WillOnce(DoAll(InvokeWithoutArgs(&closure
, &base::Closure::Run
),
811 RunCallback
<1>(PIPELINE_OK
)));
813 EXPECT_CALL(*audio_renderer_
, Pause());
814 EXPECT_CALL(*audio_renderer_
, Flush(_
))
815 .WillOnce(RunClosure
<0>());
816 EXPECT_CALL(*audio_renderer_
, Preroll(seek_time
, _
))
817 .WillOnce(RunCallback
<1>(PIPELINE_OK
));
818 EXPECT_CALL(*audio_renderer_
, SetPlaybackRate(_
));
819 EXPECT_CALL(*audio_renderer_
, SetVolume(_
));
820 EXPECT_CALL(*audio_renderer_
, Play());
822 EXPECT_CALL(callbacks_
, OnPrerollCompleted());
823 EXPECT_CALL(callbacks_
, OnSeek(PIPELINE_OK
));
826 EXPECT_EQ(pipeline_
->GetMediaTime(), seek_time
);
828 // Now that the seek is complete, verify that time updates advance the current
830 base::TimeDelta new_time
= seek_time
+ base::TimeDelta::FromMilliseconds(100);
831 audio_time_cb_
.Run(new_time
, new_time
);
833 EXPECT_EQ(pipeline_
->GetMediaTime(), new_time
);
836 static void DeletePipeline(scoped_ptr
<Pipeline
> pipeline
) {
837 // |pipeline| will go out of scope.
840 TEST_F(PipelineTest
, DeleteAfterStop
) {
842 MockDemuxerStreamVector streams
;
843 streams
.push_back(audio_stream());
844 InitializeDemuxer(&streams
);
845 InitializeAudioRenderer(audio_stream());
846 InitializePipeline(PIPELINE_OK
);
850 Pipeline
* pipeline
= pipeline_
.get();
851 pipeline
->Stop(base::Bind(&DeletePipeline
, base::Passed(&pipeline_
)));
852 message_loop_
.RunUntilIdle();
855 class PipelineTeardownTest
: public PipelineTest
{
875 PipelineTeardownTest() {}
876 virtual ~PipelineTeardownTest() {}
878 void RunTest(TeardownState state
, StopOrError stop_or_error
) {
881 case kInitAudioRenderer
:
882 case kInitVideoRenderer
:
883 DoInitialize(state
, stop_or_error
);
891 DoInitialize(state
, stop_or_error
);
892 DoSeek(state
, stop_or_error
);
896 DoInitialize(state
, stop_or_error
);
897 DoStopOrError(stop_or_error
);
903 // TODO(scherkus): We do radically different things whether teardown is
904 // invoked via stop vs error. The teardown path should be the same,
905 // see http://crbug.com/110228
906 void DoInitialize(TeardownState state
, StopOrError stop_or_error
) {
907 PipelineStatus expected_status
=
908 SetInitializeExpectations(state
, stop_or_error
);
910 EXPECT_CALL(callbacks_
, OnStart(expected_status
));
912 filter_collection_
.Pass(),
913 base::Bind(&CallbackHelper::OnEnded
, base::Unretained(&callbacks_
)),
914 base::Bind(&CallbackHelper::OnError
, base::Unretained(&callbacks_
)),
915 base::Bind(&CallbackHelper::OnStart
, base::Unretained(&callbacks_
)),
916 base::Bind(&CallbackHelper::OnMetadata
, base::Unretained(&callbacks_
)),
917 base::Bind(&CallbackHelper::OnPrerollCompleted
,
918 base::Unretained(&callbacks_
)),
919 base::Bind(&CallbackHelper::OnDurationChange
,
920 base::Unretained(&callbacks_
)));
921 message_loop_
.RunUntilIdle();
924 PipelineStatus
SetInitializeExpectations(TeardownState state
,
925 StopOrError stop_or_error
) {
926 PipelineStatus status
= PIPELINE_OK
;
927 base::Closure stop_cb
= base::Bind(
928 &CallbackHelper::OnStop
, base::Unretained(&callbacks_
));
930 if (state
== kInitDemuxer
) {
931 if (stop_or_error
== kStop
) {
932 EXPECT_CALL(*demuxer_
, Initialize(_
, _
, _
))
933 .WillOnce(DoAll(Stop(pipeline_
.get(), stop_cb
),
934 RunCallback
<1>(PIPELINE_OK
)));
935 EXPECT_CALL(callbacks_
, OnStop());
937 status
= DEMUXER_ERROR_COULD_NOT_OPEN
;
938 EXPECT_CALL(*demuxer_
, Initialize(_
, _
, _
))
939 .WillOnce(RunCallback
<1>(status
));
942 EXPECT_CALL(*demuxer_
, Stop(_
)).WillOnce(RunClosure
<0>());
948 MockDemuxerStreamVector streams
;
949 streams
.push_back(audio_stream());
950 streams
.push_back(video_stream());
951 InitializeDemuxer(&streams
, base::TimeDelta::FromSeconds(3000));
953 if (state
== kInitAudioRenderer
) {
954 if (stop_or_error
== kStop
) {
955 EXPECT_CALL(*audio_renderer_
, Initialize(_
, _
, _
, _
, _
, _
, _
))
956 .WillOnce(DoAll(Stop(pipeline_
.get(), stop_cb
),
957 RunCallback
<1>(PIPELINE_OK
)));
958 EXPECT_CALL(callbacks_
, OnStop());
960 status
= PIPELINE_ERROR_INITIALIZATION_FAILED
;
961 EXPECT_CALL(*audio_renderer_
, Initialize(_
, _
, _
, _
, _
, _
, _
))
962 .WillOnce(RunCallback
<1>(status
));
965 EXPECT_CALL(*demuxer_
, Stop(_
)).WillOnce(RunClosure
<0>());
966 EXPECT_CALL(*audio_renderer_
, Stop(_
)).WillOnce(RunClosure
<0>());
970 EXPECT_CALL(*audio_renderer_
, Initialize(_
, _
, _
, _
, _
, _
, _
))
971 .WillOnce(RunCallback
<1>(PIPELINE_OK
));
973 if (state
== kInitVideoRenderer
) {
974 if (stop_or_error
== kStop
) {
975 EXPECT_CALL(*video_renderer_
, Initialize(_
, _
, _
, _
, _
, _
, _
, _
, _
))
976 .WillOnce(DoAll(Stop(pipeline_
.get(), stop_cb
),
977 RunCallback
<2>(PIPELINE_OK
)));
978 EXPECT_CALL(callbacks_
, OnStop());
980 status
= PIPELINE_ERROR_INITIALIZATION_FAILED
;
981 EXPECT_CALL(*video_renderer_
, Initialize(_
, _
, _
, _
, _
, _
, _
, _
, _
))
982 .WillOnce(RunCallback
<2>(status
));
985 EXPECT_CALL(*demuxer_
, Stop(_
)).WillOnce(RunClosure
<0>());
986 EXPECT_CALL(*audio_renderer_
, Stop(_
)).WillOnce(RunClosure
<0>());
987 EXPECT_CALL(*video_renderer_
, Stop(_
)).WillOnce(RunClosure
<0>());
991 EXPECT_CALL(*video_renderer_
, Initialize(_
, _
, _
, _
, _
, _
, _
, _
, _
))
992 .WillOnce(RunCallback
<2>(PIPELINE_OK
));
994 EXPECT_CALL(callbacks_
, OnMetadata(_
));
996 // If we get here it's a successful initialization.
997 EXPECT_CALL(*audio_renderer_
, Preroll(base::TimeDelta(), _
))
998 .WillOnce(RunCallback
<1>(PIPELINE_OK
));
999 EXPECT_CALL(*video_renderer_
, Preroll(base::TimeDelta(), _
))
1000 .WillOnce(RunCallback
<1>(PIPELINE_OK
));
1002 EXPECT_CALL(*audio_renderer_
, SetPlaybackRate(0.0f
));
1003 EXPECT_CALL(*video_renderer_
, SetPlaybackRate(0.0f
));
1004 EXPECT_CALL(*audio_renderer_
, SetVolume(1.0f
));
1006 EXPECT_CALL(*audio_renderer_
, Play());
1007 EXPECT_CALL(*video_renderer_
, Play(_
))
1008 .WillOnce(RunClosure
<0>());
1010 if (status
== PIPELINE_OK
)
1011 EXPECT_CALL(callbacks_
, OnPrerollCompleted());
1016 void DoSeek(TeardownState state
, StopOrError stop_or_error
) {
1018 PipelineStatus status
= SetSeekExpectations(state
, stop_or_error
);
1020 EXPECT_CALL(*demuxer_
, Stop(_
)).WillOnce(RunClosure
<0>());
1021 EXPECT_CALL(*audio_renderer_
, Stop(_
)).WillOnce(RunClosure
<0>());
1022 EXPECT_CALL(*video_renderer_
, Stop(_
)).WillOnce(RunClosure
<0>());
1023 EXPECT_CALL(callbacks_
, OnSeek(status
));
1025 if (status
== PIPELINE_OK
) {
1026 EXPECT_CALL(callbacks_
, OnStop());
1029 pipeline_
->Seek(base::TimeDelta::FromSeconds(10), base::Bind(
1030 &CallbackHelper::OnSeek
, base::Unretained(&callbacks_
)));
1031 message_loop_
.RunUntilIdle();
1034 PipelineStatus
SetSeekExpectations(TeardownState state
,
1035 StopOrError stop_or_error
) {
1036 PipelineStatus status
= PIPELINE_OK
;
1037 base::Closure stop_cb
= base::Bind(
1038 &CallbackHelper::OnStop
, base::Unretained(&callbacks_
));
1040 if (state
== kPausing
) {
1041 if (stop_or_error
== kStop
) {
1042 EXPECT_CALL(*audio_renderer_
, Pause())
1043 .WillOnce(Stop(pipeline_
.get(), stop_cb
));
1045 status
= PIPELINE_ERROR_READ
;
1046 EXPECT_CALL(*audio_renderer_
, Pause())
1047 .WillOnce(SetError(pipeline_
.get(), status
));
1053 EXPECT_CALL(*audio_renderer_
, Pause());
1054 EXPECT_CALL(*video_renderer_
, Pause(_
)).WillOnce(RunClosure
<0>());
1056 if (state
== kFlushing
) {
1057 if (stop_or_error
== kStop
) {
1058 EXPECT_CALL(*audio_renderer_
, Flush(_
))
1059 .WillOnce(DoAll(Stop(pipeline_
.get(), stop_cb
), RunClosure
<0>()));
1061 status
= PIPELINE_ERROR_READ
;
1062 EXPECT_CALL(*audio_renderer_
, Flush(_
)).WillOnce(
1063 DoAll(SetError(pipeline_
.get(), status
), RunClosure
<0>()));
1069 EXPECT_CALL(*audio_renderer_
, Flush(_
)).WillOnce(RunClosure
<0>());
1070 EXPECT_CALL(*video_renderer_
, Flush(_
)).WillOnce(RunClosure
<0>());
1072 if (state
== kSeeking
) {
1073 if (stop_or_error
== kStop
) {
1074 EXPECT_CALL(*demuxer_
, Seek(_
, _
))
1075 .WillOnce(DoAll(Stop(pipeline_
.get(), stop_cb
),
1076 RunCallback
<1>(PIPELINE_OK
)));
1078 status
= PIPELINE_ERROR_READ
;
1079 EXPECT_CALL(*demuxer_
, Seek(_
, _
))
1080 .WillOnce(RunCallback
<1>(status
));
1086 EXPECT_CALL(*demuxer_
, Seek(_
, _
))
1087 .WillOnce(RunCallback
<1>(PIPELINE_OK
));
1089 if (state
== kPrerolling
) {
1090 if (stop_or_error
== kStop
) {
1091 EXPECT_CALL(*audio_renderer_
, Preroll(_
, _
))
1092 .WillOnce(DoAll(Stop(pipeline_
.get(), stop_cb
),
1093 RunCallback
<1>(PIPELINE_OK
)));
1095 status
= PIPELINE_ERROR_READ
;
1096 EXPECT_CALL(*audio_renderer_
, Preroll(_
, _
))
1097 .WillOnce(RunCallback
<1>(status
));
1103 EXPECT_CALL(*audio_renderer_
, Preroll(_
, _
))
1104 .WillOnce(RunCallback
<1>(PIPELINE_OK
));
1105 EXPECT_CALL(*video_renderer_
, Preroll(_
, _
))
1106 .WillOnce(RunCallback
<1>(PIPELINE_OK
));
1108 // Playback rate and volume are updated prior to starting.
1109 EXPECT_CALL(*audio_renderer_
, SetPlaybackRate(0.0f
));
1110 EXPECT_CALL(*video_renderer_
, SetPlaybackRate(0.0f
));
1111 EXPECT_CALL(*audio_renderer_
, SetVolume(1.0f
));
1113 if (state
== kStarting
) {
1114 if (stop_or_error
== kStop
) {
1115 EXPECT_CALL(*audio_renderer_
, Play())
1116 .WillOnce(Stop(pipeline_
.get(), stop_cb
));
1118 status
= PIPELINE_ERROR_READ
;
1119 EXPECT_CALL(*audio_renderer_
, Play())
1120 .WillOnce(SetError(pipeline_
.get(), status
));
1125 NOTREACHED() << "State not supported: " << state
;
1129 void DoStopOrError(StopOrError stop_or_error
) {
1132 EXPECT_CALL(*demuxer_
, Stop(_
)).WillOnce(RunClosure
<0>());
1133 EXPECT_CALL(*audio_renderer_
, Stop(_
)).WillOnce(RunClosure
<0>());
1134 EXPECT_CALL(*video_renderer_
, Stop(_
)).WillOnce(RunClosure
<0>());
1136 switch (stop_or_error
) {
1138 EXPECT_CALL(callbacks_
, OnStop());
1139 pipeline_
->Stop(base::Bind(
1140 &CallbackHelper::OnStop
, base::Unretained(&callbacks_
)));
1144 EXPECT_CALL(callbacks_
, OnError(PIPELINE_ERROR_READ
));
1145 pipeline_
->SetErrorForTesting(PIPELINE_ERROR_READ
);
1149 EXPECT_CALL(callbacks_
, OnStop());
1150 pipeline_
->SetErrorForTesting(PIPELINE_ERROR_READ
);
1151 pipeline_
->Stop(base::Bind(
1152 &CallbackHelper::OnStop
, base::Unretained(&callbacks_
)));
1156 message_loop_
.RunUntilIdle();
1159 DISALLOW_COPY_AND_ASSIGN(PipelineTeardownTest
);
1162 #define INSTANTIATE_TEARDOWN_TEST(stop_or_error, state) \
1163 TEST_F(PipelineTeardownTest, stop_or_error##_##state) { \
1164 RunTest(k##state, k##stop_or_error); \
1167 INSTANTIATE_TEARDOWN_TEST(Stop
, InitDemuxer
);
1168 INSTANTIATE_TEARDOWN_TEST(Stop
, InitAudioRenderer
);
1169 INSTANTIATE_TEARDOWN_TEST(Stop
, InitVideoRenderer
);
1170 INSTANTIATE_TEARDOWN_TEST(Stop
, Pausing
);
1171 INSTANTIATE_TEARDOWN_TEST(Stop
, Flushing
);
1172 INSTANTIATE_TEARDOWN_TEST(Stop
, Seeking
);
1173 INSTANTIATE_TEARDOWN_TEST(Stop
, Prerolling
);
1174 INSTANTIATE_TEARDOWN_TEST(Stop
, Starting
);
1175 INSTANTIATE_TEARDOWN_TEST(Stop
, Playing
);
1177 INSTANTIATE_TEARDOWN_TEST(Error
, InitDemuxer
);
1178 INSTANTIATE_TEARDOWN_TEST(Error
, InitAudioRenderer
);
1179 INSTANTIATE_TEARDOWN_TEST(Error
, InitVideoRenderer
);
1180 INSTANTIATE_TEARDOWN_TEST(Error
, Pausing
);
1181 INSTANTIATE_TEARDOWN_TEST(Error
, Flushing
);
1182 INSTANTIATE_TEARDOWN_TEST(Error
, Seeking
);
1183 INSTANTIATE_TEARDOWN_TEST(Error
, Prerolling
);
1184 INSTANTIATE_TEARDOWN_TEST(Error
, Starting
);
1185 INSTANTIATE_TEARDOWN_TEST(Error
, Playing
);
1187 INSTANTIATE_TEARDOWN_TEST(ErrorAndStop
, Playing
);
1189 } // namespace media