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