Home | History | Annotate | Download | only in filters
      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/renderer_impl.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 
     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 
     42 PipelineIntegrationTestBase::~PipelineIntegrationTestBase() {
     43   if (!pipeline_->IsRunning())
     44     return;
     45 
     46   Stop();
     47 }
     48 
     49 void PipelineIntegrationTestBase::OnStatusCallback(
     50     PipelineStatus status) {
     51   pipeline_status_ = status;
     52   message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
     53 }
     54 
     55 void PipelineIntegrationTestBase::OnStatusCallbackChecked(
     56     PipelineStatus expected_status,
     57     PipelineStatus status) {
     58   EXPECT_EQ(expected_status, status);
     59   OnStatusCallback(status);
     60 }
     61 
     62 PipelineStatusCB PipelineIntegrationTestBase::QuitOnStatusCB(
     63     PipelineStatus expected_status) {
     64   return base::Bind(&PipelineIntegrationTestBase::OnStatusCallbackChecked,
     65                     base::Unretained(this),
     66                     expected_status);
     67 }
     68 
     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 
     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 
     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 
     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 
     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 
    105 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
    106                                         PipelineStatus expected_status) {
    107   EXPECT_CALL(*this, OnMetadata(_))
    108       .Times(AtMost(1))
    109       .WillRepeatedly(SaveArg<0>(&metadata_));
    110   EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH))
    111       .Times(AtMost(1));
    112   CreateDemuxer(file_path);
    113   pipeline_->Start(
    114       demuxer_.get(),
    115       CreateRenderer(NULL),
    116       base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
    117       base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
    118       QuitOnStatusCB(expected_status),
    119       base::Bind(&PipelineIntegrationTestBase::OnMetadata,
    120                  base::Unretained(this)),
    121       base::Bind(&PipelineIntegrationTestBase::OnBufferingStateChanged,
    122                  base::Unretained(this)),
    123       base::Closure(),
    124       base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack,
    125                  base::Unretained(this)));
    126   message_loop_.Run();
    127   return (pipeline_status_ == PIPELINE_OK);
    128 }
    129 
    130 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
    131                                         PipelineStatus expected_status,
    132                                         kTestType test_type) {
    133   hashing_enabled_ = test_type == kHashed;
    134   clockless_playback_ = test_type == kClockless;
    135   return Start(file_path, expected_status);
    136 }
    137 
    138 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) {
    139   return Start(file_path, NULL);
    140 }
    141 
    142 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
    143                                         Decryptor* decryptor) {
    144   EXPECT_CALL(*this, OnMetadata(_))
    145       .Times(AtMost(1))
    146       .WillRepeatedly(SaveArg<0>(&metadata_));
    147   EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH))
    148       .Times(AtMost(1));
    149 
    150   CreateDemuxer(file_path);
    151   pipeline_->Start(
    152       demuxer_.get(),
    153       CreateRenderer(decryptor),
    154       base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
    155       base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
    156       base::Bind(&PipelineIntegrationTestBase::OnStatusCallback,
    157                  base::Unretained(this)),
    158       base::Bind(&PipelineIntegrationTestBase::OnMetadata,
    159                  base::Unretained(this)),
    160       base::Bind(&PipelineIntegrationTestBase::OnBufferingStateChanged,
    161                  base::Unretained(this)),
    162       base::Closure(),
    163       base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack,
    164                  base::Unretained(this)));
    165   message_loop_.Run();
    166   return (pipeline_status_ == PIPELINE_OK);
    167 }
    168 
    169 void PipelineIntegrationTestBase::Play() {
    170   pipeline_->SetPlaybackRate(1);
    171 }
    172 
    173 void PipelineIntegrationTestBase::Pause() {
    174   pipeline_->SetPlaybackRate(0);
    175 }
    176 
    177 bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time) {
    178   ended_ = false;
    179 
    180   EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH));
    181   pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK));
    182   message_loop_.Run();
    183   return (pipeline_status_ == PIPELINE_OK);
    184 }
    185 
    186 void PipelineIntegrationTestBase::Stop() {
    187   DCHECK(pipeline_->IsRunning());
    188   pipeline_->Stop(base::MessageLoop::QuitClosure());
    189   message_loop_.Run();
    190 }
    191 
    192 void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask(
    193     const base::TimeDelta& quit_time) {
    194   if (pipeline_->GetMediaTime() >= quit_time ||
    195       pipeline_status_ != PIPELINE_OK) {
    196     message_loop_.Quit();
    197     return;
    198   }
    199 
    200   message_loop_.PostDelayedTask(
    201       FROM_HERE,
    202       base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
    203                  base::Unretained(this), quit_time),
    204       base::TimeDelta::FromMilliseconds(10));
    205 }
    206 
    207 bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter(
    208     const base::TimeDelta& wait_time) {
    209   DCHECK(pipeline_->IsRunning());
    210   DCHECK_GT(pipeline_->GetPlaybackRate(), 0);
    211   DCHECK(wait_time <= pipeline_->GetMediaDuration());
    212 
    213   message_loop_.PostDelayedTask(
    214       FROM_HERE,
    215       base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
    216                  base::Unretained(this),
    217                  wait_time),
    218       base::TimeDelta::FromMilliseconds(10));
    219   message_loop_.Run();
    220   return (pipeline_status_ == PIPELINE_OK);
    221 }
    222 
    223 void PipelineIntegrationTestBase::CreateDemuxer(
    224     const base::FilePath& file_path) {
    225   FileDataSource* file_data_source = new FileDataSource();
    226   CHECK(file_data_source->Initialize(file_path)) << "Is " << file_path.value()
    227                                                  << " missing?";
    228   data_source_.reset(file_data_source);
    229 
    230   Demuxer::NeedKeyCB need_key_cb = base::Bind(
    231       &PipelineIntegrationTestBase::DemuxerNeedKeyCB, base::Unretained(this));
    232   demuxer_ =
    233       scoped_ptr<Demuxer>(new FFmpegDemuxer(message_loop_.message_loop_proxy(),
    234                                             data_source_.get(),
    235                                             need_key_cb,
    236                                             new MediaLog()));
    237 }
    238 
    239 scoped_ptr<Renderer> PipelineIntegrationTestBase::CreateRenderer(
    240     Decryptor* decryptor) {
    241   ScopedVector<VideoDecoder> video_decoders;
    242 #if !defined(MEDIA_DISABLE_LIBVPX)
    243   video_decoders.push_back(
    244       new VpxVideoDecoder(message_loop_.message_loop_proxy()));
    245 #endif  // !defined(MEDIA_DISABLE_LIBVPX)
    246   video_decoders.push_back(
    247       new FFmpegVideoDecoder(message_loop_.message_loop_proxy()));
    248 
    249   // Disable frame dropping if hashing is enabled.
    250   scoped_ptr<VideoRenderer> video_renderer(new VideoRendererImpl(
    251       message_loop_.message_loop_proxy(),
    252       video_decoders.Pass(),
    253       base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
    254                  base::Unretained(this),
    255                  decryptor),
    256       base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint,
    257                  base::Unretained(this)),
    258       false,
    259       new MediaLog()));
    260 
    261   if (!clockless_playback_) {
    262     audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy());
    263   } else {
    264     clockless_audio_sink_ = new ClocklessAudioSink();
    265   }
    266 
    267   ScopedVector<AudioDecoder> audio_decoders;
    268   audio_decoders.push_back(
    269       new FFmpegAudioDecoder(message_loop_.message_loop_proxy(), LogCB()));
    270   audio_decoders.push_back(
    271       new OpusAudioDecoder(message_loop_.message_loop_proxy()));
    272 
    273   AudioParameters out_params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
    274                              CHANNEL_LAYOUT_STEREO,
    275                              44100,
    276                              16,
    277                              512);
    278   hardware_config_.UpdateOutputConfig(out_params);
    279 
    280   scoped_ptr<AudioRenderer> audio_renderer(new AudioRendererImpl(
    281       message_loop_.message_loop_proxy(),
    282       (clockless_playback_)
    283           ? static_cast<AudioRendererSink*>(clockless_audio_sink_.get())
    284           : audio_sink_.get(),
    285       audio_decoders.Pass(),
    286       base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
    287                  base::Unretained(this),
    288                  decryptor),
    289       hardware_config_,
    290       new MediaLog()));
    291   if (hashing_enabled_)
    292     audio_sink_->StartAudioHashForTesting();
    293 
    294   scoped_ptr<RendererImpl> renderer_impl(
    295       new RendererImpl(message_loop_.message_loop_proxy(),
    296                        demuxer_.get(),
    297                        audio_renderer.Pass(),
    298                        video_renderer.Pass()));
    299 
    300   // Prevent non-deterministic buffering state callbacks from firing (e.g., slow
    301   // machine, valgrind).
    302   renderer_impl->DisableUnderflowForTesting();
    303 
    304   if (clockless_playback_)
    305     renderer_impl->EnableClocklessVideoPlaybackForTesting();
    306 
    307   return renderer_impl.PassAs<Renderer>();
    308 }
    309 
    310 void PipelineIntegrationTestBase::SetDecryptor(
    311     Decryptor* decryptor,
    312     const DecryptorReadyCB& decryptor_ready_cb) {
    313   decryptor_ready_cb.Run(
    314       decryptor,
    315       base::Bind(&PipelineIntegrationTestBase::DecryptorAttached,
    316                  base::Unretained(this)));
    317   EXPECT_CALL(*this, DecryptorAttached(true));
    318 }
    319 
    320 void PipelineIntegrationTestBase::OnVideoRendererPaint(
    321     const scoped_refptr<VideoFrame>& frame) {
    322   last_video_frame_format_ = frame->format();
    323   if (!hashing_enabled_)
    324     return;
    325   frame->HashFrameForTesting(&md5_context_);
    326 }
    327 
    328 std::string PipelineIntegrationTestBase::GetVideoHash() {
    329   DCHECK(hashing_enabled_);
    330   base::MD5Digest digest;
    331   base::MD5Final(&digest, &md5_context_);
    332   return base::MD5DigestToBase16(digest);
    333 }
    334 
    335 std::string PipelineIntegrationTestBase::GetAudioHash() {
    336   DCHECK(hashing_enabled_);
    337   return audio_sink_->GetAudioHashForTesting();
    338 }
    339 
    340 base::TimeDelta PipelineIntegrationTestBase::GetAudioTime() {
    341   DCHECK(clockless_playback_);
    342   return clockless_audio_sink_->render_time();
    343 }
    344 
    345 base::TimeTicks DummyTickClock::NowTicks() {
    346   now_ += base::TimeDelta::FromSeconds(60);
    347   return now_;
    348 }
    349 
    350 }  // namespace media
    351