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/chunk_demuxer.h"
13 #if !defined(MEDIA_DISABLE_FFMPEG)
14 #include "media/filters/ffmpeg_audio_decoder.h"
15 #include "media/filters/ffmpeg_demuxer.h"
16 #include "media/filters/ffmpeg_video_decoder.h"
18 #include "media/filters/file_data_source.h"
19 #include "media/filters/opus_audio_decoder.h"
20 #include "media/renderers/audio_renderer_impl.h"
21 #include "media/renderers/renderer_impl.h"
22 #if !defined(MEDIA_DISABLE_LIBVPX)
23 #include "media/filters/vpx_video_decoder.h"
27 using ::testing::AnyNumber
;
28 using ::testing::AtLeast
;
29 using ::testing::AtMost
;
30 using ::testing::Invoke
;
31 using ::testing::InvokeWithoutArgs
;
32 using ::testing::SaveArg
;
36 const char kNullVideoHash
[] = "d41d8cd98f00b204e9800998ecf8427e";
37 const char kNullAudioHash
[] = "0.00,0.00,0.00,0.00,0.00,0.00,";
39 PipelineIntegrationTestBase::PipelineIntegrationTestBase()
40 : hashing_enabled_(false),
41 clockless_playback_(false),
42 pipeline_(new Pipeline(message_loop_
.task_runner(), new MediaLog())),
44 pipeline_status_(PIPELINE_OK
),
45 last_video_frame_format_(PIXEL_FORMAT_UNKNOWN
),
46 last_video_frame_color_space_(COLOR_SPACE_UNSPECIFIED
),
47 hardware_config_(AudioParameters(), AudioParameters()) {
48 base::MD5Init(&md5_context_
);
51 PipelineIntegrationTestBase::~PipelineIntegrationTestBase() {
52 if (!pipeline_
->IsRunning())
58 void PipelineIntegrationTestBase::OnSeeked(base::TimeDelta seek_time
,
59 PipelineStatus status
) {
60 EXPECT_EQ(seek_time
, pipeline_
->GetMediaTime());
61 pipeline_status_
= status
;
64 void PipelineIntegrationTestBase::OnStatusCallback(
65 PipelineStatus status
) {
66 pipeline_status_
= status
;
67 message_loop_
.PostTask(FROM_HERE
, base::MessageLoop::QuitClosure());
70 void PipelineIntegrationTestBase::DemuxerEncryptedMediaInitDataCB(
72 const std::vector
<uint8
>& init_data
) {
73 DCHECK(!init_data
.empty());
74 CHECK(!encrypted_media_init_data_cb_
.is_null());
75 encrypted_media_init_data_cb_
.Run(type
, init_data
);
78 void PipelineIntegrationTestBase::OnEnded() {
81 pipeline_status_
= PIPELINE_OK
;
82 message_loop_
.PostTask(FROM_HERE
, base::MessageLoop::QuitClosure());
85 bool PipelineIntegrationTestBase::WaitUntilOnEnded() {
87 return (pipeline_status_
== PIPELINE_OK
);
90 return ended_
&& (pipeline_status_
== PIPELINE_OK
);
93 PipelineStatus
PipelineIntegrationTestBase::WaitUntilEndedOrError() {
94 if (ended_
|| pipeline_status_
!= PIPELINE_OK
)
95 return pipeline_status_
;
97 return pipeline_status_
;
100 void PipelineIntegrationTestBase::OnError(PipelineStatus status
) {
101 DCHECK_NE(status
, PIPELINE_OK
);
102 pipeline_status_
= status
;
103 message_loop_
.PostTask(FROM_HERE
, base::MessageLoop::QuitClosure());
106 PipelineStatus
PipelineIntegrationTestBase::Start(const std::string
& filename
) {
107 return Start(filename
, nullptr);
110 PipelineStatus
PipelineIntegrationTestBase::Start(const std::string
& filename
,
111 CdmContext
* cdm_context
) {
112 EXPECT_CALL(*this, OnMetadata(_
))
114 .WillRepeatedly(SaveArg
<0>(&metadata_
));
115 EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH
))
117 EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_NOTHING
))
119 CreateDemuxer(filename
);
122 EXPECT_CALL(*this, DecryptorAttached(true));
124 cdm_context
, base::Bind(&PipelineIntegrationTestBase::DecryptorAttached
,
125 base::Unretained(this)));
128 // Should never be called as the required decryption keys for the encrypted
129 // media files are provided in advance.
130 EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0);
133 demuxer_
.get(), CreateRenderer(),
134 base::Bind(&PipelineIntegrationTestBase::OnEnded
, base::Unretained(this)),
135 base::Bind(&PipelineIntegrationTestBase::OnError
, base::Unretained(this)),
136 base::Bind(&PipelineIntegrationTestBase::OnStatusCallback
,
137 base::Unretained(this)),
138 base::Bind(&PipelineIntegrationTestBase::OnMetadata
,
139 base::Unretained(this)),
140 base::Bind(&PipelineIntegrationTestBase::OnBufferingStateChanged
,
141 base::Unretained(this)),
142 base::Closure(), base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack
,
143 base::Unretained(this)),
144 base::Bind(&PipelineIntegrationTestBase::OnWaitingForDecryptionKey
,
145 base::Unretained(this)));
147 return pipeline_status_
;
150 PipelineStatus
PipelineIntegrationTestBase::Start(const std::string
& filename
,
152 hashing_enabled_
= test_type
& kHashed
;
153 clockless_playback_
= test_type
& kClockless
;
154 return Start(filename
);
157 void PipelineIntegrationTestBase::Play() {
158 pipeline_
->SetPlaybackRate(1);
161 void PipelineIntegrationTestBase::Pause() {
162 pipeline_
->SetPlaybackRate(0);
165 bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time
) {
168 EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH
))
169 .WillOnce(InvokeWithoutArgs(&message_loop_
, &base::MessageLoop::QuitNow
));
170 pipeline_
->Seek(seek_time
, base::Bind(&PipelineIntegrationTestBase::OnSeeked
,
171 base::Unretained(this), seek_time
));
173 return (pipeline_status_
== PIPELINE_OK
);
176 void PipelineIntegrationTestBase::Stop() {
177 DCHECK(pipeline_
->IsRunning());
178 pipeline_
->Stop(base::MessageLoop::QuitClosure());
182 void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask(
183 const base::TimeDelta
& quit_time
) {
184 if (pipeline_
->GetMediaTime() >= quit_time
||
185 pipeline_status_
!= PIPELINE_OK
) {
186 message_loop_
.Quit();
190 message_loop_
.PostDelayedTask(
192 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask
,
193 base::Unretained(this), quit_time
),
194 base::TimeDelta::FromMilliseconds(10));
197 bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter(
198 const base::TimeDelta
& wait_time
) {
199 DCHECK(pipeline_
->IsRunning());
200 DCHECK_GT(pipeline_
->GetPlaybackRate(), 0);
201 DCHECK(wait_time
<= pipeline_
->GetMediaDuration());
203 message_loop_
.PostDelayedTask(
205 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask
,
206 base::Unretained(this),
208 base::TimeDelta::FromMilliseconds(10));
210 return (pipeline_status_
== PIPELINE_OK
);
213 void PipelineIntegrationTestBase::CreateDemuxer(const std::string
& filename
) {
214 FileDataSource
* file_data_source
= new FileDataSource();
215 base::FilePath
file_path(GetTestDataFilePath(filename
));
216 CHECK(file_data_source
->Initialize(file_path
)) << "Is " << file_path
.value()
218 data_source_
.reset(file_data_source
);
220 Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb
=
221 base::Bind(&PipelineIntegrationTestBase::DemuxerEncryptedMediaInitDataCB
,
222 base::Unretained(this));
224 #if !defined(MEDIA_DISABLE_FFMPEG)
225 demuxer_
= scoped_ptr
<Demuxer
>(
226 new FFmpegDemuxer(message_loop_
.task_runner(), data_source_
.get(),
227 encrypted_media_init_data_cb
, new MediaLog()));
231 scoped_ptr
<Renderer
> PipelineIntegrationTestBase::CreateRenderer() {
232 ScopedVector
<VideoDecoder
> video_decoders
;
233 #if !defined(MEDIA_DISABLE_LIBVPX)
234 video_decoders
.push_back(
235 new VpxVideoDecoder(message_loop_
.task_runner()));
236 #endif // !defined(MEDIA_DISABLE_LIBVPX)
238 #if !defined(MEDIA_DISABLE_FFMPEG)
239 video_decoders
.push_back(
240 new FFmpegVideoDecoder(message_loop_
.task_runner()));
243 // Simulate a 60Hz rendering sink.
244 video_sink_
.reset(new NullVideoSink(
245 clockless_playback_
, base::TimeDelta::FromSecondsD(1.0 / 60),
246 base::Bind(&PipelineIntegrationTestBase::OnVideoFramePaint
,
247 base::Unretained(this)),
248 message_loop_
.task_runner()));
250 // Disable frame dropping if hashing is enabled.
251 scoped_ptr
<VideoRenderer
> video_renderer(new VideoRendererImpl(
252 message_loop_
.task_runner(), message_loop_
.task_runner().get(),
253 video_sink_
.get(), video_decoders
.Pass(), false, nullptr,
256 if (!clockless_playback_
) {
257 audio_sink_
= new NullAudioSink(message_loop_
.task_runner());
259 clockless_audio_sink_
= new ClocklessAudioSink();
262 ScopedVector
<AudioDecoder
> audio_decoders
;
264 #if !defined(MEDIA_DISABLE_FFMPEG)
265 audio_decoders
.push_back(
266 new FFmpegAudioDecoder(message_loop_
.task_runner(), new MediaLog()));
269 audio_decoders
.push_back(
270 new OpusAudioDecoder(message_loop_
.task_runner()));
272 // Don't allow the audio renderer to resample buffers if hashing is enabled.
273 if (!hashing_enabled_
) {
274 AudioParameters
out_params(AudioParameters::AUDIO_PCM_LOW_LATENCY
,
275 CHANNEL_LAYOUT_STEREO
,
279 hardware_config_
.UpdateOutputConfig(out_params
);
282 scoped_ptr
<AudioRenderer
> audio_renderer(new AudioRendererImpl(
283 message_loop_
.task_runner(),
284 (clockless_playback_
)
285 ? static_cast<AudioRendererSink
*>(clockless_audio_sink_
.get())
287 audio_decoders
.Pass(), hardware_config_
, new MediaLog()));
288 if (hashing_enabled_
) {
289 if (clockless_playback_
)
290 clockless_audio_sink_
->StartAudioHashForTesting();
292 audio_sink_
->StartAudioHashForTesting();
295 scoped_ptr
<RendererImpl
> renderer_impl(
296 new RendererImpl(message_loop_
.task_runner(),
297 audio_renderer
.Pass(),
298 video_renderer
.Pass()));
300 // Prevent non-deterministic buffering state callbacks from firing (e.g., slow
301 // machine, valgrind).
302 renderer_impl
->DisableUnderflowForTesting();
304 if (clockless_playback_
)
305 renderer_impl
->EnableClocklessVideoPlaybackForTesting();
307 return renderer_impl
.Pass();
310 void PipelineIntegrationTestBase::OnVideoFramePaint(
311 const scoped_refptr
<VideoFrame
>& frame
) {
312 last_video_frame_format_
= frame
->format();
314 if (frame
->metadata()->GetInteger(VideoFrameMetadata::COLOR_SPACE
, &result
))
315 last_video_frame_color_space_
= static_cast<ColorSpace
>(result
);
316 if (!hashing_enabled_
)
318 VideoFrame::HashFrameForTesting(&md5_context_
, frame
);
321 std::string
PipelineIntegrationTestBase::GetVideoHash() {
322 DCHECK(hashing_enabled_
);
323 base::MD5Digest digest
;
324 base::MD5Final(&digest
, &md5_context_
);
325 return base::MD5DigestToBase16(digest
);
328 std::string
PipelineIntegrationTestBase::GetAudioHash() {
329 DCHECK(hashing_enabled_
);
331 if (clockless_playback_
)
332 return clockless_audio_sink_
->GetAudioHashForTesting();
333 return audio_sink_
->GetAudioHashForTesting();
336 base::TimeDelta
PipelineIntegrationTestBase::GetAudioTime() {
337 DCHECK(clockless_playback_
);
338 return clockless_audio_sink_
->render_time();
341 base::TimeTicks
DummyTickClock::NowTicks() {
342 now_
+= base::TimeDelta::FromSeconds(60);