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/renderer_impl.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(_))
108      .Times(AtMost(1))
109      .WillRepeatedly(SaveArg<0>(&metadata_));
110  EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH))
111      .Times(AtMost(1));
112  CreateDemuxer(file_path);
113  pipeline_->Start(
114      demuxer_.get(),
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)),
123      base::Closure(),
124      base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack,
125                 base::Unretained(this)));
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  return Start(file_path, expected_status);
136}
137
138bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) {
139  return Start(file_path, NULL);
140}
141
142bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
143                                        Decryptor* decryptor) {
144  EXPECT_CALL(*this, OnMetadata(_))
145      .Times(AtMost(1))
146      .WillRepeatedly(SaveArg<0>(&metadata_));
147  EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH))
148      .Times(AtMost(1));
149
150  CreateDemuxer(file_path);
151  pipeline_->Start(
152      demuxer_.get(),
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)),
162      base::Closure(),
163      base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack,
164                 base::Unretained(this)));
165  message_loop_.Run();
166  return (pipeline_status_ == PIPELINE_OK);
167}
168
169void PipelineIntegrationTestBase::Play() {
170  pipeline_->SetPlaybackRate(1);
171}
172
173void PipelineIntegrationTestBase::Pause() {
174  pipeline_->SetPlaybackRate(0);
175}
176
177bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time) {
178  ended_ = false;
179
180  EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH));
181  pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK));
182  message_loop_.Run();
183  return (pipeline_status_ == PIPELINE_OK);
184}
185
186void PipelineIntegrationTestBase::Stop() {
187  DCHECK(pipeline_->IsRunning());
188  pipeline_->Stop(base::MessageLoop::QuitClosure());
189  message_loop_.Run();
190}
191
192void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask(
193    const base::TimeDelta& quit_time) {
194  if (pipeline_->GetMediaTime() >= quit_time ||
195      pipeline_status_ != PIPELINE_OK) {
196    message_loop_.Quit();
197    return;
198  }
199
200  message_loop_.PostDelayedTask(
201      FROM_HERE,
202      base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
203                 base::Unretained(this), quit_time),
204      base::TimeDelta::FromMilliseconds(10));
205}
206
207bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter(
208    const base::TimeDelta& wait_time) {
209  DCHECK(pipeline_->IsRunning());
210  DCHECK_GT(pipeline_->GetPlaybackRate(), 0);
211  DCHECK(wait_time <= pipeline_->GetMediaDuration());
212
213  message_loop_.PostDelayedTask(
214      FROM_HERE,
215      base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
216                 base::Unretained(this),
217                 wait_time),
218      base::TimeDelta::FromMilliseconds(10));
219  message_loop_.Run();
220  return (pipeline_status_ == PIPELINE_OK);
221}
222
223void 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()
227                                                 << " missing?";
228  data_source_.reset(file_data_source);
229
230  Demuxer::NeedKeyCB need_key_cb = base::Bind(
231      &PipelineIntegrationTestBase::DemuxerNeedKeyCB, base::Unretained(this));
232  demuxer_ =
233      scoped_ptr<Demuxer>(new FFmpegDemuxer(message_loop_.message_loop_proxy(),
234                                            data_source_.get(),
235                                            need_key_cb,
236                                            new MediaLog()));
237}
238
239scoped_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()));
248
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),
255                 decryptor),
256      base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint,
257                 base::Unretained(this)),
258      false,
259      new MediaLog()));
260
261  if (!clockless_playback_) {
262    audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy());
263  } else {
264    clockless_audio_sink_ = new ClocklessAudioSink();
265  }
266
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()));
272
273  AudioParameters out_params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
274                             CHANNEL_LAYOUT_STEREO,
275                             44100,
276                             16,
277                             512);
278  hardware_config_.UpdateOutputConfig(out_params);
279
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())
284          : audio_sink_.get(),
285      audio_decoders.Pass(),
286      base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
287                 base::Unretained(this),
288                 decryptor),
289      hardware_config_,
290      new MediaLog()));
291  if (hashing_enabled_)
292    audio_sink_->StartAudioHashForTesting();
293
294  scoped_ptr<RendererImpl> renderer_impl(
295      new RendererImpl(message_loop_.message_loop_proxy(),
296                       demuxer_.get(),
297                       audio_renderer.Pass(),
298                       video_renderer.Pass()));
299
300  // Prevent non-deterministic buffering state callbacks from firing (e.g., slow
301  // machine, valgrind).
302  renderer_impl->DisableUnderflowForTesting();
303
304  if (clockless_playback_)
305    renderer_impl->EnableClocklessVideoPlaybackForTesting();
306
307  return renderer_impl.PassAs<Renderer>();
308}
309
310void PipelineIntegrationTestBase::SetDecryptor(
311    Decryptor* decryptor,
312    const DecryptorReadyCB& decryptor_ready_cb) {
313  decryptor_ready_cb.Run(
314      decryptor,
315      base::Bind(&PipelineIntegrationTestBase::DecryptorAttached,
316                 base::Unretained(this)));
317  EXPECT_CALL(*this, DecryptorAttached(true));
318}
319
320void PipelineIntegrationTestBase::OnVideoRendererPaint(
321    const scoped_refptr<VideoFrame>& frame) {
322  last_video_frame_format_ = frame->format();
323  if (!hashing_enabled_)
324    return;
325  frame->HashFrameForTesting(&md5_context_);
326}
327
328std::string PipelineIntegrationTestBase::GetVideoHash() {
329  DCHECK(hashing_enabled_);
330  base::MD5Digest digest;
331  base::MD5Final(&digest, &md5_context_);
332  return base::MD5DigestToBase16(digest);
333}
334
335std::string PipelineIntegrationTestBase::GetAudioHash() {
336  DCHECK(hashing_enabled_);
337  return audio_sink_->GetAudioHashForTesting();
338}
339
340base::TimeDelta PipelineIntegrationTestBase::GetAudioTime() {
341  DCHECK(clockless_playback_);
342  return clockless_audio_sink_->render_time();
343}
344
345base::TimeTicks DummyTickClock::NowTicks() {
346  now_ += base::TimeDelta::FromSeconds(60);
347  return now_;
348}
349
350}  // namespace media
351