pipeline_integration_test_base.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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/base/time_delta_interpolator.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"
19
20using ::testing::_;
21using ::testing::AnyNumber;
22using ::testing::AtMost;
23using ::testing::SaveArg;
24
25namespace media {
26
27const char kNullVideoHash[] = "d41d8cd98f00b204e9800998ecf8427e";
28const char kNullAudioHash[] = "0.00,0.00,0.00,0.00,0.00,0.00,";
29
30PipelineIntegrationTestBase::PipelineIntegrationTestBase()
31    : hashing_enabled_(false),
32      clockless_playback_(false),
33      pipeline_(
34          new Pipeline(message_loop_.message_loop_proxy(), new MediaLog())),
35      ended_(false),
36      pipeline_status_(PIPELINE_OK),
37      last_video_frame_format_(VideoFrame::UNKNOWN),
38      hardware_config_(AudioParameters(), AudioParameters()) {
39  base::MD5Init(&md5_context_);
40
41  // Prevent non-deterministic buffering state callbacks from firing (e.g., slow
42  // machine, valgrind).
43  pipeline_->set_underflow_disabled_for_testing(true);
44}
45
46PipelineIntegrationTestBase::~PipelineIntegrationTestBase() {
47  if (!pipeline_->IsRunning())
48    return;
49
50  Stop();
51}
52
53void PipelineIntegrationTestBase::OnStatusCallback(
54    PipelineStatus status) {
55  pipeline_status_ = status;
56  message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
57}
58
59void PipelineIntegrationTestBase::OnStatusCallbackChecked(
60    PipelineStatus expected_status,
61    PipelineStatus status) {
62  EXPECT_EQ(expected_status, status);
63  OnStatusCallback(status);
64}
65
66PipelineStatusCB PipelineIntegrationTestBase::QuitOnStatusCB(
67    PipelineStatus expected_status) {
68  return base::Bind(&PipelineIntegrationTestBase::OnStatusCallbackChecked,
69                    base::Unretained(this),
70                    expected_status);
71}
72
73void PipelineIntegrationTestBase::DemuxerNeedKeyCB(
74    const std::string& type,
75    const std::vector<uint8>& init_data) {
76  DCHECK(!init_data.empty());
77  CHECK(!need_key_cb_.is_null());
78  need_key_cb_.Run(type, init_data);
79}
80
81void PipelineIntegrationTestBase::OnEnded() {
82  DCHECK(!ended_);
83  ended_ = true;
84  pipeline_status_ = PIPELINE_OK;
85  message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
86}
87
88bool PipelineIntegrationTestBase::WaitUntilOnEnded() {
89  if (ended_)
90    return (pipeline_status_ == PIPELINE_OK);
91  message_loop_.Run();
92  EXPECT_TRUE(ended_);
93  return ended_ && (pipeline_status_ == PIPELINE_OK);
94}
95
96PipelineStatus PipelineIntegrationTestBase::WaitUntilEndedOrError() {
97  if (ended_ || pipeline_status_ != PIPELINE_OK)
98    return pipeline_status_;
99  message_loop_.Run();
100  return pipeline_status_;
101}
102
103void PipelineIntegrationTestBase::OnError(PipelineStatus status) {
104  DCHECK_NE(status, PIPELINE_OK);
105  pipeline_status_ = status;
106  message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
107}
108
109bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
110                                        PipelineStatus expected_status) {
111  EXPECT_CALL(*this, OnMetadata(_))
112      .Times(AtMost(1))
113      .WillRepeatedly(SaveArg<0>(&metadata_));
114  EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH))
115      .Times(AtMost(1));
116  pipeline_->Start(
117      CreateFilterCollection(file_path, NULL),
118      base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
119      base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
120      QuitOnStatusCB(expected_status),
121      base::Bind(&PipelineIntegrationTestBase::OnMetadata,
122                 base::Unretained(this)),
123      base::Bind(&PipelineIntegrationTestBase::OnBufferingStateChanged,
124                 base::Unretained(this)),
125      base::Closure());
126  message_loop_.Run();
127  return (pipeline_status_ == PIPELINE_OK);
128}
129
130bool 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  if (clockless_playback_) {
136    pipeline_->SetTimeDeltaInterpolatorForTesting(
137        new TimeDeltaInterpolator(&dummy_clock_));
138  }
139  return Start(file_path, expected_status);
140}
141
142bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) {
143  return Start(file_path, NULL);
144}
145
146bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
147                                        Decryptor* decryptor) {
148  EXPECT_CALL(*this, OnMetadata(_))
149      .Times(AtMost(1))
150      .WillRepeatedly(SaveArg<0>(&metadata_));
151  EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH))
152      .Times(AtMost(1));
153  pipeline_->Start(
154      CreateFilterCollection(file_path, decryptor),
155      base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
156      base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
157      base::Bind(&PipelineIntegrationTestBase::OnStatusCallback,
158                 base::Unretained(this)),
159      base::Bind(&PipelineIntegrationTestBase::OnMetadata,
160                 base::Unretained(this)),
161      base::Bind(&PipelineIntegrationTestBase::OnBufferingStateChanged,
162                 base::Unretained(this)),
163      base::Closure());
164  message_loop_.Run();
165  return (pipeline_status_ == PIPELINE_OK);
166}
167
168void PipelineIntegrationTestBase::Play() {
169  pipeline_->SetPlaybackRate(1);
170}
171
172void PipelineIntegrationTestBase::Pause() {
173  pipeline_->SetPlaybackRate(0);
174}
175
176bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time) {
177  ended_ = false;
178
179  EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH));
180  pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK));
181  message_loop_.Run();
182  return (pipeline_status_ == PIPELINE_OK);
183}
184
185void PipelineIntegrationTestBase::Stop() {
186  DCHECK(pipeline_->IsRunning());
187  pipeline_->Stop(base::MessageLoop::QuitClosure());
188  message_loop_.Run();
189}
190
191void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask(
192    const base::TimeDelta& quit_time) {
193  if (pipeline_->GetMediaTime() >= quit_time ||
194      pipeline_status_ != PIPELINE_OK) {
195    message_loop_.Quit();
196    return;
197  }
198
199  message_loop_.PostDelayedTask(
200      FROM_HERE,
201      base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
202                 base::Unretained(this), quit_time),
203      base::TimeDelta::FromMilliseconds(10));
204}
205
206bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter(
207    const base::TimeDelta& wait_time) {
208  DCHECK(pipeline_->IsRunning());
209  DCHECK_GT(pipeline_->GetPlaybackRate(), 0);
210  DCHECK(wait_time <= pipeline_->GetMediaDuration());
211
212  message_loop_.PostDelayedTask(
213      FROM_HERE,
214      base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
215                 base::Unretained(this),
216                 wait_time),
217      base::TimeDelta::FromMilliseconds(10));
218  message_loop_.Run();
219  return (pipeline_status_ == PIPELINE_OK);
220}
221
222scoped_ptr<FilterCollection>
223PipelineIntegrationTestBase::CreateFilterCollection(
224    const base::FilePath& file_path,
225    Decryptor* decryptor) {
226  FileDataSource* file_data_source = new FileDataSource();
227  CHECK(file_data_source->Initialize(file_path)) << "Is " << file_path.value()
228                                                 << " missing?";
229  data_source_.reset(file_data_source);
230
231  Demuxer::NeedKeyCB need_key_cb = base::Bind(
232      &PipelineIntegrationTestBase::DemuxerNeedKeyCB, base::Unretained(this));
233  scoped_ptr<Demuxer> demuxer(
234      new FFmpegDemuxer(message_loop_.message_loop_proxy(),
235                        data_source_.get(),
236                        need_key_cb,
237                        new MediaLog()));
238  return CreateFilterCollection(demuxer.Pass(), decryptor);
239}
240
241scoped_ptr<FilterCollection>
242PipelineIntegrationTestBase::CreateFilterCollection(
243    scoped_ptr<Demuxer> demuxer,
244    Decryptor* decryptor) {
245  demuxer_ = demuxer.Pass();
246
247  scoped_ptr<FilterCollection> collection(new FilterCollection());
248  collection->SetDemuxer(demuxer_.get());
249
250  ScopedVector<VideoDecoder> video_decoders;
251#if !defined(MEDIA_DISABLE_LIBVPX)
252  video_decoders.push_back(
253      new VpxVideoDecoder(message_loop_.message_loop_proxy()));
254#endif  // !defined(MEDIA_DISABLE_LIBVPX)
255  video_decoders.push_back(
256      new FFmpegVideoDecoder(message_loop_.message_loop_proxy()));
257
258  // Disable frame dropping if hashing is enabled.
259  scoped_ptr<VideoRenderer> renderer(new VideoRendererImpl(
260      message_loop_.message_loop_proxy(),
261      video_decoders.Pass(),
262      base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
263                 base::Unretained(this),
264                 decryptor),
265      base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint,
266                 base::Unretained(this)),
267      false));
268  collection->SetVideoRenderer(renderer.Pass());
269
270  if (!clockless_playback_) {
271    audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy());
272  } else {
273    clockless_audio_sink_ = new ClocklessAudioSink();
274  }
275
276  ScopedVector<AudioDecoder> audio_decoders;
277  audio_decoders.push_back(
278      new FFmpegAudioDecoder(message_loop_.message_loop_proxy(), LogCB()));
279  audio_decoders.push_back(
280      new OpusAudioDecoder(message_loop_.message_loop_proxy()));
281
282  AudioParameters out_params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
283                             CHANNEL_LAYOUT_STEREO,
284                             44100,
285                             16,
286                             512);
287  hardware_config_.UpdateOutputConfig(out_params);
288
289  AudioRendererImpl* audio_renderer_impl = new AudioRendererImpl(
290      message_loop_.message_loop_proxy(),
291      (clockless_playback_)
292          ? static_cast<AudioRendererSink*>(clockless_audio_sink_.get())
293          : audio_sink_.get(),
294      audio_decoders.Pass(),
295      base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
296                 base::Unretained(this),
297                 decryptor),
298      &hardware_config_);
299  if (hashing_enabled_)
300    audio_sink_->StartAudioHashForTesting();
301  scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_impl);
302  collection->SetAudioRenderer(audio_renderer.Pass());
303
304  return collection.Pass();
305}
306
307void PipelineIntegrationTestBase::SetDecryptor(
308    Decryptor* decryptor,
309    const DecryptorReadyCB& decryptor_ready_cb) {
310  decryptor_ready_cb.Run(decryptor);
311}
312
313void PipelineIntegrationTestBase::OnVideoRendererPaint(
314    const scoped_refptr<VideoFrame>& frame) {
315  last_video_frame_format_ = frame->format();
316  if (!hashing_enabled_)
317    return;
318  frame->HashFrameForTesting(&md5_context_);
319}
320
321std::string PipelineIntegrationTestBase::GetVideoHash() {
322  DCHECK(hashing_enabled_);
323  base::MD5Digest digest;
324  base::MD5Final(&digest, &md5_context_);
325  return base::MD5DigestToBase16(digest);
326}
327
328std::string PipelineIntegrationTestBase::GetAudioHash() {
329  DCHECK(hashing_enabled_);
330  return audio_sink_->GetAudioHashForTesting();
331}
332
333base::TimeDelta PipelineIntegrationTestBase::GetAudioTime() {
334  DCHECK(clockless_playback_);
335  return clockless_audio_sink_->render_time();
336}
337
338base::TimeTicks DummyTickClock::NowTicks() {
339  now_ += base::TimeDelta::FromSeconds(60);
340  return now_;
341}
342
343}  // namespace media
344