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/clock.h"
10 #include "media/base/media_log.h"
11 #include "media/filters/audio_renderer_impl.h"
12 #include "media/filters/chunk_demuxer.h"
13 #include "media/filters/ffmpeg_audio_decoder.h"
14 #include "media/filters/ffmpeg_demuxer.h"
15 #include "media/filters/ffmpeg_video_decoder.h"
16 #include "media/filters/file_data_source.h"
17 #include "media/filters/opus_audio_decoder.h"
18 #include "media/filters/vpx_video_decoder.h"
21 using ::testing::AnyNumber;
22 using ::testing::AtMost;
26 const char kNullVideoHash[] = "d41d8cd98f00b204e9800998ecf8427e";
27 const char kNullAudioHash[] = "0.00,0.00,0.00,0.00,0.00,0.00,";
29 PipelineIntegrationTestBase::PipelineIntegrationTestBase()
30 : hashing_enabled_(false),
31 clockless_playback_(false),
33 new Pipeline(message_loop_.message_loop_proxy(), new MediaLog())),
35 pipeline_status_(PIPELINE_OK),
36 last_video_frame_format_(VideoFrame::UNKNOWN),
37 hardware_config_(AudioParameters(), AudioParameters()) {
38 base::MD5Init(&md5_context_);
39 EXPECT_CALL(*this, OnSetOpaque(true)).Times(AnyNumber());
42 PipelineIntegrationTestBase::~PipelineIntegrationTestBase() {
43 if (!pipeline_->IsRunning())
49 void PipelineIntegrationTestBase::OnStatusCallback(
50 PipelineStatus status) {
51 pipeline_status_ = status;
52 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
55 void PipelineIntegrationTestBase::OnStatusCallbackChecked(
56 PipelineStatus expected_status,
57 PipelineStatus status) {
58 EXPECT_EQ(expected_status, status);
59 OnStatusCallback(status);
62 PipelineStatusCB PipelineIntegrationTestBase::QuitOnStatusCB(
63 PipelineStatus expected_status) {
64 return base::Bind(&PipelineIntegrationTestBase::OnStatusCallbackChecked,
65 base::Unretained(this),
69 void PipelineIntegrationTestBase::DemuxerNeedKeyCB(
70 const std::string& type,
71 const std::vector<uint8>& init_data) {
72 DCHECK(!init_data.empty());
73 CHECK(!need_key_cb_.is_null());
74 need_key_cb_.Run(type, init_data);
77 void PipelineIntegrationTestBase::OnEnded() {
80 pipeline_status_ = PIPELINE_OK;
81 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
84 bool PipelineIntegrationTestBase::WaitUntilOnEnded() {
86 return (pipeline_status_ == PIPELINE_OK);
89 return ended_ && (pipeline_status_ == PIPELINE_OK);
92 PipelineStatus PipelineIntegrationTestBase::WaitUntilEndedOrError() {
93 if (ended_ || pipeline_status_ != PIPELINE_OK)
94 return pipeline_status_;
96 return pipeline_status_;
99 void PipelineIntegrationTestBase::OnError(PipelineStatus status) {
100 DCHECK_NE(status, PIPELINE_OK);
101 pipeline_status_ = status;
102 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
105 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
106 PipelineStatus expected_status) {
107 EXPECT_CALL(*this, OnMetadata(_)).Times(AtMost(1));
108 EXPECT_CALL(*this, OnPrerollCompleted()).Times(AtMost(1));
110 CreateFilterCollection(file_path, NULL),
111 base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
112 base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
113 QuitOnStatusCB(expected_status),
114 base::Bind(&PipelineIntegrationTestBase::OnMetadata,
115 base::Unretained(this)),
116 base::Bind(&PipelineIntegrationTestBase::OnPrerollCompleted,
117 base::Unretained(this)),
120 return (pipeline_status_ == PIPELINE_OK);
123 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
124 PipelineStatus expected_status,
125 kTestType test_type) {
126 hashing_enabled_ = test_type == kHashed;
127 clockless_playback_ = test_type == kClockless;
128 if (clockless_playback_) {
129 pipeline_->SetClockForTesting(new Clock(&dummy_clock_));
131 return Start(file_path, expected_status);
134 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) {
135 return Start(file_path, NULL);
138 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
139 Decryptor* decryptor) {
140 EXPECT_CALL(*this, OnMetadata(_)).Times(AtMost(1));
141 EXPECT_CALL(*this, OnPrerollCompleted()).Times(AtMost(1));
143 CreateFilterCollection(file_path, decryptor),
144 base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
145 base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
146 base::Bind(&PipelineIntegrationTestBase::OnStatusCallback,
147 base::Unretained(this)),
148 base::Bind(&PipelineIntegrationTestBase::OnMetadata,
149 base::Unretained(this)),
150 base::Bind(&PipelineIntegrationTestBase::OnPrerollCompleted,
151 base::Unretained(this)),
154 return (pipeline_status_ == PIPELINE_OK);
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, OnPrerollCompleted());
169 pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK));
171 return (pipeline_status_ == PIPELINE_OK);
174 void PipelineIntegrationTestBase::Stop() {
175 DCHECK(pipeline_->IsRunning());
176 pipeline_->Stop(base::MessageLoop::QuitClosure());
180 void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask(
181 const base::TimeDelta& quit_time) {
182 if (pipeline_->GetMediaTime() >= quit_time ||
183 pipeline_status_ != PIPELINE_OK) {
184 message_loop_.Quit();
188 message_loop_.PostDelayedTask(
190 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
191 base::Unretained(this), quit_time),
192 base::TimeDelta::FromMilliseconds(10));
195 bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter(
196 const base::TimeDelta& wait_time) {
197 DCHECK(pipeline_->IsRunning());
198 DCHECK_GT(pipeline_->GetPlaybackRate(), 0);
199 DCHECK(wait_time <= pipeline_->GetMediaDuration());
201 message_loop_.PostDelayedTask(
203 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
204 base::Unretained(this),
206 base::TimeDelta::FromMilliseconds(10));
208 return (pipeline_status_ == PIPELINE_OK);
211 scoped_ptr<FilterCollection>
212 PipelineIntegrationTestBase::CreateFilterCollection(
213 const base::FilePath& file_path,
214 Decryptor* decryptor) {
215 FileDataSource* file_data_source = new FileDataSource();
216 CHECK(file_data_source->Initialize(file_path));
217 data_source_.reset(file_data_source);
219 Demuxer::NeedKeyCB need_key_cb = base::Bind(
220 &PipelineIntegrationTestBase::DemuxerNeedKeyCB, base::Unretained(this));
221 scoped_ptr<Demuxer> demuxer(
222 new FFmpegDemuxer(message_loop_.message_loop_proxy(),
226 return CreateFilterCollection(demuxer.Pass(), decryptor);
229 scoped_ptr<FilterCollection>
230 PipelineIntegrationTestBase::CreateFilterCollection(
231 scoped_ptr<Demuxer> demuxer,
232 Decryptor* decryptor) {
233 demuxer_ = demuxer.Pass();
235 scoped_ptr<FilterCollection> collection(new FilterCollection());
236 collection->SetDemuxer(demuxer_.get());
238 ScopedVector<VideoDecoder> video_decoders;
239 video_decoders.push_back(
240 new VpxVideoDecoder(message_loop_.message_loop_proxy()));
241 video_decoders.push_back(
242 new FFmpegVideoDecoder(message_loop_.message_loop_proxy()));
244 // Disable frame dropping if hashing is enabled.
245 scoped_ptr<VideoRenderer> renderer(new VideoRendererImpl(
246 message_loop_.message_loop_proxy(),
247 video_decoders.Pass(),
248 base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
249 base::Unretained(this),
251 base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint,
252 base::Unretained(this)),
253 base::Bind(&PipelineIntegrationTestBase::OnSetOpaque,
254 base::Unretained(this)),
256 collection->SetVideoRenderer(renderer.Pass());
258 if (!clockless_playback_) {
259 audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy());
261 clockless_audio_sink_ = new ClocklessAudioSink();
264 ScopedVector<AudioDecoder> audio_decoders;
265 audio_decoders.push_back(
266 new FFmpegAudioDecoder(message_loop_.message_loop_proxy()));
267 audio_decoders.push_back(
268 new OpusAudioDecoder(message_loop_.message_loop_proxy()));
270 AudioParameters out_params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
271 CHANNEL_LAYOUT_STEREO,
275 hardware_config_.UpdateOutputConfig(out_params);
277 AudioRendererImpl* audio_renderer_impl = new AudioRendererImpl(
278 message_loop_.message_loop_proxy(),
279 (clockless_playback_)
280 ? static_cast<AudioRendererSink*>(clockless_audio_sink_.get())
282 audio_decoders.Pass(),
283 base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
284 base::Unretained(this),
287 // Disable underflow if hashing is enabled.
288 if (hashing_enabled_) {
289 audio_sink_->StartAudioHashForTesting();
290 audio_renderer_impl->DisableUnderflowForTesting();
292 scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_impl);
293 collection->SetAudioRenderer(audio_renderer.Pass());
295 return collection.Pass();
298 void PipelineIntegrationTestBase::SetDecryptor(
299 Decryptor* decryptor,
300 const DecryptorReadyCB& decryptor_ready_cb) {
301 decryptor_ready_cb.Run(decryptor);
304 void PipelineIntegrationTestBase::OnVideoRendererPaint(
305 const scoped_refptr<VideoFrame>& frame) {
306 last_video_frame_format_ = frame->format();
307 if (!hashing_enabled_)
309 frame->HashFrameForTesting(&md5_context_);
312 std::string PipelineIntegrationTestBase::GetVideoHash() {
313 DCHECK(hashing_enabled_);
314 base::MD5Digest digest;
315 base::MD5Final(&digest, &md5_context_);
316 return base::MD5DigestToBase16(digest);
319 std::string PipelineIntegrationTestBase::GetAudioHash() {
320 DCHECK(hashing_enabled_);
321 return audio_sink_->GetAudioHashForTesting();
324 base::TimeDelta PipelineIntegrationTestBase::GetAudioTime() {
325 DCHECK(clockless_playback_);
326 return clockless_audio_sink_->render_time();
329 base::TimeTicks DummyTickClock::NowTicks() {
330 now_ += base::TimeDelta::FromSeconds(60);