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