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/vpx_video_decoder.h"
19 using ::testing::AnyNumber;
20 using ::testing::AtMost;
24 const char kNullVideoHash[] = "d41d8cd98f00b204e9800998ecf8427e";
25 const char kNullAudioHash[] = "0.00,0.00,0.00,0.00,0.00,0.00,";
27 PipelineIntegrationTestBase::PipelineIntegrationTestBase()
28 : hashing_enabled_(false),
29 clockless_playback_(false),
30 pipeline_(new Pipeline(message_loop_.message_loop_proxy(),
33 pipeline_status_(PIPELINE_OK),
34 last_video_frame_format_(VideoFrame::UNKNOWN) {
35 base::MD5Init(&md5_context_);
36 EXPECT_CALL(*this, OnSetOpaque(true)).Times(AnyNumber());
39 PipelineIntegrationTestBase::~PipelineIntegrationTestBase() {
40 if (!pipeline_->IsRunning())
46 void PipelineIntegrationTestBase::OnStatusCallback(
47 PipelineStatus status) {
48 pipeline_status_ = status;
49 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
52 void PipelineIntegrationTestBase::OnStatusCallbackChecked(
53 PipelineStatus expected_status,
54 PipelineStatus status) {
55 EXPECT_EQ(expected_status, status);
56 OnStatusCallback(status);
59 PipelineStatusCB PipelineIntegrationTestBase::QuitOnStatusCB(
60 PipelineStatus expected_status) {
61 return base::Bind(&PipelineIntegrationTestBase::OnStatusCallbackChecked,
62 base::Unretained(this),
66 void PipelineIntegrationTestBase::DemuxerNeedKeyCB(
67 const std::string& type,
68 const std::vector<uint8>& init_data) {
69 DCHECK(!init_data.empty());
70 CHECK(!need_key_cb_.is_null());
71 need_key_cb_.Run(type, init_data);
74 void PipelineIntegrationTestBase::OnEnded() {
77 pipeline_status_ = PIPELINE_OK;
78 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
81 bool PipelineIntegrationTestBase::WaitUntilOnEnded() {
83 return (pipeline_status_ == PIPELINE_OK);
86 return ended_ && (pipeline_status_ == PIPELINE_OK);
89 PipelineStatus PipelineIntegrationTestBase::WaitUntilEndedOrError() {
90 if (ended_ || pipeline_status_ != PIPELINE_OK)
91 return pipeline_status_;
93 return pipeline_status_;
96 void PipelineIntegrationTestBase::OnError(PipelineStatus status) {
97 DCHECK_NE(status, PIPELINE_OK);
98 pipeline_status_ = status;
99 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
102 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
103 PipelineStatus expected_status) {
104 EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata))
106 EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted))
109 CreateFilterCollection(file_path, NULL),
110 base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
111 base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
112 QuitOnStatusCB(expected_status),
113 base::Bind(&PipelineIntegrationTestBase::OnBufferingState,
114 base::Unretained(this)),
117 return (pipeline_status_ == PIPELINE_OK);
120 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
121 PipelineStatus expected_status,
122 kTestType test_type) {
123 hashing_enabled_ = test_type == kHashed;
124 clockless_playback_ = test_type == kClockless;
125 return Start(file_path, expected_status);
128 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) {
129 return Start(file_path, NULL);
132 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
133 Decryptor* decryptor) {
134 EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata))
136 EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted))
139 CreateFilterCollection(file_path, decryptor),
140 base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
141 base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
142 base::Bind(&PipelineIntegrationTestBase::OnStatusCallback,
143 base::Unretained(this)),
144 base::Bind(&PipelineIntegrationTestBase::OnBufferingState,
145 base::Unretained(this)),
148 return (pipeline_status_ == PIPELINE_OK);
151 void PipelineIntegrationTestBase::Play() {
152 pipeline_->SetPlaybackRate(1);
155 void PipelineIntegrationTestBase::Pause() {
156 pipeline_->SetPlaybackRate(0);
159 bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time) {
162 EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted));
163 pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK));
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 scoped_ptr<FilterCollection>
206 PipelineIntegrationTestBase::CreateFilterCollection(
207 const base::FilePath& file_path,
208 Decryptor* decryptor) {
209 FileDataSource* file_data_source = new FileDataSource();
210 CHECK(file_data_source->Initialize(file_path));
211 data_source_.reset(file_data_source);
213 Demuxer::NeedKeyCB need_key_cb = base::Bind(
214 &PipelineIntegrationTestBase::DemuxerNeedKeyCB, base::Unretained(this));
215 scoped_ptr<Demuxer> demuxer(
216 new FFmpegDemuxer(message_loop_.message_loop_proxy(),
220 return CreateFilterCollection(demuxer.Pass(), decryptor);
223 scoped_ptr<FilterCollection>
224 PipelineIntegrationTestBase::CreateFilterCollection(
225 scoped_ptr<Demuxer> demuxer,
226 Decryptor* decryptor) {
227 demuxer_ = demuxer.Pass();
229 scoped_ptr<FilterCollection> collection(new FilterCollection());
230 collection->SetDemuxer(demuxer_.get());
232 if (!clockless_playback_) {
233 ScopedVector<VideoDecoder> video_decoders;
234 video_decoders.push_back(
235 new VpxVideoDecoder(message_loop_.message_loop_proxy()));
236 video_decoders.push_back(
237 new FFmpegVideoDecoder(message_loop_.message_loop_proxy()));
239 // Disable frame dropping if hashing is enabled.
240 scoped_ptr<VideoRenderer> renderer(new VideoRendererBase(
241 message_loop_.message_loop_proxy(),
242 video_decoders.Pass(),
243 base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
244 base::Unretained(this),
246 base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint,
247 base::Unretained(this)),
248 base::Bind(&PipelineIntegrationTestBase::OnSetOpaque,
249 base::Unretained(this)),
251 collection->SetVideoRenderer(renderer.Pass());
253 audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy());
255 // audio only for clockless_playback_
256 clockless_audio_sink_ = new ClocklessAudioSink();
259 ScopedVector<AudioDecoder> audio_decoders;
260 audio_decoders.push_back(
261 new FFmpegAudioDecoder(message_loop_.message_loop_proxy()));
262 audio_decoders.push_back(
263 new OpusAudioDecoder(message_loop_.message_loop_proxy()));
265 AudioRendererImpl* audio_renderer_impl = new AudioRendererImpl(
266 message_loop_.message_loop_proxy(),
267 (clockless_playback_)
268 ? static_cast<AudioRendererSink*>(clockless_audio_sink_.get())
270 audio_decoders.Pass(),
271 base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
272 base::Unretained(this),
275 // Disable underflow if hashing is enabled.
276 if (hashing_enabled_) {
277 audio_sink_->StartAudioHashForTesting();
278 audio_renderer_impl->DisableUnderflowForTesting();
280 scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_impl);
281 collection->SetAudioRenderer(audio_renderer.Pass());
283 return collection.Pass();
286 void PipelineIntegrationTestBase::SetDecryptor(
287 Decryptor* decryptor,
288 const DecryptorReadyCB& decryptor_ready_cb) {
289 decryptor_ready_cb.Run(decryptor);
292 void PipelineIntegrationTestBase::OnVideoRendererPaint(
293 const scoped_refptr<VideoFrame>& frame) {
294 last_video_frame_format_ = frame->format();
295 if (!hashing_enabled_)
297 frame->HashFrameForTesting(&md5_context_);
300 std::string PipelineIntegrationTestBase::GetVideoHash() {
301 DCHECK(hashing_enabled_);
302 base::MD5Digest digest;
303 base::MD5Final(&digest, &md5_context_);
304 return base::MD5DigestToBase16(digest);
307 std::string PipelineIntegrationTestBase::GetAudioHash() {
308 DCHECK(hashing_enabled_);
309 return audio_sink_->GetAudioHashForTesting();
312 base::TimeDelta PipelineIntegrationTestBase::GetAudioTime() {
313 DCHECK(clockless_playback_);
314 return clockless_audio_sink_->render_time();