Home | History | Annotate | Download | only in filters
      1 // Copyright 2014 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/renderer_impl.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback.h"
      9 #include "base/callback_helpers.h"
     10 #include "base/compiler_specific.h"
     11 #include "base/location.h"
     12 #include "base/single_thread_task_runner.h"
     13 #include "media/base/audio_renderer.h"
     14 #include "media/base/demuxer_stream_provider.h"
     15 #include "media/base/time_source.h"
     16 #include "media/base/video_renderer.h"
     17 #include "media/base/wall_clock_time_source.h"
     18 
     19 namespace media {
     20 
     21 RendererImpl::RendererImpl(
     22     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
     23     DemuxerStreamProvider* demuxer_stream_provider,
     24     scoped_ptr<AudioRenderer> audio_renderer,
     25     scoped_ptr<VideoRenderer> video_renderer)
     26     : state_(STATE_UNINITIALIZED),
     27       task_runner_(task_runner),
     28       demuxer_stream_provider_(demuxer_stream_provider),
     29       audio_renderer_(audio_renderer.Pass()),
     30       video_renderer_(video_renderer.Pass()),
     31       time_source_(NULL),
     32       time_ticking_(false),
     33       audio_buffering_state_(BUFFERING_HAVE_NOTHING),
     34       video_buffering_state_(BUFFERING_HAVE_NOTHING),
     35       audio_ended_(false),
     36       video_ended_(false),
     37       underflow_disabled_for_testing_(false),
     38       clockless_video_playback_enabled_for_testing_(false),
     39       weak_factory_(this),
     40       weak_this_(weak_factory_.GetWeakPtr()) {
     41   DVLOG(1) << __FUNCTION__;
     42 }
     43 
     44 RendererImpl::~RendererImpl() {
     45   DVLOG(1) << __FUNCTION__;
     46   DCHECK(task_runner_->BelongsToCurrentThread());
     47 
     48   // Tear down in opposite order of construction as |video_renderer_| can still
     49   // need |time_source_| (which can be |audio_renderer_|) to be alive.
     50   video_renderer_.reset();
     51   audio_renderer_.reset();
     52 
     53   FireAllPendingCallbacks();
     54 }
     55 
     56 void RendererImpl::Initialize(const base::Closure& init_cb,
     57                               const StatisticsCB& statistics_cb,
     58                               const base::Closure& ended_cb,
     59                               const PipelineStatusCB& error_cb,
     60                               const BufferingStateCB& buffering_state_cb) {
     61   DVLOG(1) << __FUNCTION__;
     62   DCHECK(task_runner_->BelongsToCurrentThread());
     63   DCHECK_EQ(state_, STATE_UNINITIALIZED) << state_;
     64   DCHECK(!init_cb.is_null());
     65   DCHECK(!statistics_cb.is_null());
     66   DCHECK(!ended_cb.is_null());
     67   DCHECK(!error_cb.is_null());
     68   DCHECK(!buffering_state_cb.is_null());
     69   DCHECK(demuxer_stream_provider_->GetStream(DemuxerStream::AUDIO) ||
     70          demuxer_stream_provider_->GetStream(DemuxerStream::VIDEO));
     71 
     72   statistics_cb_ = statistics_cb;
     73   ended_cb_ = ended_cb;
     74   error_cb_ = error_cb;
     75   buffering_state_cb_ = buffering_state_cb;
     76 
     77   init_cb_ = init_cb;
     78   state_ = STATE_INITIALIZING;
     79   InitializeAudioRenderer();
     80 }
     81 
     82 void RendererImpl::Flush(const base::Closure& flush_cb) {
     83   DVLOG(1) << __FUNCTION__;
     84   DCHECK(task_runner_->BelongsToCurrentThread());
     85   DCHECK_EQ(state_, STATE_PLAYING) << state_;
     86   DCHECK(flush_cb_.is_null());
     87 
     88   flush_cb_ = flush_cb;
     89   state_ = STATE_FLUSHING;
     90 
     91   if (time_ticking_)
     92     PausePlayback();
     93 
     94   FlushAudioRenderer();
     95 }
     96 
     97 void RendererImpl::StartPlayingFrom(base::TimeDelta time) {
     98   DVLOG(1) << __FUNCTION__;
     99   DCHECK(task_runner_->BelongsToCurrentThread());
    100   DCHECK_EQ(state_, STATE_PLAYING) << state_;
    101 
    102   time_source_->SetMediaTime(time);
    103 
    104   if (audio_renderer_)
    105     audio_renderer_->StartPlaying();
    106   if (video_renderer_)
    107     video_renderer_->StartPlayingFrom(time);
    108 }
    109 
    110 void RendererImpl::SetPlaybackRate(float playback_rate) {
    111   DVLOG(1) << __FUNCTION__ << "(" << playback_rate << ")";
    112   DCHECK(task_runner_->BelongsToCurrentThread());
    113 
    114   // Playback rate changes are only carried out while playing.
    115   if (state_ != STATE_PLAYING)
    116     return;
    117 
    118   time_source_->SetPlaybackRate(playback_rate);
    119 }
    120 
    121 void RendererImpl::SetVolume(float volume) {
    122   DVLOG(1) << __FUNCTION__;
    123   DCHECK(task_runner_->BelongsToCurrentThread());
    124 
    125   if (audio_renderer_)
    126     audio_renderer_->SetVolume(volume);
    127 }
    128 
    129 base::TimeDelta RendererImpl::GetMediaTime() {
    130   // No BelongsToCurrentThread() checking because this can be called from other
    131   // threads.
    132   return time_source_->CurrentMediaTime();
    133 }
    134 
    135 bool RendererImpl::HasAudio() {
    136   DCHECK(task_runner_->BelongsToCurrentThread());
    137   return audio_renderer_ != NULL;
    138 }
    139 
    140 bool RendererImpl::HasVideo() {
    141   DCHECK(task_runner_->BelongsToCurrentThread());
    142   return video_renderer_ != NULL;
    143 }
    144 
    145 void RendererImpl::SetCdm(MediaKeys* cdm) {
    146   DVLOG(1) << __FUNCTION__;
    147   DCHECK(task_runner_->BelongsToCurrentThread());
    148   // TODO(xhwang): Explore to possibility to move CDM setting from
    149   // WebMediaPlayerImpl to this class. See http://crbug.com/401264
    150   NOTREACHED();
    151 }
    152 
    153 void RendererImpl::DisableUnderflowForTesting() {
    154   DVLOG(1) << __FUNCTION__;
    155   DCHECK(task_runner_->BelongsToCurrentThread());
    156   DCHECK_EQ(state_, STATE_UNINITIALIZED);
    157 
    158   underflow_disabled_for_testing_ = true;
    159 }
    160 
    161 void RendererImpl::EnableClocklessVideoPlaybackForTesting() {
    162   DVLOG(1) << __FUNCTION__;
    163   DCHECK(task_runner_->BelongsToCurrentThread());
    164   DCHECK_EQ(state_, STATE_UNINITIALIZED);
    165   DCHECK(underflow_disabled_for_testing_)
    166       << "Underflow must be disabled for clockless video playback";
    167 
    168   clockless_video_playback_enabled_for_testing_ = true;
    169 }
    170 
    171 base::TimeDelta RendererImpl::GetMediaTimeForSyncingVideo() {
    172   // No BelongsToCurrentThread() checking because this can be called from other
    173   // threads.
    174   //
    175   // TODO(scherkus): Currently called from VideoRendererImpl's internal thread,
    176   // which should go away at some point http://crbug.com/110814
    177   if (clockless_video_playback_enabled_for_testing_)
    178     return base::TimeDelta::Max();
    179 
    180   return time_source_->CurrentMediaTimeForSyncingVideo();
    181 }
    182 
    183 void RendererImpl::InitializeAudioRenderer() {
    184   DVLOG(1) << __FUNCTION__;
    185   DCHECK(task_runner_->BelongsToCurrentThread());
    186   DCHECK_EQ(state_, STATE_INITIALIZING) << state_;
    187   DCHECK(!init_cb_.is_null());
    188 
    189   PipelineStatusCB done_cb =
    190       base::Bind(&RendererImpl::OnAudioRendererInitializeDone, weak_this_);
    191 
    192   if (!demuxer_stream_provider_->GetStream(DemuxerStream::AUDIO)) {
    193     audio_renderer_.reset();
    194     task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK));
    195     return;
    196   }
    197 
    198   audio_renderer_->Initialize(
    199       demuxer_stream_provider_->GetStream(DemuxerStream::AUDIO),
    200       done_cb,
    201       base::Bind(&RendererImpl::OnUpdateStatistics, weak_this_),
    202       base::Bind(&RendererImpl::OnBufferingStateChanged, weak_this_,
    203                  &audio_buffering_state_),
    204       base::Bind(&RendererImpl::OnAudioRendererEnded, weak_this_),
    205       base::Bind(&RendererImpl::OnError, weak_this_));
    206 }
    207 
    208 void RendererImpl::OnAudioRendererInitializeDone(PipelineStatus status) {
    209   DVLOG(1) << __FUNCTION__ << ": " << status;
    210   DCHECK(task_runner_->BelongsToCurrentThread());
    211   DCHECK_EQ(state_, STATE_INITIALIZING) << state_;
    212   DCHECK(!init_cb_.is_null());
    213 
    214   if (status != PIPELINE_OK) {
    215     audio_renderer_.reset();
    216     OnError(status);
    217     return;
    218   }
    219 
    220   InitializeVideoRenderer();
    221 }
    222 
    223 void RendererImpl::InitializeVideoRenderer() {
    224   DVLOG(1) << __FUNCTION__;
    225   DCHECK(task_runner_->BelongsToCurrentThread());
    226   DCHECK_EQ(state_, STATE_INITIALIZING) << state_;
    227   DCHECK(!init_cb_.is_null());
    228 
    229   PipelineStatusCB done_cb =
    230       base::Bind(&RendererImpl::OnVideoRendererInitializeDone, weak_this_);
    231 
    232   if (!demuxer_stream_provider_->GetStream(DemuxerStream::VIDEO)) {
    233     video_renderer_.reset();
    234     task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK));
    235     return;
    236   }
    237 
    238   video_renderer_->Initialize(
    239       demuxer_stream_provider_->GetStream(DemuxerStream::VIDEO),
    240       demuxer_stream_provider_->GetLiveness() ==
    241           DemuxerStreamProvider::LIVENESS_LIVE,
    242       done_cb,
    243       base::Bind(&RendererImpl::OnUpdateStatistics, weak_this_),
    244       base::Bind(&RendererImpl::OnBufferingStateChanged,
    245                  weak_this_,
    246                  &video_buffering_state_),
    247       base::Bind(&RendererImpl::OnVideoRendererEnded, weak_this_),
    248       base::Bind(&RendererImpl::OnError, weak_this_),
    249       base::Bind(&RendererImpl::GetMediaTimeForSyncingVideo,
    250                  base::Unretained(this)));
    251 }
    252 
    253 void RendererImpl::OnVideoRendererInitializeDone(PipelineStatus status) {
    254   DVLOG(1) << __FUNCTION__ << ": " << status;
    255   DCHECK(task_runner_->BelongsToCurrentThread());
    256   DCHECK_EQ(state_, STATE_INITIALIZING) << state_;
    257   DCHECK(!init_cb_.is_null());
    258 
    259   if (status != PIPELINE_OK) {
    260     audio_renderer_.reset();
    261     video_renderer_.reset();
    262     OnError(status);
    263     return;
    264   }
    265 
    266   if (audio_renderer_) {
    267     time_source_ = audio_renderer_->GetTimeSource();
    268   } else {
    269     wall_clock_time_source_.reset(new WallClockTimeSource());
    270     time_source_ = wall_clock_time_source_.get();
    271   }
    272 
    273   state_ = STATE_PLAYING;
    274   DCHECK(time_source_);
    275   DCHECK(audio_renderer_ || video_renderer_);
    276   base::ResetAndReturn(&init_cb_).Run();
    277 }
    278 
    279 void RendererImpl::FlushAudioRenderer() {
    280   DVLOG(1) << __FUNCTION__;
    281   DCHECK(task_runner_->BelongsToCurrentThread());
    282   DCHECK_EQ(state_, STATE_FLUSHING) << state_;
    283   DCHECK(!flush_cb_.is_null());
    284 
    285   if (!audio_renderer_) {
    286     OnAudioRendererFlushDone();
    287     return;
    288   }
    289 
    290   audio_renderer_->Flush(
    291       base::Bind(&RendererImpl::OnAudioRendererFlushDone, weak_this_));
    292 }
    293 
    294 void RendererImpl::OnAudioRendererFlushDone() {
    295   DVLOG(1) << __FUNCTION__;
    296   DCHECK(task_runner_->BelongsToCurrentThread());
    297 
    298   if (state_ == STATE_ERROR) {
    299     DCHECK(flush_cb_.is_null());
    300     return;
    301   }
    302 
    303   DCHECK_EQ(state_, STATE_FLUSHING) << state_;
    304   DCHECK(!flush_cb_.is_null());
    305 
    306   DCHECK_EQ(audio_buffering_state_, BUFFERING_HAVE_NOTHING);
    307   audio_ended_ = false;
    308   FlushVideoRenderer();
    309 }
    310 
    311 void RendererImpl::FlushVideoRenderer() {
    312   DVLOG(1) << __FUNCTION__;
    313   DCHECK(task_runner_->BelongsToCurrentThread());
    314   DCHECK_EQ(state_, STATE_FLUSHING) << state_;
    315   DCHECK(!flush_cb_.is_null());
    316 
    317   if (!video_renderer_) {
    318     OnVideoRendererFlushDone();
    319     return;
    320   }
    321 
    322   video_renderer_->Flush(
    323       base::Bind(&RendererImpl::OnVideoRendererFlushDone, weak_this_));
    324 }
    325 
    326 void RendererImpl::OnVideoRendererFlushDone() {
    327   DVLOG(1) << __FUNCTION__;
    328   DCHECK(task_runner_->BelongsToCurrentThread());
    329 
    330   if (state_ == STATE_ERROR) {
    331     DCHECK(flush_cb_.is_null());
    332     return;
    333   }
    334 
    335   DCHECK_EQ(state_, STATE_FLUSHING) << state_;
    336   DCHECK(!flush_cb_.is_null());
    337 
    338   DCHECK_EQ(video_buffering_state_, BUFFERING_HAVE_NOTHING);
    339   video_ended_ = false;
    340   state_ = STATE_PLAYING;
    341   base::ResetAndReturn(&flush_cb_).Run();
    342 }
    343 
    344 void RendererImpl::OnUpdateStatistics(const PipelineStatistics& stats) {
    345   DCHECK(task_runner_->BelongsToCurrentThread());
    346   statistics_cb_.Run(stats);
    347 }
    348 
    349 void RendererImpl::OnBufferingStateChanged(BufferingState* buffering_state,
    350                                            BufferingState new_buffering_state) {
    351   DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", "
    352            << new_buffering_state << ") "
    353            << (buffering_state == &audio_buffering_state_ ? "audio" : "video");
    354   DCHECK(task_runner_->BelongsToCurrentThread());
    355   bool was_waiting_for_enough_data = WaitingForEnoughData();
    356 
    357   *buffering_state = new_buffering_state;
    358 
    359   // Disable underflow by ignoring updates that renderers have ran out of data.
    360   if (state_ == STATE_PLAYING && underflow_disabled_for_testing_ &&
    361       time_ticking_) {
    362     DVLOG(1) << "Update ignored because underflow is disabled for testing.";
    363     return;
    364   }
    365 
    366   // Renderer underflowed.
    367   if (!was_waiting_for_enough_data && WaitingForEnoughData()) {
    368     PausePlayback();
    369 
    370     // TODO(scherkus): Fire BUFFERING_HAVE_NOTHING callback to alert clients of
    371     // underflow state http://crbug.com/144683
    372     return;
    373   }
    374 
    375   // Renderer prerolled.
    376   if (was_waiting_for_enough_data && !WaitingForEnoughData()) {
    377     StartPlayback();
    378     buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH);
    379     return;
    380   }
    381 }
    382 
    383 bool RendererImpl::WaitingForEnoughData() const {
    384   DCHECK(task_runner_->BelongsToCurrentThread());
    385   if (state_ != STATE_PLAYING)
    386     return false;
    387   if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH)
    388     return true;
    389   if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH)
    390     return true;
    391   return false;
    392 }
    393 
    394 void RendererImpl::PausePlayback() {
    395   DVLOG(1) << __FUNCTION__;
    396   DCHECK(task_runner_->BelongsToCurrentThread());
    397   DCHECK(time_ticking_);
    398   switch (state_) {
    399     case STATE_PLAYING:
    400       DCHECK(PlaybackHasEnded() || WaitingForEnoughData())
    401           << "Playback should only pause due to ending or underflowing";
    402       break;
    403 
    404     case STATE_FLUSHING:
    405       // It's OK to pause playback when flushing.
    406       break;
    407 
    408     case STATE_UNINITIALIZED:
    409     case STATE_INITIALIZING:
    410     case STATE_ERROR:
    411       NOTREACHED() << "Invalid state: " << state_;
    412       break;
    413   }
    414 
    415   time_ticking_ = false;
    416   time_source_->StopTicking();
    417 }
    418 
    419 void RendererImpl::StartPlayback() {
    420   DVLOG(1) << __FUNCTION__;
    421   DCHECK(task_runner_->BelongsToCurrentThread());
    422   DCHECK_EQ(state_, STATE_PLAYING);
    423   DCHECK(!time_ticking_);
    424   DCHECK(!WaitingForEnoughData());
    425 
    426   time_ticking_ = true;
    427   time_source_->StartTicking();
    428 }
    429 
    430 void RendererImpl::OnAudioRendererEnded() {
    431   DVLOG(1) << __FUNCTION__;
    432   DCHECK(task_runner_->BelongsToCurrentThread());
    433 
    434   if (state_ != STATE_PLAYING)
    435     return;
    436 
    437   DCHECK(!audio_ended_);
    438   audio_ended_ = true;
    439 
    440   RunEndedCallbackIfNeeded();
    441 }
    442 
    443 void RendererImpl::OnVideoRendererEnded() {
    444   DVLOG(1) << __FUNCTION__;
    445   DCHECK(task_runner_->BelongsToCurrentThread());
    446 
    447   if (state_ != STATE_PLAYING)
    448     return;
    449 
    450   DCHECK(!video_ended_);
    451   video_ended_ = true;
    452 
    453   RunEndedCallbackIfNeeded();
    454 }
    455 
    456 bool RendererImpl::PlaybackHasEnded() const {
    457   DVLOG(1) << __FUNCTION__;
    458   DCHECK(task_runner_->BelongsToCurrentThread());
    459 
    460   if (audio_renderer_ && !audio_ended_)
    461     return false;
    462 
    463   if (video_renderer_ && !video_ended_)
    464     return false;
    465 
    466   return true;
    467 }
    468 
    469 void RendererImpl::RunEndedCallbackIfNeeded() {
    470   DVLOG(1) << __FUNCTION__;
    471   DCHECK(task_runner_->BelongsToCurrentThread());
    472 
    473   if (!PlaybackHasEnded())
    474     return;
    475 
    476   if (time_ticking_)
    477     PausePlayback();
    478 
    479   ended_cb_.Run();
    480 }
    481 
    482 void RendererImpl::OnError(PipelineStatus error) {
    483   DVLOG(1) << __FUNCTION__ << "(" << error << ")";
    484   DCHECK(task_runner_->BelongsToCurrentThread());
    485   DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
    486 
    487   state_ = STATE_ERROR;
    488 
    489   // Pipeline will destroy |this| as the result of error.
    490   base::ResetAndReturn(&error_cb_).Run(error);
    491 
    492   FireAllPendingCallbacks();
    493 }
    494 
    495 void RendererImpl::FireAllPendingCallbacks() {
    496   DCHECK(task_runner_->BelongsToCurrentThread());
    497 
    498   if (!init_cb_.is_null())
    499     base::ResetAndReturn(&init_cb_).Run();
    500 
    501   if (!flush_cb_.is_null())
    502     base::ResetAndReturn(&flush_cb_).Run();
    503 }
    504 
    505 }  // namespace media
    506