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/vpx_video_decoder.h"
     18 
     19 using ::testing::AnyNumber;
     20 using ::testing::AtMost;
     21 
     22 namespace media {
     23 
     24 const char kNullVideoHash[] = "d41d8cd98f00b204e9800998ecf8427e";
     25 const char kNullAudioHash[] = "0.00,0.00,0.00,0.00,0.00,0.00,";
     26 
     27 PipelineIntegrationTestBase::PipelineIntegrationTestBase()
     28     : hashing_enabled_(false),
     29       pipeline_(new Pipeline(message_loop_.message_loop_proxy(),
     30                              new MediaLog())),
     31       ended_(false),
     32       pipeline_status_(PIPELINE_OK),
     33       last_video_frame_format_(VideoFrame::INVALID) {
     34   base::MD5Init(&md5_context_);
     35   EXPECT_CALL(*this, OnSetOpaque(true)).Times(AnyNumber());
     36 }
     37 
     38 PipelineIntegrationTestBase::~PipelineIntegrationTestBase() {
     39   if (!pipeline_->IsRunning())
     40     return;
     41 
     42   Stop();
     43 }
     44 
     45 void PipelineIntegrationTestBase::OnStatusCallback(
     46     PipelineStatus status) {
     47   pipeline_status_ = status;
     48   message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
     49 }
     50 
     51 void PipelineIntegrationTestBase::OnStatusCallbackChecked(
     52     PipelineStatus expected_status,
     53     PipelineStatus status) {
     54   EXPECT_EQ(expected_status, status);
     55   OnStatusCallback(status);
     56 }
     57 
     58 PipelineStatusCB PipelineIntegrationTestBase::QuitOnStatusCB(
     59     PipelineStatus expected_status) {
     60   return base::Bind(&PipelineIntegrationTestBase::OnStatusCallbackChecked,
     61                     base::Unretained(this),
     62                     expected_status);
     63 }
     64 
     65 void PipelineIntegrationTestBase::DemuxerNeedKeyCB(
     66     const std::string& type,
     67     scoped_ptr<uint8[]> init_data,
     68     int init_data_size) {
     69   DCHECK(init_data.get());
     70   DCHECK_GT(init_data_size, 0);
     71   CHECK(!need_key_cb_.is_null());
     72   need_key_cb_.Run(std::string(), type, init_data.Pass(), init_data_size);
     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                                         bool hashing_enabled) {
    124   hashing_enabled_ = hashing_enabled;
    125   return Start(file_path, expected_status);
    126 }
    127 
    128 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) {
    129   return Start(file_path, NULL);
    130 }
    131 
    132 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path,
    133                                         Decryptor* decryptor) {
    134   EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata))
    135       .Times(AtMost(1));
    136   EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted))
    137       .Times(AtMost(1));
    138   pipeline_->Start(
    139       CreateFilterCollection(file_path, decryptor),
    140       base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
    141       base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
    142       base::Bind(&PipelineIntegrationTestBase::OnStatusCallback,
    143                  base::Unretained(this)),
    144       base::Bind(&PipelineIntegrationTestBase::OnBufferingState,
    145                  base::Unretained(this)),
    146       base::Closure());
    147   message_loop_.Run();
    148   return (pipeline_status_ == PIPELINE_OK);
    149 }
    150 
    151 void PipelineIntegrationTestBase::Play() {
    152   pipeline_->SetPlaybackRate(1);
    153 }
    154 
    155 void PipelineIntegrationTestBase::Pause() {
    156   pipeline_->SetPlaybackRate(0);
    157 }
    158 
    159 bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time) {
    160   ended_ = false;
    161 
    162   EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted));
    163   pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK));
    164   message_loop_.Run();
    165   return (pipeline_status_ == PIPELINE_OK);
    166 }
    167 
    168 void PipelineIntegrationTestBase::Stop() {
    169   DCHECK(pipeline_->IsRunning());
    170   pipeline_->Stop(base::MessageLoop::QuitClosure());
    171   message_loop_.Run();
    172 }
    173 
    174 void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask(
    175     const base::TimeDelta& quit_time) {
    176   if (pipeline_->GetMediaTime() >= quit_time ||
    177       pipeline_status_ != PIPELINE_OK) {
    178     message_loop_.Quit();
    179     return;
    180   }
    181 
    182   message_loop_.PostDelayedTask(
    183       FROM_HERE,
    184       base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
    185                  base::Unretained(this), quit_time),
    186       base::TimeDelta::FromMilliseconds(10));
    187 }
    188 
    189 bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter(
    190     const base::TimeDelta& wait_time) {
    191   DCHECK(pipeline_->IsRunning());
    192   DCHECK_GT(pipeline_->GetPlaybackRate(), 0);
    193   DCHECK(wait_time <= pipeline_->GetMediaDuration());
    194 
    195   message_loop_.PostDelayedTask(
    196       FROM_HERE,
    197       base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask,
    198                  base::Unretained(this),
    199                  wait_time),
    200       base::TimeDelta::FromMilliseconds(10));
    201   message_loop_.Run();
    202   return (pipeline_status_ == PIPELINE_OK);
    203 }
    204 
    205 scoped_ptr<FilterCollection>
    206 PipelineIntegrationTestBase::CreateFilterCollection(
    207     const base::FilePath& file_path,
    208     Decryptor* decryptor) {
    209   FileDataSource* file_data_source = new FileDataSource();
    210   CHECK(file_data_source->Initialize(file_path));
    211   data_source_.reset(file_data_source);
    212 
    213   media::FFmpegNeedKeyCB need_key_cb = base::Bind(
    214       &PipelineIntegrationTestBase::DemuxerNeedKeyCB, base::Unretained(this));
    215   scoped_ptr<Demuxer> demuxer(
    216       new FFmpegDemuxer(message_loop_.message_loop_proxy(),
    217                         data_source_.get(),
    218                         need_key_cb,
    219                         new MediaLog()));
    220   return CreateFilterCollection(demuxer.Pass(), decryptor);
    221 }
    222 
    223 scoped_ptr<FilterCollection>
    224 PipelineIntegrationTestBase::CreateFilterCollection(
    225     scoped_ptr<Demuxer> demuxer,
    226     Decryptor* decryptor) {
    227   demuxer_ = demuxer.Pass();
    228 
    229   scoped_ptr<FilterCollection> collection(new FilterCollection());
    230   collection->SetDemuxer(demuxer_.get());
    231 
    232   ScopedVector<VideoDecoder> video_decoders;
    233   video_decoders.push_back(
    234       new VpxVideoDecoder(message_loop_.message_loop_proxy()));
    235   video_decoders.push_back(
    236       new FFmpegVideoDecoder(message_loop_.message_loop_proxy()));
    237 
    238   // Disable frame dropping if hashing is enabled.
    239   scoped_ptr<VideoRenderer> renderer(new VideoRendererBase(
    240       message_loop_.message_loop_proxy(),
    241       video_decoders.Pass(),
    242       base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
    243                  base::Unretained(this), decryptor),
    244       base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint,
    245                  base::Unretained(this)),
    246       base::Bind(&PipelineIntegrationTestBase::OnSetOpaque,
    247                  base::Unretained(this)),
    248       !hashing_enabled_));
    249   collection->SetVideoRenderer(renderer.Pass());
    250 
    251   audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy());
    252 
    253   ScopedVector<AudioDecoder> audio_decoders;
    254   audio_decoders.push_back(
    255       new FFmpegAudioDecoder(message_loop_.message_loop_proxy()));
    256   audio_decoders.push_back(
    257       new OpusAudioDecoder(message_loop_.message_loop_proxy()));
    258 
    259   AudioRendererImpl* audio_renderer_impl = new AudioRendererImpl(
    260       message_loop_.message_loop_proxy(),
    261       audio_sink_.get(),
    262       audio_decoders.Pass(),
    263       base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
    264                  base::Unretained(this),
    265                  decryptor),
    266       true);
    267   // Disable underflow if hashing is enabled.
    268   if (hashing_enabled_) {
    269     audio_sink_->StartAudioHashForTesting();
    270     audio_renderer_impl->DisableUnderflowForTesting();
    271   }
    272   scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_impl);
    273   collection->SetAudioRenderer(audio_renderer.Pass());
    274 
    275   return collection.Pass();
    276 }
    277 
    278 void PipelineIntegrationTestBase::SetDecryptor(
    279     Decryptor* decryptor,
    280     const DecryptorReadyCB& decryptor_ready_cb) {
    281   decryptor_ready_cb.Run(decryptor);
    282 }
    283 
    284 void PipelineIntegrationTestBase::OnVideoRendererPaint(
    285     const scoped_refptr<VideoFrame>& frame) {
    286   last_video_frame_format_ = frame->format();
    287   if (!hashing_enabled_)
    288     return;
    289   frame->HashFrameForTesting(&md5_context_);
    290 }
    291 
    292 std::string PipelineIntegrationTestBase::GetVideoHash() {
    293   DCHECK(hashing_enabled_);
    294   base::MD5Digest digest;
    295   base::MD5Final(&digest, &md5_context_);
    296   return base::MD5DigestToBase16(digest);
    297 }
    298 
    299 std::string PipelineIntegrationTestBase::GetAudioHash() {
    300   DCHECK(hashing_enabled_);
    301   return audio_sink_->GetAudioHashForTesting();
    302 }
    303 
    304 }  // namespace media
    305