Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / chromecast / media / cma / pipeline / media_pipeline_impl.cc
blob1b39870ea365e626e041e9db3359e31ebf414bc8
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"
7 #include "base/bind.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/single_thread_task_runner.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/time/time.h"
15 #include "chromecast/media/cdm/browser_cdm_cast.h"
16 #include "chromecast/media/cma/base/buffering_controller.h"
17 #include "chromecast/media/cma/base/buffering_state.h"
18 #include "chromecast/media/cma/base/cma_logging.h"
19 #include "chromecast/media/cma/base/coded_frame_provider.h"
20 #include "chromecast/media/cma/pipeline/audio_pipeline_impl.h"
21 #include "chromecast/media/cma/pipeline/video_pipeline_impl.h"
22 #include "chromecast/public/media/media_clock_device.h"
23 #include "chromecast/public/media/media_pipeline_backend.h"
24 #include "media/base/timestamp_constants.h"
26 namespace chromecast {
27 namespace media {
29 namespace {
31 // Buffering parameters when load_type is kLoadTypeUrl.
32 const base::TimeDelta kLowBufferThresholdURL(
33 base::TimeDelta::FromMilliseconds(2000));
34 const base::TimeDelta kHighBufferThresholdURL(
35 base::TimeDelta::FromMilliseconds(6000));
37 // Buffering parameters when load_type is kLoadTypeMediaSource.
38 const base::TimeDelta kLowBufferThresholdMediaSource(
39 base::TimeDelta::FromMilliseconds(0));
40 const base::TimeDelta kHighBufferThresholdMediaSource(
41 base::TimeDelta::FromMilliseconds(300));
43 // Interval between two updates of the media time.
44 const base::TimeDelta kTimeUpdateInterval(
45 base::TimeDelta::FromMilliseconds(250));
47 // Interval between two updates of the statistics is equal to:
48 // kTimeUpdateInterval * kStatisticsUpdatePeriod.
49 const int kStatisticsUpdatePeriod = 4;
51 } // namespace
53 MediaPipelineImpl::MediaPipelineImpl()
54 : has_audio_(false),
55 has_video_(false),
56 target_playback_rate_(0.0),
57 enable_time_update_(false),
58 pending_time_update_task_(false),
59 statistics_rolling_counter_(0),
60 weak_factory_(this) {
61 CMALOG(kLogControl) << __FUNCTION__;
62 weak_this_ = weak_factory_.GetWeakPtr();
63 thread_checker_.DetachFromThread();
66 MediaPipelineImpl::~MediaPipelineImpl() {
67 CMALOG(kLogControl) << __FUNCTION__;
68 DCHECK(thread_checker_.CalledOnValidThread());
70 weak_factory_.InvalidateWeakPtrs();
72 // Since av pipeline still need to access device components in their
73 // destructor, it's important to delete them first.
74 video_pipeline_.reset();
75 audio_pipeline_.reset();
76 media_pipeline_backend_.reset();
77 if (!client_.pipeline_backend_destroyed_cb.is_null())
78 client_.pipeline_backend_destroyed_cb.Run();
81 void MediaPipelineImpl::Initialize(
82 LoadType load_type,
83 scoped_ptr<MediaPipelineBackend> media_pipeline_backend) {
84 CMALOG(kLogControl) << __FUNCTION__;
85 DCHECK(thread_checker_.CalledOnValidThread());
86 media_pipeline_backend_.reset(media_pipeline_backend.release());
87 clock_device_ = media_pipeline_backend_->GetClock();
88 if (!client_.pipeline_backend_created_cb.is_null())
89 client_.pipeline_backend_created_cb.Run();
91 if (load_type == kLoadTypeURL || load_type == kLoadTypeMediaSource) {
92 base::TimeDelta low_threshold(kLowBufferThresholdURL);
93 base::TimeDelta high_threshold(kHighBufferThresholdURL);
94 if (load_type == kLoadTypeMediaSource) {
95 low_threshold = kLowBufferThresholdMediaSource;
96 high_threshold = kHighBufferThresholdMediaSource;
98 scoped_refptr<BufferingConfig> buffering_config(
99 new BufferingConfig(low_threshold, high_threshold));
100 buffering_controller_.reset(new BufferingController(
101 buffering_config,
102 base::Bind(&MediaPipelineImpl::OnBufferingNotification, weak_this_)));
105 audio_pipeline_.reset(
106 new AudioPipelineImpl(media_pipeline_backend_->GetAudio()));
108 video_pipeline_.reset(
109 new VideoPipelineImpl(media_pipeline_backend_->GetVideo()));
112 void MediaPipelineImpl::SetClient(const MediaPipelineClient& client) {
113 DCHECK(thread_checker_.CalledOnValidThread());
114 DCHECK(!client.error_cb.is_null());
115 DCHECK(!client.time_update_cb.is_null());
116 DCHECK(!client.buffering_state_cb.is_null());
117 DCHECK(!client.pipeline_backend_created_cb.is_null());
118 DCHECK(!client.pipeline_backend_destroyed_cb.is_null());
119 client_ = client;
122 void MediaPipelineImpl::SetCdm(int cdm_id) {
123 CMALOG(kLogControl) << __FUNCTION__ << " cdm_id=" << cdm_id;
124 DCHECK(thread_checker_.CalledOnValidThread());
125 NOTIMPLEMENTED();
126 // TODO(gunsch): SetCdm(int) is not implemented.
127 // One possibility would be a GetCdmByIdCB that's passed in.
130 void MediaPipelineImpl::SetCdm(BrowserCdmCast* cdm) {
131 CMALOG(kLogControl) << __FUNCTION__;
132 DCHECK(thread_checker_.CalledOnValidThread());
133 audio_pipeline_->SetCdm(cdm);
134 video_pipeline_->SetCdm(cdm);
137 AudioPipeline* MediaPipelineImpl::GetAudioPipeline() const {
138 return audio_pipeline_.get();
141 VideoPipeline* MediaPipelineImpl::GetVideoPipeline() const {
142 return video_pipeline_.get();
145 void MediaPipelineImpl::InitializeAudio(
146 const ::media::AudioDecoderConfig& config,
147 scoped_ptr<CodedFrameProvider> frame_provider,
148 const ::media::PipelineStatusCB& status_cb) {
149 DCHECK(thread_checker_.CalledOnValidThread());
150 DCHECK(!has_audio_);
151 if (clock_device_->GetState() == MediaClockDevice::kStateUninitialized &&
152 !clock_device_->SetState(MediaClockDevice::kStateIdle)) {
153 status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED);
154 return;
156 has_audio_ = true;
157 audio_pipeline_->Initialize(config, frame_provider.Pass(), status_cb);
160 void MediaPipelineImpl::InitializeVideo(
161 const std::vector<::media::VideoDecoderConfig>& configs,
162 scoped_ptr<CodedFrameProvider> frame_provider,
163 const ::media::PipelineStatusCB& status_cb) {
164 DCHECK(thread_checker_.CalledOnValidThread());
165 DCHECK(!has_video_);
166 if (clock_device_->GetState() == MediaClockDevice::kStateUninitialized &&
167 !clock_device_->SetState(MediaClockDevice::kStateIdle)) {
168 status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED);
169 return;
171 has_video_ = true;
172 video_pipeline_->Initialize(configs, frame_provider.Pass(), status_cb);
175 void MediaPipelineImpl::StartPlayingFrom(base::TimeDelta time) {
176 CMALOG(kLogControl) << __FUNCTION__ << " t0=" << time.InMilliseconds();
177 DCHECK(thread_checker_.CalledOnValidThread());
178 DCHECK(has_audio_ || has_video_);
179 DCHECK(!pending_flush_callbacks_);
181 // Reset the start of the timeline.
182 DCHECK_EQ(clock_device_->GetState(), MediaClockDevice::kStateIdle);
183 clock_device_->ResetTimeline(time.InMicroseconds());
185 // Start the clock. If the playback rate is 0, then the clock is started
186 // but does not increase.
187 if (!clock_device_->SetState(MediaClockDevice::kStateRunning)) {
188 OnError(::media::PIPELINE_ERROR_ABORT);
189 return;
192 // Enable time updates.
193 enable_time_update_ = true;
194 statistics_rolling_counter_ = 0;
195 if (!pending_time_update_task_) {
196 pending_time_update_task_ = true;
197 base::ThreadTaskRunnerHandle::Get()->PostTask(
198 FROM_HERE, base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_));
201 // Setup the audio and video pipeline for the new timeline.
202 if (has_audio_) {
203 scoped_refptr<BufferingState> buffering_state;
204 if (buffering_controller_)
205 buffering_state = buffering_controller_->AddStream("audio");
206 if (!audio_pipeline_->StartPlayingFrom(time, buffering_state)) {
207 OnError(::media::PIPELINE_ERROR_ABORT);
208 return;
211 if (has_video_) {
212 scoped_refptr<BufferingState> buffering_state;
213 if (buffering_controller_)
214 buffering_state = buffering_controller_->AddStream("video");
215 if (!video_pipeline_->StartPlayingFrom(time, buffering_state)) {
216 OnError(::media::PIPELINE_ERROR_ABORT);
217 return;
222 void MediaPipelineImpl::Flush(const ::media::PipelineStatusCB& status_cb) {
223 CMALOG(kLogControl) << __FUNCTION__;
224 DCHECK(thread_checker_.CalledOnValidThread());
225 DCHECK(has_audio_ || has_video_);
226 DCHECK(!pending_flush_callbacks_);
227 DCHECK(clock_device_->GetState() == MediaClockDevice::kStateUninitialized ||
228 clock_device_->GetState() == MediaClockDevice::kStateRunning);
230 // No need to update media time anymore.
231 enable_time_update_ = false;
233 buffering_controller_->Reset();
235 // The clock should return to idle.
236 if (!clock_device_->SetState(MediaClockDevice::kStateIdle)) {
237 status_cb.Run(::media::PIPELINE_ERROR_ABORT);
238 return;
241 // Flush both the audio and video pipeline.
242 ::media::SerialRunner::Queue bound_fns;
243 if (has_audio_) {
244 bound_fns.Push(base::Bind(
245 &AudioPipelineImpl::Flush,
246 base::Unretained(audio_pipeline_.get())));
248 if (has_video_) {
249 bound_fns.Push(base::Bind(
250 &VideoPipelineImpl::Flush,
251 base::Unretained(video_pipeline_.get())));
253 ::media::PipelineStatusCB transition_cb =
254 base::Bind(&MediaPipelineImpl::StateTransition, weak_this_, status_cb);
255 pending_flush_callbacks_ =
256 ::media::SerialRunner::Run(bound_fns, transition_cb);
259 void MediaPipelineImpl::Stop() {
260 CMALOG(kLogControl) << __FUNCTION__;
261 DCHECK(thread_checker_.CalledOnValidThread());
262 DCHECK(has_audio_ || has_video_);
264 // Cancel pending flush callbacks since we are about to stop/shutdown
265 // audio/video pipelines. This will ensure A/V Flush won't happen in
266 // stopped state.
267 pending_flush_callbacks_.reset();
269 // No need to update media time anymore.
270 enable_time_update_ = false;
272 // Release hardware resources on Stop.
273 // Note: Stop can be called from any state.
274 if (clock_device_->GetState() == MediaClockDevice::kStateRunning)
275 clock_device_->SetState(MediaClockDevice::kStateIdle);
276 if (clock_device_->GetState() == MediaClockDevice::kStateIdle)
277 clock_device_->SetState(MediaClockDevice::kStateUninitialized);
279 // Stop both the audio and video pipeline.
280 if (has_audio_)
281 audio_pipeline_->Stop();
282 if (has_video_)
283 video_pipeline_->Stop();
286 void MediaPipelineImpl::SetPlaybackRate(double rate) {
287 CMALOG(kLogControl) << __FUNCTION__ << " rate=" << rate;
288 DCHECK(thread_checker_.CalledOnValidThread());
289 target_playback_rate_ = rate;
290 if (!buffering_controller_ || !buffering_controller_->IsBuffering())
291 media_pipeline_backend_->GetClock()->SetRate(rate);
294 AudioPipelineImpl* MediaPipelineImpl::GetAudioPipelineImpl() const {
295 return audio_pipeline_.get();
298 VideoPipelineImpl* MediaPipelineImpl::GetVideoPipelineImpl() const {
299 return video_pipeline_.get();
302 void MediaPipelineImpl::StateTransition(
303 const ::media::PipelineStatusCB& status_cb,
304 ::media::PipelineStatus status) {
305 pending_flush_callbacks_.reset();
306 status_cb.Run(status);
309 void MediaPipelineImpl::OnBufferingNotification(bool is_buffering) {
310 CMALOG(kLogControl) << __FUNCTION__ << " is_buffering=" << is_buffering;
311 DCHECK(thread_checker_.CalledOnValidThread());
312 DCHECK(buffering_controller_);
314 if (!client_.buffering_state_cb.is_null()) {
315 ::media::BufferingState buffering_state = is_buffering ?
316 ::media::BUFFERING_HAVE_NOTHING : ::media::BUFFERING_HAVE_ENOUGH;
317 client_.buffering_state_cb.Run(buffering_state);
320 if (media_pipeline_backend_->GetClock()->GetState() ==
321 MediaClockDevice::kStateUninitialized) {
322 return;
325 if (is_buffering) {
326 // Do not consume data in a rebuffering phase.
327 media_pipeline_backend_->GetClock()->SetRate(0.0);
328 } else {
329 media_pipeline_backend_->GetClock()->SetRate(target_playback_rate_);
333 void MediaPipelineImpl::UpdateMediaTime() {
334 pending_time_update_task_ = false;
335 if (!enable_time_update_)
336 return;
338 if (statistics_rolling_counter_ == 0) {
339 audio_pipeline_->UpdateStatistics();
340 video_pipeline_->UpdateStatistics();
342 statistics_rolling_counter_ =
343 (statistics_rolling_counter_ + 1) % kStatisticsUpdatePeriod;
345 base::TimeDelta media_time =
346 base::TimeDelta::FromMicroseconds(clock_device_->GetTimeMicroseconds());
347 if (media_time == ::media::kNoTimestamp()) {
348 pending_time_update_task_ = true;
349 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
350 FROM_HERE, base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_),
351 kTimeUpdateInterval);
352 return;
354 base::TimeTicks stc = base::TimeTicks::Now();
356 base::TimeDelta max_rendering_time = media_time;
357 if (buffering_controller_) {
358 buffering_controller_->SetMediaTime(media_time);
360 // Receiving the same time twice in a row means playback isn't moving,
361 // so don't interpolate ahead.
362 if (media_time != last_media_time_) {
363 max_rendering_time = buffering_controller_->GetMaxRenderingTime();
364 if (max_rendering_time == ::media::kNoTimestamp())
365 max_rendering_time = media_time;
367 // Cap interpolation time to avoid interpolating too far ahead.
368 max_rendering_time =
369 std::min(max_rendering_time, media_time + 2 * kTimeUpdateInterval);
373 last_media_time_ = media_time;
374 if (!client_.time_update_cb.is_null())
375 client_.time_update_cb.Run(media_time, max_rendering_time, stc);
377 pending_time_update_task_ = true;
378 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
379 FROM_HERE, base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_),
380 kTimeUpdateInterval);
383 void MediaPipelineImpl::OnError(::media::PipelineStatus error) {
384 DCHECK(thread_checker_.CalledOnValidThread());
385 DCHECK_NE(error, ::media::PIPELINE_OK) << "PIPELINE_OK is not an error!";
386 if (!client_.error_cb.is_null())
387 client_.error_cb.Run(error);
390 } // namespace media
391 } // namespace chromecast