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.
5 #include "media/test/pipeline_integration_test_base.h"
8 #include "base/memory/scoped_vector.h"
9 #include "media/base/cdm_context.h"
10 #include "media/base/media_log.h"
11 #include "media/base/test_data_util.h"
12 #include "media/filters/audio_renderer_impl.h"
13 #include "media/filters/chunk_demuxer.h"
14 #if !defined(MEDIA_DISABLE_FFMPEG)
15 #include "media/filters/ffmpeg_audio_decoder.h"
16 #include "media/filters/ffmpeg_demuxer.h"
17 #include "media/filters/ffmpeg_video_decoder.h"
19 #include "media/filters/file_data_source.h"
20 #include "media/filters/opus_audio_decoder.h"
21 #include "media/filters/renderer_impl.h"
22 #if !defined(MEDIA_DISABLE_LIBVPX)
23 #include "media/filters/vpx_video_decoder.h"
27 using ::testing::AnyNumber
;
28 using ::testing::AtMost
;
29 using ::testing::InvokeWithoutArgs
;
30 using ::testing::SaveArg
;
34 const char kNullVideoHash
[] = "d41d8cd98f00b204e9800998ecf8427e";
35 const char kNullAudioHash
[] = "0.00,0.00,0.00,0.00,0.00,0.00,";
37 PipelineIntegrationTestBase::PipelineIntegrationTestBase()
38 : hashing_enabled_(false),
39 clockless_playback_(false),
41 new Pipeline(message_loop_
.message_loop_proxy(), new MediaLog())),
43 pipeline_status_(PIPELINE_OK
),
44 last_video_frame_format_(VideoFrame::UNKNOWN
),
45 hardware_config_(AudioParameters(), AudioParameters()) {
46 base::MD5Init(&md5_context_
);
49 PipelineIntegrationTestBase::~PipelineIntegrationTestBase() {
50 if (!pipeline_
->IsRunning())
56 void PipelineIntegrationTestBase::OnSeeked(base::TimeDelta seek_time
,
57 PipelineStatus status
) {
58 EXPECT_EQ(seek_time
, pipeline_
->GetMediaTime());
59 pipeline_status_
= status
;
62 void PipelineIntegrationTestBase::OnStatusCallback(
63 PipelineStatus status
) {
64 pipeline_status_
= status
;
65 message_loop_
.PostTask(FROM_HERE
, base::MessageLoop::QuitClosure());
68 void PipelineIntegrationTestBase::DemuxerEncryptedMediaInitDataCB(
69 const std::string
& type
,
70 const std::vector
<uint8
>& init_data
) {
71 DCHECK(!init_data
.empty());
72 CHECK(!encrypted_media_init_data_cb_
.is_null());
73 encrypted_media_init_data_cb_
.Run(type
, init_data
);
76 void PipelineIntegrationTestBase::OnEnded() {
79 pipeline_status_
= PIPELINE_OK
;
80 message_loop_
.PostTask(FROM_HERE
, base::MessageLoop::QuitClosure());
83 bool PipelineIntegrationTestBase::WaitUntilOnEnded() {
85 return (pipeline_status_
== PIPELINE_OK
);
88 return ended_
&& (pipeline_status_
== PIPELINE_OK
);
91 PipelineStatus
PipelineIntegrationTestBase::WaitUntilEndedOrError() {
92 if (ended_
|| pipeline_status_
!= PIPELINE_OK
)
93 return pipeline_status_
;
95 return pipeline_status_
;
98 void PipelineIntegrationTestBase::OnError(PipelineStatus status
) {
99 DCHECK_NE(status
, PIPELINE_OK
);
100 pipeline_status_
= status
;
101 message_loop_
.PostTask(FROM_HERE
, base::MessageLoop::QuitClosure());
104 PipelineStatus
PipelineIntegrationTestBase::Start(const std::string
& filename
) {
105 return Start(filename
, nullptr);
108 PipelineStatus
PipelineIntegrationTestBase::Start(const std::string
& filename
,
109 CdmContext
* cdm_context
) {
110 EXPECT_CALL(*this, OnMetadata(_
))
112 .WillRepeatedly(SaveArg
<0>(&metadata_
));
113 EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH
))
115 CreateDemuxer(filename
);
118 EXPECT_CALL(*this, DecryptorAttached(true));
120 cdm_context
, base::Bind(&PipelineIntegrationTestBase::DecryptorAttached
,
121 base::Unretained(this)));
125 demuxer_
.get(), CreateRenderer(),
126 base::Bind(&PipelineIntegrationTestBase::OnEnded
, base::Unretained(this)),
127 base::Bind(&PipelineIntegrationTestBase::OnError
, base::Unretained(this)),
128 base::Bind(&PipelineIntegrationTestBase::OnStatusCallback
,
129 base::Unretained(this)),
130 base::Bind(&PipelineIntegrationTestBase::OnMetadata
,
131 base::Unretained(this)),
132 base::Bind(&PipelineIntegrationTestBase::OnBufferingStateChanged
,
133 base::Unretained(this)),
134 base::Bind(&PipelineIntegrationTestBase::OnVideoFramePaint
,
135 base::Unretained(this)),
136 base::Closure(), base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack
,
137 base::Unretained(this)));
139 return pipeline_status_
;
142 PipelineStatus
PipelineIntegrationTestBase::Start(const std::string
& filename
,
143 kTestType test_type
) {
144 hashing_enabled_
= test_type
== kHashed
;
145 clockless_playback_
= test_type
== kClockless
;
146 return Start(filename
);
149 void PipelineIntegrationTestBase::Play() {
150 pipeline_
->SetPlaybackRate(1);
153 void PipelineIntegrationTestBase::Pause() {
154 pipeline_
->SetPlaybackRate(0);
157 bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time
) {
160 EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH
))
161 .WillOnce(InvokeWithoutArgs(&message_loop_
, &base::MessageLoop::QuitNow
));
162 pipeline_
->Seek(seek_time
, base::Bind(&PipelineIntegrationTestBase::OnSeeked
,
163 base::Unretained(this), seek_time
));
165 return (pipeline_status_
== PIPELINE_OK
);
168 void PipelineIntegrationTestBase::Stop() {
169 DCHECK(pipeline_
->IsRunning());
170 pipeline_
->Stop(base::MessageLoop::QuitClosure());
174 void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask(
175 const base::TimeDelta
& quit_time
) {
176 if (pipeline_
->GetMediaTime() >= quit_time
||
177 pipeline_status_
!= PIPELINE_OK
) {
178 message_loop_
.Quit();
182 message_loop_
.PostDelayedTask(
184 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask
,
185 base::Unretained(this), quit_time
),
186 base::TimeDelta::FromMilliseconds(10));
189 bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter(
190 const base::TimeDelta
& wait_time
) {
191 DCHECK(pipeline_
->IsRunning());
192 DCHECK_GT(pipeline_
->GetPlaybackRate(), 0);
193 DCHECK(wait_time
<= pipeline_
->GetMediaDuration());
195 message_loop_
.PostDelayedTask(
197 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask
,
198 base::Unretained(this),
200 base::TimeDelta::FromMilliseconds(10));
202 return (pipeline_status_
== PIPELINE_OK
);
205 void PipelineIntegrationTestBase::CreateDemuxer(const std::string
& filename
) {
206 FileDataSource
* file_data_source
= new FileDataSource();
207 base::FilePath
file_path(GetTestDataFilePath(filename
));
208 CHECK(file_data_source
->Initialize(file_path
)) << "Is " << file_path
.value()
210 data_source_
.reset(file_data_source
);
212 Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb
=
213 base::Bind(&PipelineIntegrationTestBase::DemuxerEncryptedMediaInitDataCB
,
214 base::Unretained(this));
216 #if !defined(MEDIA_DISABLE_FFMPEG)
217 demuxer_
= scoped_ptr
<Demuxer
>(
218 new FFmpegDemuxer(message_loop_
.message_loop_proxy(), data_source_
.get(),
219 encrypted_media_init_data_cb
, new MediaLog()));
223 scoped_ptr
<Renderer
> PipelineIntegrationTestBase::CreateRenderer() {
224 ScopedVector
<VideoDecoder
> video_decoders
;
225 #if !defined(MEDIA_DISABLE_LIBVPX)
226 video_decoders
.push_back(
227 new VpxVideoDecoder(message_loop_
.message_loop_proxy()));
228 #endif // !defined(MEDIA_DISABLE_LIBVPX)
230 #if !defined(MEDIA_DISABLE_FFMPEG)
231 video_decoders
.push_back(
232 new FFmpegVideoDecoder(message_loop_
.message_loop_proxy()));
235 // Disable frame dropping if hashing is enabled.
236 scoped_ptr
<VideoRenderer
> video_renderer(
237 new VideoRendererImpl(message_loop_
.message_loop_proxy(),
238 video_decoders
.Pass(), false, new MediaLog()));
240 if (!clockless_playback_
) {
241 audio_sink_
= new NullAudioSink(message_loop_
.message_loop_proxy());
243 clockless_audio_sink_
= new ClocklessAudioSink();
246 ScopedVector
<AudioDecoder
> audio_decoders
;
248 #if !defined(MEDIA_DISABLE_FFMPEG)
249 audio_decoders
.push_back(
250 new FFmpegAudioDecoder(message_loop_
.message_loop_proxy(), LogCB()));
253 audio_decoders
.push_back(
254 new OpusAudioDecoder(message_loop_
.message_loop_proxy()));
256 AudioParameters
out_params(AudioParameters::AUDIO_PCM_LOW_LATENCY
,
257 CHANNEL_LAYOUT_STEREO
,
261 hardware_config_
.UpdateOutputConfig(out_params
);
263 scoped_ptr
<AudioRenderer
> audio_renderer(new AudioRendererImpl(
264 message_loop_
.message_loop_proxy(),
265 (clockless_playback_
)
266 ? static_cast<AudioRendererSink
*>(clockless_audio_sink_
.get())
268 audio_decoders
.Pass(), hardware_config_
, new MediaLog()));
269 if (hashing_enabled_
)
270 audio_sink_
->StartAudioHashForTesting();
272 scoped_ptr
<RendererImpl
> renderer_impl(
273 new RendererImpl(message_loop_
.message_loop_proxy(),
274 audio_renderer
.Pass(),
275 video_renderer
.Pass()));
277 // Prevent non-deterministic buffering state callbacks from firing (e.g., slow
278 // machine, valgrind).
279 renderer_impl
->DisableUnderflowForTesting();
281 if (clockless_playback_
)
282 renderer_impl
->EnableClocklessVideoPlaybackForTesting();
284 return renderer_impl
.Pass();
287 void PipelineIntegrationTestBase::OnVideoFramePaint(
288 const scoped_refptr
<VideoFrame
>& frame
) {
289 last_video_frame_format_
= frame
->format();
290 if (!hashing_enabled_
)
292 frame
->HashFrameForTesting(&md5_context_
);
295 std::string
PipelineIntegrationTestBase::GetVideoHash() {
296 DCHECK(hashing_enabled_
);
297 base::MD5Digest digest
;
298 base::MD5Final(&digest
, &md5_context_
);
299 return base::MD5DigestToBase16(digest
);
302 std::string
PipelineIntegrationTestBase::GetAudioHash() {
303 DCHECK(hashing_enabled_
);
304 return audio_sink_
->GetAudioHashForTesting();
307 base::TimeDelta
PipelineIntegrationTestBase::GetAudioTime() {
308 DCHECK(clockless_playback_
);
309 return clockless_audio_sink_
->render_time();
312 base::TimeTicks
DummyTickClock::NowTicks() {
313 now_
+= base::TimeDelta::FromSeconds(60);