Add explicit |forceOnlineSignin| to user pod status
[chromium-blink-merge.git] / media / base / pipeline.cc
blob824585f5d31589206c67109cd5d48ad47553381a
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 total_bytes_(0),
43 volume_(1.0f),
44 playback_rate_(0.0f),
45 clock_(new Clock(&default_tick_clock_)),
46 waiting_for_clock_update_(false),
47 status_(PIPELINE_OK),
48 has_audio_(false),
49 has_video_(false),
50 state_(kCreated),
51 audio_ended_(false),
52 video_ended_(false),
53 text_ended_(false),
54 audio_disabled_(false),
55 demuxer_(NULL),
56 creation_time_(default_tick_clock_.NowTicks()) {
57 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
58 media_log_->AddEvent(
59 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED));
62 Pipeline::~Pipeline() {
63 DCHECK(thread_checker_.CalledOnValidThread())
64 << "Pipeline must be destroyed on same thread that created it";
65 DCHECK(!running_) << "Stop() must complete before destroying object";
66 DCHECK(stop_cb_.is_null());
67 DCHECK(seek_cb_.is_null());
69 media_log_->AddEvent(
70 media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED));
73 void Pipeline::Start(scoped_ptr<FilterCollection> collection,
74 const base::Closure& ended_cb,
75 const PipelineStatusCB& error_cb,
76 const PipelineStatusCB& seek_cb,
77 const BufferingStateCB& buffering_state_cb,
78 const base::Closure& duration_change_cb) {
79 base::AutoLock auto_lock(lock_);
80 CHECK(!running_) << "Media pipeline is already running";
81 DCHECK(!buffering_state_cb.is_null());
83 running_ = true;
84 task_runner_->PostTask(FROM_HERE, base::Bind(
85 &Pipeline::StartTask, base::Unretained(this), base::Passed(&collection),
86 ended_cb, error_cb, seek_cb, buffering_state_cb, duration_change_cb));
89 void Pipeline::Stop(const base::Closure& stop_cb) {
90 base::AutoLock auto_lock(lock_);
91 task_runner_->PostTask(FROM_HERE, base::Bind(
92 &Pipeline::StopTask, base::Unretained(this), stop_cb));
95 void Pipeline::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) {
96 base::AutoLock auto_lock(lock_);
97 if (!running_) {
98 NOTREACHED() << "Media pipeline isn't running";
99 return;
102 task_runner_->PostTask(FROM_HERE, base::Bind(
103 &Pipeline::SeekTask, base::Unretained(this), time, seek_cb));
106 bool Pipeline::IsRunning() const {
107 base::AutoLock auto_lock(lock_);
108 return running_;
111 bool Pipeline::HasAudio() const {
112 base::AutoLock auto_lock(lock_);
113 return has_audio_;
116 bool Pipeline::HasVideo() const {
117 base::AutoLock auto_lock(lock_);
118 return has_video_;
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 Ranges<TimeDelta> time_ranges;
164 for (size_t i = 0; i < buffered_time_ranges_.size(); ++i) {
165 time_ranges.Add(buffered_time_ranges_.start(i),
166 buffered_time_ranges_.end(i));
168 if (clock_->Duration() == TimeDelta() || total_bytes_ == 0)
169 return time_ranges;
170 for (size_t i = 0; i < buffered_byte_ranges_.size(); ++i) {
171 TimeDelta start = TimeForByteOffset_Locked(buffered_byte_ranges_.start(i));
172 TimeDelta end = TimeForByteOffset_Locked(buffered_byte_ranges_.end(i));
173 // Cap approximated buffered time at the length of the video.
174 end = std::min(end, clock_->Duration());
175 time_ranges.Add(start, end);
178 return time_ranges;
181 TimeDelta Pipeline::GetMediaDuration() const {
182 base::AutoLock auto_lock(lock_);
183 return clock_->Duration();
186 int64 Pipeline::GetTotalBytes() const {
187 base::AutoLock auto_lock(lock_);
188 return total_bytes_;
191 gfx::Size Pipeline::GetInitialNaturalSize() const {
192 base::AutoLock auto_lock(lock_);
193 return initial_natural_size_;
196 bool Pipeline::DidLoadingProgress() const {
197 base::AutoLock auto_lock(lock_);
198 bool ret = did_loading_progress_;
199 did_loading_progress_ = false;
200 return ret;
203 PipelineStatistics Pipeline::GetStatistics() const {
204 base::AutoLock auto_lock(lock_);
205 return statistics_;
208 void Pipeline::SetClockForTesting(Clock* clock) {
209 clock_.reset(clock);
212 void Pipeline::SetErrorForTesting(PipelineStatus status) {
213 SetError(status);
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();
224 DVLOG(2) << GetStateString(state_) << " -> " << GetStateString(next_state);
226 state_ = next_state;
227 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
230 #define RETURN_STRING(state) case state: return #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);
245 NOTREACHED();
246 return "INVALID";
249 #undef RETURN_STRING
251 Pipeline::State Pipeline::GetNextState() const {
252 DCHECK(task_runner_->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_;
258 switch (state_) {
259 case kCreated:
260 return kInitDemuxer;
262 case kInitDemuxer:
263 if (demuxer_->GetStream(DemuxerStream::AUDIO))
264 return kInitAudioRenderer;
265 if (demuxer_->GetStream(DemuxerStream::VIDEO))
266 return kInitVideoRenderer;
267 return kInitPrerolling;
269 case kInitAudioRenderer:
270 if (demuxer_->GetStream(DemuxerStream::VIDEO))
271 return kInitVideoRenderer;
272 return kInitPrerolling;
274 case kInitVideoRenderer:
275 return kInitPrerolling;
277 case kInitPrerolling:
278 return kStarting;
280 case kSeeking:
281 return kStarting;
283 case kStarting:
284 return kStarted;
286 case kStarted:
287 case kStopping:
288 case kStopped:
289 break;
291 NOTREACHED() << "State has no transition: " << state_;
292 return state_;
295 void Pipeline::OnDemuxerError(PipelineStatus error) {
296 SetError(error);
299 void Pipeline::AddTextStream(DemuxerStream* text_stream,
300 const TextTrackConfig& config) {
301 task_runner_->PostTask(FROM_HERE, base::Bind(
302 &Pipeline::AddTextStreamTask, base::Unretained(this),
303 text_stream, config));
306 void Pipeline::RemoveTextStream(DemuxerStream* text_stream) {
307 task_runner_->PostTask(FROM_HERE, base::Bind(
308 &Pipeline::RemoveTextStreamTask, base::Unretained(this),
309 text_stream));
312 void Pipeline::SetError(PipelineStatus error) {
313 DCHECK(IsRunning());
314 DCHECK_NE(PIPELINE_OK, error);
315 VLOG(1) << "Media pipeline error: " << error;
317 task_runner_->PostTask(FROM_HERE, base::Bind(
318 &Pipeline::ErrorChangedTask, base::Unretained(this), error));
320 media_log_->AddEvent(media_log_->CreatePipelineErrorEvent(error));
323 void Pipeline::OnAudioDisabled() {
324 DCHECK(IsRunning());
325 task_runner_->PostTask(FROM_HERE, base::Bind(
326 &Pipeline::AudioDisabledTask, base::Unretained(this)));
327 media_log_->AddEvent(
328 media_log_->CreateEvent(MediaLogEvent::AUDIO_RENDERER_DISABLED));
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_);
336 if (!has_audio_)
337 return;
338 if (waiting_for_clock_update_ && time < clock_->Elapsed())
339 return;
341 // TODO(scherkus): |state_| should only be accessed on pipeline thread, see
342 // http://crbug.com/137973
343 if (state_ == kSeeking)
344 return;
346 clock_->SetTime(time, max_time);
347 StartClockIfWaitingForTimeUpdate_Locked();
350 void Pipeline::OnVideoTimeUpdate(TimeDelta max_time) {
351 DCHECK(IsRunning());
352 base::AutoLock auto_lock(lock_);
354 if (has_audio_)
355 return;
357 // TODO(scherkus): |state_| should only be accessed on pipeline thread, see
358 // http://crbug.com/137973
359 if (state_ == kSeeking)
360 return;
362 DCHECK(!waiting_for_clock_update_);
363 clock_->SetMaxTime(max_time);
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);
373 base::AutoLock auto_lock(lock_);
374 clock_->SetDuration(duration);
375 if (!duration_change_cb_.is_null())
376 duration_change_cb_.Run();
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);
391 base::AutoLock auto_lock(lock_);
392 total_bytes_ = total_bytes;
395 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;
412 void Pipeline::OnStateTransition(PipelineStatus status) {
413 // Force post to process state transitions after current execution frame.
414 task_runner_->PostTask(FROM_HERE, base::Bind(
415 &Pipeline::StateTransitionTask, base::Unretained(this), status));
418 void Pipeline::StateTransitionTask(PipelineStatus status) {
419 DCHECK(task_runner_->BelongsToCurrentThread());
421 // No-op any state transitions if we're stopping.
422 if (state_ == kStopping || state_ == kStopped)
423 return;
425 // Preserve existing abnormal status, otherwise update based on the result of
426 // the previous operation.
427 status_ = (status_ != PIPELINE_OK ? status_ : status);
429 if (status_ != PIPELINE_OK) {
430 ErrorChangedTask(status_);
431 return;
434 // Guard against accidentally clearing |pending_callbacks_| for states that
435 // use it as well as states that should not be using it.
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();
443 PipelineStatusCB done_cb = base::Bind(
444 &Pipeline::OnStateTransition, base::Unretained(this));
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);
452 case kInitAudioRenderer:
453 return InitializeAudioRenderer(done_cb);
455 case kInitVideoRenderer:
456 return InitializeVideoRenderer(done_cb);
458 case kInitPrerolling:
459 filter_collection_.reset();
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());
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;
473 if (!audio_renderer_ && !video_renderer_) {
474 done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER);
475 return;
478 buffering_state_cb_.Run(kHaveMetadata);
480 return DoInitialPreroll(done_cb);
482 case kStarting:
483 return DoPlay(done_cb);
485 case kStarted:
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();
497 DCHECK(!seek_cb_.is_null());
498 DCHECK_EQ(status_, PIPELINE_OK);
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);
506 case kStopping:
507 case kStopped:
508 case kCreated:
509 case kSeeking:
510 NOTREACHED() << "State has no transition: " << state_;
511 return;
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.
519 // That being said, deleting the renderers while keeping |pending_callbacks_|
520 // running on the media thread would result in crashes.
521 void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) {
522 DCHECK(task_runner_->BelongsToCurrentThread());
523 DCHECK(!pending_callbacks_.get());
524 SerialRunner::Queue bound_fns;
526 base::TimeDelta seek_timestamp = demuxer_->GetStartTime();
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::DoSeek(
545 base::TimeDelta seek_timestamp,
546 const PipelineStatusCB& done_cb) {
547 DCHECK(task_runner_->BelongsToCurrentThread());
548 DCHECK(!pending_callbacks_.get());
549 SerialRunner::Queue bound_fns;
551 // Pause.
552 if (audio_renderer_) {
553 bound_fns.Push(base::Bind(
554 &AudioRenderer::Pause, base::Unretained(audio_renderer_.get())));
556 if (video_renderer_) {
557 bound_fns.Push(base::Bind(
558 &VideoRenderer::Pause, base::Unretained(video_renderer_.get())));
560 if (text_renderer_) {
561 bound_fns.Push(base::Bind(
562 &TextRenderer::Pause, base::Unretained(text_renderer_.get())));
565 // Flush.
566 if (audio_renderer_) {
567 bound_fns.Push(base::Bind(
568 &AudioRenderer::Flush, base::Unretained(audio_renderer_.get())));
570 if (video_renderer_) {
571 bound_fns.Push(base::Bind(
572 &VideoRenderer::Flush, base::Unretained(video_renderer_.get())));
574 if (text_renderer_) {
575 bound_fns.Push(base::Bind(
576 &TextRenderer::Flush, base::Unretained(text_renderer_.get())));
579 // Seek demuxer.
580 bound_fns.Push(base::Bind(
581 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
583 // Preroll renderers.
584 if (audio_renderer_) {
585 bound_fns.Push(base::Bind(
586 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
587 seek_timestamp));
590 if (video_renderer_) {
591 bound_fns.Push(base::Bind(
592 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
593 seek_timestamp));
596 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
599 void Pipeline::DoPlay(const PipelineStatusCB& done_cb) {
600 DCHECK(task_runner_->BelongsToCurrentThread());
601 DCHECK(!pending_callbacks_.get());
602 SerialRunner::Queue bound_fns;
604 PlaybackRateChangedTask(GetPlaybackRate());
605 VolumeChangedTask(GetVolume());
607 if (audio_renderer_) {
608 bound_fns.Push(base::Bind(
609 &AudioRenderer::Play, base::Unretained(audio_renderer_.get())));
612 if (video_renderer_) {
613 bound_fns.Push(base::Bind(
614 &VideoRenderer::Play, base::Unretained(video_renderer_.get())));
617 if (text_renderer_) {
618 bound_fns.Push(base::Bind(
619 &TextRenderer::Play, base::Unretained(text_renderer_.get())));
622 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
625 void Pipeline::DoStop(const PipelineStatusCB& done_cb) {
626 DCHECK(task_runner_->BelongsToCurrentThread());
627 DCHECK(!pending_callbacks_.get());
628 SerialRunner::Queue bound_fns;
630 if (demuxer_) {
631 bound_fns.Push(base::Bind(
632 &Demuxer::Stop, base::Unretained(demuxer_)));
635 if (audio_renderer_) {
636 bound_fns.Push(base::Bind(
637 &AudioRenderer::Stop, base::Unretained(audio_renderer_.get())));
640 if (video_renderer_) {
641 bound_fns.Push(base::Bind(
642 &VideoRenderer::Stop, base::Unretained(video_renderer_.get())));
645 if (text_renderer_) {
646 bound_fns.Push(base::Bind(
647 &TextRenderer::Stop, base::Unretained(text_renderer_.get())));
650 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
653 void Pipeline::OnStopCompleted(PipelineStatus status) {
654 DCHECK(task_runner_->BelongsToCurrentThread());
655 DCHECK_EQ(state_, kStopping);
657 base::AutoLock l(lock_);
658 running_ = false;
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;
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();
675 if (!stop_cb_.is_null()) {
676 error_cb_.Reset();
677 base::ResetAndReturn(&stop_cb_).Run();
679 // NOTE: pipeline may be deleted at this point in time as a result of
680 // executing |stop_cb_|.
681 return;
683 if (!error_cb_.is_null()) {
684 DCHECK_NE(status_, PIPELINE_OK);
685 base::ResetAndReturn(&error_cb_).Run(status_);
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;
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;
704 void Pipeline::OnAudioRendererEnded() {
705 // Force post to process ended tasks after current execution frame.
706 task_runner_->PostTask(FROM_HERE, base::Bind(
707 &Pipeline::DoAudioRendererEnded, base::Unretained(this)));
708 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::AUDIO_ENDED));
711 void Pipeline::OnVideoRendererEnded() {
712 // Force post to process ended tasks after current execution frame.
713 task_runner_->PostTask(FROM_HERE, base::Bind(
714 &Pipeline::DoVideoRendererEnded, base::Unretained(this)));
715 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::VIDEO_ENDED));
718 void Pipeline::OnTextRendererEnded() {
719 // Force post to process ended messages after current execution frame.
720 task_runner_->PostTask(FROM_HERE, base::Bind(
721 &Pipeline::DoTextRendererEnded, base::Unretained(this)));
722 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
725 // Called from any thread.
726 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) {
727 base::AutoLock auto_lock(lock_);
728 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded;
729 statistics_.video_bytes_decoded += stats.video_bytes_decoded;
730 statistics_.video_frames_decoded += stats.video_frames_decoded;
731 statistics_.video_frames_dropped += stats.video_frames_dropped;
734 void Pipeline::StartTask(scoped_ptr<FilterCollection> filter_collection,
735 const base::Closure& ended_cb,
736 const PipelineStatusCB& error_cb,
737 const PipelineStatusCB& seek_cb,
738 const BufferingStateCB& buffering_state_cb,
739 const base::Closure& duration_change_cb) {
740 DCHECK(task_runner_->BelongsToCurrentThread());
741 CHECK_EQ(kCreated, state_)
742 << "Media pipeline cannot be started more than once";
744 filter_collection_ = filter_collection.Pass();
745 ended_cb_ = ended_cb;
746 error_cb_ = error_cb;
747 seek_cb_ = seek_cb;
748 buffering_state_cb_ = buffering_state_cb;
749 duration_change_cb_ = duration_change_cb;
751 text_renderer_ = filter_collection_->GetTextRenderer();
753 if (text_renderer_) {
754 text_renderer_->Initialize(
755 base::Bind(&Pipeline::OnTextRendererEnded, base::Unretained(this)));
758 StateTransitionTask(PIPELINE_OK);
761 void Pipeline::StopTask(const base::Closure& stop_cb) {
762 DCHECK(task_runner_->BelongsToCurrentThread());
763 DCHECK(stop_cb_.is_null());
765 if (state_ == kStopped) {
766 stop_cb.Run();
767 return;
770 stop_cb_ = stop_cb;
772 // We may already be stopping due to a runtime error.
773 if (state_ == kStopping)
774 return;
776 SetState(kStopping);
777 pending_callbacks_.reset();
778 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this)));
781 void Pipeline::ErrorChangedTask(PipelineStatus error) {
782 DCHECK(task_runner_->BelongsToCurrentThread());
783 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
785 if (state_ == kStopping || state_ == kStopped)
786 return;
788 SetState(kStopping);
789 pending_callbacks_.reset();
790 status_ = error;
792 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this)));
795 void Pipeline::PlaybackRateChangedTask(float playback_rate) {
796 DCHECK(task_runner_->BelongsToCurrentThread());
798 // Playback rate changes are only carried out while playing.
799 if (state_ != kStarting && state_ != kStarted)
800 return;
803 base::AutoLock auto_lock(lock_);
804 clock_->SetPlaybackRate(playback_rate);
807 if (audio_renderer_)
808 audio_renderer_->SetPlaybackRate(playback_rate_);
809 if (video_renderer_)
810 video_renderer_->SetPlaybackRate(playback_rate_);
813 void Pipeline::VolumeChangedTask(float volume) {
814 DCHECK(task_runner_->BelongsToCurrentThread());
816 // Volume changes are only carried out while playing.
817 if (state_ != kStarting && state_ != kStarted)
818 return;
820 if (audio_renderer_)
821 audio_renderer_->SetVolume(volume);
824 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
825 DCHECK(task_runner_->BelongsToCurrentThread());
826 DCHECK(stop_cb_.is_null());
828 // Suppress seeking if we're not fully started.
829 if (state_ != kStarted) {
830 DCHECK(state_ == kStopping || state_ == kStopped)
831 << "Receive extra seek in unexpected state: " << state_;
833 // TODO(scherkus): should we run the callback? I'm tempted to say the API
834 // will only execute the first Seek() request.
835 DVLOG(1) << "Media pipeline has not started, ignoring seek to "
836 << time.InMicroseconds() << " (current state: " << state_ << ")";
837 return;
840 DCHECK(seek_cb_.is_null());
842 SetState(kSeeking);
843 base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime());
844 seek_cb_ = seek_cb;
845 audio_ended_ = false;
846 video_ended_ = false;
847 text_ended_ = false;
849 // Kick off seeking!
851 base::AutoLock auto_lock(lock_);
852 if (clock_->IsPlaying())
853 clock_->Pause();
854 clock_->SetTime(seek_timestamp, seek_timestamp);
856 DoSeek(seek_timestamp, base::Bind(
857 &Pipeline::OnStateTransition, base::Unretained(this)));
860 void Pipeline::DoAudioRendererEnded() {
861 DCHECK(task_runner_->BelongsToCurrentThread());
863 if (state_ != kStarted)
864 return;
866 DCHECK(!audio_ended_);
867 audio_ended_ = true;
869 // Start clock since there is no more audio to trigger clock updates.
870 if (!audio_disabled_) {
871 base::AutoLock auto_lock(lock_);
872 clock_->SetMaxTime(clock_->Duration());
873 StartClockIfWaitingForTimeUpdate_Locked();
876 RunEndedCallbackIfNeeded();
879 void Pipeline::DoVideoRendererEnded() {
880 DCHECK(task_runner_->BelongsToCurrentThread());
882 if (state_ != kStarted)
883 return;
885 DCHECK(!video_ended_);
886 video_ended_ = true;
888 RunEndedCallbackIfNeeded();
891 void Pipeline::DoTextRendererEnded() {
892 DCHECK(task_runner_->BelongsToCurrentThread());
894 if (state_ != kStarted)
895 return;
897 DCHECK(!text_ended_);
898 text_ended_ = true;
900 RunEndedCallbackIfNeeded();
903 void Pipeline::RunEndedCallbackIfNeeded() {
904 DCHECK(task_runner_->BelongsToCurrentThread());
906 if (audio_renderer_ && !audio_ended_ && !audio_disabled_)
907 return;
909 if (video_renderer_ && !video_ended_)
910 return;
912 if (text_renderer_ && text_renderer_->HasTracks() && !text_ended_)
913 return;
916 base::AutoLock auto_lock(lock_);
917 clock_->EndOfStream();
920 DCHECK_EQ(status_, PIPELINE_OK);
921 ended_cb_.Run();
924 void Pipeline::AudioDisabledTask() {
925 DCHECK(task_runner_->BelongsToCurrentThread());
927 base::AutoLock auto_lock(lock_);
928 has_audio_ = false;
929 audio_disabled_ = true;
931 // Notify our demuxer that we're no longer rendering audio.
932 demuxer_->OnAudioRendererDisabled();
934 // Start clock since there is no more audio to trigger clock updates.
935 clock_->SetMaxTime(clock_->Duration());
936 StartClockIfWaitingForTimeUpdate_Locked();
939 void Pipeline::AddTextStreamTask(DemuxerStream* text_stream,
940 const TextTrackConfig& config) {
941 DCHECK(task_runner_->BelongsToCurrentThread());
942 // TODO(matthewjheaney): fix up text_ended_ when text stream
943 // is added (http://crbug.com/321446).
944 text_renderer_->AddTextStream(text_stream, config);
947 void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) {
948 DCHECK(task_runner_->BelongsToCurrentThread());
949 text_renderer_->RemoveTextStream(text_stream);
952 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) {
953 DCHECK(task_runner_->BelongsToCurrentThread());
955 demuxer_ = filter_collection_->GetDemuxer();
956 demuxer_->Initialize(this, done_cb, text_renderer_);
959 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) {
960 DCHECK(task_runner_->BelongsToCurrentThread());
962 audio_renderer_ = filter_collection_->GetAudioRenderer();
963 audio_renderer_->Initialize(
964 demuxer_->GetStream(DemuxerStream::AUDIO),
965 done_cb,
966 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)),
967 base::Bind(&Pipeline::OnAudioUnderflow, base::Unretained(this)),
968 base::Bind(&Pipeline::OnAudioTimeUpdate, base::Unretained(this)),
969 base::Bind(&Pipeline::OnAudioRendererEnded, base::Unretained(this)),
970 base::Bind(&Pipeline::OnAudioDisabled, base::Unretained(this)),
971 base::Bind(&Pipeline::SetError, base::Unretained(this)));
974 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) {
975 DCHECK(task_runner_->BelongsToCurrentThread());
977 // Get an initial natural size so we have something when we signal
978 // the kHaveMetadata buffering state.
980 // TODO(acolwell): We have to query demuxer outside of the lock to prevent a
981 // deadlock between ChunkDemuxer and Pipeline. See http://crbug.com/334325 for
982 // ideas on removing locking from ChunkDemuxer.
983 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
984 gfx::Size initial_natural_size =
985 stream->video_decoder_config().natural_size();
987 base::AutoLock l(lock_);
988 initial_natural_size_ = initial_natural_size;
991 video_renderer_ = filter_collection_->GetVideoRenderer();
992 video_renderer_->Initialize(
993 stream,
994 done_cb,
995 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)),
996 base::Bind(&Pipeline::OnVideoTimeUpdate, base::Unretained(this)),
997 base::Bind(&Pipeline::OnVideoRendererEnded, base::Unretained(this)),
998 base::Bind(&Pipeline::SetError, base::Unretained(this)),
999 base::Bind(&Pipeline::GetMediaTime, base::Unretained(this)),
1000 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this)));
1003 void Pipeline::OnAudioUnderflow() {
1004 if (!task_runner_->BelongsToCurrentThread()) {
1005 task_runner_->PostTask(FROM_HERE, base::Bind(
1006 &Pipeline::OnAudioUnderflow, base::Unretained(this)));
1007 return;
1010 if (state_ != kStarted)
1011 return;
1013 if (audio_renderer_)
1014 audio_renderer_->ResumeAfterUnderflow();
1017 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() {
1018 lock_.AssertAcquired();
1019 if (!waiting_for_clock_update_)
1020 return;
1022 waiting_for_clock_update_ = false;
1023 clock_->Play();
1026 } // namespace media