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