pipeline_integration_test_base.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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.
4
5#include "media/filters/pipeline_integration_test_base.h"
6
7#include "base/bind.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"
18
19using ::testing::AnyNumber;
20using ::testing::AtMost;
21
22namespace media {
23
24const char kNullVideoHash[] = "d41d8cd98f00b204e9800998ecf8427e";
25const char kNullAudioHash[] = "0.00,0.00,0.00,0.00,0.00,0.00,";
26
27PipelineIntegrationTestBase::PipelineIntegrationTestBase()
28    : hashing_enabled_(false),
29      pipeline_(new Pipeline(message_loop_.message_loop_proxy(),
30                             new MediaLog())),
31      ended_(false),
32      pipeline_status_(PIPELINE_OK),
33      last_video_frame_format_(VideoFrame::INVALID) {
34  base::MD5Init(&md5_context_);
35  EXPECT_CALL(*this, OnSetOpaque(true)).Times(AnyNumber());
36}
37
38PipelineIntegrationTestBase::~PipelineIntegrationTestBase() {
39  if (!pipeline_->IsRunning())
40    return;
41
42  Stop();
43}
44
45void PipelineIntegrationTestBase::OnStatusCallback(
46    PipelineStatus status) {
47  pipeline_status_ = status;
48  message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
49}
50
51void PipelineIntegrationTestBase::OnStatusCallbackChecked(
52    PipelineStatus expected_status,
53    PipelineStatus status) {
54  EXPECT_EQ(expected_status, status);
55  OnStatusCallback(status);
56}
57
58PipelineStatusCB PipelineIntegrationTestBase::QuitOnStatusCB(
59    PipelineStatus expected_status) {
60  return base::Bind(&PipelineIntegrationTestBase::OnStatusCallbackChecked,
61                    base::Unretained(this),
62                    expected_status);
63}
64
65void PipelineIntegrationTestBase::DemuxerNeedKeyCB(
66    const std::string& type,
67    scoped_ptr<uint8[]> init_data,
68    int init_data_size) {
69  DCHECK(init_data.get());
70  DCHECK_GT(init_data_size, 0);
71  CHECK(!need_key_cb_.is_null());
72  need_key_cb_.Run(std::string(), type, init_data.Pass(), init_data_size);
73}
74
75void PipelineIntegrationTestBase::OnEnded() {
76  DCHECK(!ended_);
77  ended_ = true;
78  pipeline_status_ = PIPELINE_OK;
79  message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
80}
81
82bool PipelineIntegrationTestBase::WaitUntilOnEnded() {
83  if (ended_)
84    return (pipeline_status_ == PIPELINE_OK);
85  message_loop_.Run();
86  EXPECT_TRUE(ended_);
87  return ended_ && (pipeline_status_ == PIPELINE_OK);
88}
89
90PipelineStatus PipelineIntegrationTestBase::WaitUntilEndedOrError() {
91  if (ended_ || pipeline_status_ != PIPELINE_OK)
92    return pipeline_status_;
93  message_loop_.Run();
94  return pipeline_status_;
95}
96
97void PipelineIntegrationTestBase::OnError(PipelineStatus status) {
98  DCHECK_NE(status, PIPELINE_OK);
99  pipeline_status_ = status;
100  message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
101}
102
103bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
104                                        PipelineStatus expected_status) {
105  EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata))
106      .Times(AtMost(1));
107  EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted))
108      .Times(AtMost(1));
109  pipeline_->Start(
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::OnBufferingState,
115                 base::Unretained(this)),
116      base::Closure());
117  message_loop_.Run();
118  return (pipeline_status_ == PIPELINE_OK);
119}
120
121bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
122                                        PipelineStatus expected_status,
123                                        bool hashing_enabled) {
124  hashing_enabled_ = hashing_enabled;
125  return Start(file_path, expected_status);
126}
127
128bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) {
129  return Start(file_path, NULL);
130}
131
132bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
133                                        Decryptor* decryptor) {
134  EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata))
135      .Times(AtMost(1));
136  EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted))
137      .Times(AtMost(1));
138  pipeline_->Start(
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)),
146      base::Closure());
147  message_loop_.Run();
148  return (pipeline_status_ == PIPELINE_OK);
149}
150
151void PipelineIntegrationTestBase::Play() {
152  pipeline_->SetPlaybackRate(1);
153}
154
155void PipelineIntegrationTestBase::Pause() {
156  pipeline_->SetPlaybackRate(0);
157}
158
159bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time) {
160  ended_ = false;
161
162  EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted));
163  pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK));
164  message_loop_.Run();
165  return (pipeline_status_ == PIPELINE_OK);
166}
167
168void PipelineIntegrationTestBase::Stop() {
169  DCHECK(pipeline_->IsRunning());
170  pipeline_->Stop(base::MessageLoop::QuitClosure());
171  message_loop_.Run();
172}
173
174void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask(
175    const base::TimeDelta& quit_time) {
176  if (pipeline_->GetMediaTime() >= quit_time ||
177      pipeline_status_ != PIPELINE_OK) {
178    message_loop_.Quit();
179    return;
180  }
181
182  message_loop_.PostDelayedTask(
183      FROM_HERE,
184      base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
185                 base::Unretained(this), quit_time),
186      base::TimeDelta::FromMilliseconds(10));
187}
188
189bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter(
190    const base::TimeDelta& wait_time) {
191  DCHECK(pipeline_->IsRunning());
192  DCHECK_GT(pipeline_->GetPlaybackRate(), 0);
193  DCHECK(wait_time <= pipeline_->GetMediaDuration());
194
195  message_loop_.PostDelayedTask(
196      FROM_HERE,
197      base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
198                 base::Unretained(this),
199                 wait_time),
200      base::TimeDelta::FromMilliseconds(10));
201  message_loop_.Run();
202  return (pipeline_status_ == PIPELINE_OK);
203}
204
205scoped_ptr<FilterCollection>
206PipelineIntegrationTestBase::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);
212
213  media::FFmpegNeedKeyCB need_key_cb =
214      base::Bind(&PipelineIntegrationTestBase::DemuxerNeedKeyCB,
215                 base::Unretained(this));
216  scoped_ptr<Demuxer> demuxer(new FFmpegDemuxer(
217      message_loop_.message_loop_proxy(), data_source_.get(), need_key_cb));
218  return CreateFilterCollection(demuxer.Pass(), decryptor);
219}
220
221scoped_ptr<FilterCollection>
222PipelineIntegrationTestBase::CreateFilterCollection(
223    scoped_ptr<Demuxer> demuxer,
224    Decryptor* decryptor) {
225  demuxer_ = demuxer.Pass();
226
227  scoped_ptr<FilterCollection> collection(new FilterCollection());
228  collection->SetDemuxer(demuxer_.get());
229
230  ScopedVector<VideoDecoder> video_decoders;
231  video_decoders.push_back(
232      new VpxVideoDecoder(message_loop_.message_loop_proxy()));
233  video_decoders.push_back(
234      new FFmpegVideoDecoder(message_loop_.message_loop_proxy()));
235
236  // Disable frame dropping if hashing is enabled.
237  scoped_ptr<VideoRenderer> renderer(new VideoRendererBase(
238      message_loop_.message_loop_proxy(),
239      video_decoders.Pass(),
240      base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
241                 base::Unretained(this), decryptor),
242      base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint,
243                 base::Unretained(this)),
244      base::Bind(&PipelineIntegrationTestBase::OnSetOpaque,
245                 base::Unretained(this)),
246      !hashing_enabled_));
247  collection->SetVideoRenderer(renderer.Pass());
248
249  audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy());
250
251  ScopedVector<AudioDecoder> audio_decoders;
252  audio_decoders.push_back(
253      new FFmpegAudioDecoder(message_loop_.message_loop_proxy()));
254  audio_decoders.push_back(
255      new OpusAudioDecoder(message_loop_.message_loop_proxy()));
256
257  AudioRendererImpl* audio_renderer_impl = new AudioRendererImpl(
258      message_loop_.message_loop_proxy(),
259      audio_sink_.get(),
260      audio_decoders.Pass(),
261      base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
262                 base::Unretained(this),
263                 decryptor),
264      true);
265  // Disable underflow if hashing is enabled.
266  if (hashing_enabled_) {
267    audio_sink_->StartAudioHashForTesting();
268    audio_renderer_impl->DisableUnderflowForTesting();
269  }
270  scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_impl);
271  collection->SetAudioRenderer(audio_renderer.Pass());
272
273  return collection.Pass();
274}
275
276void PipelineIntegrationTestBase::SetDecryptor(
277    Decryptor* decryptor,
278    const DecryptorReadyCB& decryptor_ready_cb) {
279  decryptor_ready_cb.Run(decryptor);
280}
281
282void PipelineIntegrationTestBase::OnVideoRendererPaint(
283    const scoped_refptr<VideoFrame>& frame) {
284  last_video_frame_format_ = frame->format();
285  if (!hashing_enabled_)
286    return;
287  frame->HashFrameForTesting(&md5_context_);
288}
289
290std::string PipelineIntegrationTestBase::GetVideoHash() {
291  DCHECK(hashing_enabled_);
292  base::MD5Digest digest;
293  base::MD5Final(&digest, &md5_context_);
294  return base::MD5DigestToBase16(digest);
295}
296
297std::string PipelineIntegrationTestBase::GetAudioHash() {
298  DCHECK(hashing_enabled_);
299  return audio_sink_->GetAudioHashForTesting();
300}
301
302}  // namespace media
303