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 "media/filters/renderer_impl.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/callback.h" | |
9 #include "base/callback_helpers.h" | |
10 #include "base/compiler_specific.h" | |
11 #include "base/location.h" | |
12 #include "base/single_thread_task_runner.h" | |
13 #include "media/base/audio_renderer.h" | |
14 #include "media/base/demuxer.h" | |
15 #include "media/base/filter_collection.h" | |
16 #include "media/base/time_delta_interpolator.h" | |
17 #include "media/base/time_source.h" | |
18 #include "media/base/video_renderer.h" | |
19 | |
20 namespace media { | |
21 | |
22 RendererImpl::RendererImpl( | |
23 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | |
24 Demuxer* demuxer, | |
25 scoped_ptr<AudioRenderer> audio_renderer, | |
26 scoped_ptr<VideoRenderer> video_renderer) | |
27 : state_(kUninitialized), | |
28 task_runner_(task_runner), | |
29 demuxer_(demuxer), | |
30 audio_renderer_(audio_renderer.Pass()), | |
31 video_renderer_(video_renderer.Pass()), | |
32 time_source_(NULL), | |
33 audio_buffering_state_(BUFFERING_HAVE_NOTHING), | |
34 video_buffering_state_(BUFFERING_HAVE_NOTHING), | |
35 audio_ended_(false), | |
36 video_ended_(false), | |
37 underflow_disabled_for_testing_(false), | |
38 interpolator_(new TimeDeltaInterpolator(&default_tick_clock_)), | |
39 interpolation_state_(INTERPOLATION_STOPPED), | |
40 weak_factory_(this), | |
41 weak_this_(weak_factory_.GetWeakPtr()) { | |
42 DVLOG(1) << __FUNCTION__; | |
43 interpolator_->SetBounds(base::TimeDelta(), base::TimeDelta()); | |
44 } | |
45 | |
46 RendererImpl::~RendererImpl() { | |
47 DVLOG(1) << __FUNCTION__; | |
48 DCHECK(task_runner_->BelongsToCurrentThread()); | |
49 | |
50 audio_renderer_.reset(); | |
51 video_renderer_.reset(); | |
52 | |
53 FireAllPendingCallbacks(); | |
54 } | |
55 | |
56 void RendererImpl::Initialize(const PipelineStatusCB& init_cb, | |
57 const StatisticsCB& statistics_cb, | |
58 const base::Closure& ended_cb, | |
59 const PipelineStatusCB& error_cb, | |
60 const BufferingStateCB& buffering_state_cb, | |
61 const TimeDeltaCB& get_duration_cb) { | |
62 DVLOG(1) << __FUNCTION__; | |
63 DCHECK(task_runner_->BelongsToCurrentThread()); | |
64 DCHECK_EQ(state_, kUninitialized) << state_; | |
65 DCHECK(!init_cb.is_null()); | |
66 DCHECK(!statistics_cb.is_null()); | |
67 DCHECK(!ended_cb.is_null()); | |
68 DCHECK(!error_cb.is_null()); | |
69 DCHECK(!buffering_state_cb.is_null()); | |
70 DCHECK(!get_duration_cb.is_null()); | |
71 DCHECK(demuxer_->GetStream(DemuxerStream::AUDIO) || | |
72 demuxer_->GetStream(DemuxerStream::VIDEO)); | |
73 | |
74 statistics_cb_ = statistics_cb; | |
75 ended_cb_ = ended_cb; | |
76 error_cb_ = error_cb; | |
77 buffering_state_cb_ = buffering_state_cb; | |
78 get_duration_cb_ = get_duration_cb; | |
79 | |
80 init_cb_ = init_cb; | |
damienv1
2014/08/06 21:43:48
nit: |init_cb_| is not really needed: it could be
xhwang
2014/08/07 05:46:49
In media code, we don't like outstanding callbacks
| |
81 state_ = kInitializing; | |
82 InitializeAudioRenderer(); | |
83 } | |
84 | |
85 void RendererImpl::Flush(const base::Closure& flush_cb) { | |
86 DVLOG(2) << __FUNCTION__; | |
87 DCHECK(task_runner_->BelongsToCurrentThread()); | |
88 DCHECK_EQ(state_, kPlaying) << state_; | |
89 DCHECK(flush_cb_.is_null()); | |
90 | |
91 { | |
92 base::AutoLock auto_lock(interpolator_lock_); | |
93 PauseClockAndStopTicking_Locked(); | |
94 } | |
95 | |
96 flush_cb_ = flush_cb; | |
97 state_ = kFlushing; | |
98 FlushAudioRenderer(); | |
99 } | |
100 | |
101 void RendererImpl::StartPlayingFrom(base::TimeDelta timestamp) { | |
102 DVLOG(2) << __FUNCTION__; | |
103 DCHECK(task_runner_->BelongsToCurrentThread()); | |
104 DCHECK_EQ(state_, kPlaying) << state_; | |
105 | |
106 { | |
107 base::AutoLock auto_lock(interpolator_lock_); | |
108 interpolator_->SetBounds(timestamp, timestamp); | |
109 } | |
110 | |
111 if (time_source_) | |
112 time_source_->SetMediaTime(timestamp); | |
113 if (audio_renderer_) | |
114 audio_renderer_->StartPlaying(); | |
115 if (video_renderer_) | |
116 video_renderer_->StartPlaying(); | |
117 } | |
118 | |
119 void RendererImpl::SetPlaybackRate(float playback_rate) { | |
120 DVLOG(2) << __FUNCTION__ << "(" << playback_rate << ")"; | |
121 DCHECK(task_runner_->BelongsToCurrentThread()); | |
122 | |
123 // Playback rate changes are only carried out while playing. | |
124 if (state_ != kPlaying) | |
125 return; | |
126 | |
127 { | |
128 base::AutoLock auto_lock(interpolator_lock_); | |
129 interpolator_->SetPlaybackRate(playback_rate); | |
130 } | |
131 | |
132 if (time_source_) | |
133 time_source_->SetPlaybackRate(playback_rate); | |
134 } | |
135 | |
136 void RendererImpl::SetVolume(float volume) { | |
137 DVLOG(1) << __FUNCTION__; | |
138 DCHECK(task_runner_->BelongsToCurrentThread()); | |
139 | |
140 if (audio_renderer_) | |
141 audio_renderer_->SetVolume(volume); | |
142 } | |
143 | |
144 base::TimeDelta RendererImpl::GetMediaTime() { | |
145 // No BelongsToCurrentThread() checking because this can be called from other | |
146 // threads. | |
147 base::AutoLock auto_lock(interpolator_lock_); | |
148 return interpolator_->GetInterpolatedTime(); | |
149 } | |
150 | |
151 bool RendererImpl::HasAudio() { | |
152 DCHECK(task_runner_->BelongsToCurrentThread()); | |
153 return audio_renderer_ != NULL; | |
154 } | |
155 | |
156 bool RendererImpl::HasVideo() { | |
157 DCHECK(task_runner_->BelongsToCurrentThread()); | |
158 return video_renderer_ != NULL; | |
159 } | |
160 | |
161 void RendererImpl::SetCdm(MediaKeys* cdm) { | |
162 DVLOG(1) << __FUNCTION__; | |
163 DCHECK(task_runner_->BelongsToCurrentThread()); | |
164 // TODO(xhwang): Explore to possibility to move CDM setting from | |
165 // WebMediaPlayerImpl to this class. | |
damienv1
2014/08/06 21:43:48
What is the plan on this side ?
xhwang
2014/08/07 05:46:49
See http://crbug.com/401264
| |
166 NOTREACHED(); | |
167 } | |
168 | |
169 void RendererImpl::DisableUnderflowForTesting() { | |
170 DVLOG(2) << __FUNCTION__; | |
171 DCHECK(task_runner_->BelongsToCurrentThread()); | |
172 DCHECK_EQ(state_, kUninitialized); | |
173 | |
174 underflow_disabled_for_testing_ = true; | |
175 } | |
176 | |
177 void RendererImpl::SetTimeDeltaInterpolatorForTesting( | |
178 TimeDeltaInterpolator* interpolator) { | |
179 DVLOG(2) << __FUNCTION__; | |
180 DCHECK(task_runner_->BelongsToCurrentThread()); | |
181 DCHECK_EQ(state_, kUninitialized); | |
182 | |
183 interpolator_.reset(interpolator); | |
184 } | |
185 | |
186 base::TimeDelta RendererImpl::GetMediaDuration() { | |
187 DCHECK(task_runner_->BelongsToCurrentThread()); | |
188 return get_duration_cb_.Run(); | |
189 } | |
190 | |
191 void RendererImpl::InitializeAudioRenderer() { | |
192 DVLOG(2) << __FUNCTION__; | |
193 DCHECK(task_runner_->BelongsToCurrentThread()); | |
194 DCHECK_EQ(state_, kInitializing) << state_; | |
195 DCHECK(!init_cb_.is_null()); | |
196 | |
197 PipelineStatusCB done_cb = | |
198 base::Bind(&RendererImpl::OnAudioRendererInitializeDone, weak_this_); | |
damienv1
2014/08/06 21:43:47
To avoid the modifications in AudioRenderer, we co
xhwang
2014/08/07 05:46:49
In media code, the contract is that the callee (no
| |
199 | |
200 if (!demuxer_->GetStream(DemuxerStream::AUDIO)) { | |
201 audio_renderer_.reset(); | |
202 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK)); | |
damienv1
2014/08/06 21:43:47
No need for PostTask if using BindToCurrentLoop fo
xhwang
2014/08/07 05:46:49
scherkus may have different opinion :) Basically w
| |
203 return; | |
204 } | |
205 | |
206 audio_renderer_->Initialize( | |
207 demuxer_->GetStream(DemuxerStream::AUDIO), | |
208 done_cb, | |
209 base::Bind(&RendererImpl::OnUpdateStatistics, weak_this_), | |
210 base::Bind(&RendererImpl::OnAudioTimeUpdate, weak_this_), | |
211 base::Bind(&RendererImpl::OnBufferingStateChanged, weak_this_, | |
212 &audio_buffering_state_), | |
213 base::Bind(&RendererImpl::OnAudioRendererEnded, weak_this_), | |
214 base::Bind(&RendererImpl::OnError, weak_this_)); | |
215 } | |
216 | |
217 void RendererImpl::OnAudioRendererInitializeDone(PipelineStatus status) { | |
218 DVLOG(2) << __FUNCTION__ << ": " << status; | |
219 DCHECK(task_runner_->BelongsToCurrentThread()); | |
220 DCHECK_EQ(state_, kInitializing) << state_; | |
221 DCHECK(!init_cb_.is_null()); | |
222 | |
223 if (status != PIPELINE_OK) { | |
224 audio_renderer_.reset(); | |
225 state_ = kError; | |
226 base::ResetAndReturn(&init_cb_).Run(status); | |
227 return; | |
228 } | |
229 | |
230 if (audio_renderer_) | |
231 time_source_ = audio_renderer_->GetTimeSource(); | |
232 | |
233 InitializeVideoRenderer(); | |
234 } | |
235 | |
236 void RendererImpl::InitializeVideoRenderer() { | |
237 DVLOG(2) << __FUNCTION__; | |
238 DCHECK(task_runner_->BelongsToCurrentThread()); | |
239 DCHECK_EQ(state_, kInitializing) << state_; | |
240 DCHECK(!init_cb_.is_null()); | |
241 | |
242 PipelineStatusCB done_cb = | |
243 base::Bind(&RendererImpl::OnVideoRendererInitializeDone, weak_this_); | |
244 | |
245 if (!demuxer_->GetStream(DemuxerStream::VIDEO)) { | |
246 video_renderer_.reset(); | |
247 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK)); | |
248 return; | |
249 } | |
250 | |
251 video_renderer_->Initialize( | |
252 demuxer_->GetStream(DemuxerStream::VIDEO), | |
253 demuxer_->GetLiveness() == Demuxer::LIVENESS_LIVE, | |
254 done_cb, | |
255 base::Bind(&RendererImpl::OnUpdateStatistics, weak_this_), | |
256 base::Bind(&RendererImpl::OnVideoTimeUpdate, weak_this_), | |
257 base::Bind(&RendererImpl::OnBufferingStateChanged, weak_this_, | |
258 &video_buffering_state_), | |
259 base::Bind(&RendererImpl::OnVideoRendererEnded, weak_this_), | |
260 base::Bind(&RendererImpl::OnError, weak_this_), | |
261 base::Bind(&RendererImpl::GetMediaTime, base::Unretained(this)), | |
262 base::Bind(&RendererImpl::GetMediaDuration, base::Unretained(this))); | |
263 } | |
264 | |
265 void RendererImpl::OnVideoRendererInitializeDone(PipelineStatus status) { | |
266 DVLOG(2) << __FUNCTION__ << ": " << status; | |
267 DCHECK(task_runner_->BelongsToCurrentThread()); | |
268 DCHECK_EQ(state_, kInitializing) << state_; | |
269 DCHECK(!init_cb_.is_null()); | |
270 | |
271 if (status != PIPELINE_OK) { | |
272 audio_renderer_.reset(); | |
273 video_renderer_.reset(); | |
274 state_ = kError; | |
275 base::ResetAndReturn(&init_cb_).Run(status); | |
276 return; | |
277 } | |
278 | |
279 state_ = kPlaying; | |
280 DCHECK(audio_renderer_ || video_renderer_); | |
281 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); | |
282 } | |
283 | |
284 void RendererImpl::FlushAudioRenderer() { | |
285 DVLOG(1) << __FUNCTION__; | |
286 DCHECK(task_runner_->BelongsToCurrentThread()); | |
287 DCHECK_EQ(state_, kFlushing) << state_; | |
288 DCHECK(!flush_cb_.is_null()); | |
289 | |
290 base::Closure done_cb = | |
291 base::Bind(&RendererImpl::OnAudioRendererFlushDone, weak_this_); | |
292 | |
293 if (!audio_renderer_) { | |
294 done_cb.Run(); | |
damienv1
2014/08/06 21:43:47
nit: sounds overkill to create a bind state in thi
xhwang
2014/08/07 05:46:49
Will do.
xhwang
2014/08/22 19:11:31
Done.
| |
295 return; | |
296 } | |
297 | |
298 audio_renderer_->Flush(done_cb); | |
299 } | |
300 | |
301 void RendererImpl::OnAudioRendererFlushDone() { | |
302 DVLOG(1) << __FUNCTION__; | |
303 DCHECK(task_runner_->BelongsToCurrentThread()); | |
304 | |
305 if (state_ == kError) { | |
306 DCHECK(flush_cb_.is_null()); | |
307 return; | |
308 } | |
309 | |
310 DCHECK_EQ(state_, kFlushing) << state_; | |
311 DCHECK(!flush_cb_.is_null()); | |
312 | |
313 DCHECK_EQ(audio_buffering_state_, BUFFERING_HAVE_NOTHING); | |
314 audio_ended_ = false; | |
315 FlushVideoRenderer(); | |
316 } | |
317 | |
318 void RendererImpl::FlushVideoRenderer() { | |
319 DVLOG(1) << __FUNCTION__; | |
320 DCHECK(task_runner_->BelongsToCurrentThread()); | |
321 DCHECK_EQ(state_, kFlushing) << state_; | |
322 DCHECK(!flush_cb_.is_null()); | |
323 | |
324 base::Closure done_cb = | |
325 base::Bind(&RendererImpl::OnVideoRendererFlushDone, weak_this_); | |
326 | |
327 if (!video_renderer_) { | |
328 done_cb.Run(); | |
329 return; | |
330 } | |
331 | |
332 video_renderer_->Flush(done_cb); | |
333 } | |
334 | |
335 void RendererImpl::OnVideoRendererFlushDone() { | |
336 DVLOG(1) << __FUNCTION__; | |
337 DCHECK(task_runner_->BelongsToCurrentThread()); | |
338 | |
339 if (state_ == kError) { | |
340 DCHECK(flush_cb_.is_null()); | |
341 return; | |
342 } | |
343 | |
344 DCHECK_EQ(state_, kFlushing) << state_; | |
345 DCHECK(!flush_cb_.is_null()); | |
346 | |
347 DCHECK_EQ(video_buffering_state_, BUFFERING_HAVE_NOTHING); | |
348 video_ended_ = false; | |
349 state_ = kPlaying; | |
350 base::ResetAndReturn(&flush_cb_).Run(); | |
351 } | |
352 | |
353 void RendererImpl::OnAudioTimeUpdate(base::TimeDelta time, | |
354 base::TimeDelta max_time) { | |
355 DVLOG(3) << __FUNCTION__ << "(" << time.InMilliseconds() | |
356 << ", " << max_time.InMilliseconds() << ")"; | |
357 DCHECK(task_runner_->BelongsToCurrentThread()); | |
358 DCHECK_LE(time.InMicroseconds(), max_time.InMicroseconds()); | |
359 | |
360 base::AutoLock auto_lock(interpolator_lock_); | |
361 | |
362 if (interpolation_state_ == INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE && | |
363 time < interpolator_->GetInterpolatedTime()) { | |
364 return; | |
365 } | |
366 | |
367 if (state_ == kFlushing) | |
368 return; | |
369 | |
370 interpolator_->SetBounds(time, max_time); | |
371 StartClockIfWaitingForTimeUpdate_Locked(); | |
372 } | |
373 | |
374 void RendererImpl::OnVideoTimeUpdate(base::TimeDelta max_time) { | |
375 DVLOG(3) << __FUNCTION__ << "(" << max_time.InMilliseconds() << ")"; | |
376 DCHECK(task_runner_->BelongsToCurrentThread()); | |
377 | |
378 if (audio_renderer_) | |
379 return; | |
380 | |
381 if (state_ == kFlushing) | |
382 return; | |
383 | |
384 base::AutoLock auto_lock(interpolator_lock_); | |
385 DCHECK_NE(interpolation_state_, INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE); | |
386 interpolator_->SetUpperBound(max_time); | |
387 } | |
388 | |
389 void RendererImpl::OnUpdateStatistics(const PipelineStatistics& stats) { | |
390 DCHECK(task_runner_->BelongsToCurrentThread()); | |
391 statistics_cb_.Run(stats); | |
392 } | |
393 | |
394 void RendererImpl::OnBufferingStateChanged(BufferingState* buffering_state, | |
395 BufferingState new_buffering_state) { | |
396 DVLOG(2) << __FUNCTION__ << "(" << *buffering_state << ", " | |
397 << new_buffering_state << ") " | |
398 << (buffering_state == &audio_buffering_state_ ? "audio" : "video"); | |
399 DCHECK(task_runner_->BelongsToCurrentThread()); | |
400 bool was_waiting_for_enough_data = WaitingForEnoughData(); | |
401 | |
402 *buffering_state = new_buffering_state; | |
403 | |
404 // Disable underflow by ignoring updates that renderers have ran out of data. | |
405 if (state_ == kPlaying && underflow_disabled_for_testing_ && | |
406 interpolation_state_ != INTERPOLATION_STOPPED) { | |
407 DVLOG(2) << "Update ignored because underflow is disabled for testing."; | |
408 return; | |
409 } | |
410 | |
411 // Renderer underflowed. | |
412 if (!was_waiting_for_enough_data && WaitingForEnoughData()) { | |
413 PausePlayback(); | |
414 | |
415 // TODO(scherkus): Fire BUFFERING_HAVE_NOTHING callback to alert clients of | |
416 // underflow state http://crbug.com/144683 | |
417 return; | |
418 } | |
419 | |
420 // Renderer prerolled. | |
421 if (was_waiting_for_enough_data && !WaitingForEnoughData()) { | |
422 StartPlayback(); | |
423 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH); | |
424 return; | |
425 } | |
426 } | |
427 | |
428 bool RendererImpl::WaitingForEnoughData() const { | |
429 DCHECK(task_runner_->BelongsToCurrentThread()); | |
430 if (state_ != kPlaying) | |
431 return false; | |
432 if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH) | |
433 return true; | |
434 if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH) | |
435 return true; | |
436 return false; | |
437 } | |
438 | |
439 void RendererImpl::PausePlayback() { | |
440 DVLOG(2) << __FUNCTION__; | |
441 DCHECK(task_runner_->BelongsToCurrentThread()); | |
442 DCHECK_EQ(state_, kPlaying); | |
443 DCHECK(WaitingForEnoughData()); | |
444 | |
445 base::AutoLock auto_lock(interpolator_lock_); | |
446 PauseClockAndStopTicking_Locked(); | |
447 } | |
448 | |
449 void RendererImpl::StartPlayback() { | |
450 DVLOG(2) << __FUNCTION__; | |
451 DCHECK(task_runner_->BelongsToCurrentThread()); | |
452 DCHECK_EQ(state_, kPlaying); | |
453 DCHECK_EQ(interpolation_state_, INTERPOLATION_STOPPED); | |
454 DCHECK(!WaitingForEnoughData()); | |
455 | |
456 if (time_source_) { | |
457 // We use audio stream to update the interpolator. So if there is such a | |
458 // stream, we pause the interpolator until we receive a valid timestamp. | |
459 base::AutoLock auto_lock(interpolator_lock_); | |
460 interpolation_state_ = INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE; | |
461 time_source_->StartTicking(); | |
462 } else { | |
463 base::AutoLock auto_lock(interpolator_lock_); | |
464 interpolation_state_ = INTERPOLATION_STARTED; | |
465 interpolator_->SetUpperBound(get_duration_cb_.Run()); | |
466 interpolator_->StartInterpolating(); | |
467 } | |
468 } | |
469 | |
470 void RendererImpl::PauseClockAndStopTicking_Locked() { | |
471 DVLOG(2) << __FUNCTION__; | |
472 interpolator_lock_.AssertAcquired(); | |
473 switch (interpolation_state_) { | |
474 case INTERPOLATION_STOPPED: | |
475 return; | |
476 | |
477 case INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE: | |
478 time_source_->StopTicking(); | |
479 break; | |
480 | |
481 case INTERPOLATION_STARTED: | |
482 if (time_source_) | |
483 time_source_->StopTicking(); | |
484 interpolator_->StopInterpolating(); | |
485 break; | |
486 } | |
487 | |
488 interpolation_state_ = INTERPOLATION_STOPPED; | |
489 } | |
490 | |
491 void RendererImpl::StartClockIfWaitingForTimeUpdate_Locked() { | |
492 interpolator_lock_.AssertAcquired(); | |
493 if (interpolation_state_ != INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE) | |
494 return; | |
495 | |
496 interpolation_state_ = INTERPOLATION_STARTED; | |
497 interpolator_->StartInterpolating(); | |
498 } | |
499 | |
500 void RendererImpl::OnAudioRendererEnded() { | |
501 DVLOG(2) << __FUNCTION__; | |
502 DCHECK(task_runner_->BelongsToCurrentThread()); | |
503 | |
504 if (state_ != kPlaying) | |
505 return; | |
506 | |
507 DCHECK(!audio_ended_); | |
508 audio_ended_ = true; | |
509 | |
510 // Start clock since there is no more audio to trigger clock updates. | |
511 { | |
512 base::AutoLock auto_lock(interpolator_lock_); | |
513 interpolator_->SetUpperBound(get_duration_cb_.Run()); | |
514 StartClockIfWaitingForTimeUpdate_Locked(); | |
515 } | |
516 | |
517 RunEndedCallbackIfNeeded(); | |
518 } | |
519 | |
520 void RendererImpl::OnVideoRendererEnded() { | |
521 DVLOG(2) << __FUNCTION__; | |
522 DCHECK(task_runner_->BelongsToCurrentThread()); | |
523 | |
524 if (state_ != kPlaying) | |
525 return; | |
526 | |
527 DCHECK(!video_ended_); | |
528 video_ended_ = true; | |
529 | |
530 RunEndedCallbackIfNeeded(); | |
531 } | |
532 | |
533 void RendererImpl::RunEndedCallbackIfNeeded() { | |
534 DVLOG(2) << __FUNCTION__; | |
535 DCHECK(task_runner_->BelongsToCurrentThread()); | |
536 | |
537 if (audio_renderer_ && !audio_ended_) | |
538 return; | |
539 | |
540 if (video_renderer_ && !video_ended_) | |
541 return; | |
542 | |
543 { | |
544 base::AutoLock auto_lock(interpolator_lock_); | |
545 PauseClockAndStopTicking_Locked(); | |
546 base::TimeDelta duration = get_duration_cb_.Run(); | |
547 interpolator_->SetBounds(duration, duration); | |
548 } | |
549 | |
550 ended_cb_.Run(); | |
551 } | |
552 | |
553 void RendererImpl::OnError(PipelineStatus error) { | |
554 DVLOG(2) << __FUNCTION__; | |
555 DCHECK(task_runner_->BelongsToCurrentThread()); | |
556 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; | |
557 | |
558 state_ = kError; | |
559 | |
560 // Pipeline will destroy |this| as the result of error. | |
561 base::ResetAndReturn(&error_cb_).Run(error); | |
562 | |
563 FireAllPendingCallbacks(); | |
564 } | |
565 | |
566 void RendererImpl::FireAllPendingCallbacks() { | |
567 DCHECK(task_runner_->BelongsToCurrentThread()); | |
568 | |
569 if (!init_cb_.is_null()) | |
570 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT); | |
571 | |
572 if (!flush_cb_.is_null()) | |
573 base::ResetAndReturn(&flush_cb_).Run(); | |
574 } | |
575 | |
576 } // namespace media | |
OLD | NEW |