• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Pipeline(const scoped_refptr<base::SingleThreadTaskRunner> & task_runner,MediaLog * media_log)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 
~Pipeline()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 
Start(scoped_ptr<FilterCollection> collection,const base::Closure & ended_cb,const PipelineStatusCB & error_cb,const PipelineStatusCB & seek_cb,const PipelineMetadataCB & metadata_cb,const base::Closure & preroll_completed_cb,const base::Closure & duration_change_cb)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 
Stop(const base::Closure & stop_cb)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 
Seek(TimeDelta time,const PipelineStatusCB & seek_cb)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 
IsRunning() const117 bool Pipeline::IsRunning() const {
118   base::AutoLock auto_lock(lock_);
119   return running_;
120 }
121 
GetPlaybackRate() const122 float Pipeline::GetPlaybackRate() const {
123   base::AutoLock auto_lock(lock_);
124   return playback_rate_;
125 }
126 
SetPlaybackRate(float playback_rate)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 
GetVolume() const140 float Pipeline::GetVolume() const {
141   base::AutoLock auto_lock(lock_);
142   return volume_;
143 }
144 
SetVolume(float volume)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 
GetMediaTime() const157 TimeDelta Pipeline::GetMediaTime() const {
158   base::AutoLock auto_lock(lock_);
159   return clock_->Elapsed();
160 }
161 
GetBufferedTimeRanges() const162 Ranges<TimeDelta> Pipeline::GetBufferedTimeRanges() const {
163   base::AutoLock auto_lock(lock_);
164   return buffered_time_ranges_;
165 }
166 
GetMediaDuration() const167 TimeDelta Pipeline::GetMediaDuration() const {
168   base::AutoLock auto_lock(lock_);
169   return clock_->Duration();
170 }
171 
DidLoadingProgress()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 
GetStatistics() const179 PipelineStatistics Pipeline::GetStatistics() const {
180   base::AutoLock auto_lock(lock_);
181   return statistics_;
182 }
183 
SetClockForTesting(Clock * clock)184 void Pipeline::SetClockForTesting(Clock* clock) {
185   clock_.reset(clock);
186 }
187 
SetErrorForTesting(PipelineStatus status)188 void Pipeline::SetErrorForTesting(PipelineStatus status) {
189   SetError(status);
190 }
191 
SetState(State next_state)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 
GetStateString(State state)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 
GetNextState() const226 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 
OnDemuxerError(PipelineStatus error)267 void Pipeline::OnDemuxerError(PipelineStatus error) {
268   SetError(error);
269 }
270 
AddTextStream(DemuxerStream * text_stream,const TextTrackConfig & config)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 
RemoveTextStream(DemuxerStream * text_stream)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 
SetError(PipelineStatus error)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 
OnAudioTimeUpdate(TimeDelta time,TimeDelta max_time)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 
OnVideoTimeUpdate(TimeDelta max_time)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 
SetDuration(TimeDelta duration)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 
OnStateTransition(PipelineStatus status)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 
StateTransitionTask(PipelineStatus status)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.
DoInitialPreroll(const PipelineStatusCB & done_cb)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 
DoSeek(base::TimeDelta seek_timestamp,const PipelineStatusCB & done_cb)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 
DoStop(const PipelineStatusCB & done_cb)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 
OnStopCompleted(PipelineStatus status)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 
AddBufferedTimeRange(base::TimeDelta start,base::TimeDelta end)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 
OnAudioRendererEnded()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 
OnVideoRendererEnded()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 
OnTextRendererEnded()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.
OnUpdateStatistics(const PipelineStatistics & stats)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 
StartTask()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 
StopTask(const base::Closure & stop_cb)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 
ErrorChangedTask(PipelineStatus error)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 
PlaybackRateChangedTask(float playback_rate)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 
VolumeChangedTask(float volume)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 
SeekTask(TimeDelta time,const PipelineStatusCB & seek_cb)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 
DoAudioRendererEnded()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 
DoVideoRendererEnded()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 
DoTextRendererEnded()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 
RunEndedCallbackIfNeeded()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 
AddTextStreamTask(DemuxerStream * text_stream,const TextTrackConfig & config)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 
RemoveTextStreamTask(DemuxerStream * text_stream)835 void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) {
836   DCHECK(task_runner_->BelongsToCurrentThread());
837   text_renderer_->RemoveTextStream(text_stream);
838 }
839 
InitializeDemuxer(const PipelineStatusCB & done_cb)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 
InitializeAudioRenderer(const PipelineStatusCB & done_cb)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 
InitializeVideoRenderer(const PipelineStatusCB & done_cb)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 
OnAudioUnderflow()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 
BufferingStateChanged(BufferingState * buffering_state,BufferingState new_buffering_state)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 
WaitingForEnoughData() const913 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 
StartWaitingForEnoughData()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 
StartPlayback()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 
PauseClockAndStopRendering_Locked()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 
StartClockIfWaitingForTimeUpdate_Locked()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