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