• Home
  • History
  • Annotate
  • 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/command_line.h"
13 #include "base/compiler_specific.h"
14 #include "base/location.h"
15 #include "base/metrics/histogram.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "base/synchronization/condition_variable.h"
21 #include "media/base/media_log.h"
22 #include "media/base/media_switches.h"
23 #include "media/base/renderer.h"
24 #include "media/base/text_renderer.h"
25 #include "media/base/text_track_config.h"
26 #include "media/base/video_decoder_config.h"
27 
28 using base::TimeDelta;
29 
30 namespace media {
31 
Pipeline(const scoped_refptr<base::SingleThreadTaskRunner> & task_runner,MediaLog * media_log)32 Pipeline::Pipeline(
33     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
34     MediaLog* media_log)
35     : task_runner_(task_runner),
36       media_log_(media_log),
37       running_(false),
38       did_loading_progress_(false),
39       volume_(1.0f),
40       playback_rate_(0.0f),
41       status_(PIPELINE_OK),
42       is_initialized_(false),
43       state_(kCreated),
44       renderer_ended_(false),
45       text_renderer_ended_(false),
46       demuxer_(NULL),
47       weak_factory_(this) {
48   media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
49   media_log_->AddEvent(
50       media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED));
51 }
52 
~Pipeline()53 Pipeline::~Pipeline() {
54   DCHECK(thread_checker_.CalledOnValidThread())
55       << "Pipeline must be destroyed on same thread that created it";
56   DCHECK(!running_) << "Stop() must complete before destroying object";
57   DCHECK(stop_cb_.is_null());
58   DCHECK(seek_cb_.is_null());
59 
60   media_log_->AddEvent(
61       media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED));
62 }
63 
Start(Demuxer * demuxer,scoped_ptr<Renderer> renderer,const base::Closure & ended_cb,const PipelineStatusCB & error_cb,const PipelineStatusCB & seek_cb,const PipelineMetadataCB & metadata_cb,const BufferingStateCB & buffering_state_cb,const base::Closure & duration_change_cb,const AddTextTrackCB & add_text_track_cb)64 void Pipeline::Start(Demuxer* demuxer,
65                      scoped_ptr<Renderer> renderer,
66                      const base::Closure& ended_cb,
67                      const PipelineStatusCB& error_cb,
68                      const PipelineStatusCB& seek_cb,
69                      const PipelineMetadataCB& metadata_cb,
70                      const BufferingStateCB& buffering_state_cb,
71                      const base::Closure& duration_change_cb,
72                      const AddTextTrackCB& add_text_track_cb) {
73   DCHECK(!ended_cb.is_null());
74   DCHECK(!error_cb.is_null());
75   DCHECK(!seek_cb.is_null());
76   DCHECK(!metadata_cb.is_null());
77   DCHECK(!buffering_state_cb.is_null());
78 
79   base::AutoLock auto_lock(lock_);
80   CHECK(!running_) << "Media pipeline is already running";
81   running_ = true;
82 
83   demuxer_ = demuxer;
84   renderer_ = renderer.Pass();
85   ended_cb_ = ended_cb;
86   error_cb_ = error_cb;
87   seek_cb_ = seek_cb;
88   metadata_cb_ = metadata_cb;
89   buffering_state_cb_ = buffering_state_cb;
90   duration_change_cb_ = duration_change_cb;
91   add_text_track_cb_ = add_text_track_cb;
92 
93   task_runner_->PostTask(
94       FROM_HERE, base::Bind(&Pipeline::StartTask, weak_factory_.GetWeakPtr()));
95 }
96 
Stop(const base::Closure & stop_cb)97 void Pipeline::Stop(const base::Closure& stop_cb) {
98   DVLOG(2) << __FUNCTION__;
99   task_runner_->PostTask(
100       FROM_HERE,
101       base::Bind(&Pipeline::StopTask, weak_factory_.GetWeakPtr(), stop_cb));
102 }
103 
Seek(TimeDelta time,const PipelineStatusCB & seek_cb)104 void Pipeline::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) {
105   base::AutoLock auto_lock(lock_);
106   if (!running_) {
107     DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek().";
108     return;
109   }
110 
111   task_runner_->PostTask(
112       FROM_HERE,
113       base::Bind(
114           &Pipeline::SeekTask, weak_factory_.GetWeakPtr(), 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,
135                            base::Bind(&Pipeline::PlaybackRateChangedTask,
136                                       weak_factory_.GetWeakPtr(),
137                                       playback_rate));
138   }
139 }
140 
GetVolume() const141 float Pipeline::GetVolume() const {
142   base::AutoLock auto_lock(lock_);
143   return volume_;
144 }
145 
SetVolume(float volume)146 void Pipeline::SetVolume(float volume) {
147   if (volume < 0.0f || volume > 1.0f)
148     return;
149 
150   base::AutoLock auto_lock(lock_);
151   volume_ = volume;
152   if (running_) {
153     task_runner_->PostTask(
154         FROM_HERE,
155         base::Bind(
156             &Pipeline::VolumeChangedTask, weak_factory_.GetWeakPtr(), volume));
157   }
158 }
159 
GetMediaTime() const160 TimeDelta Pipeline::GetMediaTime() const {
161   base::AutoLock auto_lock(lock_);
162   if (!renderer_)
163     return TimeDelta();
164 
165   TimeDelta media_time = renderer_->GetMediaTime();
166   return std::min(media_time, duration_);
167 }
168 
GetBufferedTimeRanges() const169 Ranges<TimeDelta> Pipeline::GetBufferedTimeRanges() const {
170   base::AutoLock auto_lock(lock_);
171   return buffered_time_ranges_;
172 }
173 
GetMediaDuration() const174 TimeDelta Pipeline::GetMediaDuration() const {
175   base::AutoLock auto_lock(lock_);
176   return duration_;
177 }
178 
DidLoadingProgress()179 bool Pipeline::DidLoadingProgress() {
180   base::AutoLock auto_lock(lock_);
181   bool ret = did_loading_progress_;
182   did_loading_progress_ = false;
183   return ret;
184 }
185 
GetStatistics() const186 PipelineStatistics Pipeline::GetStatistics() const {
187   base::AutoLock auto_lock(lock_);
188   return statistics_;
189 }
190 
SetErrorForTesting(PipelineStatus status)191 void Pipeline::SetErrorForTesting(PipelineStatus status) {
192   OnError(status);
193 }
194 
HasWeakPtrsForTesting() const195 bool Pipeline::HasWeakPtrsForTesting() const {
196   DCHECK(task_runner_->BelongsToCurrentThread());
197   return weak_factory_.HasWeakPtrs();
198 }
199 
SetState(State next_state)200 void Pipeline::SetState(State next_state) {
201   DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state);
202 
203   state_ = next_state;
204   media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
205 }
206 
207 #define RETURN_STRING(state) case state: return #state;
208 
GetStateString(State state)209 const char* Pipeline::GetStateString(State state) {
210   switch (state) {
211     RETURN_STRING(kCreated);
212     RETURN_STRING(kInitDemuxer);
213     RETURN_STRING(kInitRenderer);
214     RETURN_STRING(kSeeking);
215     RETURN_STRING(kPlaying);
216     RETURN_STRING(kStopping);
217     RETURN_STRING(kStopped);
218   }
219   NOTREACHED();
220   return "INVALID";
221 }
222 
223 #undef RETURN_STRING
224 
GetNextState() const225 Pipeline::State Pipeline::GetNextState() const {
226   DCHECK(task_runner_->BelongsToCurrentThread());
227   DCHECK(stop_cb_.is_null())
228       << "State transitions don't happen when stopping";
229   DCHECK_EQ(status_, PIPELINE_OK)
230       << "State transitions don't happen when there's an error: " << status_;
231 
232   switch (state_) {
233     case kCreated:
234       return kInitDemuxer;
235 
236     case kInitDemuxer:
237       return kInitRenderer;
238 
239     case kInitRenderer:
240     case kSeeking:
241       return kPlaying;
242 
243     case kPlaying:
244     case kStopping:
245     case kStopped:
246       break;
247   }
248   NOTREACHED() << "State has no transition: " << state_;
249   return state_;
250 }
251 
OnDemuxerError(PipelineStatus error)252 void Pipeline::OnDemuxerError(PipelineStatus error) {
253   task_runner_->PostTask(FROM_HERE,
254                          base::Bind(&Pipeline::ErrorChangedTask,
255                                     weak_factory_.GetWeakPtr(),
256                                     error));
257 }
258 
AddTextStream(DemuxerStream * text_stream,const TextTrackConfig & config)259 void Pipeline::AddTextStream(DemuxerStream* text_stream,
260                              const TextTrackConfig& config) {
261   task_runner_->PostTask(FROM_HERE,
262                          base::Bind(&Pipeline::AddTextStreamTask,
263                                     weak_factory_.GetWeakPtr(),
264                                     text_stream,
265                                     config));
266 }
267 
RemoveTextStream(DemuxerStream * text_stream)268 void Pipeline::RemoveTextStream(DemuxerStream* text_stream) {
269   task_runner_->PostTask(FROM_HERE,
270                          base::Bind(&Pipeline::RemoveTextStreamTask,
271                                     weak_factory_.GetWeakPtr(),
272                                     text_stream));
273 }
274 
OnError(PipelineStatus error)275 void Pipeline::OnError(PipelineStatus error) {
276   DCHECK(task_runner_->BelongsToCurrentThread());
277   DCHECK(IsRunning());
278   DCHECK_NE(PIPELINE_OK, error);
279   VLOG(1) << "Media pipeline error: " << error;
280 
281   task_runner_->PostTask(FROM_HERE, base::Bind(
282       &Pipeline::ErrorChangedTask, weak_factory_.GetWeakPtr(), error));
283 }
284 
SetDuration(TimeDelta duration)285 void Pipeline::SetDuration(TimeDelta duration) {
286   DCHECK(IsRunning());
287   media_log_->AddEvent(
288       media_log_->CreateTimeEvent(
289           MediaLogEvent::DURATION_SET, "duration", duration));
290   UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration);
291 
292   base::AutoLock auto_lock(lock_);
293   duration_ = duration;
294   if (!duration_change_cb_.is_null())
295     duration_change_cb_.Run();
296 }
297 
OnStateTransition(PipelineStatus status)298 void Pipeline::OnStateTransition(PipelineStatus status) {
299   DCHECK(task_runner_->BelongsToCurrentThread());
300   // Force post to process state transitions after current execution frame.
301   task_runner_->PostTask(
302       FROM_HERE,
303       base::Bind(
304           &Pipeline::StateTransitionTask, weak_factory_.GetWeakPtr(), status));
305 }
306 
StateTransitionTask(PipelineStatus status)307 void Pipeline::StateTransitionTask(PipelineStatus status) {
308   DCHECK(task_runner_->BelongsToCurrentThread());
309 
310   // No-op any state transitions if we're stopping.
311   if (state_ == kStopping || state_ == kStopped)
312     return;
313 
314   // Preserve existing abnormal status, otherwise update based on the result of
315   // the previous operation.
316   status_ = (status_ != PIPELINE_OK ? status_ : status);
317 
318   if (status_ != PIPELINE_OK) {
319     ErrorChangedTask(status_);
320     return;
321   }
322 
323   // Guard against accidentally clearing |pending_callbacks_| for states that
324   // use it as well as states that should not be using it.
325   DCHECK_EQ(pending_callbacks_.get() != NULL, state_ == kSeeking);
326 
327   pending_callbacks_.reset();
328 
329   PipelineStatusCB done_cb =
330       base::Bind(&Pipeline::OnStateTransition, weak_factory_.GetWeakPtr());
331 
332   // Switch states, performing any entrance actions for the new state as well.
333   SetState(GetNextState());
334   switch (state_) {
335     case kInitDemuxer:
336       return InitializeDemuxer(done_cb);
337 
338     case kInitRenderer:
339       return InitializeRenderer(base::Bind(done_cb, PIPELINE_OK));
340 
341     case kPlaying:
342       // Report metadata the first time we enter the playing state.
343       if (!is_initialized_) {
344         is_initialized_ = true;
345         ReportMetadata();
346         start_timestamp_ = demuxer_->GetStartTime();
347       }
348 
349       base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
350 
351       DCHECK(start_timestamp_ >= base::TimeDelta());
352       renderer_->StartPlayingFrom(start_timestamp_);
353 
354       if (text_renderer_)
355         text_renderer_->StartPlaying();
356 
357       PlaybackRateChangedTask(GetPlaybackRate());
358       VolumeChangedTask(GetVolume());
359       return;
360 
361     case kStopping:
362     case kStopped:
363     case kCreated:
364     case kSeeking:
365       NOTREACHED() << "State has no transition: " << state_;
366       return;
367   }
368 }
369 
370 // Note that the usage of base::Unretained() with the renderers is considered
371 // safe as they are owned by |pending_callbacks_| and share the same lifetime.
372 //
373 // That being said, deleting the renderers while keeping |pending_callbacks_|
374 // running on the media thread would result in crashes.
DoSeek(TimeDelta seek_timestamp,const PipelineStatusCB & done_cb)375 void Pipeline::DoSeek(TimeDelta seek_timestamp,
376                       const PipelineStatusCB& done_cb) {
377   DCHECK(task_runner_->BelongsToCurrentThread());
378   DCHECK(!pending_callbacks_.get());
379   DCHECK_EQ(state_, kSeeking);
380   SerialRunner::Queue bound_fns;
381 
382   // Pause.
383   if (text_renderer_) {
384     bound_fns.Push(base::Bind(
385         &TextRenderer::Pause, base::Unretained(text_renderer_.get())));
386   }
387 
388   // Flush.
389   DCHECK(renderer_);
390   bound_fns.Push(
391       base::Bind(&Renderer::Flush, base::Unretained(renderer_.get())));
392 
393   if (text_renderer_) {
394     bound_fns.Push(base::Bind(
395         &TextRenderer::Flush, base::Unretained(text_renderer_.get())));
396   }
397 
398   // Seek demuxer.
399   bound_fns.Push(base::Bind(
400       &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
401 
402   pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
403 }
404 
DoStop(const PipelineStatusCB & done_cb)405 void Pipeline::DoStop(const PipelineStatusCB& done_cb) {
406   DVLOG(2) << __FUNCTION__;
407   DCHECK(task_runner_->BelongsToCurrentThread());
408   DCHECK(!pending_callbacks_.get());
409 
410   // TODO(scherkus): Enforce that Renderer is only called on a single thread,
411   // even for accessing media time http://crbug.com/370634
412   scoped_ptr<Renderer> renderer;
413   {
414     base::AutoLock auto_lock(lock_);
415     renderer.swap(renderer_);
416   }
417   renderer.reset();
418   text_renderer_.reset();
419 
420   if (demuxer_) {
421     demuxer_->Stop();
422     demuxer_ = NULL;
423   }
424 
425   task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK));
426 }
427 
OnStopCompleted(PipelineStatus status)428 void Pipeline::OnStopCompleted(PipelineStatus status) {
429   DVLOG(2) << __FUNCTION__;
430   DCHECK(task_runner_->BelongsToCurrentThread());
431   DCHECK_EQ(state_, kStopping);
432   DCHECK(!renderer_);
433   DCHECK(!text_renderer_);
434 
435   {
436     base::AutoLock auto_lock(lock_);
437     running_ = false;
438   }
439 
440   SetState(kStopped);
441   demuxer_ = NULL;
442 
443   // If we stop during initialization/seeking we want to run |seek_cb_|
444   // followed by |stop_cb_| so we don't leave outstanding callbacks around.
445   if (!seek_cb_.is_null()) {
446     base::ResetAndReturn(&seek_cb_).Run(status_);
447     error_cb_.Reset();
448   }
449   if (!stop_cb_.is_null()) {
450     error_cb_.Reset();
451 
452     // Invalid all weak pointers so it's safe to destroy |this| on the render
453     // main thread.
454     weak_factory_.InvalidateWeakPtrs();
455 
456     base::ResetAndReturn(&stop_cb_).Run();
457 
458     // NOTE: pipeline may be deleted at this point in time as a result of
459     // executing |stop_cb_|.
460     return;
461   }
462   if (!error_cb_.is_null()) {
463     DCHECK_NE(status_, PIPELINE_OK);
464     base::ResetAndReturn(&error_cb_).Run(status_);
465   }
466 }
467 
AddBufferedTimeRange(TimeDelta start,TimeDelta end)468 void Pipeline::AddBufferedTimeRange(TimeDelta start, TimeDelta end) {
469   DCHECK(IsRunning());
470   base::AutoLock auto_lock(lock_);
471   buffered_time_ranges_.Add(start, end);
472   did_loading_progress_ = true;
473 }
474 
475 // Called from any thread.
OnUpdateStatistics(const PipelineStatistics & stats)476 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) {
477   base::AutoLock auto_lock(lock_);
478   statistics_.audio_bytes_decoded += stats.audio_bytes_decoded;
479   statistics_.video_bytes_decoded += stats.video_bytes_decoded;
480   statistics_.video_frames_decoded += stats.video_frames_decoded;
481   statistics_.video_frames_dropped += stats.video_frames_dropped;
482 }
483 
StartTask()484 void Pipeline::StartTask() {
485   DCHECK(task_runner_->BelongsToCurrentThread());
486 
487   CHECK_EQ(kCreated, state_)
488       << "Media pipeline cannot be started more than once";
489 
490   text_renderer_ = CreateTextRenderer();
491   if (text_renderer_) {
492     text_renderer_->Initialize(
493         base::Bind(&Pipeline::OnTextRendererEnded, weak_factory_.GetWeakPtr()));
494   }
495 
496   StateTransitionTask(PIPELINE_OK);
497 }
498 
StopTask(const base::Closure & stop_cb)499 void Pipeline::StopTask(const base::Closure& stop_cb) {
500   DCHECK(task_runner_->BelongsToCurrentThread());
501   DCHECK(stop_cb_.is_null());
502 
503   if (state_ == kStopped) {
504     // Invalid all weak pointers so it's safe to destroy |this| on the render
505     // main thread.
506     weak_factory_.InvalidateWeakPtrs();
507 
508     // NOTE: pipeline may be deleted at this point in time as a result of
509     // executing |stop_cb|.
510     stop_cb.Run();
511 
512     return;
513   }
514 
515   stop_cb_ = stop_cb;
516 
517   // We may already be stopping due to a runtime error.
518   if (state_ == kStopping)
519     return;
520 
521   // Do not report statistics if the pipeline is not fully initialized.
522   if (state_ == kSeeking || state_ == kPlaying) {
523     PipelineStatistics stats = GetStatistics();
524     if (renderer_->HasVideo() && stats.video_frames_decoded > 0) {
525       UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount",
526                            stats.video_frames_dropped);
527     }
528   }
529 
530   SetState(kStopping);
531   pending_callbacks_.reset();
532   DoStop(base::Bind(&Pipeline::OnStopCompleted, weak_factory_.GetWeakPtr()));
533 }
534 
ErrorChangedTask(PipelineStatus error)535 void Pipeline::ErrorChangedTask(PipelineStatus error) {
536   DCHECK(task_runner_->BelongsToCurrentThread());
537   DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
538 
539   media_log_->AddEvent(media_log_->CreatePipelineErrorEvent(error));
540 
541   if (state_ == kStopping || state_ == kStopped)
542     return;
543 
544   SetState(kStopping);
545   pending_callbacks_.reset();
546   status_ = error;
547 
548   DoStop(base::Bind(&Pipeline::OnStopCompleted, weak_factory_.GetWeakPtr()));
549 }
550 
PlaybackRateChangedTask(float playback_rate)551 void Pipeline::PlaybackRateChangedTask(float playback_rate) {
552   DCHECK(task_runner_->BelongsToCurrentThread());
553 
554   // Playback rate changes are only carried out while playing.
555   if (state_ != kPlaying)
556     return;
557 
558   renderer_->SetPlaybackRate(playback_rate);
559 }
560 
VolumeChangedTask(float volume)561 void Pipeline::VolumeChangedTask(float volume) {
562   DCHECK(task_runner_->BelongsToCurrentThread());
563 
564   // Volume changes are only carried out while playing.
565   if (state_ != kPlaying)
566     return;
567 
568   renderer_->SetVolume(volume);
569 }
570 
SeekTask(TimeDelta time,const PipelineStatusCB & seek_cb)571 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
572   DCHECK(task_runner_->BelongsToCurrentThread());
573   DCHECK(stop_cb_.is_null());
574 
575   // Suppress seeking if we're not fully started.
576   if (state_ != kPlaying) {
577     DCHECK(state_ == kStopping || state_ == kStopped)
578         << "Receive extra seek in unexpected state: " << state_;
579 
580     // TODO(scherkus): should we run the callback?  I'm tempted to say the API
581     // will only execute the first Seek() request.
582     DVLOG(1) << "Media pipeline has not started, ignoring seek to "
583              << time.InMicroseconds() << " (current state: " << state_ << ")";
584     return;
585   }
586 
587   DCHECK(seek_cb_.is_null());
588 
589   const base::TimeDelta seek_timestamp =
590       std::max(time, demuxer_->GetStartTime());
591 
592   SetState(kSeeking);
593   seek_cb_ = seek_cb;
594   renderer_ended_ = false;
595   text_renderer_ended_ = false;
596   start_timestamp_ = seek_timestamp;
597 
598   DoSeek(seek_timestamp,
599          base::Bind(&Pipeline::OnStateTransition, weak_factory_.GetWeakPtr()));
600 }
601 
OnRendererEnded()602 void Pipeline::OnRendererEnded() {
603   DCHECK(task_runner_->BelongsToCurrentThread());
604   media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED));
605 
606   if (state_ != kPlaying)
607     return;
608 
609   DCHECK(!renderer_ended_);
610   renderer_ended_ = true;
611 
612   RunEndedCallbackIfNeeded();
613 }
614 
OnTextRendererEnded()615 void Pipeline::OnTextRendererEnded() {
616   DCHECK(task_runner_->BelongsToCurrentThread());
617   media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
618 
619   if (state_ != kPlaying)
620     return;
621 
622   DCHECK(!text_renderer_ended_);
623   text_renderer_ended_ = true;
624 
625   RunEndedCallbackIfNeeded();
626 }
627 
RunEndedCallbackIfNeeded()628 void Pipeline::RunEndedCallbackIfNeeded() {
629   DCHECK(task_runner_->BelongsToCurrentThread());
630 
631   if (renderer_ && !renderer_ended_)
632     return;
633 
634   if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_)
635     return;
636 
637   DCHECK_EQ(status_, PIPELINE_OK);
638   ended_cb_.Run();
639 }
640 
CreateTextRenderer()641 scoped_ptr<TextRenderer> Pipeline::CreateTextRenderer() {
642   DCHECK(task_runner_->BelongsToCurrentThread());
643 
644   const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
645   if (!cmd_line->HasSwitch(switches::kEnableInbandTextTracks))
646     return scoped_ptr<media::TextRenderer>();
647 
648   return scoped_ptr<media::TextRenderer>(new media::TextRenderer(
649       task_runner_,
650       base::Bind(&Pipeline::OnAddTextTrack, weak_factory_.GetWeakPtr())));
651 }
652 
AddTextStreamTask(DemuxerStream * text_stream,const TextTrackConfig & config)653 void Pipeline::AddTextStreamTask(DemuxerStream* text_stream,
654                                  const TextTrackConfig& config) {
655   DCHECK(task_runner_->BelongsToCurrentThread());
656   // TODO(matthewjheaney): fix up text_ended_ when text stream
657   // is added (http://crbug.com/321446).
658   if (text_renderer_)
659     text_renderer_->AddTextStream(text_stream, config);
660 }
661 
RemoveTextStreamTask(DemuxerStream * text_stream)662 void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) {
663   DCHECK(task_runner_->BelongsToCurrentThread());
664   if (text_renderer_)
665     text_renderer_->RemoveTextStream(text_stream);
666 }
667 
OnAddTextTrack(const TextTrackConfig & config,const AddTextTrackDoneCB & done_cb)668 void Pipeline::OnAddTextTrack(const TextTrackConfig& config,
669                               const AddTextTrackDoneCB& done_cb) {
670   DCHECK(task_runner_->BelongsToCurrentThread());
671   add_text_track_cb_.Run(config, done_cb);
672 }
673 
InitializeDemuxer(const PipelineStatusCB & done_cb)674 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) {
675   DCHECK(task_runner_->BelongsToCurrentThread());
676   demuxer_->Initialize(this, done_cb, text_renderer_);
677 }
678 
InitializeRenderer(const base::Closure & done_cb)679 void Pipeline::InitializeRenderer(const base::Closure& done_cb) {
680   DCHECK(task_runner_->BelongsToCurrentThread());
681 
682   if (!demuxer_->GetStream(DemuxerStream::AUDIO) &&
683       !demuxer_->GetStream(DemuxerStream::VIDEO)) {
684     {
685       base::AutoLock auto_lock(lock_);
686       renderer_.reset();
687     }
688     OnError(PIPELINE_ERROR_COULD_NOT_RENDER);
689     return;
690   }
691 
692   base::WeakPtr<Pipeline> weak_this = weak_factory_.GetWeakPtr();
693   renderer_->Initialize(
694       done_cb,
695       base::Bind(&Pipeline::OnUpdateStatistics, weak_this),
696       base::Bind(&Pipeline::OnRendererEnded, weak_this),
697       base::Bind(&Pipeline::OnError, weak_this),
698       base::Bind(&Pipeline::BufferingStateChanged, weak_this));
699 }
700 
ReportMetadata()701 void Pipeline::ReportMetadata() {
702   DCHECK(task_runner_->BelongsToCurrentThread());
703   PipelineMetadata metadata;
704   metadata.has_audio = renderer_->HasAudio();
705   metadata.has_video = renderer_->HasVideo();
706   metadata.timeline_offset = demuxer_->GetTimelineOffset();
707   DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
708   if (stream) {
709     metadata.natural_size = stream->video_decoder_config().natural_size();
710     metadata.video_rotation = stream->video_rotation();
711   }
712   metadata_cb_.Run(metadata);
713 }
714 
BufferingStateChanged(BufferingState new_buffering_state)715 void Pipeline::BufferingStateChanged(BufferingState new_buffering_state) {
716   DVLOG(1) << __FUNCTION__ << "(" << new_buffering_state << ") ";
717   DCHECK(task_runner_->BelongsToCurrentThread());
718   buffering_state_cb_.Run(new_buffering_state);
719 }
720 
721 }  // namespace media
722