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