pipeline_integration_test_base.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
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      clockless_playback_(false),
30      pipeline_(new Pipeline(message_loop_.message_loop_proxy(),
31                             new MediaLog())),
32      ended_(false),
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());
37}
38
39PipelineIntegrationTestBase::~PipelineIntegrationTestBase() {
40  if (!pipeline_->IsRunning())
41    return;
42
43  Stop();
44}
45
46void PipelineIntegrationTestBase::OnStatusCallback(
47    PipelineStatus status) {
48  pipeline_status_ = status;
49  message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
50}
51
52void PipelineIntegrationTestBase::OnStatusCallbackChecked(
53    PipelineStatus expected_status,
54    PipelineStatus status) {
55  EXPECT_EQ(expected_status, status);
56  OnStatusCallback(status);
57}
58
59PipelineStatusCB PipelineIntegrationTestBase::QuitOnStatusCB(
60    PipelineStatus expected_status) {
61  return base::Bind(&PipelineIntegrationTestBase::OnStatusCallbackChecked,
62                    base::Unretained(this),
63                    expected_status);
64}
65
66void 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);
72}
73
74void PipelineIntegrationTestBase::OnEnded() {
75  DCHECK(!ended_);
76  ended_ = true;
77  pipeline_status_ = PIPELINE_OK;
78  message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
79}
80
81bool PipelineIntegrationTestBase::WaitUntilOnEnded() {
82  if (ended_)
83    return (pipeline_status_ == PIPELINE_OK);
84  message_loop_.Run();
85  EXPECT_TRUE(ended_);
86  return ended_ && (pipeline_status_ == PIPELINE_OK);
87}
88
89PipelineStatus PipelineIntegrationTestBase::WaitUntilEndedOrError() {
90  if (ended_ || pipeline_status_ != PIPELINE_OK)
91    return pipeline_status_;
92  message_loop_.Run();
93  return pipeline_status_;
94}
95
96void PipelineIntegrationTestBase::OnError(PipelineStatus status) {
97  DCHECK_NE(status, PIPELINE_OK);
98  pipeline_status_ = status;
99  message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
100}
101
102bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
103                                        PipelineStatus expected_status) {
104  EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata))
105      .Times(AtMost(1));
106  EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted))
107      .Times(AtMost(1));
108  pipeline_->Start(
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)),
115      base::Closure());
116  message_loop_.Run();
117  return (pipeline_status_ == PIPELINE_OK);
118}
119
120bool 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);
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  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(),
217                        data_source_.get(),
218                        need_key_cb,
219                        new MediaLog()));
220  return CreateFilterCollection(demuxer.Pass(), decryptor);
221}
222
223scoped_ptr<FilterCollection>
224PipelineIntegrationTestBase::CreateFilterCollection(
225    scoped_ptr<Demuxer> demuxer,
226    Decryptor* decryptor) {
227  demuxer_ = demuxer.Pass();
228
229  scoped_ptr<FilterCollection> collection(new FilterCollection());
230  collection->SetDemuxer(demuxer_.get());
231
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()));
238
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),
245                   decryptor),
246        base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint,
247                   base::Unretained(this)),
248        base::Bind(&PipelineIntegrationTestBase::OnSetOpaque,
249                   base::Unretained(this)),
250        !hashing_enabled_));
251    collection->SetVideoRenderer(renderer.Pass());
252
253    audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy());
254  } else {
255    // audio only for clockless_playback_
256    clockless_audio_sink_ = new ClocklessAudioSink();
257  }
258
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()));
264
265  AudioRendererImpl* audio_renderer_impl = new AudioRendererImpl(
266      message_loop_.message_loop_proxy(),
267      (clockless_playback_)
268          ? static_cast<AudioRendererSink*>(clockless_audio_sink_.get())
269          : audio_sink_.get(),
270      audio_decoders.Pass(),
271      base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
272                 base::Unretained(this),
273                 decryptor),
274      true);
275  // Disable underflow if hashing is enabled.
276  if (hashing_enabled_) {
277    audio_sink_->StartAudioHashForTesting();
278    audio_renderer_impl->DisableUnderflowForTesting();
279  }
280  scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_impl);
281  collection->SetAudioRenderer(audio_renderer.Pass());
282
283  return collection.Pass();
284}
285
286void PipelineIntegrationTestBase::SetDecryptor(
287    Decryptor* decryptor,
288    const DecryptorReadyCB& decryptor_ready_cb) {
289  decryptor_ready_cb.Run(decryptor);
290}
291
292void PipelineIntegrationTestBase::OnVideoRendererPaint(
293    const scoped_refptr<VideoFrame>& frame) {
294  last_video_frame_format_ = frame->format();
295  if (!hashing_enabled_)
296    return;
297  frame->HashFrameForTesting(&md5_context_);
298}
299
300std::string PipelineIntegrationTestBase::GetVideoHash() {
301  DCHECK(hashing_enabled_);
302  base::MD5Digest digest;
303  base::MD5Final(&digest, &md5_context_);
304  return base::MD5DigestToBase16(digest);
305}
306
307std::string PipelineIntegrationTestBase::GetAudioHash() {
308  DCHECK(hashing_enabled_);
309  return audio_sink_->GetAudioHashForTesting();
310}
311
312base::TimeDelta PipelineIntegrationTestBase::GetAudioTime() {
313  DCHECK(clockless_playback_);
314  return clockless_audio_sink_->render_time();
315}
316
317}  // namespace media
318