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::SaveArg;
27 const char kNullVideoHash[] = "d41d8cd98f00b204e9800998ecf8427e";
28 const char kNullAudioHash[] = "0.00,0.00,0.00,0.00,0.00,0.00,";
30 PipelineIntegrationTestBase::PipelineIntegrationTestBase()
31 : hashing_enabled_(false),
32 clockless_playback_(false),
34 new Pipeline(message_loop_.message_loop_proxy(), new MediaLog())),
36 pipeline_status_(PIPELINE_OK),
37 last_video_frame_format_(VideoFrame::UNKNOWN),
38 hardware_config_(AudioParameters(), AudioParameters()) {
39 base::MD5Init(&md5_context_);
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(_))
109 .WillRepeatedly(SaveArg<0>(&metadata_));
110 EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH))
112 CreateDemuxer(file_path);
115 CreateRenderer(NULL),
116 base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
117 base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
118 QuitOnStatusCB(expected_status),
119 base::Bind(&PipelineIntegrationTestBase::OnMetadata,
120 base::Unretained(this)),
121 base::Bind(&PipelineIntegrationTestBase::OnBufferingStateChanged,
122 base::Unretained(this)),
124 base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack,
125 base::Unretained(this)));
127 return (pipeline_status_ == PIPELINE_OK);
130 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
131 PipelineStatus expected_status,
132 kTestType test_type) {
133 hashing_enabled_ = test_type == kHashed;
134 clockless_playback_ = test_type == kClockless;
135 return Start(file_path, expected_status);
138 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) {
139 return Start(file_path, NULL);
142 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
143 Decryptor* decryptor) {
144 EXPECT_CALL(*this, OnMetadata(_))
146 .WillRepeatedly(SaveArg<0>(&metadata_));
147 EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH))
150 CreateDemuxer(file_path);
153 CreateRenderer(decryptor),
154 base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
155 base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
156 base::Bind(&PipelineIntegrationTestBase::OnStatusCallback,
157 base::Unretained(this)),
158 base::Bind(&PipelineIntegrationTestBase::OnMetadata,
159 base::Unretained(this)),
160 base::Bind(&PipelineIntegrationTestBase::OnBufferingStateChanged,
161 base::Unretained(this)),
163 base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack,
164 base::Unretained(this)));
166 return (pipeline_status_ == PIPELINE_OK);
169 void PipelineIntegrationTestBase::Play() {
170 pipeline_->SetPlaybackRate(1);
173 void PipelineIntegrationTestBase::Pause() {
174 pipeline_->SetPlaybackRate(0);
177 bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time) {
180 EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH));
181 pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK));
183 return (pipeline_status_ == PIPELINE_OK);
186 void PipelineIntegrationTestBase::Stop() {
187 DCHECK(pipeline_->IsRunning());
188 pipeline_->Stop(base::MessageLoop::QuitClosure());
192 void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask(
193 const base::TimeDelta& quit_time) {
194 if (pipeline_->GetMediaTime() >= quit_time ||
195 pipeline_status_ != PIPELINE_OK) {
196 message_loop_.Quit();
200 message_loop_.PostDelayedTask(
202 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
203 base::Unretained(this), quit_time),
204 base::TimeDelta::FromMilliseconds(10));
207 bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter(
208 const base::TimeDelta& wait_time) {
209 DCHECK(pipeline_->IsRunning());
210 DCHECK_GT(pipeline_->GetPlaybackRate(), 0);
211 DCHECK(wait_time <= pipeline_->GetMediaDuration());
213 message_loop_.PostDelayedTask(
215 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
216 base::Unretained(this),
218 base::TimeDelta::FromMilliseconds(10));
220 return (pipeline_status_ == PIPELINE_OK);
223 void PipelineIntegrationTestBase::CreateDemuxer(
224 const base::FilePath& file_path) {
225 FileDataSource* file_data_source = new FileDataSource();
226 CHECK(file_data_source->Initialize(file_path)) << "Is " << file_path.value()
228 data_source_.reset(file_data_source);
230 Demuxer::NeedKeyCB need_key_cb = base::Bind(
231 &PipelineIntegrationTestBase::DemuxerNeedKeyCB, base::Unretained(this));
233 scoped_ptr<Demuxer>(new FFmpegDemuxer(message_loop_.message_loop_proxy(),
239 scoped_ptr<Renderer> PipelineIntegrationTestBase::CreateRenderer(
240 Decryptor* decryptor) {
241 ScopedVector<VideoDecoder> video_decoders;
242 #if !defined(MEDIA_DISABLE_LIBVPX)
243 video_decoders.push_back(
244 new VpxVideoDecoder(message_loop_.message_loop_proxy()));
245 #endif // !defined(MEDIA_DISABLE_LIBVPX)
246 video_decoders.push_back(
247 new FFmpegVideoDecoder(message_loop_.message_loop_proxy()));
249 // Disable frame dropping if hashing is enabled.
250 scoped_ptr<VideoRenderer> video_renderer(new VideoRendererImpl(
251 message_loop_.message_loop_proxy(),
252 video_decoders.Pass(),
253 base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
254 base::Unretained(this),
256 base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint,
257 base::Unretained(this)),
261 if (!clockless_playback_) {
262 audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy());
264 clockless_audio_sink_ = new ClocklessAudioSink();
267 ScopedVector<AudioDecoder> audio_decoders;
268 audio_decoders.push_back(
269 new FFmpegAudioDecoder(message_loop_.message_loop_proxy(), LogCB()));
270 audio_decoders.push_back(
271 new OpusAudioDecoder(message_loop_.message_loop_proxy()));
273 AudioParameters out_params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
274 CHANNEL_LAYOUT_STEREO,
278 hardware_config_.UpdateOutputConfig(out_params);
280 scoped_ptr<AudioRenderer> audio_renderer(new AudioRendererImpl(
281 message_loop_.message_loop_proxy(),
282 (clockless_playback_)
283 ? static_cast<AudioRendererSink*>(clockless_audio_sink_.get())
285 audio_decoders.Pass(),
286 base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
287 base::Unretained(this),
291 if (hashing_enabled_)
292 audio_sink_->StartAudioHashForTesting();
294 scoped_ptr<RendererImpl> renderer_impl(
295 new RendererImpl(message_loop_.message_loop_proxy(),
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.PassAs<Renderer>();
310 void PipelineIntegrationTestBase::SetDecryptor(
311 Decryptor* decryptor,
312 const DecryptorReadyCB& decryptor_ready_cb) {
313 decryptor_ready_cb.Run(
315 base::Bind(&PipelineIntegrationTestBase::DecryptorAttached,
316 base::Unretained(this)));
317 EXPECT_CALL(*this, DecryptorAttached(true));
320 void PipelineIntegrationTestBase::OnVideoRendererPaint(
321 const scoped_refptr<VideoFrame>& frame) {
322 last_video_frame_format_ = frame->format();
323 if (!hashing_enabled_)
325 frame->HashFrameForTesting(&md5_context_);
328 std::string PipelineIntegrationTestBase::GetVideoHash() {
329 DCHECK(hashing_enabled_);
330 base::MD5Digest digest;
331 base::MD5Final(&digest, &md5_context_);
332 return base::MD5DigestToBase16(digest);
335 std::string PipelineIntegrationTestBase::GetAudioHash() {
336 DCHECK(hashing_enabled_);
337 return audio_sink_->GetAudioHashForTesting();
340 base::TimeDelta PipelineIntegrationTestBase::GetAudioTime() {
341 DCHECK(clockless_playback_);
342 return clockless_audio_sink_->render_time();
345 base::TimeTicks DummyTickClock::NowTicks() {
346 now_ += base::TimeDelta::FromSeconds(60);