Home | History | Annotate | Download | only in base
      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/base/pipeline.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/bind.h"
     10 #include "base/callback.h"
     11 #include "base/callback_helpers.h"
     12 #include "base/compiler_specific.h"
     13 #include "base/location.h"
     14 #include "base/metrics/histogram.h"
     15 #include "base/single_thread_task_runner.h"
     16 #include "base/stl_util.h"
     17 #include "base/strings/string_number_conversions.h"
     18 #include "base/strings/string_util.h"
     19 #include "base/synchronization/condition_variable.h"
     20 #include "media/base/audio_decoder.h"
     21 #include "media/base/audio_renderer.h"
     22 #include "media/base/clock.h"
     23 #include "media/base/filter_collection.h"
     24 #include "media/base/media_log.h"
     25 #include "media/base/text_renderer.h"
     26 #include "media/base/text_track_config.h"
     27 #include "media/base/video_decoder.h"
     28 #include "media/base/video_decoder_config.h"
     29 #include "media/base/video_renderer.h"
     30 
     31 using base::TimeDelta;
     32 
     33 namespace media {
     34 
     35 Pipeline::Pipeline(
     36     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
     37     MediaLog* media_log)
     38     : task_runner_(task_runner),
     39       media_log_(media_log),
     40       running_(false),
     41       did_loading_progress_(false),
     42       volume_(1.0f),
     43       playback_rate_(0.0f),
     44       clock_(new Clock(&default_tick_clock_)),
     45       clock_state_(CLOCK_PAUSED),
     46       status_(PIPELINE_OK),
     47       state_(kCreated),
     48       audio_ended_(false),
     49       video_ended_(false),
     50       text_ended_(false),
     51       audio_buffering_state_(BUFFERING_HAVE_NOTHING),
     52       video_buffering_state_(BUFFERING_HAVE_NOTHING),
     53       demuxer_(NULL),
     54       creation_time_(default_tick_clock_.NowTicks()) {
     55   media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
     56   media_log_->AddEvent(
     57       media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED));
     58 }
     59 
     60 Pipeline::~Pipeline() {
     61   DCHECK(thread_checker_.CalledOnValidThread())
     62       << "Pipeline must be destroyed on same thread that created it";
     63   DCHECK(!running_) << "Stop() must complete before destroying object";
     64   DCHECK(stop_cb_.is_null());
     65   DCHECK(seek_cb_.is_null());
     66 
     67   media_log_->AddEvent(
     68       media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED));
     69 }
     70 
     71 void Pipeline::Start(scoped_ptr<FilterCollection> collection,
     72                      const base::Closure& ended_cb,
     73                      const PipelineStatusCB& error_cb,
     74                      const PipelineStatusCB& seek_cb,
     75                      const PipelineMetadataCB& metadata_cb,
     76                      const base::Closure& preroll_completed_cb,
     77                      const base::Closure& duration_change_cb) {
     78   DCHECK(!ended_cb.is_null());
     79   DCHECK(!error_cb.is_null());
     80   DCHECK(!seek_cb.is_null());
     81   DCHECK(!metadata_cb.is_null());
     82   DCHECK(!preroll_completed_cb.is_null());
     83 
     84   base::AutoLock auto_lock(lock_);
     85   CHECK(!running_) << "Media pipeline is already running";
     86   running_ = true;
     87 
     88   filter_collection_ = collection.Pass();
     89   ended_cb_ = ended_cb;
     90   error_cb_ = error_cb;
     91   seek_cb_ = seek_cb;
     92   metadata_cb_ = metadata_cb;
     93   preroll_completed_cb_ = preroll_completed_cb;
     94   duration_change_cb_ = duration_change_cb;
     95 
     96   task_runner_->PostTask(
     97       FROM_HERE, base::Bind(&Pipeline::StartTask, base::Unretained(this)));
     98 }
     99 
    100 void Pipeline::Stop(const base::Closure& stop_cb) {
    101   base::AutoLock auto_lock(lock_);
    102   task_runner_->PostTask(FROM_HERE, base::Bind(
    103       &Pipeline::StopTask, base::Unretained(this), stop_cb));
    104 }
    105 
    106 void Pipeline::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) {
    107   base::AutoLock auto_lock(lock_);
    108   if (!running_) {
    109     NOTREACHED() << "Media pipeline isn't running";
    110     return;
    111   }
    112 
    113   task_runner_->PostTask(FROM_HERE, base::Bind(
    114       &Pipeline::SeekTask, base::Unretained(this), time, seek_cb));
    115 }
    116 
    117 bool Pipeline::IsRunning() const {
    118   base::AutoLock auto_lock(lock_);
    119   return running_;
    120 }
    121 
    122 float Pipeline::GetPlaybackRate() const {
    123   base::AutoLock auto_lock(lock_);
    124   return playback_rate_;
    125 }
    126 
    127 void Pipeline::SetPlaybackRate(float playback_rate) {
    128   if (playback_rate < 0.0f)
    129     return;
    130 
    131   base::AutoLock auto_lock(lock_);
    132   playback_rate_ = playback_rate;
    133   if (running_) {
    134     task_runner_->PostTask(FROM_HERE, base::Bind(
    135         &Pipeline::PlaybackRateChangedTask, base::Unretained(this),
    136         playback_rate));
    137   }
    138 }
    139 
    140 float Pipeline::GetVolume() const {
    141   base::AutoLock auto_lock(lock_);
    142   return volume_;
    143 }
    144 
    145 void Pipeline::SetVolume(float volume) {
    146   if (volume < 0.0f || volume > 1.0f)
    147     return;
    148 
    149   base::AutoLock auto_lock(lock_);
    150   volume_ = volume;
    151   if (running_) {
    152     task_runner_->PostTask(FROM_HERE, base::Bind(
    153         &Pipeline::VolumeChangedTask, base::Unretained(this), volume));
    154   }
    155 }
    156 
    157 TimeDelta Pipeline::GetMediaTime() const {
    158   base::AutoLock auto_lock(lock_);
    159   return clock_->Elapsed();
    160 }
    161 
    162 Ranges<TimeDelta> Pipeline::GetBufferedTimeRanges() const {
    163   base::AutoLock auto_lock(lock_);
    164   return buffered_time_ranges_;
    165 }
    166 
    167 TimeDelta Pipeline::GetMediaDuration() const {
    168   base::AutoLock auto_lock(lock_);
    169   return clock_->Duration();
    170 }
    171 
    172 bool Pipeline::DidLoadingProgress() {
    173   base::AutoLock auto_lock(lock_);
    174   bool ret = did_loading_progress_;
    175   did_loading_progress_ = false;
    176   return ret;
    177 }
    178 
    179 PipelineStatistics Pipeline::GetStatistics() const {
    180   base::AutoLock auto_lock(lock_);
    181   return statistics_;
    182 }
    183 
    184 void Pipeline::SetClockForTesting(Clock* clock) {
    185   clock_.reset(clock);
    186 }
    187 
    188 void Pipeline::SetErrorForTesting(PipelineStatus status) {
    189   SetError(status);
    190 }
    191 
    192 void Pipeline::SetState(State next_state) {
    193   if (state_ != kPlaying && next_state == kPlaying &&
    194       !creation_time_.is_null()) {
    195     UMA_HISTOGRAM_TIMES("Media.TimeToPipelineStarted",
    196                         default_tick_clock_.NowTicks() - creation_time_);
    197     creation_time_ = base::TimeTicks();
    198   }
    199 
    200   DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state);
    201 
    202   state_ = next_state;
    203   media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
    204 }
    205 
    206 #define RETURN_STRING(state) case state: return #state;
    207 
    208 const char* Pipeline::GetStateString(State state) {
    209   switch (state) {
    210     RETURN_STRING(kCreated);
    211     RETURN_STRING(kInitDemuxer);
    212     RETURN_STRING(kInitAudioRenderer);
    213     RETURN_STRING(kInitVideoRenderer);
    214     RETURN_STRING(kInitPrerolling);
    215     RETURN_STRING(kSeeking);
    216     RETURN_STRING(kPlaying);
    217     RETURN_STRING(kStopping);
    218     RETURN_STRING(kStopped);
    219   }
    220   NOTREACHED();
    221   return "INVALID";
    222 }
    223 
    224 #undef RETURN_STRING
    225 
    226 Pipeline::State Pipeline::GetNextState() const {
    227   DCHECK(task_runner_->BelongsToCurrentThread());
    228   DCHECK(stop_cb_.is_null())
    229       << "State transitions don't happen when stopping";
    230   DCHECK_EQ(status_, PIPELINE_OK)
    231       << "State transitions don't happen when there's an error: " << status_;
    232 
    233   switch (state_) {
    234     case kCreated:
    235       return kInitDemuxer;
    236 
    237     case kInitDemuxer:
    238       if (demuxer_->GetStream(DemuxerStream::AUDIO))
    239         return kInitAudioRenderer;
    240       if (demuxer_->GetStream(DemuxerStream::VIDEO))
    241         return kInitVideoRenderer;
    242       return kInitPrerolling;
    243 
    244     case kInitAudioRenderer:
    245       if (demuxer_->GetStream(DemuxerStream::VIDEO))
    246         return kInitVideoRenderer;
    247       return kInitPrerolling;
    248 
    249     case kInitVideoRenderer:
    250       return kInitPrerolling;
    251 
    252     case kInitPrerolling:
    253       return kPlaying;
    254 
    255     case kSeeking:
    256       return kPlaying;
    257 
    258     case kPlaying:
    259     case kStopping:
    260     case kStopped:
    261       break;
    262   }
    263   NOTREACHED() << "State has no transition: " << state_;
    264   return state_;
    265 }
    266 
    267 void Pipeline::OnDemuxerError(PipelineStatus error) {
    268   SetError(error);
    269 }
    270 
    271 void Pipeline::AddTextStream(DemuxerStream* text_stream,
    272                              const TextTrackConfig& config) {
    273   task_runner_->PostTask(FROM_HERE, base::Bind(
    274     &Pipeline::AddTextStreamTask, base::Unretained(this),
    275     text_stream, config));
    276 }
    277 
    278 void Pipeline::RemoveTextStream(DemuxerStream* text_stream) {
    279   task_runner_->PostTask(FROM_HERE, base::Bind(
    280     &Pipeline::RemoveTextStreamTask, base::Unretained(this),
    281     text_stream));
    282 }
    283 
    284 void Pipeline::SetError(PipelineStatus error) {
    285   DCHECK(IsRunning());
    286   DCHECK_NE(PIPELINE_OK, error);
    287   VLOG(1) << "Media pipeline error: " << error;
    288 
    289   task_runner_->PostTask(FROM_HERE, base::Bind(
    290       &Pipeline::ErrorChangedTask, base::Unretained(this), error));
    291 
    292   media_log_->AddEvent(media_log_->CreatePipelineErrorEvent(error));
    293 }
    294 
    295 void Pipeline::OnAudioTimeUpdate(TimeDelta time, TimeDelta max_time) {
    296   DCHECK(task_runner_->BelongsToCurrentThread());
    297   DCHECK_LE(time.InMicroseconds(), max_time.InMicroseconds());
    298   base::AutoLock auto_lock(lock_);
    299 
    300   if (clock_state_ == CLOCK_WAITING_FOR_AUDIO_TIME_UPDATE &&
    301       time < clock_->Elapsed()) {
    302     return;
    303   }
    304 
    305   if (state_ == kSeeking)
    306     return;
    307 
    308   clock_->SetTime(time, max_time);
    309   StartClockIfWaitingForTimeUpdate_Locked();
    310 }
    311 
    312 void Pipeline::OnVideoTimeUpdate(TimeDelta max_time) {
    313   DCHECK(task_runner_->BelongsToCurrentThread());
    314 
    315   if (audio_renderer_)
    316     return;
    317 
    318   if (state_ == kSeeking)
    319     return;
    320 
    321   base::AutoLock auto_lock(lock_);
    322   DCHECK_NE(clock_state_, CLOCK_WAITING_FOR_AUDIO_TIME_UPDATE);
    323   clock_->SetMaxTime(max_time);
    324 }
    325 
    326 void Pipeline::SetDuration(TimeDelta duration) {
    327   DCHECK(IsRunning());
    328   media_log_->AddEvent(
    329       media_log_->CreateTimeEvent(
    330           MediaLogEvent::DURATION_SET, "duration", duration));
    331   UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration);
    332 
    333   base::AutoLock auto_lock(lock_);
    334   clock_->SetDuration(duration);
    335   if (!duration_change_cb_.is_null())
    336     duration_change_cb_.Run();
    337 }
    338 
    339 void Pipeline::OnStateTransition(PipelineStatus status) {
    340   // Force post to process state transitions after current execution frame.
    341   task_runner_->PostTask(FROM_HERE, base::Bind(
    342       &Pipeline::StateTransitionTask, base::Unretained(this), status));
    343 }
    344 
    345 void Pipeline::StateTransitionTask(PipelineStatus status) {
    346   DCHECK(task_runner_->BelongsToCurrentThread());
    347 
    348   // No-op any state transitions if we're stopping.
    349   if (state_ == kStopping || state_ == kStopped)
    350     return;
    351 
    352   // Preserve existing abnormal status, otherwise update based on the result of
    353   // the previous operation.
    354   status_ = (status_ != PIPELINE_OK ? status_ : status);
    355 
    356   if (status_ != PIPELINE_OK) {
    357     ErrorChangedTask(status_);
    358     return;
    359   }
    360 
    361   // Guard against accidentally clearing |pending_callbacks_| for states that
    362   // use it as well as states that should not be using it.
    363   DCHECK_EQ(pending_callbacks_.get() != NULL,
    364             (state_ == kInitPrerolling || state_ == kSeeking));
    365 
    366   pending_callbacks_.reset();
    367 
    368   PipelineStatusCB done_cb = base::Bind(
    369       &Pipeline::OnStateTransition, base::Unretained(this));
    370 
    371   // Switch states, performing any entrance actions for the new state as well.
    372   SetState(GetNextState());
    373   switch (state_) {
    374     case kInitDemuxer:
    375       return InitializeDemuxer(done_cb);
    376 
    377     case kInitAudioRenderer:
    378       return InitializeAudioRenderer(done_cb);
    379 
    380     case kInitVideoRenderer:
    381       return InitializeVideoRenderer(done_cb);
    382 
    383     case kInitPrerolling:
    384       filter_collection_.reset();
    385       {
    386         base::AutoLock l(lock_);
    387         // We do not want to start the clock running. We only want to set the
    388         // base media time so our timestamp calculations will be correct.
    389         clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime());
    390       }
    391       if (!audio_renderer_ && !video_renderer_) {
    392         done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER);
    393         return;
    394       }
    395 
    396       {
    397         PipelineMetadata metadata;
    398         metadata.has_audio = audio_renderer_;
    399         metadata.has_video = video_renderer_;
    400         metadata.timeline_offset = demuxer_->GetTimelineOffset();
    401         DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
    402         if (stream)
    403           metadata.natural_size = stream->video_decoder_config().natural_size();
    404         metadata_cb_.Run(metadata);
    405       }
    406 
    407       return DoInitialPreroll(done_cb);
    408 
    409     case kPlaying:
    410       PlaybackRateChangedTask(GetPlaybackRate());
    411       VolumeChangedTask(GetVolume());
    412 
    413       // We enter this state from either kInitPrerolling or kSeeking. As of now
    414       // both those states call Preroll(), which means by time we enter this
    415       // state we've already buffered enough data. Forcefully update the
    416       // buffering state, which start the clock and renderers and transition
    417       // into kPlaying state.
    418       //
    419       // TODO(scherkus): Remove after renderers are taught to fire buffering
    420       // state callbacks http://crbug.com/144683
    421       DCHECK(WaitingForEnoughData());
    422       if (audio_renderer_)
    423         BufferingStateChanged(&audio_buffering_state_, BUFFERING_HAVE_ENOUGH);
    424       if (video_renderer_)
    425         BufferingStateChanged(&video_buffering_state_, BUFFERING_HAVE_ENOUGH);
    426       return;
    427 
    428     case kStopping:
    429     case kStopped:
    430     case kCreated:
    431     case kSeeking:
    432       NOTREACHED() << "State has no transition: " << state_;
    433       return;
    434   }
    435 }
    436 
    437 // Note that the usage of base::Unretained() with the audio/video renderers
    438 // in the following DoXXX() functions is considered safe as they are owned by
    439 // |pending_callbacks_| and share the same lifetime.
    440 //
    441 // That being said, deleting the renderers while keeping |pending_callbacks_|
    442 // running on the media thread would result in crashes.
    443 void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) {
    444   DCHECK(task_runner_->BelongsToCurrentThread());
    445   DCHECK(!pending_callbacks_.get());
    446   SerialRunner::Queue bound_fns;
    447 
    448   base::TimeDelta seek_timestamp = demuxer_->GetStartTime();
    449 
    450   // Preroll renderers.
    451   if (audio_renderer_) {
    452     bound_fns.Push(base::Bind(
    453         &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
    454         seek_timestamp));
    455   }
    456 
    457   if (video_renderer_) {
    458     bound_fns.Push(base::Bind(
    459         &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
    460         seek_timestamp));
    461 
    462     // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering
    463     // state callbacks http://crbug.com/144683
    464     bound_fns.Push(base::Bind(&VideoRenderer::Play,
    465                               base::Unretained(video_renderer_.get())));
    466   }
    467 
    468   if (text_renderer_) {
    469     bound_fns.Push(base::Bind(
    470         &TextRenderer::Play, base::Unretained(text_renderer_.get())));
    471   }
    472 
    473   pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
    474 }
    475 
    476 void Pipeline::DoSeek(
    477     base::TimeDelta seek_timestamp,
    478     const PipelineStatusCB& done_cb) {
    479   DCHECK(task_runner_->BelongsToCurrentThread());
    480   DCHECK(!pending_callbacks_.get());
    481   SerialRunner::Queue bound_fns;
    482 
    483   // Pause.
    484   if (text_renderer_) {
    485     bound_fns.Push(base::Bind(
    486         &TextRenderer::Pause, base::Unretained(text_renderer_.get())));
    487   }
    488 
    489   // Flush.
    490   if (audio_renderer_) {
    491     bound_fns.Push(base::Bind(
    492         &AudioRenderer::Flush, base::Unretained(audio_renderer_.get())));
    493 
    494     // TODO(scherkus): Remove after AudioRenderer is taught to fire buffering
    495     // state callbacks http://crbug.com/144683
    496     bound_fns.Push(base::Bind(&Pipeline::BufferingStateChanged,
    497                               base::Unretained(this),
    498                               &audio_buffering_state_,
    499                               BUFFERING_HAVE_NOTHING));
    500   }
    501   if (video_renderer_) {
    502     bound_fns.Push(base::Bind(
    503         &VideoRenderer::Flush, base::Unretained(video_renderer_.get())));
    504 
    505     // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering
    506     // state callbacks http://crbug.com/144683
    507     bound_fns.Push(base::Bind(&Pipeline::BufferingStateChanged,
    508                               base::Unretained(this),
    509                               &video_buffering_state_,
    510                               BUFFERING_HAVE_NOTHING));
    511   }
    512   if (text_renderer_) {
    513     bound_fns.Push(base::Bind(
    514         &TextRenderer::Flush, base::Unretained(text_renderer_.get())));
    515   }
    516 
    517   // Seek demuxer.
    518   bound_fns.Push(base::Bind(
    519       &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
    520 
    521   // Preroll renderers.
    522   if (audio_renderer_) {
    523     bound_fns.Push(base::Bind(
    524         &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
    525         seek_timestamp));
    526   }
    527 
    528   if (video_renderer_) {
    529     bound_fns.Push(base::Bind(
    530         &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
    531         seek_timestamp));
    532 
    533     // TODO(scherkus): Remove after renderers are taught to fire buffering
    534     // state callbacks http://crbug.com/144683
    535     bound_fns.Push(base::Bind(&VideoRenderer::Play,
    536                               base::Unretained(video_renderer_.get())));
    537   }
    538 
    539   if (text_renderer_) {
    540     bound_fns.Push(base::Bind(
    541         &TextRenderer::Play, base::Unretained(text_renderer_.get())));
    542   }
    543 
    544   pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
    545 }
    546 
    547 void Pipeline::DoStop(const PipelineStatusCB& done_cb) {
    548   DCHECK(task_runner_->BelongsToCurrentThread());
    549   DCHECK(!pending_callbacks_.get());
    550   SerialRunner::Queue bound_fns;
    551 
    552   if (demuxer_) {
    553     bound_fns.Push(base::Bind(
    554         &Demuxer::Stop, base::Unretained(demuxer_)));
    555   }
    556 
    557   if (audio_renderer_) {
    558     bound_fns.Push(base::Bind(
    559         &AudioRenderer::Stop, base::Unretained(audio_renderer_.get())));
    560   }
    561 
    562   if (video_renderer_) {
    563     bound_fns.Push(base::Bind(
    564         &VideoRenderer::Stop, base::Unretained(video_renderer_.get())));
    565   }
    566 
    567   if (text_renderer_) {
    568     bound_fns.Push(base::Bind(
    569         &TextRenderer::Stop, base::Unretained(text_renderer_.get())));
    570   }
    571 
    572   pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
    573 }
    574 
    575 void Pipeline::OnStopCompleted(PipelineStatus status) {
    576   DCHECK(task_runner_->BelongsToCurrentThread());
    577   DCHECK_EQ(state_, kStopping);
    578   {
    579     base::AutoLock l(lock_);
    580     running_ = false;
    581   }
    582 
    583   SetState(kStopped);
    584   pending_callbacks_.reset();
    585   filter_collection_.reset();
    586   audio_renderer_.reset();
    587   video_renderer_.reset();
    588   text_renderer_.reset();
    589   demuxer_ = NULL;
    590 
    591   // If we stop during initialization/seeking we want to run |seek_cb_|
    592   // followed by |stop_cb_| so we don't leave outstanding callbacks around.
    593   if (!seek_cb_.is_null()) {
    594     base::ResetAndReturn(&seek_cb_).Run(status_);
    595     error_cb_.Reset();
    596   }
    597   if (!stop_cb_.is_null()) {
    598     error_cb_.Reset();
    599     base::ResetAndReturn(&stop_cb_).Run();
    600 
    601     // NOTE: pipeline may be deleted at this point in time as a result of
    602     // executing |stop_cb_|.
    603     return;
    604   }
    605   if (!error_cb_.is_null()) {
    606     DCHECK_NE(status_, PIPELINE_OK);
    607     base::ResetAndReturn(&error_cb_).Run(status_);
    608   }
    609 }
    610 
    611 void Pipeline::AddBufferedTimeRange(base::TimeDelta start,
    612                                     base::TimeDelta end) {
    613   DCHECK(IsRunning());
    614   base::AutoLock auto_lock(lock_);
    615   buffered_time_ranges_.Add(start, end);
    616   did_loading_progress_ = true;
    617 }
    618 
    619 void Pipeline::OnAudioRendererEnded() {
    620   // Force post to process ended tasks after current execution frame.
    621   task_runner_->PostTask(FROM_HERE, base::Bind(
    622       &Pipeline::DoAudioRendererEnded, base::Unretained(this)));
    623   media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::AUDIO_ENDED));
    624 }
    625 
    626 void Pipeline::OnVideoRendererEnded() {
    627   // Force post to process ended tasks after current execution frame.
    628   task_runner_->PostTask(FROM_HERE, base::Bind(
    629       &Pipeline::DoVideoRendererEnded, base::Unretained(this)));
    630   media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::VIDEO_ENDED));
    631 }
    632 
    633 void Pipeline::OnTextRendererEnded() {
    634   // Force post to process ended messages after current execution frame.
    635   task_runner_->PostTask(FROM_HERE, base::Bind(
    636       &Pipeline::DoTextRendererEnded, base::Unretained(this)));
    637   media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
    638 }
    639 
    640 // Called from any thread.
    641 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) {
    642   base::AutoLock auto_lock(lock_);
    643   statistics_.audio_bytes_decoded += stats.audio_bytes_decoded;
    644   statistics_.video_bytes_decoded += stats.video_bytes_decoded;
    645   statistics_.video_frames_decoded += stats.video_frames_decoded;
    646   statistics_.video_frames_dropped += stats.video_frames_dropped;
    647 }
    648 
    649 void Pipeline::StartTask() {
    650   DCHECK(task_runner_->BelongsToCurrentThread());
    651   CHECK_EQ(kCreated, state_)
    652       << "Media pipeline cannot be started more than once";
    653 
    654   text_renderer_ = filter_collection_->GetTextRenderer();
    655 
    656   if (text_renderer_) {
    657     text_renderer_->Initialize(
    658         base::Bind(&Pipeline::OnTextRendererEnded, base::Unretained(this)));
    659   }
    660 
    661   StateTransitionTask(PIPELINE_OK);
    662 }
    663 
    664 void Pipeline::StopTask(const base::Closure& stop_cb) {
    665   DCHECK(task_runner_->BelongsToCurrentThread());
    666   DCHECK(stop_cb_.is_null());
    667 
    668   if (state_ == kStopped) {
    669     stop_cb.Run();
    670     return;
    671   }
    672 
    673   stop_cb_ = stop_cb;
    674 
    675   // We may already be stopping due to a runtime error.
    676   if (state_ == kStopping)
    677     return;
    678 
    679   SetState(kStopping);
    680   pending_callbacks_.reset();
    681   DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this)));
    682 }
    683 
    684 void Pipeline::ErrorChangedTask(PipelineStatus error) {
    685   DCHECK(task_runner_->BelongsToCurrentThread());
    686   DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
    687 
    688   if (state_ == kStopping || state_ == kStopped)
    689     return;
    690 
    691   SetState(kStopping);
    692   pending_callbacks_.reset();
    693   status_ = error;
    694 
    695   DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this)));
    696 }
    697 
    698 void Pipeline::PlaybackRateChangedTask(float playback_rate) {
    699   DCHECK(task_runner_->BelongsToCurrentThread());
    700 
    701   // Playback rate changes are only carried out while playing.
    702   if (state_ != kPlaying)
    703     return;
    704 
    705   {
    706     base::AutoLock auto_lock(lock_);
    707     clock_->SetPlaybackRate(playback_rate);
    708   }
    709 
    710   if (audio_renderer_)
    711     audio_renderer_->SetPlaybackRate(playback_rate_);
    712   if (video_renderer_)
    713     video_renderer_->SetPlaybackRate(playback_rate_);
    714 }
    715 
    716 void Pipeline::VolumeChangedTask(float volume) {
    717   DCHECK(task_runner_->BelongsToCurrentThread());
    718 
    719   // Volume changes are only carried out while playing.
    720   if (state_ != kPlaying)
    721     return;
    722 
    723   if (audio_renderer_)
    724     audio_renderer_->SetVolume(volume);
    725 }
    726 
    727 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
    728   DCHECK(task_runner_->BelongsToCurrentThread());
    729   DCHECK(stop_cb_.is_null());
    730 
    731   // Suppress seeking if we're not fully started.
    732   if (state_ != kPlaying) {
    733     DCHECK(state_ == kStopping || state_ == kStopped)
    734         << "Receive extra seek in unexpected state: " << state_;
    735 
    736     // TODO(scherkus): should we run the callback?  I'm tempted to say the API
    737     // will only execute the first Seek() request.
    738     DVLOG(1) << "Media pipeline has not started, ignoring seek to "
    739              << time.InMicroseconds() << " (current state: " << state_ << ")";
    740     return;
    741   }
    742 
    743   DCHECK(seek_cb_.is_null());
    744 
    745   SetState(kSeeking);
    746   base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime());
    747   seek_cb_ = seek_cb;
    748   audio_ended_ = false;
    749   video_ended_ = false;
    750   text_ended_ = false;
    751 
    752   // Kick off seeking!
    753   {
    754     base::AutoLock auto_lock(lock_);
    755     PauseClockAndStopRendering_Locked();
    756     clock_->SetTime(seek_timestamp, seek_timestamp);
    757   }
    758   DoSeek(seek_timestamp, base::Bind(
    759       &Pipeline::OnStateTransition, base::Unretained(this)));
    760 }
    761 
    762 void Pipeline::DoAudioRendererEnded() {
    763   DCHECK(task_runner_->BelongsToCurrentThread());
    764 
    765   if (state_ != kPlaying)
    766     return;
    767 
    768   DCHECK(!audio_ended_);
    769   audio_ended_ = true;
    770 
    771   // Start clock since there is no more audio to trigger clock updates.
    772   {
    773     base::AutoLock auto_lock(lock_);
    774     clock_->SetMaxTime(clock_->Duration());
    775     StartClockIfWaitingForTimeUpdate_Locked();
    776   }
    777 
    778   RunEndedCallbackIfNeeded();
    779 }
    780 
    781 void Pipeline::DoVideoRendererEnded() {
    782   DCHECK(task_runner_->BelongsToCurrentThread());
    783 
    784   if (state_ != kPlaying)
    785     return;
    786 
    787   DCHECK(!video_ended_);
    788   video_ended_ = true;
    789 
    790   RunEndedCallbackIfNeeded();
    791 }
    792 
    793 void Pipeline::DoTextRendererEnded() {
    794   DCHECK(task_runner_->BelongsToCurrentThread());
    795 
    796   if (state_ != kPlaying)
    797     return;
    798 
    799   DCHECK(!text_ended_);
    800   text_ended_ = true;
    801 
    802   RunEndedCallbackIfNeeded();
    803 }
    804 
    805 void Pipeline::RunEndedCallbackIfNeeded() {
    806   DCHECK(task_runner_->BelongsToCurrentThread());
    807 
    808   if (audio_renderer_ && !audio_ended_)
    809     return;
    810 
    811   if (video_renderer_ && !video_ended_)
    812     return;
    813 
    814   if (text_renderer_ && text_renderer_->HasTracks() && !text_ended_)
    815     return;
    816 
    817   {
    818     base::AutoLock auto_lock(lock_);
    819     PauseClockAndStopRendering_Locked();
    820     clock_->SetTime(clock_->Duration(), clock_->Duration());
    821   }
    822 
    823   DCHECK_EQ(status_, PIPELINE_OK);
    824   ended_cb_.Run();
    825 }
    826 
    827 void Pipeline::AddTextStreamTask(DemuxerStream* text_stream,
    828                                  const TextTrackConfig& config) {
    829   DCHECK(task_runner_->BelongsToCurrentThread());
    830   // TODO(matthewjheaney): fix up text_ended_ when text stream
    831   // is added (http://crbug.com/321446).
    832   text_renderer_->AddTextStream(text_stream, config);
    833 }
    834 
    835 void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) {
    836   DCHECK(task_runner_->BelongsToCurrentThread());
    837   text_renderer_->RemoveTextStream(text_stream);
    838 }
    839 
    840 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) {
    841   DCHECK(task_runner_->BelongsToCurrentThread());
    842 
    843   demuxer_ = filter_collection_->GetDemuxer();
    844   demuxer_->Initialize(this, done_cb, text_renderer_);
    845 }
    846 
    847 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) {
    848   DCHECK(task_runner_->BelongsToCurrentThread());
    849 
    850   audio_renderer_ = filter_collection_->GetAudioRenderer();
    851   audio_renderer_->Initialize(
    852       demuxer_->GetStream(DemuxerStream::AUDIO),
    853       done_cb,
    854       base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)),
    855       base::Bind(&Pipeline::OnAudioUnderflow, base::Unretained(this)),
    856       base::Bind(&Pipeline::OnAudioTimeUpdate, base::Unretained(this)),
    857       base::Bind(&Pipeline::OnAudioRendererEnded, base::Unretained(this)),
    858       base::Bind(&Pipeline::SetError, base::Unretained(this)));
    859 }
    860 
    861 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) {
    862   DCHECK(task_runner_->BelongsToCurrentThread());
    863 
    864   video_renderer_ = filter_collection_->GetVideoRenderer();
    865   video_renderer_->Initialize(
    866       demuxer_->GetStream(DemuxerStream::VIDEO),
    867       demuxer_->GetLiveness() == Demuxer::LIVENESS_LIVE,
    868       done_cb,
    869       base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)),
    870       base::Bind(&Pipeline::OnVideoTimeUpdate, base::Unretained(this)),
    871       base::Bind(&Pipeline::OnVideoRendererEnded, base::Unretained(this)),
    872       base::Bind(&Pipeline::SetError, base::Unretained(this)),
    873       base::Bind(&Pipeline::GetMediaTime, base::Unretained(this)),
    874       base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this)));
    875 }
    876 
    877 void Pipeline::OnAudioUnderflow() {
    878   if (!task_runner_->BelongsToCurrentThread()) {
    879     task_runner_->PostTask(FROM_HERE, base::Bind(
    880         &Pipeline::OnAudioUnderflow, base::Unretained(this)));
    881     return;
    882   }
    883 
    884   if (state_ != kPlaying)
    885     return;
    886 
    887   if (audio_renderer_)
    888     audio_renderer_->ResumeAfterUnderflow();
    889 }
    890 
    891 void Pipeline::BufferingStateChanged(BufferingState* buffering_state,
    892                                      BufferingState new_buffering_state) {
    893   DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", "
    894            << " " << new_buffering_state << ") "
    895            << (buffering_state == &audio_buffering_state_ ? "audio" : "video");
    896   DCHECK(task_runner_->BelongsToCurrentThread());
    897   bool was_waiting_for_enough_data = WaitingForEnoughData();
    898   *buffering_state = new_buffering_state;
    899 
    900   // Renderer underflowed.
    901   if (!was_waiting_for_enough_data && WaitingForEnoughData()) {
    902     StartWaitingForEnoughData();
    903     return;
    904   }
    905 
    906   // Renderer prerolled.
    907   if (was_waiting_for_enough_data && !WaitingForEnoughData()) {
    908     StartPlayback();
    909     return;
    910   }
    911 }
    912 
    913 bool Pipeline::WaitingForEnoughData() const {
    914   DCHECK(task_runner_->BelongsToCurrentThread());
    915   if (state_ != kPlaying)
    916     return false;
    917   if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH)
    918     return true;
    919   if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH)
    920     return true;
    921   return false;
    922 }
    923 
    924 void Pipeline::StartWaitingForEnoughData() {
    925   DVLOG(1) << __FUNCTION__;
    926   DCHECK_EQ(state_, kPlaying);
    927   DCHECK(WaitingForEnoughData());
    928 
    929   base::AutoLock auto_lock(lock_);
    930   PauseClockAndStopRendering_Locked();
    931 }
    932 
    933 void Pipeline::StartPlayback() {
    934   DVLOG(1) << __FUNCTION__;
    935   DCHECK_EQ(state_, kPlaying);
    936   DCHECK_EQ(clock_state_, CLOCK_PAUSED);
    937   DCHECK(!WaitingForEnoughData());
    938 
    939   if (audio_renderer_) {
    940     // We use audio stream to update the clock. So if there is such a
    941     // stream, we pause the clock until we receive a valid timestamp.
    942     base::AutoLock auto_lock(lock_);
    943     clock_state_ = CLOCK_WAITING_FOR_AUDIO_TIME_UPDATE;
    944     audio_renderer_->StartRendering();
    945   } else {
    946     base::AutoLock auto_lock(lock_);
    947     clock_state_ = CLOCK_PLAYING;
    948     clock_->SetMaxTime(clock_->Duration());
    949     clock_->Play();
    950   }
    951 
    952   preroll_completed_cb_.Run();
    953   if (!seek_cb_.is_null())
    954     base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
    955 }
    956 
    957 void Pipeline::PauseClockAndStopRendering_Locked() {
    958   lock_.AssertAcquired();
    959   switch (clock_state_) {
    960     case CLOCK_PAUSED:
    961       return;
    962 
    963     case CLOCK_WAITING_FOR_AUDIO_TIME_UPDATE:
    964       audio_renderer_->StopRendering();
    965       break;
    966 
    967     case CLOCK_PLAYING:
    968       if (audio_renderer_)
    969         audio_renderer_->StopRendering();
    970       clock_->Pause();
    971       break;
    972   }
    973 
    974   clock_state_ = CLOCK_PAUSED;
    975 }
    976 
    977 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() {
    978   lock_.AssertAcquired();
    979   if (clock_state_ != CLOCK_WAITING_FOR_AUDIO_TIME_UPDATE)
    980     return;
    981 
    982   clock_state_ = CLOCK_PLAYING;
    983   clock_->Play();
    984 }
    985 
    986 }  // namespace media
    987