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/filters/pipeline_integration_test_base.h"
8 #include "base/memory/scoped_vector.h"
9 #include "media/base/media_log.h"
10 #include "media/filters/audio_renderer_impl.h"
11 #include "media/filters/chunk_demuxer.h"
12 #include "media/filters/ffmpeg_audio_decoder.h"
13 #include "media/filters/ffmpeg_demuxer.h"
14 #include "media/filters/ffmpeg_video_decoder.h"
15 #include "media/filters/file_data_source.h"
16 #include "media/filters/opus_audio_decoder.h"
17 #include "media/filters/renderer_impl.h"
18 #include "media/filters/vpx_video_decoder.h"
21 using ::testing::AnyNumber;
22 using ::testing::AtMost;
23 using ::testing::InvokeWithoutArgs;
24 using ::testing::SaveArg;
28 const char kNullVideoHash[] = "d41d8cd98f00b204e9800998ecf8427e";
29 const char kNullAudioHash[] = "0.00,0.00,0.00,0.00,0.00,0.00,";
31 PipelineIntegrationTestBase::PipelineIntegrationTestBase()
32 : hashing_enabled_(false),
33 clockless_playback_(false),
35 new Pipeline(message_loop_.message_loop_proxy(), new MediaLog())),
37 pipeline_status_(PIPELINE_OK),
38 last_video_frame_format_(VideoFrame::UNKNOWN),
39 hardware_config_(AudioParameters(), AudioParameters()) {
40 base::MD5Init(&md5_context_);
43 PipelineIntegrationTestBase::~PipelineIntegrationTestBase() {
44 if (!pipeline_->IsRunning())
50 void PipelineIntegrationTestBase::SaveStatus(PipelineStatus status) {
51 pipeline_status_ = status;
54 void PipelineIntegrationTestBase::OnStatusCallback(
55 PipelineStatus status) {
56 pipeline_status_ = status;
57 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
60 void PipelineIntegrationTestBase::OnStatusCallbackChecked(
61 PipelineStatus expected_status,
62 PipelineStatus status) {
63 EXPECT_EQ(expected_status, status);
64 OnStatusCallback(status);
67 PipelineStatusCB PipelineIntegrationTestBase::QuitOnStatusCB(
68 PipelineStatus expected_status) {
69 return base::Bind(&PipelineIntegrationTestBase::OnStatusCallbackChecked,
70 base::Unretained(this),
74 void PipelineIntegrationTestBase::DemuxerNeedKeyCB(
75 const std::string& type,
76 const std::vector<uint8>& init_data) {
77 DCHECK(!init_data.empty());
78 CHECK(!need_key_cb_.is_null());
79 need_key_cb_.Run(type, init_data);
82 void PipelineIntegrationTestBase::OnEnded() {
85 pipeline_status_ = PIPELINE_OK;
86 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
89 bool PipelineIntegrationTestBase::WaitUntilOnEnded() {
91 return (pipeline_status_ == PIPELINE_OK);
94 return ended_ && (pipeline_status_ == PIPELINE_OK);
97 PipelineStatus PipelineIntegrationTestBase::WaitUntilEndedOrError() {
98 if (ended_ || pipeline_status_ != PIPELINE_OK)
99 return pipeline_status_;
101 return pipeline_status_;
104 void PipelineIntegrationTestBase::OnError(PipelineStatus status) {
105 DCHECK_NE(status, PIPELINE_OK);
106 pipeline_status_ = status;
107 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
110 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
111 PipelineStatus expected_status) {
112 EXPECT_CALL(*this, OnMetadata(_))
114 .WillRepeatedly(SaveArg<0>(&metadata_));
115 EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH))
117 CreateDemuxer(file_path);
120 CreateRenderer(NULL),
121 base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
122 base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
123 QuitOnStatusCB(expected_status),
124 base::Bind(&PipelineIntegrationTestBase::OnMetadata,
125 base::Unretained(this)),
126 base::Bind(&PipelineIntegrationTestBase::OnBufferingStateChanged,
127 base::Unretained(this)),
129 base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack,
130 base::Unretained(this)));
132 return (pipeline_status_ == PIPELINE_OK);
135 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
136 PipelineStatus expected_status,
137 kTestType test_type) {
138 hashing_enabled_ = test_type == kHashed;
139 clockless_playback_ = test_type == kClockless;
140 return Start(file_path, expected_status);
143 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) {
144 return Start(file_path, NULL);
147 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
148 Decryptor* decryptor) {
149 EXPECT_CALL(*this, OnMetadata(_))
151 .WillRepeatedly(SaveArg<0>(&metadata_));
152 EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH))
155 CreateDemuxer(file_path);
158 CreateRenderer(decryptor),
159 base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
160 base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
161 base::Bind(&PipelineIntegrationTestBase::OnStatusCallback,
162 base::Unretained(this)),
163 base::Bind(&PipelineIntegrationTestBase::OnMetadata,
164 base::Unretained(this)),
165 base::Bind(&PipelineIntegrationTestBase::OnBufferingStateChanged,
166 base::Unretained(this)),
168 base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack,
169 base::Unretained(this)));
171 return (pipeline_status_ == PIPELINE_OK);
174 void PipelineIntegrationTestBase::Play() {
175 pipeline_->SetPlaybackRate(1);
178 void PipelineIntegrationTestBase::Pause() {
179 pipeline_->SetPlaybackRate(0);
182 bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time) {
185 EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH))
186 .WillOnce(InvokeWithoutArgs(&message_loop_, &base::MessageLoop::QuitNow));
187 pipeline_->Seek(seek_time,
188 base::Bind(&PipelineIntegrationTestBase::SaveStatus,
189 base::Unretained(this)));
191 return (pipeline_status_ == PIPELINE_OK);
194 void PipelineIntegrationTestBase::Stop() {
195 DCHECK(pipeline_->IsRunning());
196 pipeline_->Stop(base::MessageLoop::QuitClosure());
200 void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask(
201 const base::TimeDelta& quit_time) {
202 if (pipeline_->GetMediaTime() >= quit_time ||
203 pipeline_status_ != PIPELINE_OK) {
204 message_loop_.Quit();
208 message_loop_.PostDelayedTask(
210 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
211 base::Unretained(this), quit_time),
212 base::TimeDelta::FromMilliseconds(10));
215 bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter(
216 const base::TimeDelta& wait_time) {
217 DCHECK(pipeline_->IsRunning());
218 DCHECK_GT(pipeline_->GetPlaybackRate(), 0);
219 DCHECK(wait_time <= pipeline_->GetMediaDuration());
221 message_loop_.PostDelayedTask(
223 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
224 base::Unretained(this),
226 base::TimeDelta::FromMilliseconds(10));
228 return (pipeline_status_ == PIPELINE_OK);
231 void PipelineIntegrationTestBase::CreateDemuxer(
232 const base::FilePath& file_path) {
233 FileDataSource* file_data_source = new FileDataSource();
234 CHECK(file_data_source->Initialize(file_path)) << "Is " << file_path.value()
236 data_source_.reset(file_data_source);
238 Demuxer::NeedKeyCB need_key_cb = base::Bind(
239 &PipelineIntegrationTestBase::DemuxerNeedKeyCB, base::Unretained(this));
241 scoped_ptr<Demuxer>(new FFmpegDemuxer(message_loop_.message_loop_proxy(),
247 scoped_ptr<Renderer> PipelineIntegrationTestBase::CreateRenderer(
248 Decryptor* decryptor) {
249 ScopedVector<VideoDecoder> video_decoders;
250 #if !defined(MEDIA_DISABLE_LIBVPX)
251 video_decoders.push_back(
252 new VpxVideoDecoder(message_loop_.message_loop_proxy()));
253 #endif // !defined(MEDIA_DISABLE_LIBVPX)
254 video_decoders.push_back(
255 new FFmpegVideoDecoder(message_loop_.message_loop_proxy()));
257 // Disable frame dropping if hashing is enabled.
258 scoped_ptr<VideoRenderer> video_renderer(new VideoRendererImpl(
259 message_loop_.message_loop_proxy(),
260 video_decoders.Pass(),
261 base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
262 base::Unretained(this),
264 base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint,
265 base::Unretained(this)),
269 if (!clockless_playback_) {
270 audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy());
272 clockless_audio_sink_ = new ClocklessAudioSink();
275 ScopedVector<AudioDecoder> audio_decoders;
276 audio_decoders.push_back(
277 new FFmpegAudioDecoder(message_loop_.message_loop_proxy(), LogCB()));
278 audio_decoders.push_back(
279 new OpusAudioDecoder(message_loop_.message_loop_proxy()));
281 AudioParameters out_params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
282 CHANNEL_LAYOUT_STEREO,
286 hardware_config_.UpdateOutputConfig(out_params);
288 scoped_ptr<AudioRenderer> audio_renderer(new AudioRendererImpl(
289 message_loop_.message_loop_proxy(),
290 (clockless_playback_)
291 ? static_cast<AudioRendererSink*>(clockless_audio_sink_.get())
293 audio_decoders.Pass(),
294 base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
295 base::Unretained(this),
299 if (hashing_enabled_)
300 audio_sink_->StartAudioHashForTesting();
302 scoped_ptr<RendererImpl> renderer_impl(
303 new RendererImpl(message_loop_.message_loop_proxy(),
304 audio_renderer.Pass(),
305 video_renderer.Pass()));
307 // Prevent non-deterministic buffering state callbacks from firing (e.g., slow
308 // machine, valgrind).
309 renderer_impl->DisableUnderflowForTesting();
311 if (clockless_playback_)
312 renderer_impl->EnableClocklessVideoPlaybackForTesting();
314 return renderer_impl.Pass();
317 void PipelineIntegrationTestBase::SetDecryptor(
318 Decryptor* decryptor,
319 const DecryptorReadyCB& decryptor_ready_cb) {
320 decryptor_ready_cb.Run(
322 base::Bind(&PipelineIntegrationTestBase::DecryptorAttached,
323 base::Unretained(this)));
324 EXPECT_CALL(*this, DecryptorAttached(true));
327 void PipelineIntegrationTestBase::OnVideoRendererPaint(
328 const scoped_refptr<VideoFrame>& frame) {
329 last_video_frame_format_ = frame->format();
330 if (!hashing_enabled_)
332 frame->HashFrameForTesting(&md5_context_);
335 std::string PipelineIntegrationTestBase::GetVideoHash() {
336 DCHECK(hashing_enabled_);
337 base::MD5Digest digest;
338 base::MD5Final(&digest, &md5_context_);
339 return base::MD5DigestToBase16(digest);
342 std::string PipelineIntegrationTestBase::GetAudioHash() {
343 DCHECK(hashing_enabled_);
344 return audio_sink_->GetAudioHashForTesting();
347 base::TimeDelta PipelineIntegrationTestBase::GetAudioTime() {
348 DCHECK(clockless_playback_);
349 return clockless_audio_sink_->render_time();
352 base::TimeTicks DummyTickClock::NowTicks() {
353 now_ += base::TimeDelta::FromSeconds(60);