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/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 
     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 
     40 PipelineIntegrationTestBase::~PipelineIntegrationTestBase() {
     41   if (!pipeline_->IsRunning())
     42     return;
     43 
     44   Stop();
     45 }
     46 
     47 void PipelineIntegrationTestBase::OnStatusCallback(
     48     PipelineStatus status) {
     49   pipeline_status_ = status;
     50   message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
     51 }
     52 
     53 void PipelineIntegrationTestBase::OnStatusCallbackChecked(
     54     PipelineStatus expected_status,
     55     PipelineStatus status) {
     56   EXPECT_EQ(expected_status, status);
     57   OnStatusCallback(status);
     58 }
     59 
     60 PipelineStatusCB PipelineIntegrationTestBase::QuitOnStatusCB(
     61     PipelineStatus expected_status) {
     62   return base::Bind(&PipelineIntegrationTestBase::OnStatusCallbackChecked,
     63                     base::Unretained(this),
     64                     expected_status);
     65 }
     66 
     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 
     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 
     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 
     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 
     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 
    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 
    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 
    132 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) {
    133   return Start(file_path, NULL);
    134 }
    135 
    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 
    155 void PipelineIntegrationTestBase::Play() {
    156   pipeline_->SetPlaybackRate(1);
    157 }
    158 
    159 void PipelineIntegrationTestBase::Pause() {
    160   pipeline_->SetPlaybackRate(0);
    161 }
    162 
    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 
    172 void PipelineIntegrationTestBase::Stop() {
    173   DCHECK(pipeline_->IsRunning());
    174   pipeline_->Stop(base::MessageLoop::QuitClosure());
    175   message_loop_.Run();
    176 }
    177 
    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 
    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>
    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>
    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 
    288 void PipelineIntegrationTestBase::SetDecryptor(
    289     Decryptor* decryptor,
    290     const DecryptorReadyCB& decryptor_ready_cb) {
    291   decryptor_ready_cb.Run(decryptor);
    292 }
    293 
    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 
    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 
    309 std::string PipelineIntegrationTestBase::GetAudioHash() {
    310   DCHECK(hashing_enabled_);
    311   return audio_sink_->GetAudioHashForTesting();
    312 }
    313 
    314 base::TimeDelta PipelineIntegrationTestBase::GetAudioTime() {
    315   DCHECK(clockless_playback_);
    316   return clockless_audio_sink_->render_time();
    317 }
    318 
    319 base::TimeTicks DummyTickClock::NowTicks() {
    320   now_ += base::TimeDelta::FromSeconds(60);
    321   return now_;
    322 }
    323 
    324 }  // namespace media
    325