Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / media / base / pipeline.cc
blob89097a9d63193de7f08bda12742b46d6f3093ec0
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 waiting_for_clock_update_(false),
46 status_(PIPELINE_OK),
47 state_(kCreated),
48 audio_ended_(false),
49 video_ended_(false),
50 text_ended_(false),
51 audio_disabled_(false),
52 demuxer_(NULL),
53 creation_time_(default_tick_clock_.NowTicks()) {
54 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
55 media_log_->AddEvent(
56 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED));
59 Pipeline::~Pipeline() {
60 DCHECK(thread_checker_.CalledOnValidThread())
61 << "Pipeline must be destroyed on same thread that created it";
62 DCHECK(!running_) << "Stop() must complete before destroying object";
63 DCHECK(stop_cb_.is_null());
64 DCHECK(seek_cb_.is_null());
66 media_log_->AddEvent(
67 media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED));
70 void Pipeline::Start(scoped_ptr<FilterCollection> collection,
71 const base::Closure& ended_cb,
72 const PipelineStatusCB& error_cb,
73 const PipelineStatusCB& seek_cb,
74 const PipelineMetadataCB& metadata_cb,
75 const base::Closure& preroll_completed_cb,
76 const base::Closure& duration_change_cb) {
77 DCHECK(!ended_cb.is_null());
78 DCHECK(!error_cb.is_null());
79 DCHECK(!seek_cb.is_null());
80 DCHECK(!metadata_cb.is_null());
81 DCHECK(!preroll_completed_cb.is_null());
83 base::AutoLock auto_lock(lock_);
84 CHECK(!running_) << "Media pipeline is already running";
85 running_ = true;
87 filter_collection_ = collection.Pass();
88 ended_cb_ = ended_cb;
89 error_cb_ = error_cb;
90 seek_cb_ = seek_cb;
91 metadata_cb_ = metadata_cb;
92 preroll_completed_cb_ = preroll_completed_cb;
93 duration_change_cb_ = duration_change_cb;
95 task_runner_->PostTask(
96 FROM_HERE, base::Bind(&Pipeline::StartTask, base::Unretained(this)));
99 void Pipeline::Stop(const base::Closure& stop_cb) {
100 base::AutoLock auto_lock(lock_);
101 task_runner_->PostTask(FROM_HERE, base::Bind(
102 &Pipeline::StopTask, base::Unretained(this), stop_cb));
105 void Pipeline::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) {
106 base::AutoLock auto_lock(lock_);
107 if (!running_) {
108 NOTREACHED() << "Media pipeline isn't running";
109 return;
112 task_runner_->PostTask(FROM_HERE, base::Bind(
113 &Pipeline::SeekTask, base::Unretained(this), time, seek_cb));
116 bool Pipeline::IsRunning() const {
117 base::AutoLock auto_lock(lock_);
118 return running_;
121 float Pipeline::GetPlaybackRate() const {
122 base::AutoLock auto_lock(lock_);
123 return playback_rate_;
126 void Pipeline::SetPlaybackRate(float playback_rate) {
127 if (playback_rate < 0.0f)
128 return;
130 base::AutoLock auto_lock(lock_);
131 playback_rate_ = playback_rate;
132 if (running_) {
133 task_runner_->PostTask(FROM_HERE, base::Bind(
134 &Pipeline::PlaybackRateChangedTask, base::Unretained(this),
135 playback_rate));
139 float Pipeline::GetVolume() const {
140 base::AutoLock auto_lock(lock_);
141 return volume_;
144 void Pipeline::SetVolume(float volume) {
145 if (volume < 0.0f || volume > 1.0f)
146 return;
148 base::AutoLock auto_lock(lock_);
149 volume_ = volume;
150 if (running_) {
151 task_runner_->PostTask(FROM_HERE, base::Bind(
152 &Pipeline::VolumeChangedTask, base::Unretained(this), volume));
156 TimeDelta Pipeline::GetMediaTime() const {
157 base::AutoLock auto_lock(lock_);
158 return clock_->Elapsed();
161 Ranges<TimeDelta> Pipeline::GetBufferedTimeRanges() {
162 base::AutoLock auto_lock(lock_);
163 return buffered_time_ranges_;
166 TimeDelta Pipeline::GetMediaDuration() const {
167 base::AutoLock auto_lock(lock_);
168 return clock_->Duration();
171 bool Pipeline::DidLoadingProgress() const {
172 base::AutoLock auto_lock(lock_);
173 bool ret = did_loading_progress_;
174 did_loading_progress_ = false;
175 return ret;
178 PipelineStatistics Pipeline::GetStatistics() const {
179 base::AutoLock auto_lock(lock_);
180 return statistics_;
183 void Pipeline::SetClockForTesting(Clock* clock) {
184 clock_.reset(clock);
187 void Pipeline::SetErrorForTesting(PipelineStatus status) {
188 SetError(status);
191 void Pipeline::SetState(State next_state) {
192 if (state_ != kStarted && next_state == kStarted &&
193 !creation_time_.is_null()) {
194 UMA_HISTOGRAM_TIMES("Media.TimeToPipelineStarted",
195 default_tick_clock_.NowTicks() - creation_time_);
196 creation_time_ = base::TimeTicks();
199 DVLOG(2) << GetStateString(state_) << " -> " << GetStateString(next_state);
201 state_ = next_state;
202 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
205 #define RETURN_STRING(state) case state: return #state;
207 const char* Pipeline::GetStateString(State state) {
208 switch (state) {
209 RETURN_STRING(kCreated);
210 RETURN_STRING(kInitDemuxer);
211 RETURN_STRING(kInitAudioRenderer);
212 RETURN_STRING(kInitVideoRenderer);
213 RETURN_STRING(kInitPrerolling);
214 RETURN_STRING(kSeeking);
215 RETURN_STRING(kStarting);
216 RETURN_STRING(kStarted);
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 kStarting;
255 case kSeeking:
256 return kStarting;
258 case kStarting:
259 return kStarted;
261 case kStarted:
262 case kStopping:
263 case kStopped:
264 break;
266 NOTREACHED() << "State has no transition: " << state_;
267 return state_;
270 void Pipeline::OnDemuxerError(PipelineStatus error) {
271 SetError(error);
274 void Pipeline::AddTextStream(DemuxerStream* text_stream,
275 const TextTrackConfig& config) {
276 task_runner_->PostTask(FROM_HERE, base::Bind(
277 &Pipeline::AddTextStreamTask, base::Unretained(this),
278 text_stream, config));
281 void Pipeline::RemoveTextStream(DemuxerStream* text_stream) {
282 task_runner_->PostTask(FROM_HERE, base::Bind(
283 &Pipeline::RemoveTextStreamTask, base::Unretained(this),
284 text_stream));
287 void Pipeline::SetError(PipelineStatus error) {
288 DCHECK(IsRunning());
289 DCHECK_NE(PIPELINE_OK, error);
290 VLOG(1) << "Media pipeline error: " << error;
292 task_runner_->PostTask(FROM_HERE, base::Bind(
293 &Pipeline::ErrorChangedTask, base::Unretained(this), error));
295 media_log_->AddEvent(media_log_->CreatePipelineErrorEvent(error));
298 void Pipeline::OnAudioDisabled() {
299 DCHECK(IsRunning());
300 task_runner_->PostTask(FROM_HERE, base::Bind(
301 &Pipeline::AudioDisabledTask, base::Unretained(this)));
302 media_log_->AddEvent(
303 media_log_->CreateEvent(MediaLogEvent::AUDIO_RENDERER_DISABLED));
306 void Pipeline::OnAudioTimeUpdate(TimeDelta time, TimeDelta max_time) {
307 DCHECK_LE(time.InMicroseconds(), max_time.InMicroseconds());
308 DCHECK(IsRunning());
309 base::AutoLock auto_lock(lock_);
311 if (audio_disabled_)
312 return;
314 if (waiting_for_clock_update_ && time < clock_->Elapsed())
315 return;
317 // TODO(scherkus): |state_| should only be accessed on pipeline thread, see
318 // http://crbug.com/137973
319 if (state_ == kSeeking)
320 return;
322 clock_->SetTime(time, max_time);
323 StartClockIfWaitingForTimeUpdate_Locked();
326 void Pipeline::OnVideoTimeUpdate(TimeDelta max_time) {
327 DCHECK(IsRunning());
328 base::AutoLock auto_lock(lock_);
330 if (audio_renderer_ && !audio_disabled_)
331 return;
333 // TODO(scherkus): |state_| should only be accessed on pipeline thread, see
334 // http://crbug.com/137973
335 if (state_ == kSeeking)
336 return;
338 DCHECK(!waiting_for_clock_update_);
339 clock_->SetMaxTime(max_time);
342 void Pipeline::SetDuration(TimeDelta duration) {
343 DCHECK(IsRunning());
344 media_log_->AddEvent(
345 media_log_->CreateTimeEvent(
346 MediaLogEvent::DURATION_SET, "duration", duration));
347 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration);
349 base::AutoLock auto_lock(lock_);
350 clock_->SetDuration(duration);
351 if (!duration_change_cb_.is_null())
352 duration_change_cb_.Run();
355 void Pipeline::OnStateTransition(PipelineStatus status) {
356 // Force post to process state transitions after current execution frame.
357 task_runner_->PostTask(FROM_HERE, base::Bind(
358 &Pipeline::StateTransitionTask, base::Unretained(this), status));
361 void Pipeline::StateTransitionTask(PipelineStatus status) {
362 DCHECK(task_runner_->BelongsToCurrentThread());
364 // No-op any state transitions if we're stopping.
365 if (state_ == kStopping || state_ == kStopped)
366 return;
368 // Preserve existing abnormal status, otherwise update based on the result of
369 // the previous operation.
370 status_ = (status_ != PIPELINE_OK ? status_ : status);
372 if (status_ != PIPELINE_OK) {
373 ErrorChangedTask(status_);
374 return;
377 // Guard against accidentally clearing |pending_callbacks_| for states that
378 // use it as well as states that should not be using it.
380 // TODO(scherkus): Make every state transition use |pending_callbacks_|.
381 DCHECK_EQ(pending_callbacks_.get() != NULL,
382 (state_ == kInitPrerolling || state_ == kStarting ||
383 state_ == kSeeking));
384 pending_callbacks_.reset();
386 PipelineStatusCB done_cb = base::Bind(
387 &Pipeline::OnStateTransition, base::Unretained(this));
389 // Switch states, performing any entrance actions for the new state as well.
390 SetState(GetNextState());
391 switch (state_) {
392 case kInitDemuxer:
393 return InitializeDemuxer(done_cb);
395 case kInitAudioRenderer:
396 return InitializeAudioRenderer(done_cb);
398 case kInitVideoRenderer:
399 return InitializeVideoRenderer(done_cb);
401 case kInitPrerolling:
402 filter_collection_.reset();
404 base::AutoLock l(lock_);
405 // We do not want to start the clock running. We only want to set the
406 // base media time so our timestamp calculations will be correct.
407 clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime());
409 if (!audio_renderer_ && !video_renderer_) {
410 done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER);
411 return;
415 PipelineMetadata metadata;
416 metadata.has_audio = audio_renderer_;
417 metadata.has_video = video_renderer_;
418 metadata.timeline_offset = demuxer_->GetTimelineOffset();
419 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
420 if (stream)
421 metadata.natural_size = stream->video_decoder_config().natural_size();
422 metadata_cb_.Run(metadata);
425 return DoInitialPreroll(done_cb);
427 case kStarting:
428 return DoPlay(done_cb);
430 case kStarted:
432 base::AutoLock l(lock_);
433 // We use audio stream to update the clock. So if there is such a
434 // stream, we pause the clock until we receive a valid timestamp.
435 waiting_for_clock_update_ = true;
436 if (!audio_renderer_ || audio_disabled_) {
437 clock_->SetMaxTime(clock_->Duration());
438 StartClockIfWaitingForTimeUpdate_Locked();
442 DCHECK(!seek_cb_.is_null());
443 DCHECK_EQ(status_, PIPELINE_OK);
445 // Fire canplaythrough immediately after playback begins because of
446 // crbug.com/106480.
447 // TODO(vrk): set ready state to HaveFutureData when bug above is fixed.
448 preroll_completed_cb_.Run();
449 return base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
451 case kStopping:
452 case kStopped:
453 case kCreated:
454 case kSeeking:
455 NOTREACHED() << "State has no transition: " << state_;
456 return;
460 // Note that the usage of base::Unretained() with the audio/video renderers
461 // in the following DoXXX() functions is considered safe as they are owned by
462 // |pending_callbacks_| and share the same lifetime.
464 // That being said, deleting the renderers while keeping |pending_callbacks_|
465 // running on the media thread would result in crashes.
466 void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) {
467 DCHECK(task_runner_->BelongsToCurrentThread());
468 DCHECK(!pending_callbacks_.get());
469 SerialRunner::Queue bound_fns;
471 base::TimeDelta seek_timestamp = demuxer_->GetStartTime();
473 // Preroll renderers.
474 if (audio_renderer_) {
475 bound_fns.Push(base::Bind(
476 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
477 seek_timestamp));
480 if (video_renderer_) {
481 bound_fns.Push(base::Bind(
482 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
483 seek_timestamp));
486 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
489 void Pipeline::DoSeek(
490 base::TimeDelta seek_timestamp,
491 const PipelineStatusCB& done_cb) {
492 DCHECK(task_runner_->BelongsToCurrentThread());
493 DCHECK(!pending_callbacks_.get());
494 SerialRunner::Queue bound_fns;
496 // Pause.
497 if (audio_renderer_) {
498 bound_fns.Push(base::Bind(
499 &AudioRenderer::Pause, base::Unretained(audio_renderer_.get())));
501 if (video_renderer_) {
502 bound_fns.Push(base::Bind(
503 &VideoRenderer::Pause, base::Unretained(video_renderer_.get())));
505 if (text_renderer_) {
506 bound_fns.Push(base::Bind(
507 &TextRenderer::Pause, base::Unretained(text_renderer_.get())));
510 // Flush.
511 if (audio_renderer_) {
512 bound_fns.Push(base::Bind(
513 &AudioRenderer::Flush, base::Unretained(audio_renderer_.get())));
515 if (video_renderer_) {
516 bound_fns.Push(base::Bind(
517 &VideoRenderer::Flush, base::Unretained(video_renderer_.get())));
519 if (text_renderer_) {
520 bound_fns.Push(base::Bind(
521 &TextRenderer::Flush, base::Unretained(text_renderer_.get())));
524 // Seek demuxer.
525 bound_fns.Push(base::Bind(
526 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
528 // Preroll renderers.
529 if (audio_renderer_) {
530 bound_fns.Push(base::Bind(
531 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
532 seek_timestamp));
535 if (video_renderer_) {
536 bound_fns.Push(base::Bind(
537 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
538 seek_timestamp));
541 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
544 void Pipeline::DoPlay(const PipelineStatusCB& done_cb) {
545 DCHECK(task_runner_->BelongsToCurrentThread());
546 DCHECK(!pending_callbacks_.get());
547 SerialRunner::Queue bound_fns;
549 PlaybackRateChangedTask(GetPlaybackRate());
550 VolumeChangedTask(GetVolume());
552 if (audio_renderer_) {
553 bound_fns.Push(base::Bind(
554 &AudioRenderer::Play, base::Unretained(audio_renderer_.get())));
557 if (video_renderer_) {
558 bound_fns.Push(base::Bind(
559 &VideoRenderer::Play, base::Unretained(video_renderer_.get())));
562 if (text_renderer_) {
563 bound_fns.Push(base::Bind(
564 &TextRenderer::Play, base::Unretained(text_renderer_.get())));
567 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
570 void Pipeline::DoStop(const PipelineStatusCB& done_cb) {
571 DCHECK(task_runner_->BelongsToCurrentThread());
572 DCHECK(!pending_callbacks_.get());
573 SerialRunner::Queue bound_fns;
575 if (demuxer_) {
576 bound_fns.Push(base::Bind(
577 &Demuxer::Stop, base::Unretained(demuxer_)));
580 if (audio_renderer_) {
581 bound_fns.Push(base::Bind(
582 &AudioRenderer::Stop, base::Unretained(audio_renderer_.get())));
585 if (video_renderer_) {
586 bound_fns.Push(base::Bind(
587 &VideoRenderer::Stop, base::Unretained(video_renderer_.get())));
590 if (text_renderer_) {
591 bound_fns.Push(base::Bind(
592 &TextRenderer::Stop, base::Unretained(text_renderer_.get())));
595 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
598 void Pipeline::OnStopCompleted(PipelineStatus status) {
599 DCHECK(task_runner_->BelongsToCurrentThread());
600 DCHECK_EQ(state_, kStopping);
602 base::AutoLock l(lock_);
603 running_ = false;
606 SetState(kStopped);
607 pending_callbacks_.reset();
608 filter_collection_.reset();
609 audio_renderer_.reset();
610 video_renderer_.reset();
611 text_renderer_.reset();
612 demuxer_ = NULL;
614 // If we stop during initialization/seeking we want to run |seek_cb_|
615 // followed by |stop_cb_| so we don't leave outstanding callbacks around.
616 if (!seek_cb_.is_null()) {
617 base::ResetAndReturn(&seek_cb_).Run(status_);
618 error_cb_.Reset();
620 if (!stop_cb_.is_null()) {
621 error_cb_.Reset();
622 base::ResetAndReturn(&stop_cb_).Run();
624 // NOTE: pipeline may be deleted at this point in time as a result of
625 // executing |stop_cb_|.
626 return;
628 if (!error_cb_.is_null()) {
629 DCHECK_NE(status_, PIPELINE_OK);
630 base::ResetAndReturn(&error_cb_).Run(status_);
634 void Pipeline::AddBufferedTimeRange(base::TimeDelta start,
635 base::TimeDelta end) {
636 DCHECK(IsRunning());
637 base::AutoLock auto_lock(lock_);
638 buffered_time_ranges_.Add(start, end);
639 did_loading_progress_ = true;
642 void Pipeline::OnAudioRendererEnded() {
643 // Force post to process ended tasks after current execution frame.
644 task_runner_->PostTask(FROM_HERE, base::Bind(
645 &Pipeline::DoAudioRendererEnded, base::Unretained(this)));
646 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::AUDIO_ENDED));
649 void Pipeline::OnVideoRendererEnded() {
650 // Force post to process ended tasks after current execution frame.
651 task_runner_->PostTask(FROM_HERE, base::Bind(
652 &Pipeline::DoVideoRendererEnded, base::Unretained(this)));
653 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::VIDEO_ENDED));
656 void Pipeline::OnTextRendererEnded() {
657 // Force post to process ended messages after current execution frame.
658 task_runner_->PostTask(FROM_HERE, base::Bind(
659 &Pipeline::DoTextRendererEnded, base::Unretained(this)));
660 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
663 // Called from any thread.
664 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) {
665 base::AutoLock auto_lock(lock_);
666 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded;
667 statistics_.video_bytes_decoded += stats.video_bytes_decoded;
668 statistics_.video_frames_decoded += stats.video_frames_decoded;
669 statistics_.video_frames_dropped += stats.video_frames_dropped;
672 void Pipeline::StartTask() {
673 DCHECK(task_runner_->BelongsToCurrentThread());
674 CHECK_EQ(kCreated, state_)
675 << "Media pipeline cannot be started more than once";
677 text_renderer_ = filter_collection_->GetTextRenderer();
679 if (text_renderer_) {
680 text_renderer_->Initialize(
681 base::Bind(&Pipeline::OnTextRendererEnded, base::Unretained(this)));
684 StateTransitionTask(PIPELINE_OK);
687 void Pipeline::StopTask(const base::Closure& stop_cb) {
688 DCHECK(task_runner_->BelongsToCurrentThread());
689 DCHECK(stop_cb_.is_null());
691 if (state_ == kStopped) {
692 stop_cb.Run();
693 return;
696 stop_cb_ = stop_cb;
698 // We may already be stopping due to a runtime error.
699 if (state_ == kStopping)
700 return;
702 SetState(kStopping);
703 pending_callbacks_.reset();
704 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this)));
707 void Pipeline::ErrorChangedTask(PipelineStatus error) {
708 DCHECK(task_runner_->BelongsToCurrentThread());
709 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
711 if (state_ == kStopping || state_ == kStopped)
712 return;
714 SetState(kStopping);
715 pending_callbacks_.reset();
716 status_ = error;
718 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this)));
721 void Pipeline::PlaybackRateChangedTask(float playback_rate) {
722 DCHECK(task_runner_->BelongsToCurrentThread());
724 // Playback rate changes are only carried out while playing.
725 if (state_ != kStarting && state_ != kStarted)
726 return;
729 base::AutoLock auto_lock(lock_);
730 clock_->SetPlaybackRate(playback_rate);
733 if (audio_renderer_)
734 audio_renderer_->SetPlaybackRate(playback_rate_);
735 if (video_renderer_)
736 video_renderer_->SetPlaybackRate(playback_rate_);
739 void Pipeline::VolumeChangedTask(float volume) {
740 DCHECK(task_runner_->BelongsToCurrentThread());
742 // Volume changes are only carried out while playing.
743 if (state_ != kStarting && state_ != kStarted)
744 return;
746 if (audio_renderer_)
747 audio_renderer_->SetVolume(volume);
750 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
751 DCHECK(task_runner_->BelongsToCurrentThread());
752 DCHECK(stop_cb_.is_null());
754 // Suppress seeking if we're not fully started.
755 if (state_ != kStarted) {
756 DCHECK(state_ == kStopping || state_ == kStopped)
757 << "Receive extra seek in unexpected state: " << state_;
759 // TODO(scherkus): should we run the callback? I'm tempted to say the API
760 // will only execute the first Seek() request.
761 DVLOG(1) << "Media pipeline has not started, ignoring seek to "
762 << time.InMicroseconds() << " (current state: " << state_ << ")";
763 return;
766 DCHECK(seek_cb_.is_null());
768 SetState(kSeeking);
769 base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime());
770 seek_cb_ = seek_cb;
771 audio_ended_ = false;
772 video_ended_ = false;
773 text_ended_ = false;
775 // Kick off seeking!
777 base::AutoLock auto_lock(lock_);
778 if (clock_->IsPlaying())
779 clock_->Pause();
780 clock_->SetTime(seek_timestamp, seek_timestamp);
782 DoSeek(seek_timestamp, base::Bind(
783 &Pipeline::OnStateTransition, base::Unretained(this)));
786 void Pipeline::DoAudioRendererEnded() {
787 DCHECK(task_runner_->BelongsToCurrentThread());
789 if (state_ != kStarted)
790 return;
792 DCHECK(!audio_ended_);
793 audio_ended_ = true;
795 // Start clock since there is no more audio to trigger clock updates.
796 if (!audio_disabled_) {
797 base::AutoLock auto_lock(lock_);
798 clock_->SetMaxTime(clock_->Duration());
799 StartClockIfWaitingForTimeUpdate_Locked();
802 RunEndedCallbackIfNeeded();
805 void Pipeline::DoVideoRendererEnded() {
806 DCHECK(task_runner_->BelongsToCurrentThread());
808 if (state_ != kStarted)
809 return;
811 DCHECK(!video_ended_);
812 video_ended_ = true;
814 RunEndedCallbackIfNeeded();
817 void Pipeline::DoTextRendererEnded() {
818 DCHECK(task_runner_->BelongsToCurrentThread());
820 if (state_ != kStarted)
821 return;
823 DCHECK(!text_ended_);
824 text_ended_ = true;
826 RunEndedCallbackIfNeeded();
829 void Pipeline::RunEndedCallbackIfNeeded() {
830 DCHECK(task_runner_->BelongsToCurrentThread());
832 if (audio_renderer_ && !audio_ended_ && !audio_disabled_)
833 return;
835 if (video_renderer_ && !video_ended_)
836 return;
838 if (text_renderer_ && text_renderer_->HasTracks() && !text_ended_)
839 return;
842 base::AutoLock auto_lock(lock_);
843 clock_->EndOfStream();
846 DCHECK_EQ(status_, PIPELINE_OK);
847 ended_cb_.Run();
850 void Pipeline::AudioDisabledTask() {
851 DCHECK(task_runner_->BelongsToCurrentThread());
853 base::AutoLock auto_lock(lock_);
854 audio_disabled_ = true;
856 // Notify our demuxer that we're no longer rendering audio.
857 demuxer_->OnAudioRendererDisabled();
859 // Start clock since there is no more audio to trigger clock updates.
860 clock_->SetMaxTime(clock_->Duration());
861 StartClockIfWaitingForTimeUpdate_Locked();
864 void Pipeline::AddTextStreamTask(DemuxerStream* text_stream,
865 const TextTrackConfig& config) {
866 DCHECK(task_runner_->BelongsToCurrentThread());
867 // TODO(matthewjheaney): fix up text_ended_ when text stream
868 // is added (http://crbug.com/321446).
869 text_renderer_->AddTextStream(text_stream, config);
872 void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) {
873 DCHECK(task_runner_->BelongsToCurrentThread());
874 text_renderer_->RemoveTextStream(text_stream);
877 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) {
878 DCHECK(task_runner_->BelongsToCurrentThread());
880 demuxer_ = filter_collection_->GetDemuxer();
881 demuxer_->Initialize(this, done_cb, text_renderer_);
884 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) {
885 DCHECK(task_runner_->BelongsToCurrentThread());
887 audio_renderer_ = filter_collection_->GetAudioRenderer();
888 audio_renderer_->Initialize(
889 demuxer_->GetStream(DemuxerStream::AUDIO),
890 done_cb,
891 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)),
892 base::Bind(&Pipeline::OnAudioUnderflow, base::Unretained(this)),
893 base::Bind(&Pipeline::OnAudioTimeUpdate, base::Unretained(this)),
894 base::Bind(&Pipeline::OnAudioRendererEnded, base::Unretained(this)),
895 base::Bind(&Pipeline::OnAudioDisabled, base::Unretained(this)),
896 base::Bind(&Pipeline::SetError, base::Unretained(this)));
899 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) {
900 DCHECK(task_runner_->BelongsToCurrentThread());
902 video_renderer_ = filter_collection_->GetVideoRenderer();
903 video_renderer_->Initialize(
904 demuxer_->GetStream(DemuxerStream::VIDEO),
905 demuxer_->GetLiveness() == Demuxer::LIVENESS_LIVE,
906 done_cb,
907 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)),
908 base::Bind(&Pipeline::OnVideoTimeUpdate, base::Unretained(this)),
909 base::Bind(&Pipeline::OnVideoRendererEnded, base::Unretained(this)),
910 base::Bind(&Pipeline::SetError, base::Unretained(this)),
911 base::Bind(&Pipeline::GetMediaTime, base::Unretained(this)),
912 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this)));
915 void Pipeline::OnAudioUnderflow() {
916 if (!task_runner_->BelongsToCurrentThread()) {
917 task_runner_->PostTask(FROM_HERE, base::Bind(
918 &Pipeline::OnAudioUnderflow, base::Unretained(this)));
919 return;
922 if (state_ != kStarted)
923 return;
925 if (audio_renderer_)
926 audio_renderer_->ResumeAfterUnderflow();
929 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() {
930 lock_.AssertAcquired();
931 if (!waiting_for_clock_update_)
932 return;
934 waiting_for_clock_update_ = false;
935 clock_->Play();
938 } // namespace media