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"
10 #include "base/callback.h"
11 #include "base/callback_helpers.h"
12 #include "base/compiler_specific.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/metrics/histogram.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/synchronization/condition_variable.h"
19 #include "media/base/audio_decoder.h"
20 #include "media/base/audio_renderer.h"
21 #include "media/base/clock.h"
22 #include "media/base/filter_collection.h"
23 #include "media/base/media_log.h"
24 #include "media/base/video_decoder.h"
25 #include "media/base/video_decoder_config.h"
26 #include "media/base/video_renderer.h"
28 using base::TimeDelta
;
32 Pipeline::Pipeline(const scoped_refptr
<base::MessageLoopProxy
>& message_loop
,
34 : message_loop_(message_loop
),
35 media_log_(media_log
),
37 did_loading_progress_(false),
42 clock_(new Clock(&default_tick_clock_
)),
43 waiting_for_clock_update_(false),
50 audio_disabled_(false),
52 creation_time_(default_tick_clock_
.NowTicks()) {
53 media_log_
->AddEvent(media_log_
->CreatePipelineStateChangedEvent(kCreated
));
55 media_log_
->CreateEvent(MediaLogEvent::PIPELINE_CREATED
));
58 Pipeline::~Pipeline() {
59 DCHECK(thread_checker_
.CalledOnValidThread())
60 << "Pipeline must be destroyed on same thread that created it";
61 DCHECK(!running_
) << "Stop() must complete before destroying object";
62 DCHECK(stop_cb_
.is_null());
63 DCHECK(seek_cb_
.is_null());
66 media_log_
->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED
));
69 void Pipeline::Start(scoped_ptr
<FilterCollection
> collection
,
70 const base::Closure
& ended_cb
,
71 const PipelineStatusCB
& error_cb
,
72 const PipelineStatusCB
& seek_cb
,
73 const BufferingStateCB
& buffering_state_cb
,
74 const base::Closure
& duration_change_cb
) {
75 base::AutoLock
auto_lock(lock_
);
76 CHECK(!running_
) << "Media pipeline is already running";
77 DCHECK(!buffering_state_cb
.is_null());
80 message_loop_
->PostTask(FROM_HERE
, base::Bind(
81 &Pipeline::StartTask
, base::Unretained(this), base::Passed(&collection
),
82 ended_cb
, error_cb
, seek_cb
, buffering_state_cb
, duration_change_cb
));
85 void Pipeline::Stop(const base::Closure
& stop_cb
) {
86 base::AutoLock
auto_lock(lock_
);
87 message_loop_
->PostTask(FROM_HERE
, base::Bind(
88 &Pipeline::StopTask
, base::Unretained(this), stop_cb
));
91 void Pipeline::Seek(TimeDelta time
, const PipelineStatusCB
& seek_cb
) {
92 base::AutoLock
auto_lock(lock_
);
94 NOTREACHED() << "Media pipeline isn't running";
98 message_loop_
->PostTask(FROM_HERE
, base::Bind(
99 &Pipeline::SeekTask
, base::Unretained(this), time
, seek_cb
));
102 bool Pipeline::IsRunning() const {
103 base::AutoLock
auto_lock(lock_
);
107 bool Pipeline::HasAudio() const {
108 base::AutoLock
auto_lock(lock_
);
112 bool Pipeline::HasVideo() const {
113 base::AutoLock
auto_lock(lock_
);
117 float Pipeline::GetPlaybackRate() const {
118 base::AutoLock
auto_lock(lock_
);
119 return playback_rate_
;
122 void Pipeline::SetPlaybackRate(float playback_rate
) {
123 if (playback_rate
< 0.0f
)
126 base::AutoLock
auto_lock(lock_
);
127 playback_rate_
= playback_rate
;
129 message_loop_
->PostTask(FROM_HERE
, base::Bind(
130 &Pipeline::PlaybackRateChangedTask
, base::Unretained(this),
135 float Pipeline::GetVolume() const {
136 base::AutoLock
auto_lock(lock_
);
140 void Pipeline::SetVolume(float volume
) {
141 if (volume
< 0.0f
|| volume
> 1.0f
)
144 base::AutoLock
auto_lock(lock_
);
147 message_loop_
->PostTask(FROM_HERE
, base::Bind(
148 &Pipeline::VolumeChangedTask
, base::Unretained(this), volume
));
152 TimeDelta
Pipeline::GetMediaTime() const {
153 base::AutoLock
auto_lock(lock_
);
154 return clock_
->Elapsed();
157 Ranges
<TimeDelta
> Pipeline::GetBufferedTimeRanges() {
158 base::AutoLock
auto_lock(lock_
);
159 Ranges
<TimeDelta
> time_ranges
;
160 for (size_t i
= 0; i
< buffered_time_ranges_
.size(); ++i
) {
161 time_ranges
.Add(buffered_time_ranges_
.start(i
),
162 buffered_time_ranges_
.end(i
));
164 if (clock_
->Duration() == TimeDelta() || total_bytes_
== 0)
166 for (size_t i
= 0; i
< buffered_byte_ranges_
.size(); ++i
) {
167 TimeDelta start
= TimeForByteOffset_Locked(buffered_byte_ranges_
.start(i
));
168 TimeDelta end
= TimeForByteOffset_Locked(buffered_byte_ranges_
.end(i
));
169 // Cap approximated buffered time at the length of the video.
170 end
= std::min(end
, clock_
->Duration());
171 time_ranges
.Add(start
, end
);
177 TimeDelta
Pipeline::GetMediaDuration() const {
178 base::AutoLock
auto_lock(lock_
);
179 return clock_
->Duration();
182 int64
Pipeline::GetTotalBytes() const {
183 base::AutoLock
auto_lock(lock_
);
187 void Pipeline::GetNaturalVideoSize(gfx::Size
* out_size
) const {
189 base::AutoLock
auto_lock(lock_
);
190 *out_size
= natural_size_
;
193 bool Pipeline::DidLoadingProgress() const {
194 base::AutoLock
auto_lock(lock_
);
195 bool ret
= did_loading_progress_
;
196 did_loading_progress_
= false;
200 PipelineStatistics
Pipeline::GetStatistics() const {
201 base::AutoLock
auto_lock(lock_
);
205 void Pipeline::SetClockForTesting(Clock
* clock
) {
209 void Pipeline::SetErrorForTesting(PipelineStatus status
) {
213 void Pipeline::SetState(State next_state
) {
214 if (state_
!= kStarted
&& next_state
== kStarted
&&
215 !creation_time_
.is_null()) {
216 UMA_HISTOGRAM_TIMES("Media.TimeToPipelineStarted",
217 default_tick_clock_
.NowTicks() - creation_time_
);
218 creation_time_
= base::TimeTicks();
221 DVLOG(2) << GetStateString(state_
) << " -> " << GetStateString(next_state
);
224 media_log_
->AddEvent(media_log_
->CreatePipelineStateChangedEvent(next_state
));
227 #define RETURN_STRING(state) case state: return #state;
229 const char* Pipeline::GetStateString(State state
) {
231 RETURN_STRING(kCreated
);
232 RETURN_STRING(kInitDemuxer
);
233 RETURN_STRING(kInitAudioRenderer
);
234 RETURN_STRING(kInitVideoRenderer
);
235 RETURN_STRING(kInitPrerolling
);
236 RETURN_STRING(kSeeking
);
237 RETURN_STRING(kStarting
);
238 RETURN_STRING(kStarted
);
239 RETURN_STRING(kStopping
);
240 RETURN_STRING(kStopped
);
248 Pipeline::State
Pipeline::GetNextState() const {
249 DCHECK(message_loop_
->BelongsToCurrentThread());
250 DCHECK(stop_cb_
.is_null())
251 << "State transitions don't happen when stopping";
252 DCHECK_EQ(status_
, PIPELINE_OK
)
253 << "State transitions don't happen when there's an error: " << status_
;
260 if (demuxer_
->GetStream(DemuxerStream::AUDIO
))
261 return kInitAudioRenderer
;
262 if (demuxer_
->GetStream(DemuxerStream::VIDEO
))
263 return kInitVideoRenderer
;
264 return kInitPrerolling
;
266 case kInitAudioRenderer
:
267 if (demuxer_
->GetStream(DemuxerStream::VIDEO
))
268 return kInitVideoRenderer
;
269 return kInitPrerolling
;
271 case kInitVideoRenderer
:
272 return kInitPrerolling
;
274 case kInitPrerolling
:
288 NOTREACHED() << "State has no transition: " << state_
;
292 void Pipeline::OnDemuxerError(PipelineStatus error
) {
296 void Pipeline::SetError(PipelineStatus error
) {
298 DCHECK_NE(PIPELINE_OK
, error
);
299 VLOG(1) << "Media pipeline error: " << error
;
301 message_loop_
->PostTask(FROM_HERE
, base::Bind(
302 &Pipeline::ErrorChangedTask
, base::Unretained(this), error
));
304 media_log_
->AddEvent(media_log_
->CreatePipelineErrorEvent(error
));
307 void Pipeline::OnAudioDisabled() {
309 message_loop_
->PostTask(FROM_HERE
, base::Bind(
310 &Pipeline::AudioDisabledTask
, base::Unretained(this)));
311 media_log_
->AddEvent(
312 media_log_
->CreateEvent(MediaLogEvent::AUDIO_RENDERER_DISABLED
));
315 void Pipeline::OnAudioTimeUpdate(TimeDelta time
, TimeDelta max_time
) {
316 DCHECK_LE(time
.InMicroseconds(), max_time
.InMicroseconds());
318 base::AutoLock
auto_lock(lock_
);
322 if (waiting_for_clock_update_
&& time
< clock_
->Elapsed())
325 // TODO(scherkus): |state_| should only be accessed on pipeline thread, see
326 // http://crbug.com/137973
327 if (state_
== kSeeking
)
330 clock_
->SetTime(time
, max_time
);
331 StartClockIfWaitingForTimeUpdate_Locked();
334 void Pipeline::OnVideoTimeUpdate(TimeDelta max_time
) {
336 base::AutoLock
auto_lock(lock_
);
341 // TODO(scherkus): |state_| should only be accessed on pipeline thread, see
342 // http://crbug.com/137973
343 if (state_
== kSeeking
)
346 DCHECK(!waiting_for_clock_update_
);
347 clock_
->SetMaxTime(max_time
);
350 void Pipeline::SetDuration(TimeDelta duration
) {
352 media_log_
->AddEvent(
353 media_log_
->CreateTimeEvent(
354 MediaLogEvent::DURATION_SET
, "duration", duration
));
355 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration
);
357 base::AutoLock
auto_lock(lock_
);
358 clock_
->SetDuration(duration
);
359 if (!duration_change_cb_
.is_null())
360 duration_change_cb_
.Run();
363 void Pipeline::SetTotalBytes(int64 total_bytes
) {
365 media_log_
->AddEvent(
366 media_log_
->CreateStringEvent(
367 MediaLogEvent::TOTAL_BYTES_SET
, "total_bytes",
368 base::Int64ToString(total_bytes
)));
369 int64 total_mbytes
= total_bytes
>> 20;
370 if (total_mbytes
> kint32max
)
371 total_mbytes
= kint32max
;
372 UMA_HISTOGRAM_CUSTOM_COUNTS(
373 "Media.TotalMBytes", static_cast<int32
>(total_mbytes
), 1, kint32max
, 50);
375 base::AutoLock
auto_lock(lock_
);
376 total_bytes_
= total_bytes
;
379 TimeDelta
Pipeline::TimeForByteOffset_Locked(int64 byte_offset
) const {
380 lock_
.AssertAcquired();
381 TimeDelta time_offset
= byte_offset
* clock_
->Duration() / total_bytes_
;
382 // Since the byte->time calculation is approximate, fudge the beginning &
383 // ending areas to look better.
384 TimeDelta epsilon
= clock_
->Duration() / 100;
385 if (time_offset
< epsilon
)
387 if (time_offset
+ epsilon
> clock_
->Duration())
388 return clock_
->Duration();
392 void Pipeline::OnStateTransition(PipelineStatus status
) {
393 // Force post to process state transitions after current execution frame.
394 message_loop_
->PostTask(FROM_HERE
, base::Bind(
395 &Pipeline::StateTransitionTask
, base::Unretained(this), status
));
398 void Pipeline::StateTransitionTask(PipelineStatus status
) {
399 DCHECK(message_loop_
->BelongsToCurrentThread());
401 // No-op any state transitions if we're stopping.
402 if (state_
== kStopping
|| state_
== kStopped
)
405 // Preserve existing abnormal status, otherwise update based on the result of
406 // the previous operation.
407 status_
= (status_
!= PIPELINE_OK
? status_
: status
);
409 if (status_
!= PIPELINE_OK
) {
410 ErrorChangedTask(status_
);
414 // Guard against accidentally clearing |pending_callbacks_| for states that
415 // use it as well as states that should not be using it.
417 // TODO(scherkus): Make every state transition use |pending_callbacks_|.
418 DCHECK_EQ(pending_callbacks_
.get() != NULL
,
419 (state_
== kInitPrerolling
|| state_
== kStarting
||
420 state_
== kSeeking
));
421 pending_callbacks_
.reset();
423 PipelineStatusCB done_cb
= base::Bind(
424 &Pipeline::OnStateTransition
, base::Unretained(this));
426 // Switch states, performing any entrance actions for the new state as well.
427 SetState(GetNextState());
430 return InitializeDemuxer(done_cb
);
432 case kInitAudioRenderer
:
433 return InitializeAudioRenderer(done_cb
);
435 case kInitVideoRenderer
:
436 return InitializeVideoRenderer(done_cb
);
438 case kInitPrerolling
:
439 filter_collection_
.reset();
441 base::AutoLock
l(lock_
);
442 // We do not want to start the clock running. We only want to set the
443 // base media time so our timestamp calculations will be correct.
444 clock_
->SetTime(demuxer_
->GetStartTime(), demuxer_
->GetStartTime());
446 // TODO(scherkus): |has_audio_| should be true no matter what --
447 // otherwise people with muted/disabled sound cards will make our
448 // default controls look as if every video doesn't contain an audio
450 has_audio_
= audio_renderer_
!= NULL
&& !audio_disabled_
;
451 has_video_
= video_renderer_
!= NULL
;
453 if (!audio_renderer_
&& !video_renderer_
) {
454 done_cb
.Run(PIPELINE_ERROR_COULD_NOT_RENDER
);
458 buffering_state_cb_
.Run(kHaveMetadata
);
460 return DoInitialPreroll(done_cb
);
463 return DoPlay(done_cb
);
467 base::AutoLock
l(lock_
);
468 // We use audio stream to update the clock. So if there is such a
469 // stream, we pause the clock until we receive a valid timestamp.
470 waiting_for_clock_update_
= true;
472 clock_
->SetMaxTime(clock_
->Duration());
473 StartClockIfWaitingForTimeUpdate_Locked();
477 DCHECK(!seek_cb_
.is_null());
478 DCHECK_EQ(status_
, PIPELINE_OK
);
480 // Fire canplaythrough immediately after playback begins because of
482 // TODO(vrk): set ready state to HaveFutureData when bug above is fixed.
483 buffering_state_cb_
.Run(kPrerollCompleted
);
484 return base::ResetAndReturn(&seek_cb_
).Run(PIPELINE_OK
);
490 NOTREACHED() << "State has no transition: " << state_
;
495 // Note that the usage of base::Unretained() with the audio/video renderers
496 // in the following DoXXX() functions is considered safe as they are owned by
497 // |pending_callbacks_| and share the same lifetime.
499 // That being said, deleting the renderers while keeping |pending_callbacks_|
500 // running on the media thread would result in crashes.
501 void Pipeline::DoInitialPreroll(const PipelineStatusCB
& done_cb
) {
502 DCHECK(message_loop_
->BelongsToCurrentThread());
503 DCHECK(!pending_callbacks_
.get());
504 SerialRunner::Queue bound_fns
;
506 base::TimeDelta seek_timestamp
= demuxer_
->GetStartTime();
508 // Preroll renderers.
509 if (audio_renderer_
) {
510 bound_fns
.Push(base::Bind(
511 &AudioRenderer::Preroll
, base::Unretained(audio_renderer_
.get()),
515 if (video_renderer_
) {
516 bound_fns
.Push(base::Bind(
517 &VideoRenderer::Preroll
, base::Unretained(video_renderer_
.get()),
521 pending_callbacks_
= SerialRunner::Run(bound_fns
, done_cb
);
524 void Pipeline::DoSeek(
525 base::TimeDelta seek_timestamp
,
526 const PipelineStatusCB
& done_cb
) {
527 DCHECK(message_loop_
->BelongsToCurrentThread());
528 DCHECK(!pending_callbacks_
.get());
529 SerialRunner::Queue bound_fns
;
532 if (audio_renderer_
) {
533 bound_fns
.Push(base::Bind(
534 &AudioRenderer::Pause
, base::Unretained(audio_renderer_
.get())));
536 if (video_renderer_
) {
537 bound_fns
.Push(base::Bind(
538 &VideoRenderer::Pause
, base::Unretained(video_renderer_
.get())));
542 if (audio_renderer_
) {
543 bound_fns
.Push(base::Bind(
544 &AudioRenderer::Flush
, base::Unretained(audio_renderer_
.get())));
546 if (video_renderer_
) {
547 bound_fns
.Push(base::Bind(
548 &VideoRenderer::Flush
, base::Unretained(video_renderer_
.get())));
552 bound_fns
.Push(base::Bind(
553 &Demuxer::Seek
, base::Unretained(demuxer_
), seek_timestamp
));
555 // Preroll renderers.
556 if (audio_renderer_
) {
557 bound_fns
.Push(base::Bind(
558 &AudioRenderer::Preroll
, base::Unretained(audio_renderer_
.get()),
562 if (video_renderer_
) {
563 bound_fns
.Push(base::Bind(
564 &VideoRenderer::Preroll
, base::Unretained(video_renderer_
.get()),
568 pending_callbacks_
= SerialRunner::Run(bound_fns
, done_cb
);
571 void Pipeline::DoPlay(const PipelineStatusCB
& done_cb
) {
572 DCHECK(message_loop_
->BelongsToCurrentThread());
573 DCHECK(!pending_callbacks_
.get());
574 SerialRunner::Queue bound_fns
;
576 PlaybackRateChangedTask(GetPlaybackRate());
577 VolumeChangedTask(GetVolume());
579 if (audio_renderer_
) {
580 bound_fns
.Push(base::Bind(
581 &AudioRenderer::Play
, base::Unretained(audio_renderer_
.get())));
584 if (video_renderer_
) {
585 bound_fns
.Push(base::Bind(
586 &VideoRenderer::Play
, base::Unretained(video_renderer_
.get())));
589 pending_callbacks_
= SerialRunner::Run(bound_fns
, done_cb
);
592 void Pipeline::DoStop(const PipelineStatusCB
& done_cb
) {
593 DCHECK(message_loop_
->BelongsToCurrentThread());
594 DCHECK(!pending_callbacks_
.get());
595 SerialRunner::Queue bound_fns
;
598 bound_fns
.Push(base::Bind(
599 &Demuxer::Stop
, base::Unretained(demuxer_
)));
602 if (audio_renderer_
) {
603 bound_fns
.Push(base::Bind(
604 &AudioRenderer::Stop
, base::Unretained(audio_renderer_
.get())));
607 if (video_renderer_
) {
608 bound_fns
.Push(base::Bind(
609 &VideoRenderer::Stop
, base::Unretained(video_renderer_
.get())));
612 pending_callbacks_
= SerialRunner::Run(bound_fns
, done_cb
);
615 void Pipeline::OnStopCompleted(PipelineStatus status
) {
616 DCHECK(message_loop_
->BelongsToCurrentThread());
617 DCHECK_EQ(state_
, kStopping
);
619 base::AutoLock
l(lock_
);
624 pending_callbacks_
.reset();
625 filter_collection_
.reset();
626 audio_renderer_
.reset();
627 video_renderer_
.reset();
630 // If we stop during initialization/seeking we want to run |seek_cb_|
631 // followed by |stop_cb_| so we don't leave outstanding callbacks around.
632 if (!seek_cb_
.is_null()) {
633 base::ResetAndReturn(&seek_cb_
).Run(status_
);
636 if (!stop_cb_
.is_null()) {
638 base::ResetAndReturn(&stop_cb_
).Run();
640 // NOTE: pipeline may be deleted at this point in time as a result of
641 // executing |stop_cb_|.
644 if (!error_cb_
.is_null()) {
645 DCHECK_NE(status_
, PIPELINE_OK
);
646 base::ResetAndReturn(&error_cb_
).Run(status_
);
650 void Pipeline::AddBufferedByteRange(int64 start
, int64 end
) {
652 base::AutoLock
auto_lock(lock_
);
653 buffered_byte_ranges_
.Add(start
, end
);
654 did_loading_progress_
= true;
657 void Pipeline::AddBufferedTimeRange(base::TimeDelta start
,
658 base::TimeDelta end
) {
660 base::AutoLock
auto_lock(lock_
);
661 buffered_time_ranges_
.Add(start
, end
);
662 did_loading_progress_
= true;
665 void Pipeline::OnNaturalVideoSizeChanged(const gfx::Size
& size
) {
667 media_log_
->AddEvent(media_log_
->CreateVideoSizeSetEvent(
668 size
.width(), size
.height()));
670 base::AutoLock
auto_lock(lock_
);
671 natural_size_
= size
;
674 void Pipeline::OnAudioRendererEnded() {
675 // Force post to process ended messages after current execution frame.
676 message_loop_
->PostTask(FROM_HERE
, base::Bind(
677 &Pipeline::DoAudioRendererEnded
, base::Unretained(this)));
678 media_log_
->AddEvent(media_log_
->CreateEvent(MediaLogEvent::AUDIO_ENDED
));
681 void Pipeline::OnVideoRendererEnded() {
682 // Force post to process ended messages after current execution frame.
683 message_loop_
->PostTask(FROM_HERE
, base::Bind(
684 &Pipeline::DoVideoRendererEnded
, base::Unretained(this)));
685 media_log_
->AddEvent(media_log_
->CreateEvent(MediaLogEvent::VIDEO_ENDED
));
688 // Called from any thread.
689 void Pipeline::OnUpdateStatistics(const PipelineStatistics
& stats
) {
690 base::AutoLock
auto_lock(lock_
);
691 statistics_
.audio_bytes_decoded
+= stats
.audio_bytes_decoded
;
692 statistics_
.video_bytes_decoded
+= stats
.video_bytes_decoded
;
693 statistics_
.video_frames_decoded
+= stats
.video_frames_decoded
;
694 statistics_
.video_frames_dropped
+= stats
.video_frames_dropped
;
697 void Pipeline::StartTask(scoped_ptr
<FilterCollection
> filter_collection
,
698 const base::Closure
& ended_cb
,
699 const PipelineStatusCB
& error_cb
,
700 const PipelineStatusCB
& seek_cb
,
701 const BufferingStateCB
& buffering_state_cb
,
702 const base::Closure
& duration_change_cb
) {
703 DCHECK(message_loop_
->BelongsToCurrentThread());
704 CHECK_EQ(kCreated
, state_
)
705 << "Media pipeline cannot be started more than once";
707 filter_collection_
= filter_collection
.Pass();
708 ended_cb_
= ended_cb
;
709 error_cb_
= error_cb
;
711 buffering_state_cb_
= buffering_state_cb
;
712 duration_change_cb_
= duration_change_cb
;
714 StateTransitionTask(PIPELINE_OK
);
717 void Pipeline::StopTask(const base::Closure
& stop_cb
) {
718 DCHECK(message_loop_
->BelongsToCurrentThread());
719 DCHECK(stop_cb_
.is_null());
721 if (state_
== kStopped
) {
728 // We may already be stopping due to a runtime error.
729 if (state_
== kStopping
)
733 pending_callbacks_
.reset();
734 DoStop(base::Bind(&Pipeline::OnStopCompleted
, base::Unretained(this)));
737 void Pipeline::ErrorChangedTask(PipelineStatus error
) {
738 DCHECK(message_loop_
->BelongsToCurrentThread());
739 DCHECK_NE(PIPELINE_OK
, error
) << "PIPELINE_OK isn't an error!";
741 if (state_
== kStopping
|| state_
== kStopped
)
745 pending_callbacks_
.reset();
748 DoStop(base::Bind(&Pipeline::OnStopCompleted
, base::Unretained(this)));
751 void Pipeline::PlaybackRateChangedTask(float playback_rate
) {
752 DCHECK(message_loop_
->BelongsToCurrentThread());
754 // Playback rate changes are only carried out while playing.
755 if (state_
!= kStarting
&& state_
!= kStarted
)
759 base::AutoLock
auto_lock(lock_
);
760 clock_
->SetPlaybackRate(playback_rate
);
764 audio_renderer_
->SetPlaybackRate(playback_rate_
);
766 video_renderer_
->SetPlaybackRate(playback_rate_
);
769 void Pipeline::VolumeChangedTask(float volume
) {
770 DCHECK(message_loop_
->BelongsToCurrentThread());
772 // Volume changes are only carried out while playing.
773 if (state_
!= kStarting
&& state_
!= kStarted
)
777 audio_renderer_
->SetVolume(volume
);
780 void Pipeline::SeekTask(TimeDelta time
, const PipelineStatusCB
& seek_cb
) {
781 DCHECK(message_loop_
->BelongsToCurrentThread());
782 DCHECK(stop_cb_
.is_null());
784 // Suppress seeking if we're not fully started.
785 if (state_
!= kStarted
) {
786 DCHECK(state_
== kStopping
|| state_
== kStopped
)
787 << "Receive extra seek in unexpected state: " << state_
;
789 // TODO(scherkus): should we run the callback? I'm tempted to say the API
790 // will only execute the first Seek() request.
791 DVLOG(1) << "Media pipeline has not started, ignoring seek to "
792 << time
.InMicroseconds() << " (current state: " << state_
<< ")";
796 DCHECK(seek_cb_
.is_null());
799 base::TimeDelta seek_timestamp
= std::max(time
, demuxer_
->GetStartTime());
801 audio_ended_
= false;
802 video_ended_
= false;
806 base::AutoLock
auto_lock(lock_
);
807 if (clock_
->IsPlaying())
809 clock_
->SetTime(seek_timestamp
, seek_timestamp
);
811 DoSeek(seek_timestamp
, base::Bind(
812 &Pipeline::OnStateTransition
, base::Unretained(this)));
815 void Pipeline::DoAudioRendererEnded() {
816 DCHECK(message_loop_
->BelongsToCurrentThread());
818 if (state_
!= kStarted
)
821 DCHECK(!audio_ended_
);
824 // Start clock since there is no more audio to trigger clock updates.
825 if (!audio_disabled_
) {
826 base::AutoLock
auto_lock(lock_
);
827 clock_
->SetMaxTime(clock_
->Duration());
828 StartClockIfWaitingForTimeUpdate_Locked();
831 RunEndedCallbackIfNeeded();
834 void Pipeline::DoVideoRendererEnded() {
835 DCHECK(message_loop_
->BelongsToCurrentThread());
837 if (state_
!= kStarted
)
840 DCHECK(!video_ended_
);
843 RunEndedCallbackIfNeeded();
846 void Pipeline::RunEndedCallbackIfNeeded() {
847 DCHECK(message_loop_
->BelongsToCurrentThread());
849 if (audio_renderer_
&& !audio_ended_
&& !audio_disabled_
)
852 if (video_renderer_
&& !video_ended_
)
856 base::AutoLock
auto_lock(lock_
);
857 clock_
->EndOfStream();
860 DCHECK_EQ(status_
, PIPELINE_OK
);
864 void Pipeline::AudioDisabledTask() {
865 DCHECK(message_loop_
->BelongsToCurrentThread());
867 base::AutoLock
auto_lock(lock_
);
869 audio_disabled_
= true;
871 // Notify our demuxer that we're no longer rendering audio.
872 demuxer_
->OnAudioRendererDisabled();
874 // Start clock since there is no more audio to trigger clock updates.
875 clock_
->SetMaxTime(clock_
->Duration());
876 StartClockIfWaitingForTimeUpdate_Locked();
879 void Pipeline::InitializeDemuxer(const PipelineStatusCB
& done_cb
) {
880 DCHECK(message_loop_
->BelongsToCurrentThread());
882 demuxer_
= filter_collection_
->GetDemuxer();
883 demuxer_
->Initialize(this, done_cb
);
886 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB
& done_cb
) {
887 DCHECK(message_loop_
->BelongsToCurrentThread());
889 audio_renderer_
= filter_collection_
->GetAudioRenderer();
890 audio_renderer_
->Initialize(
891 demuxer_
->GetStream(DemuxerStream::AUDIO
),
893 base::Bind(&Pipeline::OnUpdateStatistics
, base::Unretained(this)),
894 base::Bind(&Pipeline::OnAudioUnderflow
, base::Unretained(this)),
895 base::Bind(&Pipeline::OnAudioTimeUpdate
, base::Unretained(this)),
896 base::Bind(&Pipeline::OnAudioRendererEnded
, base::Unretained(this)),
897 base::Bind(&Pipeline::OnAudioDisabled
, base::Unretained(this)),
898 base::Bind(&Pipeline::SetError
, base::Unretained(this)));
901 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB
& done_cb
) {
902 DCHECK(message_loop_
->BelongsToCurrentThread());
904 DemuxerStream
* stream
= demuxer_
->GetStream(DemuxerStream::VIDEO
);
907 // Get an initial natural size so we have something when we signal
908 // the kHaveMetadata buffering state.
909 base::AutoLock
l(lock_
);
910 natural_size_
= stream
->video_decoder_config().natural_size();
913 video_renderer_
= filter_collection_
->GetVideoRenderer();
914 video_renderer_
->Initialize(
917 base::Bind(&Pipeline::OnUpdateStatistics
, base::Unretained(this)),
918 base::Bind(&Pipeline::OnVideoTimeUpdate
, base::Unretained(this)),
919 base::Bind(&Pipeline::OnNaturalVideoSizeChanged
, base::Unretained(this)),
920 base::Bind(&Pipeline::OnVideoRendererEnded
, base::Unretained(this)),
921 base::Bind(&Pipeline::SetError
, base::Unretained(this)),
922 base::Bind(&Pipeline::GetMediaTime
, base::Unretained(this)),
923 base::Bind(&Pipeline::GetMediaDuration
, base::Unretained(this)));
926 void Pipeline::OnAudioUnderflow() {
927 if (!message_loop_
->BelongsToCurrentThread()) {
928 message_loop_
->PostTask(FROM_HERE
, base::Bind(
929 &Pipeline::OnAudioUnderflow
, base::Unretained(this)));
933 if (state_
!= kStarted
)
937 audio_renderer_
->ResumeAfterUnderflow();
940 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() {
941 lock_
.AssertAcquired();
942 if (!waiting_for_clock_update_
)
945 waiting_for_clock_update_
= false;