1 // Copyright 2014 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 "chromecast/media/cma/pipeline/media_pipeline_impl.h"
8 #include "base/callback.h"
9 #include "base/callback_helpers.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop_proxy.h"
13 #include "base/time/time.h"
14 #include "chromecast/media/cdm/browser_cdm_cast.h"
15 #include "chromecast/media/cma/backend/media_clock_device.h"
16 #include "chromecast/media/cma/backend/media_pipeline_device.h"
17 #include "chromecast/media/cma/base/buffering_controller.h"
18 #include "chromecast/media/cma/base/buffering_state.h"
19 #include "chromecast/media/cma/base/cma_logging.h"
20 #include "chromecast/media/cma/base/coded_frame_provider.h"
21 #include "chromecast/media/cma/pipeline/audio_pipeline_impl.h"
22 #include "chromecast/media/cma/pipeline/video_pipeline_impl.h"
23 #include "media/base/buffers.h"
25 namespace chromecast
{
30 // Buffering parameters when load_type is kLoadTypeUrl.
31 const base::TimeDelta
kLowBufferThresholdURL(
32 base::TimeDelta::FromMilliseconds(2000));
33 const base::TimeDelta
kHighBufferThresholdURL(
34 base::TimeDelta::FromMilliseconds(6000));
36 // Buffering parameters when load_type is kLoadTypeMediaSource.
37 const base::TimeDelta
kLowBufferThresholdMediaSource(
38 base::TimeDelta::FromMilliseconds(0));
39 const base::TimeDelta
kHighBufferThresholdMediaSource(
40 base::TimeDelta::FromMilliseconds(300));
42 // Interval between two updates of the media time.
43 const base::TimeDelta
kTimeUpdateInterval(
44 base::TimeDelta::FromMilliseconds(250));
46 // Interval between two updates of the statistics is equal to:
47 // kTimeUpdateInterval * kStatisticsUpdatePeriod.
48 const int kStatisticsUpdatePeriod
= 4;
52 MediaPipelineImpl::MediaPipelineImpl()
55 target_playback_rate_(0.0),
56 enable_time_update_(false),
57 pending_time_update_task_(false),
58 statistics_rolling_counter_(0),
60 CMALOG(kLogControl
) << __FUNCTION__
;
61 weak_this_
= weak_factory_
.GetWeakPtr();
62 thread_checker_
.DetachFromThread();
65 MediaPipelineImpl::~MediaPipelineImpl() {
66 CMALOG(kLogControl
) << __FUNCTION__
;
67 DCHECK(thread_checker_
.CalledOnValidThread());
70 void MediaPipelineImpl::Initialize(
72 scoped_ptr
<MediaPipelineDevice
> media_pipeline_device
) {
73 CMALOG(kLogControl
) << __FUNCTION__
;
74 DCHECK(thread_checker_
.CalledOnValidThread());
75 media_pipeline_device_
.reset(media_pipeline_device
.release());
76 clock_device_
= media_pipeline_device_
->GetMediaClockDevice();
78 if (load_type
== kLoadTypeURL
|| load_type
== kLoadTypeMediaSource
) {
79 base::TimeDelta
low_threshold(kLowBufferThresholdURL
);
80 base::TimeDelta
high_threshold(kHighBufferThresholdURL
);
81 if (load_type
== kLoadTypeMediaSource
) {
82 low_threshold
= kLowBufferThresholdMediaSource
;
83 high_threshold
= kHighBufferThresholdMediaSource
;
85 scoped_refptr
<BufferingConfig
> buffering_config(
86 new BufferingConfig(low_threshold
, high_threshold
));
87 buffering_controller_
.reset(new BufferingController(
89 base::Bind(&MediaPipelineImpl::OnBufferingNotification
, weak_this_
)));
92 audio_pipeline_
.reset(new AudioPipelineImpl(
93 media_pipeline_device_
->GetAudioPipelineDevice()));
95 video_pipeline_
.reset(new VideoPipelineImpl(
96 media_pipeline_device_
->GetVideoPipelineDevice()));
99 void MediaPipelineImpl::SetClient(const MediaPipelineClient
& client
) {
100 DCHECK(thread_checker_
.CalledOnValidThread());
101 DCHECK(!client
.error_cb
.is_null());
102 DCHECK(!client
.time_update_cb
.is_null());
103 DCHECK(!client
.buffering_state_cb
.is_null());
107 void MediaPipelineImpl::SetCdm(int cdm_id
) {
108 CMALOG(kLogControl
) << __FUNCTION__
<< " cdm_id=" << cdm_id
;
109 DCHECK(thread_checker_
.CalledOnValidThread());
111 // TODO(gunsch): SetCdm(int) is not implemented.
112 // One possibility would be a GetCdmByIdCB that's passed in.
115 void MediaPipelineImpl::SetCdm(::media::BrowserCdm
* media_keys
) {
116 CMALOG(kLogControl
) << __FUNCTION__
;
117 DCHECK(thread_checker_
.CalledOnValidThread());
118 audio_pipeline_
->SetCdm(static_cast<BrowserCdmCast
*>(media_keys
));
119 video_pipeline_
->SetCdm(static_cast<BrowserCdmCast
*>(media_keys
));
122 AudioPipeline
* MediaPipelineImpl::GetAudioPipeline() const {
123 return audio_pipeline_
.get();
126 VideoPipeline
* MediaPipelineImpl::GetVideoPipeline() const {
127 return video_pipeline_
.get();
130 void MediaPipelineImpl::InitializeAudio(
131 const ::media::AudioDecoderConfig
& config
,
132 scoped_ptr
<CodedFrameProvider
> frame_provider
,
133 const ::media::PipelineStatusCB
& status_cb
) {
134 DCHECK(thread_checker_
.CalledOnValidThread());
136 if (clock_device_
->GetState() == MediaClockDevice::kStateUninitialized
&&
137 !clock_device_
->SetState(MediaClockDevice::kStateIdle
)) {
138 status_cb
.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED
);
142 audio_pipeline_
->Initialize(config
, frame_provider
.Pass(), status_cb
);
145 void MediaPipelineImpl::InitializeVideo(
146 const ::media::VideoDecoderConfig
& config
,
147 scoped_ptr
<CodedFrameProvider
> frame_provider
,
148 const ::media::PipelineStatusCB
& status_cb
) {
149 DCHECK(thread_checker_
.CalledOnValidThread());
151 if (clock_device_
->GetState() == MediaClockDevice::kStateUninitialized
&&
152 !clock_device_
->SetState(MediaClockDevice::kStateIdle
)) {
153 status_cb
.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED
);
157 video_pipeline_
->Initialize(config
, frame_provider
.Pass(), status_cb
);
160 void MediaPipelineImpl::StartPlayingFrom(base::TimeDelta time
) {
161 CMALOG(kLogControl
) << __FUNCTION__
<< " t0=" << time
.InMilliseconds();
162 DCHECK(thread_checker_
.CalledOnValidThread());
163 DCHECK(has_audio_
|| has_video_
);
164 DCHECK(!pending_callbacks_
);
166 // Reset the start of the timeline.
167 DCHECK_EQ(clock_device_
->GetState(), MediaClockDevice::kStateIdle
);
168 clock_device_
->ResetTimeline(time
);
170 // Start the clock. If the playback rate is 0, then the clock is started
171 // but does not increase.
172 if (!clock_device_
->SetState(MediaClockDevice::kStateRunning
)) {
173 OnError(::media::PIPELINE_ERROR_ABORT
);
177 // Enable time updates.
178 enable_time_update_
= true;
179 statistics_rolling_counter_
= 0;
180 if (!pending_time_update_task_
) {
181 pending_time_update_task_
= true;
182 base::MessageLoopProxy::current()->PostTask(
184 base::Bind(&MediaPipelineImpl::UpdateMediaTime
, weak_this_
));
187 // Setup the audio and video pipeline for the new timeline.
189 scoped_refptr
<BufferingState
> buffering_state
;
190 if (buffering_controller_
)
191 buffering_state
= buffering_controller_
->AddStream("audio");
192 if (!audio_pipeline_
->StartPlayingFrom(time
, buffering_state
)) {
193 OnError(::media::PIPELINE_ERROR_ABORT
);
198 scoped_refptr
<BufferingState
> buffering_state
;
199 if (buffering_controller_
)
200 buffering_state
= buffering_controller_
->AddStream("video");
201 if (!video_pipeline_
->StartPlayingFrom(time
, buffering_state
)) {
202 OnError(::media::PIPELINE_ERROR_ABORT
);
208 void MediaPipelineImpl::Flush(const ::media::PipelineStatusCB
& status_cb
) {
209 CMALOG(kLogControl
) << __FUNCTION__
;
210 DCHECK(thread_checker_
.CalledOnValidThread());
211 DCHECK(has_audio_
|| has_video_
);
212 DCHECK(!pending_callbacks_
);
214 // No need to update media time anymore.
215 enable_time_update_
= false;
217 buffering_controller_
->Reset();
219 // The clock should return to idle.
220 if (!clock_device_
->SetState(MediaClockDevice::kStateIdle
)) {
221 status_cb
.Run(::media::PIPELINE_ERROR_ABORT
);
225 // Flush both the audio and video pipeline.
226 ::media::SerialRunner::Queue bound_fns
;
228 bound_fns
.Push(base::Bind(
229 &AudioPipelineImpl::Flush
,
230 base::Unretained(audio_pipeline_
.get())));
233 bound_fns
.Push(base::Bind(
234 &VideoPipelineImpl::Flush
,
235 base::Unretained(video_pipeline_
.get())));
237 ::media::PipelineStatusCB transition_cb
=
238 base::Bind(&MediaPipelineImpl::StateTransition
, weak_this_
, status_cb
);
240 ::media::SerialRunner::Run(bound_fns
, transition_cb
);
243 void MediaPipelineImpl::Stop() {
244 CMALOG(kLogControl
) << __FUNCTION__
;
245 DCHECK(thread_checker_
.CalledOnValidThread());
246 DCHECK(has_audio_
|| has_video_
);
247 DCHECK(!pending_callbacks_
);
249 // No need to update media time anymore.
250 enable_time_update_
= false;
252 // Release hardware resources on Stop.
253 // Note: Stop can be called from any state.
254 if (clock_device_
->GetState() == MediaClockDevice::kStateRunning
)
255 clock_device_
->SetState(MediaClockDevice::kStateIdle
);
256 if (clock_device_
->GetState() == MediaClockDevice::kStateIdle
)
257 clock_device_
->SetState(MediaClockDevice::kStateUninitialized
);
259 // Stop both the audio and video pipeline.
261 audio_pipeline_
->Stop();
263 video_pipeline_
->Stop();
266 void MediaPipelineImpl::SetPlaybackRate(float rate
) {
267 CMALOG(kLogControl
) << __FUNCTION__
<< " rate=" << rate
;
268 DCHECK(thread_checker_
.CalledOnValidThread());
269 target_playback_rate_
= rate
;
270 if (!buffering_controller_
|| !buffering_controller_
->IsBuffering())
271 media_pipeline_device_
->GetMediaClockDevice()->SetRate(rate
);
274 AudioPipelineImpl
* MediaPipelineImpl::GetAudioPipelineImpl() const {
275 return audio_pipeline_
.get();
278 VideoPipelineImpl
* MediaPipelineImpl::GetVideoPipelineImpl() const {
279 return video_pipeline_
.get();
282 void MediaPipelineImpl::StateTransition(
283 const ::media::PipelineStatusCB
& status_cb
,
284 ::media::PipelineStatus status
) {
285 pending_callbacks_
.reset();
286 status_cb
.Run(status
);
289 void MediaPipelineImpl::OnBufferingNotification(bool is_buffering
) {
290 CMALOG(kLogControl
) << __FUNCTION__
<< " is_buffering=" << is_buffering
;
291 DCHECK(thread_checker_
.CalledOnValidThread());
292 DCHECK(buffering_controller_
);
294 if (!client_
.buffering_state_cb
.is_null()) {
295 ::media::BufferingState buffering_state
= is_buffering
?
296 ::media::BUFFERING_HAVE_NOTHING
: ::media::BUFFERING_HAVE_ENOUGH
;
297 client_
.buffering_state_cb
.Run(buffering_state
);
300 if (media_pipeline_device_
->GetMediaClockDevice()->GetState() ==
301 MediaClockDevice::kStateUninitialized
) {
306 // Do not consume data in a rebuffering phase.
307 media_pipeline_device_
->GetMediaClockDevice()->SetRate(0.0);
309 media_pipeline_device_
->GetMediaClockDevice()->SetRate(
310 target_playback_rate_
);
314 void MediaPipelineImpl::UpdateMediaTime() {
315 pending_time_update_task_
= false;
316 if (!enable_time_update_
)
319 if (statistics_rolling_counter_
== 0) {
320 audio_pipeline_
->UpdateStatistics();
321 video_pipeline_
->UpdateStatistics();
323 statistics_rolling_counter_
=
324 (statistics_rolling_counter_
+ 1) % kStatisticsUpdatePeriod
;
326 base::TimeDelta
media_time(clock_device_
->GetTime());
327 if (media_time
== ::media::kNoTimestamp()) {
328 pending_time_update_task_
= true;
329 base::MessageLoopProxy::current()->PostDelayedTask(
331 base::Bind(&MediaPipelineImpl::UpdateMediaTime
, weak_this_
),
332 kTimeUpdateInterval
);
335 base::TimeTicks stc
= base::TimeTicks::Now();
337 base::TimeDelta max_rendering_time
= media_time
;
338 if (buffering_controller_
) {
339 buffering_controller_
->SetMediaTime(media_time
);
341 // Receiving the same time twice in a row means playback isn't moving,
342 // so don't interpolate ahead.
343 if (media_time
!= last_media_time_
) {
344 max_rendering_time
= buffering_controller_
->GetMaxRenderingTime();
345 if (max_rendering_time
== ::media::kNoTimestamp())
346 max_rendering_time
= media_time
;
348 // Cap interpolation time to avoid interpolating too far ahead.
350 std::min(max_rendering_time
, media_time
+ 2 * kTimeUpdateInterval
);
354 last_media_time_
= media_time
;
355 if (!client_
.time_update_cb
.is_null())
356 client_
.time_update_cb
.Run(media_time
, max_rendering_time
, stc
);
358 pending_time_update_task_
= true;
359 base::MessageLoopProxy::current()->PostDelayedTask(
361 base::Bind(&MediaPipelineImpl::UpdateMediaTime
, weak_this_
),
362 kTimeUpdateInterval
);
365 void MediaPipelineImpl::OnError(::media::PipelineStatus error
) {
366 DCHECK(thread_checker_
.CalledOnValidThread());
367 DCHECK_NE(error
, ::media::PIPELINE_OK
) << "PIPELINE_OK is not an error!";
368 if (!client_
.error_cb
.is_null())
369 client_
.error_cb
.Run(error
);
373 } // namespace chromecast