OLD | NEW |
---|---|
(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_) { | |
damienv1
2014/11/20 17:28:28
nit: remove curly braces (here and below)
gunsch
2014/11/20 19:50:27
Done.
| |
260 audio_pipeline_->Stop(); | |
261 } | |
262 if (has_video_) { | |
263 video_pipeline_->Stop(); | |
264 } | |
265 } | |
266 | |
267 void MediaPipelineImpl::SetPlaybackRate(float rate) { | |
268 CMALOG(kLogControl) << __FUNCTION__ << " rate=" << rate; | |
269 DCHECK(thread_checker_.CalledOnValidThread()); | |
270 target_playback_rate_ = rate; | |
271 if (!buffering_controller_ || !buffering_controller_->IsBuffering()) { | |
damienv1
2014/11/20 17:28:28
nit: curly brace not needed.
gunsch
2014/11/20 19:50:27
Done.
| |
272 media_pipeline_device_->GetMediaClockDevice()->SetRate(rate); | |
273 } | |
274 } | |
275 | |
276 AudioPipelineImpl* MediaPipelineImpl::GetAudioPipelineImpl() const { | |
277 return audio_pipeline_.get(); | |
278 } | |
279 | |
280 VideoPipelineImpl* MediaPipelineImpl::GetVideoPipelineImpl() const { | |
281 return video_pipeline_.get(); | |
282 } | |
283 | |
284 void MediaPipelineImpl::StateTransition( | |
285 const ::media::PipelineStatusCB& status_cb, | |
286 ::media::PipelineStatus status) { | |
287 pending_callbacks_.reset(); | |
288 status_cb.Run(status); | |
289 } | |
290 | |
291 void MediaPipelineImpl::OnBufferingNotification(bool is_buffering) { | |
292 CMALOG(kLogControl) << __FUNCTION__ << " is_buffering=" << is_buffering; | |
293 DCHECK(thread_checker_.CalledOnValidThread()); | |
294 DCHECK(buffering_controller_); | |
295 | |
296 if (!client_.buffering_state_cb.is_null()) { | |
297 ::media::BufferingState buffering_state = is_buffering ? | |
298 ::media::BUFFERING_HAVE_NOTHING : ::media::BUFFERING_HAVE_ENOUGH; | |
299 client_.buffering_state_cb.Run(buffering_state); | |
300 } | |
301 | |
302 if (media_pipeline_device_->GetMediaClockDevice()->GetState() == | |
303 MediaClockDevice::kStateUninitialized) { | |
304 return; | |
305 } | |
306 | |
307 if (is_buffering) { | |
308 // Do not consume data in a rebuffering phase. | |
309 media_pipeline_device_->GetMediaClockDevice()->SetRate(0.0); | |
310 } else { | |
311 media_pipeline_device_->GetMediaClockDevice()->SetRate( | |
312 target_playback_rate_); | |
313 } | |
314 } | |
315 | |
316 void MediaPipelineImpl::UpdateMediaTime() { | |
317 pending_time_update_task_ = false; | |
318 if (!enable_time_update_) | |
319 return; | |
320 | |
321 if (statistics_rolling_counter_ == 0) { | |
322 audio_pipeline_->UpdateStatistics(); | |
323 video_pipeline_->UpdateStatistics(); | |
324 } | |
325 statistics_rolling_counter_ = | |
326 (statistics_rolling_counter_ + 1) % kStatisticsUpdatePeriod; | |
327 | |
328 base::TimeDelta media_time(clock_device_->GetTime()); | |
329 if (media_time == ::media::kNoTimestamp()) { | |
330 pending_time_update_task_ = true; | |
331 base::MessageLoopProxy::current()->PostDelayedTask( | |
332 FROM_HERE, | |
333 base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_), | |
334 kTimeUpdateInterval); | |
335 return; | |
336 } | |
337 base::TimeTicks stc = base::TimeTicks::Now(); | |
338 | |
339 base::TimeDelta max_rendering_time = media_time; | |
340 if (buffering_controller_) { | |
341 buffering_controller_->SetMediaTime(media_time); | |
342 | |
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; | |
347 } | |
348 } | |
349 | |
350 last_media_time_ = media_time; | |
351 if (!client_.time_update_cb.is_null()) | |
352 client_.time_update_cb.Run(media_time, max_rendering_time, stc); | |
353 | |
354 pending_time_update_task_ = true; | |
355 base::MessageLoopProxy::current()->PostDelayedTask( | |
356 FROM_HERE, | |
357 base::Bind(&MediaPipelineImpl::UpdateMediaTime, weak_this_), | |
358 kTimeUpdateInterval); | |
359 } | |
360 | |
361 void MediaPipelineImpl::OnError(::media::PipelineStatus error) { | |
362 DCHECK(thread_checker_.CalledOnValidThread()); | |
363 DCHECK_NE(error, ::media::PIPELINE_OK) << "PIPELINE_OK is not an error!"; | |
364 if (!client_.error_cb.is_null()) | |
365 client_.error_cb.Run(error); | |
366 } | |
367 | |
368 } // namespace media | |
369 } // namespace chromecast | |
OLD | NEW |