Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(155)

Side by Side Diff: chromecast/media/cma/pipeline/media_pipeline_impl.cc

Issue 741863002: Chromecast: adds a media pipeline feeding data to CMA device backends. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@cma-cdm
Patch Set: address nits, merge Pause/Flush Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "chromecast/media/cma/pipeline/media_pipeline_impl.h"
6
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/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"
24
25 namespace chromecast {
26 namespace media {
27
28 namespace {
29
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));
35
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));
41
42 // Interval between two updates of the media time.
43 const base::TimeDelta kTimeUpdateInterval(
44 base::TimeDelta::FromMilliseconds(250));
45
46 // Interval between two updates of the statistics is equal to:
47 // kTimeUpdateInterval * kStatisticsUpdatePeriod.
48 const int kStatisticsUpdatePeriod = 4;
49
50 } // namespace
51
52 MediaPipelineImpl::MediaPipelineImpl()
53 : has_audio_(false),
54 has_video_(false),
55 target_playback_rate_(0.0),
56 enable_time_update_(false),
57 pending_time_update_task_(false),
58 statistics_rolling_counter_(0),
59 weak_factory_(this) {
60 CMALOG(kLogControl) << __FUNCTION__;
61 weak_this_ = weak_factory_.GetWeakPtr();
62 thread_checker_.DetachFromThread();
63 }
64
65 MediaPipelineImpl::~MediaPipelineImpl() {
66 CMALOG(kLogControl) << __FUNCTION__;
67 DCHECK(thread_checker_.CalledOnValidThread());
68 }
69
70 void MediaPipelineImpl::Initialize(
71 LoadType load_type,
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();
77
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;
84 }
85 scoped_refptr<BufferingConfig> buffering_config(
86 new BufferingConfig(low_threshold, high_threshold));
87 buffering_controller_.reset(new BufferingController(
88 buffering_config,
89 base::Bind(&MediaPipelineImpl::OnBufferingNotification, weak_this_)));
90 }
91
92 audio_pipeline_.reset(new AudioPipelineImpl(
93 media_pipeline_device_->GetAudioPipelineDevice()));
94
95 video_pipeline_.reset(new VideoPipelineImpl(
96 media_pipeline_device_->GetVideoPipelineDevice()));
97 }
98
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());
104 client_ = client;
105 }
106
107 void MediaPipelineImpl::SetCdm(int cdm_id) {
108 CMALOG(kLogControl) << __FUNCTION__ << " cdm_id=" << cdm_id;
109 DCHECK(thread_checker_.CalledOnValidThread());
110 NOTIMPLEMENTED();
111 // TODO(gunsch): SetCdm(int) is not implemented.
112 // One possibility would be a GetCdmByIdCB that's passed in.
113 }
114
115 void MediaPipelineImpl::SetCdm(::media::BrowserCdm* media_keys) {
116 CMALOG(kLogControl) << __FUNCTION__;
117 audio_pipeline_->SetCdm(static_cast<BrowserCdmCast*>(media_keys));
118 video_pipeline_->SetCdm(static_cast<BrowserCdmCast*>(media_keys));
119 }
120
121 AudioPipeline* MediaPipelineImpl::GetAudioPipeline() const {
122 return audio_pipeline_.get();
123 }
124
125 VideoPipeline* MediaPipelineImpl::GetVideoPipeline() const {
126 return video_pipeline_.get();
127 }
128
129 void MediaPipelineImpl::InitializeAudio(
130 const ::media::AudioDecoderConfig& config,
131 scoped_ptr<CodedFrameProvider> frame_provider,
132 const ::media::PipelineStatusCB& status_cb) {
133 DCHECK(thread_checker_.CalledOnValidThread());
134 DCHECK(!has_audio_);
135 if (clock_device_->GetState() == MediaClockDevice::kStateUninitialized &&
136 !clock_device_->SetState(MediaClockDevice::kStateIdle)) {
137 status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED);
138 return;
139 }
140 has_audio_ = true;
141 audio_pipeline_->Initialize(config, frame_provider.Pass(), status_cb);
142 }
143
144 void MediaPipelineImpl::InitializeVideo(
145 const ::media::VideoDecoderConfig& config,
146 scoped_ptr<CodedFrameProvider> frame_provider,
147 const ::media::PipelineStatusCB& status_cb) {
148 DCHECK(thread_checker_.CalledOnValidThread());
149 DCHECK(!has_video_);
150 if (clock_device_->GetState() == MediaClockDevice::kStateUninitialized &&
151 !clock_device_->SetState(MediaClockDevice::kStateIdle)) {
152 status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED);
153 return;
154 }
155 has_video_ = true;
156 video_pipeline_->Initialize(config, frame_provider.Pass(), status_cb);
157 }
158
159 void MediaPipelineImpl::StartPlayingFrom(base::TimeDelta time) {
160 CMALOG(kLogControl) << __FUNCTION__ << " t0=" << time.InMilliseconds();
161 DCHECK(thread_checker_.CalledOnValidThread());
162 DCHECK(has_audio_ || has_video_);
163 DCHECK(!pending_callbacks_);
164
165 // Reset the start of the timeline.
166 DCHECK_EQ(clock_device_->GetState(), MediaClockDevice::kStateIdle);
167 clock_device_->ResetTimeline(time);
168
169 // Start the clock. If the playback rate is 0, then the clock is started
170 // but does not increase.
171 if (!clock_device_->SetState(MediaClockDevice::kStateRunning)) {
172 OnError(::media::PIPELINE_ERROR_ABORT);
173 return;
174 }
175
176 // Enable time updates.
177 enable_time_update_ = true;
178 statistics_rolling_counter_ = 0;
179 if (!pending_time_update_task_) {
180 pending_time_update_task_ = true;
181 base::MessageLoopProxy::current()->PostTask(
182 FROM_HERE,
183 base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_));
184 }
185
186 // Setup the audio and video pipeline for the new timeline.
187 if (has_audio_) {
188 scoped_refptr<BufferingState> buffering_state;
189 if (buffering_controller_)
190 buffering_state = buffering_controller_->AddStream();
191 if (!audio_pipeline_->StartPlayingFrom(time, buffering_state)) {
192 OnError(::media::PIPELINE_ERROR_ABORT);
193 return;
194 }
195 }
196 if (has_video_) {
197 scoped_refptr<BufferingState> buffering_state;
198 if (buffering_controller_)
199 buffering_state = buffering_controller_->AddStream();
200 if (!video_pipeline_->StartPlayingFrom(time, buffering_state)) {
201 OnError(::media::PIPELINE_ERROR_ABORT);
202 return;
203 }
204 }
205 }
206
207 void MediaPipelineImpl::Flush(const ::media::PipelineStatusCB& status_cb) {
208 CMALOG(kLogControl) << __FUNCTION__;
209 DCHECK(thread_checker_.CalledOnValidThread());
210 DCHECK(has_audio_ || has_video_);
211 DCHECK(!pending_callbacks_);
212
213 // No need to update media time anymore.
214 enable_time_update_ = false;
215
216 buffering_controller_->Reset();
217
218 // The clock should return to idle.
219 if (!clock_device_->SetState(MediaClockDevice::kStateIdle)) {
220 status_cb.Run(::media::PIPELINE_ERROR_ABORT);
221 return;
222 }
223
224 // Flush both the audio and video pipeline.
225 ::media::SerialRunner::Queue bound_fns;
226 if (has_audio_) {
227 bound_fns.Push(base::Bind(
228 &AudioPipelineImpl::Flush,
229 base::Unretained(audio_pipeline_.get())));
230 }
231 if (has_video_) {
232 bound_fns.Push(base::Bind(
233 &VideoPipelineImpl::Flush,
234 base::Unretained(video_pipeline_.get())));
235 }
236 ::media::PipelineStatusCB transition_cb =
237 base::Bind(&MediaPipelineImpl::StateTransition, weak_this_, status_cb);
238 pending_callbacks_ =
239 ::media::SerialRunner::Run(bound_fns, transition_cb);
240 }
241
242 void MediaPipelineImpl::Stop() {
243 CMALOG(kLogControl) << __FUNCTION__;
244 DCHECK(thread_checker_.CalledOnValidThread());
245 DCHECK(has_audio_ || has_video_);
246 DCHECK(!pending_callbacks_);
247
248 // No need to update media time anymore.
249 enable_time_update_ = false;
250
251 // Release hardware resources on Stop.
252 // Note: Stop can be called from any state.
253 if (clock_device_->GetState() == MediaClockDevice::kStateRunning)
254 clock_device_->SetState(MediaClockDevice::kStateIdle);
255 if (clock_device_->GetState() == MediaClockDevice::kStateIdle)
256 clock_device_->SetState(MediaClockDevice::kStateUninitialized);
257
258 // Stop both the audio and video pipeline.
259 if (has_audio_)
260 audio_pipeline_->Stop();
261 if (has_video_)
262 video_pipeline_->Stop();
263 }
264
265 void MediaPipelineImpl::SetPlaybackRate(float rate) {
266 CMALOG(kLogControl) << __FUNCTION__ << " rate=" << rate;
267 DCHECK(thread_checker_.CalledOnValidThread());
268 target_playback_rate_ = rate;
269 if (!buffering_controller_ || !buffering_controller_->IsBuffering())
270 media_pipeline_device_->GetMediaClockDevice()->SetRate(rate);
271 }
272
273 AudioPipelineImpl* MediaPipelineImpl::GetAudioPipelineImpl() const {
274 return audio_pipeline_.get();
275 }
276
277 VideoPipelineImpl* MediaPipelineImpl::GetVideoPipelineImpl() const {
278 return video_pipeline_.get();
279 }
280
281 void MediaPipelineImpl::StateTransition(
282 const ::media::PipelineStatusCB& status_cb,
283 ::media::PipelineStatus status) {
284 pending_callbacks_.reset();
285 status_cb.Run(status);
286 }
287
288 void MediaPipelineImpl::OnBufferingNotification(bool is_buffering) {
289 CMALOG(kLogControl) << __FUNCTION__ << " is_buffering=" << is_buffering;
290 DCHECK(thread_checker_.CalledOnValidThread());
291 DCHECK(buffering_controller_);
292
293 if (!client_.buffering_state_cb.is_null()) {
294 ::media::BufferingState buffering_state = is_buffering ?
295 ::media::BUFFERING_HAVE_NOTHING : ::media::BUFFERING_HAVE_ENOUGH;
296 client_.buffering_state_cb.Run(buffering_state);
297 }
298
299 if (media_pipeline_device_->GetMediaClockDevice()->GetState() ==
300 MediaClockDevice::kStateUninitialized) {
301 return;
302 }
303
304 if (is_buffering) {
305 // Do not consume data in a rebuffering phase.
306 media_pipeline_device_->GetMediaClockDevice()->SetRate(0.0);
307 } else {
308 media_pipeline_device_->GetMediaClockDevice()->SetRate(
309 target_playback_rate_);
310 }
311 }
312
313 void MediaPipelineImpl::UpdateMediaTime() {
314 pending_time_update_task_ = false;
315 if (!enable_time_update_)
316 return;
317
318 if (statistics_rolling_counter_ == 0) {
319 audio_pipeline_->UpdateStatistics();
320 video_pipeline_->UpdateStatistics();
321 }
322 statistics_rolling_counter_ =
323 (statistics_rolling_counter_ + 1) % kStatisticsUpdatePeriod;
324
325 base::TimeDelta media_time(clock_device_->GetTime());
326 if (media_time == ::media::kNoTimestamp()) {
327 pending_time_update_task_ = true;
328 base::MessageLoopProxy::current()->PostDelayedTask(
329 FROM_HERE,
330 base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_),
331 kTimeUpdateInterval);
332 return;
333 }
334 base::TimeTicks stc = base::TimeTicks::Now();
335
336 base::TimeDelta max_rendering_time = media_time;
337 if (buffering_controller_) {
338 buffering_controller_->SetMediaTime(media_time);
339
340 if (media_time != last_media_time_) {
341 max_rendering_time = buffering_controller_->GetMaxRenderingTime();
342 if (max_rendering_time == ::media::kNoTimestamp())
343 max_rendering_time = media_time;
344 }
345 }
346
347 last_media_time_ = media_time;
348 if (!client_.time_update_cb.is_null())
349 client_.time_update_cb.Run(media_time, max_rendering_time, stc);
350
351 pending_time_update_task_ = true;
352 base::MessageLoopProxy::current()->PostDelayedTask(
353 FROM_HERE,
354 base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_),
355 kTimeUpdateInterval);
356 }
357
358 void MediaPipelineImpl::OnError(::media::PipelineStatus error) {
359 DCHECK(thread_checker_.CalledOnValidThread());
360 DCHECK_NE(error, ::media::PIPELINE_OK) << "PIPELINE_OK is not an error!";
361 if (!client_.error_cb.is_null())
362 client_.error_cb.Run(error);
363 }
364
365 } // namespace media
366 } // namespace chromecast
OLDNEW
« no previous file with comments | « chromecast/media/cma/pipeline/media_pipeline_impl.h ('k') | chromecast/media/cma/pipeline/video_pipeline.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698