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