ToolbarOriginChipView is dead; remove the suppression for it.
[chromium-blink-merge.git] / media / base / pipeline.cc
blob5cfc144bc3823e0de96eed5345a952526709fc57
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.
5 #include "media/base/pipeline.h"
7 #include <algorithm>
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"
31 using base::TimeDelta;
33 namespace media {
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));
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());
67 media_log_->AddEvent(
68 media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED));
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());
84 base::AutoLock auto_lock(lock_);
85 CHECK(!running_) << "Media pipeline is already running";
86 running_ = true;
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;
96 task_runner_->PostTask(
97 FROM_HERE, base::Bind(&Pipeline::StartTask, base::Unretained(this)));
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));
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;
113 task_runner_->PostTask(FROM_HERE, base::Bind(
114 &Pipeline::SeekTask, base::Unretained(this), time, seek_cb));
117 bool Pipeline::IsRunning() const {
118 base::AutoLock auto_lock(lock_);
119 return running_;
122 float Pipeline::GetPlaybackRate() const {
123 base::AutoLock auto_lock(lock_);
124 return playback_rate_;
127 void Pipeline::SetPlaybackRate(float playback_rate) {
128 if (playback_rate < 0.0f)
129 return;
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));
140 float Pipeline::GetVolume() const {
141 base::AutoLock auto_lock(lock_);
142 return volume_;
145 void Pipeline::SetVolume(float volume) {
146 if (volume < 0.0f || volume > 1.0f)
147 return;
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));
157 TimeDelta Pipeline::GetMediaTime() const {
158 base::AutoLock auto_lock(lock_);
159 return clock_->Elapsed();
162 Ranges<TimeDelta> Pipeline::GetBufferedTimeRanges() const {
163 base::AutoLock auto_lock(lock_);
164 return buffered_time_ranges_;
167 TimeDelta Pipeline::GetMediaDuration() const {
168 base::AutoLock auto_lock(lock_);
169 return clock_->Duration();
172 bool Pipeline::DidLoadingProgress() {
173 base::AutoLock auto_lock(lock_);
174 bool ret = did_loading_progress_;
175 did_loading_progress_ = false;
176 return ret;
179 PipelineStatistics Pipeline::GetStatistics() const {
180 base::AutoLock auto_lock(lock_);
181 return statistics_;
184 void Pipeline::SetClockForTesting(Clock* clock) {
185 clock_.reset(clock);
188 void Pipeline::SetErrorForTesting(PipelineStatus status) {
189 SetError(status);
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();
200 DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state);
202 state_ = next_state;
203 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
206 #define RETURN_STRING(state) case state: return #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);
220 NOTREACHED();
221 return "INVALID";
224 #undef RETURN_STRING
226 Pipeline::State Pipeline::GetNextState() const {
227 DCHECK(task_runner_->BelongsToCurrentThread());
228 DCHECK(stop_cb_.is_null())
229 << "State transitions don't happen when stopping";
230 DCHECK_EQ(status_, PIPELINE_OK)
231 << "State transitions don't happen when there's an error: " << status_;
233 switch (state_) {
234 case kCreated:
235 return kInitDemuxer;
237 case kInitDemuxer:
238 if (demuxer_->GetStream(DemuxerStream::AUDIO))
239 return kInitAudioRenderer;
240 if (demuxer_->GetStream(DemuxerStream::VIDEO))
241 return kInitVideoRenderer;
242 return kInitPrerolling;
244 case kInitAudioRenderer:
245 if (demuxer_->GetStream(DemuxerStream::VIDEO))
246 return kInitVideoRenderer;
247 return kInitPrerolling;
249 case kInitVideoRenderer:
250 return kInitPrerolling;
252 case kInitPrerolling:
253 return kPlaying;
255 case kSeeking:
256 return kPlaying;
258 case kPlaying:
259 case kStopping:
260 case kStopped:
261 break;
263 NOTREACHED() << "State has no transition: " << state_;
264 return state_;
267 void Pipeline::OnDemuxerError(PipelineStatus error) {
268 SetError(error);
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));
278 void Pipeline::RemoveTextStream(DemuxerStream* text_stream) {
279 task_runner_->PostTask(FROM_HERE, base::Bind(
280 &Pipeline::RemoveTextStreamTask, base::Unretained(this),
281 text_stream));
284 void Pipeline::SetError(PipelineStatus error) {
285 DCHECK(IsRunning());
286 DCHECK_NE(PIPELINE_OK, error);
287 VLOG(1) << "Media pipeline error: " << error;
289 task_runner_->PostTask(FROM_HERE, base::Bind(
290 &Pipeline::ErrorChangedTask, base::Unretained(this), error));
292 media_log_->AddEvent(media_log_->CreatePipelineErrorEvent(error));
295 void Pipeline::OnAudioTimeUpdate(TimeDelta time, TimeDelta max_time) {
296 DCHECK_LE(time.InMicroseconds(), max_time.InMicroseconds());
297 DCHECK(IsRunning());
298 base::AutoLock auto_lock(lock_);
300 if (clock_state_ == CLOCK_WAITING_FOR_AUDIO_TIME_UPDATE &&
301 time < clock_->Elapsed()) {
302 return;
305 // TODO(scherkus): |state_| should only be accessed on pipeline thread, see
306 // http://crbug.com/137973
307 if (state_ == kSeeking)
308 return;
310 clock_->SetTime(time, max_time);
311 StartClockIfWaitingForTimeUpdate_Locked();
314 void Pipeline::OnVideoTimeUpdate(TimeDelta max_time) {
315 DCHECK(IsRunning());
316 base::AutoLock auto_lock(lock_);
318 if (audio_renderer_)
319 return;
321 // TODO(scherkus): |state_| should only be accessed on pipeline thread, see
322 // http://crbug.com/137973
323 if (state_ == kSeeking)
324 return;
326 DCHECK_NE(clock_state_, CLOCK_WAITING_FOR_AUDIO_TIME_UPDATE);
327 clock_->SetMaxTime(max_time);
330 void Pipeline::SetDuration(TimeDelta duration) {
331 DCHECK(IsRunning());
332 media_log_->AddEvent(
333 media_log_->CreateTimeEvent(
334 MediaLogEvent::DURATION_SET, "duration", duration));
335 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration);
337 base::AutoLock auto_lock(lock_);
338 clock_->SetDuration(duration);
339 if (!duration_change_cb_.is_null())
340 duration_change_cb_.Run();
343 void Pipeline::OnStateTransition(PipelineStatus status) {
344 // Force post to process state transitions after current execution frame.
345 task_runner_->PostTask(FROM_HERE, base::Bind(
346 &Pipeline::StateTransitionTask, base::Unretained(this), status));
349 void Pipeline::StateTransitionTask(PipelineStatus status) {
350 DCHECK(task_runner_->BelongsToCurrentThread());
352 // No-op any state transitions if we're stopping.
353 if (state_ == kStopping || state_ == kStopped)
354 return;
356 // Preserve existing abnormal status, otherwise update based on the result of
357 // the previous operation.
358 status_ = (status_ != PIPELINE_OK ? status_ : status);
360 if (status_ != PIPELINE_OK) {
361 ErrorChangedTask(status_);
362 return;
365 // Guard against accidentally clearing |pending_callbacks_| for states that
366 // use it as well as states that should not be using it.
367 DCHECK_EQ(pending_callbacks_.get() != NULL,
368 (state_ == kInitPrerolling || state_ == kSeeking));
370 pending_callbacks_.reset();
372 PipelineStatusCB done_cb = base::Bind(
373 &Pipeline::OnStateTransition, base::Unretained(this));
375 // Switch states, performing any entrance actions for the new state as well.
376 SetState(GetNextState());
377 switch (state_) {
378 case kInitDemuxer:
379 return InitializeDemuxer(done_cb);
381 case kInitAudioRenderer:
382 return InitializeAudioRenderer(done_cb);
384 case kInitVideoRenderer:
385 return InitializeVideoRenderer(done_cb);
387 case kInitPrerolling:
388 filter_collection_.reset();
390 base::AutoLock l(lock_);
391 // We do not want to start the clock running. We only want to set the
392 // base media time so our timestamp calculations will be correct.
393 clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime());
395 if (!audio_renderer_ && !video_renderer_) {
396 done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER);
397 return;
401 PipelineMetadata metadata;
402 metadata.has_audio = audio_renderer_;
403 metadata.has_video = video_renderer_;
404 metadata.timeline_offset = demuxer_->GetTimelineOffset();
405 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
406 if (stream)
407 metadata.natural_size = stream->video_decoder_config().natural_size();
408 metadata_cb_.Run(metadata);
411 return DoInitialPreroll(done_cb);
413 case kPlaying:
414 PlaybackRateChangedTask(GetPlaybackRate());
415 VolumeChangedTask(GetVolume());
417 // We enter this state from either kInitPrerolling or kSeeking. As of now
418 // both those states call Preroll(), which means by time we enter this
419 // state we've already buffered enough data. Forcefully update the
420 // buffering state, which start the clock and renderers and transition
421 // into kPlaying state.
423 // TODO(scherkus): Remove after renderers are taught to fire buffering
424 // state callbacks http://crbug.com/144683
425 DCHECK(WaitingForEnoughData());
426 if (audio_renderer_)
427 BufferingStateChanged(&audio_buffering_state_, BUFFERING_HAVE_ENOUGH);
428 if (video_renderer_)
429 BufferingStateChanged(&video_buffering_state_, BUFFERING_HAVE_ENOUGH);
430 return;
432 case kStopping:
433 case kStopped:
434 case kCreated:
435 case kSeeking:
436 NOTREACHED() << "State has no transition: " << state_;
437 return;
441 // Note that the usage of base::Unretained() with the audio/video renderers
442 // in the following DoXXX() functions is considered safe as they are owned by
443 // |pending_callbacks_| and share the same lifetime.
445 // That being said, deleting the renderers while keeping |pending_callbacks_|
446 // running on the media thread would result in crashes.
447 void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) {
448 DCHECK(task_runner_->BelongsToCurrentThread());
449 DCHECK(!pending_callbacks_.get());
450 SerialRunner::Queue bound_fns;
452 base::TimeDelta seek_timestamp = demuxer_->GetStartTime();
454 // Preroll renderers.
455 if (audio_renderer_) {
456 bound_fns.Push(base::Bind(
457 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
458 seek_timestamp));
461 if (video_renderer_) {
462 bound_fns.Push(base::Bind(
463 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
464 seek_timestamp));
466 // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering
467 // state callbacks http://crbug.com/144683
468 bound_fns.Push(base::Bind(&VideoRenderer::Play,
469 base::Unretained(video_renderer_.get())));
472 if (text_renderer_) {
473 bound_fns.Push(base::Bind(
474 &TextRenderer::Play, base::Unretained(text_renderer_.get())));
477 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
480 void Pipeline::DoSeek(
481 base::TimeDelta seek_timestamp,
482 const PipelineStatusCB& done_cb) {
483 DCHECK(task_runner_->BelongsToCurrentThread());
484 DCHECK(!pending_callbacks_.get());
485 SerialRunner::Queue bound_fns;
487 // Pause.
488 if (text_renderer_) {
489 bound_fns.Push(base::Bind(
490 &TextRenderer::Pause, base::Unretained(text_renderer_.get())));
493 // Flush.
494 if (audio_renderer_) {
495 bound_fns.Push(base::Bind(
496 &AudioRenderer::Flush, base::Unretained(audio_renderer_.get())));
498 // TODO(scherkus): Remove after AudioRenderer is taught to fire buffering
499 // state callbacks http://crbug.com/144683
500 bound_fns.Push(base::Bind(&Pipeline::BufferingStateChanged,
501 base::Unretained(this),
502 &audio_buffering_state_,
503 BUFFERING_HAVE_NOTHING));
505 if (video_renderer_) {
506 bound_fns.Push(base::Bind(
507 &VideoRenderer::Flush, base::Unretained(video_renderer_.get())));
509 // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering
510 // state callbacks http://crbug.com/144683
511 bound_fns.Push(base::Bind(&Pipeline::BufferingStateChanged,
512 base::Unretained(this),
513 &video_buffering_state_,
514 BUFFERING_HAVE_NOTHING));
516 if (text_renderer_) {
517 bound_fns.Push(base::Bind(
518 &TextRenderer::Flush, base::Unretained(text_renderer_.get())));
521 // Seek demuxer.
522 bound_fns.Push(base::Bind(
523 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
525 // Preroll renderers.
526 if (audio_renderer_) {
527 bound_fns.Push(base::Bind(
528 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
529 seek_timestamp));
532 if (video_renderer_) {
533 bound_fns.Push(base::Bind(
534 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
535 seek_timestamp));
537 // TODO(scherkus): Remove after renderers are taught to fire buffering
538 // state callbacks http://crbug.com/144683
539 bound_fns.Push(base::Bind(&VideoRenderer::Play,
540 base::Unretained(video_renderer_.get())));
543 if (text_renderer_) {
544 bound_fns.Push(base::Bind(
545 &TextRenderer::Play, base::Unretained(text_renderer_.get())));
548 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
551 void Pipeline::DoStop(const PipelineStatusCB& done_cb) {
552 DCHECK(task_runner_->BelongsToCurrentThread());
553 DCHECK(!pending_callbacks_.get());
554 SerialRunner::Queue bound_fns;
556 if (demuxer_) {
557 bound_fns.Push(base::Bind(
558 &Demuxer::Stop, base::Unretained(demuxer_)));
561 if (audio_renderer_) {
562 bound_fns.Push(base::Bind(
563 &AudioRenderer::Stop, base::Unretained(audio_renderer_.get())));
566 if (video_renderer_) {
567 bound_fns.Push(base::Bind(
568 &VideoRenderer::Stop, base::Unretained(video_renderer_.get())));
571 if (text_renderer_) {
572 bound_fns.Push(base::Bind(
573 &TextRenderer::Stop, base::Unretained(text_renderer_.get())));
576 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
579 void Pipeline::OnStopCompleted(PipelineStatus status) {
580 DCHECK(task_runner_->BelongsToCurrentThread());
581 DCHECK_EQ(state_, kStopping);
583 base::AutoLock l(lock_);
584 running_ = false;
587 SetState(kStopped);
588 pending_callbacks_.reset();
589 filter_collection_.reset();
590 audio_renderer_.reset();
591 video_renderer_.reset();
592 text_renderer_.reset();
593 demuxer_ = NULL;
595 // If we stop during initialization/seeking we want to run |seek_cb_|
596 // followed by |stop_cb_| so we don't leave outstanding callbacks around.
597 if (!seek_cb_.is_null()) {
598 base::ResetAndReturn(&seek_cb_).Run(status_);
599 error_cb_.Reset();
601 if (!stop_cb_.is_null()) {
602 error_cb_.Reset();
603 base::ResetAndReturn(&stop_cb_).Run();
605 // NOTE: pipeline may be deleted at this point in time as a result of
606 // executing |stop_cb_|.
607 return;
609 if (!error_cb_.is_null()) {
610 DCHECK_NE(status_, PIPELINE_OK);
611 base::ResetAndReturn(&error_cb_).Run(status_);
615 void Pipeline::AddBufferedTimeRange(base::TimeDelta start,
616 base::TimeDelta end) {
617 DCHECK(IsRunning());
618 base::AutoLock auto_lock(lock_);
619 buffered_time_ranges_.Add(start, end);
620 did_loading_progress_ = true;
623 void Pipeline::OnAudioRendererEnded() {
624 // Force post to process ended tasks after current execution frame.
625 task_runner_->PostTask(FROM_HERE, base::Bind(
626 &Pipeline::DoAudioRendererEnded, base::Unretained(this)));
627 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::AUDIO_ENDED));
630 void Pipeline::OnVideoRendererEnded() {
631 // Force post to process ended tasks after current execution frame.
632 task_runner_->PostTask(FROM_HERE, base::Bind(
633 &Pipeline::DoVideoRendererEnded, base::Unretained(this)));
634 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::VIDEO_ENDED));
637 void Pipeline::OnTextRendererEnded() {
638 // Force post to process ended messages after current execution frame.
639 task_runner_->PostTask(FROM_HERE, base::Bind(
640 &Pipeline::DoTextRendererEnded, base::Unretained(this)));
641 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
644 // Called from any thread.
645 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) {
646 base::AutoLock auto_lock(lock_);
647 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded;
648 statistics_.video_bytes_decoded += stats.video_bytes_decoded;
649 statistics_.video_frames_decoded += stats.video_frames_decoded;
650 statistics_.video_frames_dropped += stats.video_frames_dropped;
653 void Pipeline::StartTask() {
654 DCHECK(task_runner_->BelongsToCurrentThread());
655 CHECK_EQ(kCreated, state_)
656 << "Media pipeline cannot be started more than once";
658 text_renderer_ = filter_collection_->GetTextRenderer();
660 if (text_renderer_) {
661 text_renderer_->Initialize(
662 base::Bind(&Pipeline::OnTextRendererEnded, base::Unretained(this)));
665 StateTransitionTask(PIPELINE_OK);
668 void Pipeline::StopTask(const base::Closure& stop_cb) {
669 DCHECK(task_runner_->BelongsToCurrentThread());
670 DCHECK(stop_cb_.is_null());
672 if (state_ == kStopped) {
673 stop_cb.Run();
674 return;
677 stop_cb_ = stop_cb;
679 // We may already be stopping due to a runtime error.
680 if (state_ == kStopping)
681 return;
683 SetState(kStopping);
684 pending_callbacks_.reset();
685 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this)));
688 void Pipeline::ErrorChangedTask(PipelineStatus error) {
689 DCHECK(task_runner_->BelongsToCurrentThread());
690 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
692 if (state_ == kStopping || state_ == kStopped)
693 return;
695 SetState(kStopping);
696 pending_callbacks_.reset();
697 status_ = error;
699 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this)));
702 void Pipeline::PlaybackRateChangedTask(float playback_rate) {
703 DCHECK(task_runner_->BelongsToCurrentThread());
705 // Playback rate changes are only carried out while playing.
706 if (state_ != kPlaying)
707 return;
710 base::AutoLock auto_lock(lock_);
711 clock_->SetPlaybackRate(playback_rate);
714 if (audio_renderer_)
715 audio_renderer_->SetPlaybackRate(playback_rate_);
716 if (video_renderer_)
717 video_renderer_->SetPlaybackRate(playback_rate_);
720 void Pipeline::VolumeChangedTask(float volume) {
721 DCHECK(task_runner_->BelongsToCurrentThread());
723 // Volume changes are only carried out while playing.
724 if (state_ != kPlaying)
725 return;
727 if (audio_renderer_)
728 audio_renderer_->SetVolume(volume);
731 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
732 DCHECK(task_runner_->BelongsToCurrentThread());
733 DCHECK(stop_cb_.is_null());
735 // Suppress seeking if we're not fully started.
736 if (state_ != kPlaying) {
737 DCHECK(state_ == kStopping || state_ == kStopped)
738 << "Receive extra seek in unexpected state: " << state_;
740 // TODO(scherkus): should we run the callback? I'm tempted to say the API
741 // will only execute the first Seek() request.
742 DVLOG(1) << "Media pipeline has not started, ignoring seek to "
743 << time.InMicroseconds() << " (current state: " << state_ << ")";
744 return;
747 DCHECK(seek_cb_.is_null());
749 SetState(kSeeking);
750 base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime());
751 seek_cb_ = seek_cb;
752 audio_ended_ = false;
753 video_ended_ = false;
754 text_ended_ = false;
756 // Kick off seeking!
758 base::AutoLock auto_lock(lock_);
759 PauseClockAndStopRendering_Locked();
760 clock_->SetTime(seek_timestamp, seek_timestamp);
762 DoSeek(seek_timestamp, base::Bind(
763 &Pipeline::OnStateTransition, base::Unretained(this)));
766 void Pipeline::DoAudioRendererEnded() {
767 DCHECK(task_runner_->BelongsToCurrentThread());
769 if (state_ != kPlaying)
770 return;
772 DCHECK(!audio_ended_);
773 audio_ended_ = true;
775 // Start clock since there is no more audio to trigger clock updates.
777 base::AutoLock auto_lock(lock_);
778 clock_->SetMaxTime(clock_->Duration());
779 StartClockIfWaitingForTimeUpdate_Locked();
782 RunEndedCallbackIfNeeded();
785 void Pipeline::DoVideoRendererEnded() {
786 DCHECK(task_runner_->BelongsToCurrentThread());
788 if (state_ != kPlaying)
789 return;
791 DCHECK(!video_ended_);
792 video_ended_ = true;
794 RunEndedCallbackIfNeeded();
797 void Pipeline::DoTextRendererEnded() {
798 DCHECK(task_runner_->BelongsToCurrentThread());
800 if (state_ != kPlaying)
801 return;
803 DCHECK(!text_ended_);
804 text_ended_ = true;
806 RunEndedCallbackIfNeeded();
809 void Pipeline::RunEndedCallbackIfNeeded() {
810 DCHECK(task_runner_->BelongsToCurrentThread());
812 if (audio_renderer_ && !audio_ended_)
813 return;
815 if (video_renderer_ && !video_ended_)
816 return;
818 if (text_renderer_ && text_renderer_->HasTracks() && !text_ended_)
819 return;
822 base::AutoLock auto_lock(lock_);
823 PauseClockAndStopRendering_Locked();
824 clock_->SetTime(clock_->Duration(), clock_->Duration());
827 DCHECK_EQ(status_, PIPELINE_OK);
828 ended_cb_.Run();
831 void Pipeline::AddTextStreamTask(DemuxerStream* text_stream,
832 const TextTrackConfig& config) {
833 DCHECK(task_runner_->BelongsToCurrentThread());
834 // TODO(matthewjheaney): fix up text_ended_ when text stream
835 // is added (http://crbug.com/321446).
836 text_renderer_->AddTextStream(text_stream, config);
839 void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) {
840 DCHECK(task_runner_->BelongsToCurrentThread());
841 text_renderer_->RemoveTextStream(text_stream);
844 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) {
845 DCHECK(task_runner_->BelongsToCurrentThread());
847 demuxer_ = filter_collection_->GetDemuxer();
848 demuxer_->Initialize(this, done_cb, text_renderer_);
851 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) {
852 DCHECK(task_runner_->BelongsToCurrentThread());
854 audio_renderer_ = filter_collection_->GetAudioRenderer();
855 audio_renderer_->Initialize(
856 demuxer_->GetStream(DemuxerStream::AUDIO),
857 done_cb,
858 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)),
859 base::Bind(&Pipeline::OnAudioUnderflow, base::Unretained(this)),
860 base::Bind(&Pipeline::OnAudioTimeUpdate, base::Unretained(this)),
861 base::Bind(&Pipeline::OnAudioRendererEnded, base::Unretained(this)),
862 base::Bind(&Pipeline::SetError, base::Unretained(this)));
865 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) {
866 DCHECK(task_runner_->BelongsToCurrentThread());
868 video_renderer_ = filter_collection_->GetVideoRenderer();
869 video_renderer_->Initialize(
870 demuxer_->GetStream(DemuxerStream::VIDEO),
871 demuxer_->GetLiveness() == Demuxer::LIVENESS_LIVE,
872 done_cb,
873 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)),
874 base::Bind(&Pipeline::OnVideoTimeUpdate, base::Unretained(this)),
875 base::Bind(&Pipeline::OnVideoRendererEnded, base::Unretained(this)),
876 base::Bind(&Pipeline::SetError, base::Unretained(this)),
877 base::Bind(&Pipeline::GetMediaTime, base::Unretained(this)),
878 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this)));
881 void Pipeline::OnAudioUnderflow() {
882 if (!task_runner_->BelongsToCurrentThread()) {
883 task_runner_->PostTask(FROM_HERE, base::Bind(
884 &Pipeline::OnAudioUnderflow, base::Unretained(this)));
885 return;
888 if (state_ != kPlaying)
889 return;
891 if (audio_renderer_)
892 audio_renderer_->ResumeAfterUnderflow();
895 void Pipeline::BufferingStateChanged(BufferingState* buffering_state,
896 BufferingState new_buffering_state) {
897 DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", "
898 << " " << new_buffering_state << ") "
899 << (buffering_state == &audio_buffering_state_ ? "audio" : "video");
900 DCHECK(task_runner_->BelongsToCurrentThread());
901 bool was_waiting_for_enough_data = WaitingForEnoughData();
902 *buffering_state = new_buffering_state;
904 // Renderer underflowed.
905 if (!was_waiting_for_enough_data && WaitingForEnoughData()) {
906 StartWaitingForEnoughData();
907 return;
910 // Renderer prerolled.
911 if (was_waiting_for_enough_data && !WaitingForEnoughData()) {
912 StartPlayback();
913 return;
917 bool Pipeline::WaitingForEnoughData() const {
918 DCHECK(task_runner_->BelongsToCurrentThread());
919 if (state_ != kPlaying)
920 return false;
921 if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH)
922 return true;
923 if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH)
924 return true;
925 return false;
928 void Pipeline::StartWaitingForEnoughData() {
929 DVLOG(1) << __FUNCTION__;
930 DCHECK_EQ(state_, kPlaying);
931 DCHECK(WaitingForEnoughData());
933 base::AutoLock auto_lock(lock_);
934 PauseClockAndStopRendering_Locked();
937 void Pipeline::StartPlayback() {
938 DVLOG(1) << __FUNCTION__;
939 DCHECK_EQ(state_, kPlaying);
940 DCHECK_EQ(clock_state_, CLOCK_PAUSED);
941 DCHECK(!WaitingForEnoughData());
943 if (audio_renderer_) {
944 // We use audio stream to update the clock. So if there is such a
945 // stream, we pause the clock until we receive a valid timestamp.
946 base::AutoLock auto_lock(lock_);
947 clock_state_ = CLOCK_WAITING_FOR_AUDIO_TIME_UPDATE;
948 audio_renderer_->StartRendering();
949 } else {
950 base::AutoLock auto_lock(lock_);
951 clock_state_ = CLOCK_PLAYING;
952 clock_->SetMaxTime(clock_->Duration());
953 clock_->Play();
956 preroll_completed_cb_.Run();
957 if (!seek_cb_.is_null())
958 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
961 void Pipeline::PauseClockAndStopRendering_Locked() {
962 lock_.AssertAcquired();
963 switch (clock_state_) {
964 case CLOCK_PAUSED:
965 return;
967 case CLOCK_WAITING_FOR_AUDIO_TIME_UPDATE:
968 audio_renderer_->StopRendering();
969 break;
971 case CLOCK_PLAYING:
972 if (audio_renderer_)
973 audio_renderer_->StopRendering();
974 clock_->Pause();
975 break;
978 clock_state_ = CLOCK_PAUSED;
981 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() {
982 lock_.AssertAcquired();
983 if (clock_state_ != CLOCK_WAITING_FOR_AUDIO_TIME_UPDATE)
984 return;
986 clock_state_ = CLOCK_PLAYING;
987 clock_->Play();
990 } // namespace media