pipeline_integration_test_base.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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(
73      std::string(), std::string(), type, init_data.Pass(), init_data_size);
74}
75
76void PipelineIntegrationTestBase::OnEnded() {
77  DCHECK(!ended_);
78  ended_ = true;
79  pipeline_status_ = PIPELINE_OK;
80  message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
81}
82
83bool PipelineIntegrationTestBase::WaitUntilOnEnded() {
84  if (ended_)
85    return (pipeline_status_ == PIPELINE_OK);
86  message_loop_.Run();
87  EXPECT_TRUE(ended_);
88  return ended_ && (pipeline_status_ == PIPELINE_OK);
89}
90
91PipelineStatus PipelineIntegrationTestBase::WaitUntilEndedOrError() {
92  if (ended_ || pipeline_status_ != PIPELINE_OK)
93    return pipeline_status_;
94  message_loop_.Run();
95  return pipeline_status_;
96}
97
98void PipelineIntegrationTestBase::OnError(PipelineStatus status) {
99  DCHECK_NE(status, PIPELINE_OK);
100  pipeline_status_ = status;
101  message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
102}
103
104bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
105                                        PipelineStatus expected_status) {
106  EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata))
107      .Times(AtMost(1));
108  EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted))
109      .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::OnBufferingState,
116                 base::Unretained(this)),
117      base::Closure());
118  message_loop_.Run();
119  return (pipeline_status_ == PIPELINE_OK);
120}
121
122bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
123                                        PipelineStatus expected_status,
124                                        bool hashing_enabled) {
125  hashing_enabled_ = hashing_enabled;
126  return Start(file_path, expected_status);
127}
128
129bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) {
130  return Start(file_path, NULL);
131}
132
133bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
134                                        Decryptor* decryptor) {
135  EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata))
136      .Times(AtMost(1));
137  EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted))
138      .Times(AtMost(1));
139  pipeline_->Start(
140      CreateFilterCollection(file_path, decryptor),
141      base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
142      base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
143      base::Bind(&PipelineIntegrationTestBase::OnStatusCallback,
144                 base::Unretained(this)),
145      base::Bind(&PipelineIntegrationTestBase::OnBufferingState,
146                 base::Unretained(this)),
147      base::Closure());
148  message_loop_.Run();
149  return (pipeline_status_ == PIPELINE_OK);
150}
151
152void PipelineIntegrationTestBase::Play() {
153  pipeline_->SetPlaybackRate(1);
154}
155
156void PipelineIntegrationTestBase::Pause() {
157  pipeline_->SetPlaybackRate(0);
158}
159
160bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time) {
161  ended_ = false;
162
163  EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted));
164  pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK));
165  message_loop_.Run();
166  return (pipeline_status_ == PIPELINE_OK);
167}
168
169void PipelineIntegrationTestBase::Stop() {
170  DCHECK(pipeline_->IsRunning());
171  pipeline_->Stop(base::MessageLoop::QuitClosure());
172  message_loop_.Run();
173}
174
175void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask(
176    const base::TimeDelta& quit_time) {
177  if (pipeline_->GetMediaTime() >= quit_time ||
178      pipeline_status_ != PIPELINE_OK) {
179    message_loop_.Quit();
180    return;
181  }
182
183  message_loop_.PostDelayedTask(
184      FROM_HERE,
185      base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
186                 base::Unretained(this), quit_time),
187      base::TimeDelta::FromMilliseconds(10));
188}
189
190bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter(
191    const base::TimeDelta& wait_time) {
192  DCHECK(pipeline_->IsRunning());
193  DCHECK_GT(pipeline_->GetPlaybackRate(), 0);
194  DCHECK(wait_time <= pipeline_->GetMediaDuration());
195
196  message_loop_.PostDelayedTask(
197      FROM_HERE,
198      base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
199                 base::Unretained(this),
200                 wait_time),
201      base::TimeDelta::FromMilliseconds(10));
202  message_loop_.Run();
203  return (pipeline_status_ == PIPELINE_OK);
204}
205
206scoped_ptr<FilterCollection>
207PipelineIntegrationTestBase::CreateFilterCollection(
208    const base::FilePath& file_path,
209    Decryptor* decryptor) {
210  FileDataSource* file_data_source = new FileDataSource();
211  CHECK(file_data_source->Initialize(file_path));
212  data_source_.reset(file_data_source);
213
214  media::FFmpegNeedKeyCB need_key_cb =
215      base::Bind(&PipelineIntegrationTestBase::DemuxerNeedKeyCB,
216                 base::Unretained(this));
217  scoped_ptr<Demuxer> demuxer(new FFmpegDemuxer(
218      message_loop_.message_loop_proxy(), data_source_.get(), need_key_cb));
219  return CreateFilterCollection(demuxer.Pass(), decryptor);
220}
221
222scoped_ptr<FilterCollection>
223PipelineIntegrationTestBase::CreateFilterCollection(
224    scoped_ptr<Demuxer> demuxer,
225    Decryptor* decryptor) {
226  demuxer_ = demuxer.Pass();
227
228  scoped_ptr<FilterCollection> collection(new FilterCollection());
229  collection->SetDemuxer(demuxer_.get());
230
231  ScopedVector<VideoDecoder> video_decoders;
232  video_decoders.push_back(
233      new VpxVideoDecoder(message_loop_.message_loop_proxy()));
234  video_decoders.push_back(
235      new FFmpegVideoDecoder(message_loop_.message_loop_proxy()));
236
237  // Disable frame dropping if hashing is enabled.
238  scoped_ptr<VideoRenderer> renderer(new VideoRendererBase(
239      message_loop_.message_loop_proxy(),
240      video_decoders.Pass(),
241      base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
242                 base::Unretained(this), decryptor),
243      base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint,
244                 base::Unretained(this)),
245      base::Bind(&PipelineIntegrationTestBase::OnSetOpaque,
246                 base::Unretained(this)),
247      !hashing_enabled_));
248  collection->SetVideoRenderer(renderer.Pass());
249
250  audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy());
251
252  ScopedVector<AudioDecoder> audio_decoders;
253  audio_decoders.push_back(
254      new FFmpegAudioDecoder(message_loop_.message_loop_proxy()));
255  audio_decoders.push_back(
256      new OpusAudioDecoder(message_loop_.message_loop_proxy()));
257
258  AudioRendererImpl* audio_renderer_impl = new AudioRendererImpl(
259      message_loop_.message_loop_proxy(),
260      audio_sink_,
261      audio_decoders.Pass(),
262      base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
263                 base::Unretained(this), decryptor));
264  // Disable underflow if hashing is enabled.
265  if (hashing_enabled_) {
266    audio_sink_->StartAudioHashForTesting();
267    audio_renderer_impl->DisableUnderflowForTesting();
268  }
269  scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_impl);
270  collection->SetAudioRenderer(audio_renderer.Pass());
271
272  return collection.Pass();
273}
274
275void PipelineIntegrationTestBase::SetDecryptor(
276    Decryptor* decryptor,
277    const DecryptorReadyCB& decryptor_ready_cb) {
278  decryptor_ready_cb.Run(decryptor);
279}
280
281void PipelineIntegrationTestBase::OnVideoRendererPaint(
282    const scoped_refptr<VideoFrame>& frame) {
283  last_video_frame_format_ = frame->format();
284  if (!hashing_enabled_)
285    return;
286  frame->HashFrameForTesting(&md5_context_);
287}
288
289std::string PipelineIntegrationTestBase::GetVideoHash() {
290  DCHECK(hashing_enabled_);
291  base::MD5Digest digest;
292  base::MD5Final(&digest, &md5_context_);
293  return base::MD5DigestToBase16(digest);
294}
295
296std::string PipelineIntegrationTestBase::GetAudioHash() {
297  DCHECK(hashing_enabled_);
298  return audio_sink_->GetAudioHashForTesting();
299}
300
301}  // namespace media
302