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_(STATE_UNINITIALIZED), | |
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_, STATE_UNINITIALIZED) << 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; | |
81 state_ = STATE_INITIALIZING; | |
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_, STATE_PLAYING) << 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_ = STATE_FLUSHING; | |
98 FlushAudioRenderer(); | |
99 } | |
100 | |
101 void RendererImpl::StartPlayingFrom(base::TimeDelta time) { | |
102 DVLOG(2) << __FUNCTION__; | |
103 DCHECK(task_runner_->BelongsToCurrentThread()); | |
104 DCHECK_EQ(state_, STATE_PLAYING) << state_; | |
105 | |
106 { | |
107 base::AutoLock auto_lock(interpolator_lock_); | |
108 interpolator_->SetBounds(time, time); | |
109 } | |
110 | |
111 if (time_source_) | |
112 time_source_->SetMediaTime(time); | |
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_ != STATE_PLAYING) | |
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. 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_, STATE_UNINITIALIZED); | |
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_, STATE_UNINITIALIZED); | |
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_, STATE_INITIALIZING) << state_; | |
195 DCHECK(!init_cb_.is_null()); | |
196 | |
197 PipelineStatusCB done_cb = | |
198 base::Bind(&RendererImpl::OnAudioRendererInitializeDone, weak_this_); | |
199 | |
200 if (!demuxer_->GetStream(DemuxerStream::AUDIO)) { | |
201 audio_renderer_.reset(); | |
202 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK)); | |
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_, STATE_INITIALIZING) << state_; | |
221 DCHECK(!init_cb_.is_null()); | |
222 | |
223 if (status != PIPELINE_OK) { | |
224 audio_renderer_.reset(); | |
225 state_ = STATE_ERROR; | |
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_, STATE_INITIALIZING) << 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_, STATE_INITIALIZING) << state_; | |
269 DCHECK(!init_cb_.is_null()); | |
270 | |
271 if (status != PIPELINE_OK) { | |
272 audio_renderer_.reset(); | |
273 video_renderer_.reset(); | |
274 state_ = STATE_ERROR; | |
275 base::ResetAndReturn(&init_cb_).Run(status); | |
276 return; | |
277 } | |
278 | |
279 state_ = STATE_PLAYING; | |
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_, STATE_FLUSHING) << state_; | |
288 DCHECK(!flush_cb_.is_null()); | |
289 | |
290 if (!audio_renderer_) { | |
291 OnAudioRendererFlushDone(); | |
292 return; | |
293 } | |
294 | |
295 audio_renderer_->Flush( | |
296 base::Bind(&RendererImpl::OnAudioRendererFlushDone, weak_this_)); | |
297 } | |
298 | |
299 void RendererImpl::OnAudioRendererFlushDone() { | |
300 DVLOG(1) << __FUNCTION__; | |
301 DCHECK(task_runner_->BelongsToCurrentThread()); | |
302 | |
303 if (state_ == STATE_ERROR) { | |
304 DCHECK(flush_cb_.is_null()); | |
305 return; | |
306 } | |
307 | |
308 DCHECK_EQ(state_, STATE_FLUSHING) << state_; | |
309 DCHECK(!flush_cb_.is_null()); | |
310 | |
311 DCHECK_EQ(audio_buffering_state_, BUFFERING_HAVE_NOTHING); | |
312 audio_ended_ = false; | |
313 FlushVideoRenderer(); | |
314 } | |
315 | |
316 void RendererImpl::FlushVideoRenderer() { | |
317 DVLOG(1) << __FUNCTION__; | |
318 DCHECK(task_runner_->BelongsToCurrentThread()); | |
319 DCHECK_EQ(state_, STATE_FLUSHING) << state_; | |
320 DCHECK(!flush_cb_.is_null()); | |
321 | |
322 if (!video_renderer_) { | |
323 OnVideoRendererFlushDone(); | |
324 return; | |
325 } | |
326 | |
327 video_renderer_->Flush( | |
328 base::Bind(&RendererImpl::OnVideoRendererFlushDone, weak_this_)); | |
329 } | |
330 | |
331 void RendererImpl::OnVideoRendererFlushDone() { | |
332 DVLOG(1) << __FUNCTION__; | |
333 DCHECK(task_runner_->BelongsToCurrentThread()); | |
334 | |
335 if (state_ == STATE_ERROR) { | |
336 DCHECK(flush_cb_.is_null()); | |
337 return; | |
338 } | |
339 | |
340 DCHECK_EQ(state_, STATE_FLUSHING) << state_; | |
341 DCHECK(!flush_cb_.is_null()); | |
342 | |
343 DCHECK_EQ(video_buffering_state_, BUFFERING_HAVE_NOTHING); | |
344 video_ended_ = false; | |
345 state_ = STATE_PLAYING; | |
346 base::ResetAndReturn(&flush_cb_).Run(); | |
347 } | |
348 | |
349 void RendererImpl::OnAudioTimeUpdate(base::TimeDelta time, | |
350 base::TimeDelta max_time) { | |
351 DVLOG(3) << __FUNCTION__ << "(" << time.InMilliseconds() | |
352 << ", " << max_time.InMilliseconds() << ")"; | |
353 DCHECK(task_runner_->BelongsToCurrentThread()); | |
354 DCHECK_LE(time.InMicroseconds(), max_time.InMicroseconds()); | |
355 | |
356 base::AutoLock auto_lock(interpolator_lock_); | |
357 | |
358 if (interpolation_state_ == INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE && | |
359 time < interpolator_->GetInterpolatedTime()) { | |
360 return; | |
361 } | |
362 | |
363 if (state_ == STATE_FLUSHING) | |
364 return; | |
365 | |
366 interpolator_->SetBounds(time, max_time); | |
367 StartClockIfWaitingForTimeUpdate_Locked(); | |
368 } | |
369 | |
370 void RendererImpl::OnVideoTimeUpdate(base::TimeDelta max_time) { | |
371 DVLOG(3) << __FUNCTION__ << "(" << max_time.InMilliseconds() << ")"; | |
372 DCHECK(task_runner_->BelongsToCurrentThread()); | |
373 | |
374 if (audio_renderer_) | |
375 return; | |
376 | |
377 if (state_ == STATE_FLUSHING) | |
378 return; | |
379 | |
380 base::AutoLock auto_lock(interpolator_lock_); | |
381 DCHECK_NE(interpolation_state_, INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE); | |
382 interpolator_->SetUpperBound(max_time); | |
383 } | |
384 | |
385 void RendererImpl::OnUpdateStatistics(const PipelineStatistics& stats) { | |
386 DCHECK(task_runner_->BelongsToCurrentThread()); | |
387 statistics_cb_.Run(stats); | |
388 } | |
389 | |
390 void RendererImpl::OnBufferingStateChanged(BufferingState* buffering_state, | |
391 BufferingState new_buffering_state) { | |
392 DVLOG(2) << __FUNCTION__ << "(" << *buffering_state << ", " | |
393 << new_buffering_state << ") " | |
394 << (buffering_state == &audio_buffering_state_ ? "audio" : "video"); | |
395 DCHECK(task_runner_->BelongsToCurrentThread()); | |
396 bool was_waiting_for_enough_data = WaitingForEnoughData(); | |
397 | |
398 *buffering_state = new_buffering_state; | |
399 | |
400 // Disable underflow by ignoring updates that renderers have ran out of data. | |
401 if (state_ == STATE_PLAYING && underflow_disabled_for_testing_ && | |
402 interpolation_state_ != INTERPOLATION_STOPPED) { | |
403 DVLOG(2) << "Update ignored because underflow is disabled for testing."; | |
404 return; | |
405 } | |
406 | |
407 // Renderer underflowed. | |
408 if (!was_waiting_for_enough_data && WaitingForEnoughData()) { | |
409 PausePlayback(); | |
410 | |
411 // TODO(scherkus): Fire BUFFERING_HAVE_NOTHING callback to alert clients of | |
412 // underflow state http://crbug.com/144683 | |
413 return; | |
414 } | |
415 | |
416 // Renderer prerolled. | |
417 if (was_waiting_for_enough_data && !WaitingForEnoughData()) { | |
418 StartPlayback(); | |
419 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH); | |
420 return; | |
421 } | |
422 } | |
423 | |
424 bool RendererImpl::WaitingForEnoughData() const { | |
425 DCHECK(task_runner_->BelongsToCurrentThread()); | |
426 if (state_ != STATE_PLAYING) | |
427 return false; | |
428 if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH) | |
429 return true; | |
430 if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH) | |
431 return true; | |
432 return false; | |
433 } | |
434 | |
435 void RendererImpl::PausePlayback() { | |
436 DVLOG(2) << __FUNCTION__; | |
437 DCHECK(task_runner_->BelongsToCurrentThread()); | |
438 DCHECK_EQ(state_, STATE_PLAYING); | |
439 DCHECK(WaitingForEnoughData()); | |
440 | |
441 base::AutoLock auto_lock(interpolator_lock_); | |
442 PauseClockAndStopTicking_Locked(); | |
443 } | |
444 | |
445 void RendererImpl::StartPlayback() { | |
446 DVLOG(2) << __FUNCTION__; | |
447 DCHECK(task_runner_->BelongsToCurrentThread()); | |
448 DCHECK_EQ(state_, STATE_PLAYING); | |
449 DCHECK_EQ(interpolation_state_, INTERPOLATION_STOPPED); | |
450 DCHECK(!WaitingForEnoughData()); | |
451 | |
452 if (time_source_) { | |
453 // We use audio stream to update the interpolator. So if there is such a | |
454 // stream, we pause the interpolator until we receive a valid time update. | |
455 base::AutoLock auto_lock(interpolator_lock_); | |
456 interpolation_state_ = INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE; | |
457 time_source_->StartTicking(); | |
458 } else { | |
459 base::AutoLock auto_lock(interpolator_lock_); | |
460 interpolation_state_ = INTERPOLATION_STARTED; | |
461 interpolator_->SetUpperBound(get_duration_cb_.Run()); | |
462 interpolator_->StartInterpolating(); | |
463 } | |
464 } | |
465 | |
466 void RendererImpl::PauseClockAndStopTicking_Locked() { | |
467 DVLOG(2) << __FUNCTION__; | |
468 interpolator_lock_.AssertAcquired(); | |
469 switch (interpolation_state_) { | |
470 case INTERPOLATION_STOPPED: | |
471 return; | |
472 | |
473 case INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE: | |
474 time_source_->StopTicking(); | |
475 break; | |
476 | |
477 case INTERPOLATION_STARTED: | |
478 if (time_source_) | |
479 time_source_->StopTicking(); | |
480 interpolator_->StopInterpolating(); | |
481 break; | |
482 } | |
483 | |
484 interpolation_state_ = INTERPOLATION_STOPPED; | |
485 } | |
486 | |
487 void RendererImpl::StartClockIfWaitingForTimeUpdate_Locked() { | |
488 interpolator_lock_.AssertAcquired(); | |
489 if (interpolation_state_ != INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE) | |
490 return; | |
491 | |
492 interpolation_state_ = INTERPOLATION_STARTED; | |
493 interpolator_->StartInterpolating(); | |
494 } | |
495 | |
496 void RendererImpl::OnAudioRendererEnded() { | |
497 DVLOG(2) << __FUNCTION__; | |
498 DCHECK(task_runner_->BelongsToCurrentThread()); | |
499 | |
500 if (state_ != STATE_PLAYING) | |
501 return; | |
502 | |
503 DCHECK(!audio_ended_); | |
504 audio_ended_ = true; | |
505 | |
506 // Start clock since there is no more audio to trigger clock updates. | |
507 { | |
508 base::AutoLock auto_lock(interpolator_lock_); | |
509 interpolator_->SetUpperBound(get_duration_cb_.Run()); | |
510 StartClockIfWaitingForTimeUpdate_Locked(); | |
511 } | |
512 | |
513 RunEndedCallbackIfNeeded(); | |
514 } | |
515 | |
516 void RendererImpl::OnVideoRendererEnded() { | |
517 DVLOG(2) << __FUNCTION__; | |
518 DCHECK(task_runner_->BelongsToCurrentThread()); | |
519 | |
520 if (state_ != STATE_PLAYING) | |
521 return; | |
522 | |
523 DCHECK(!video_ended_); | |
524 video_ended_ = true; | |
525 | |
526 RunEndedCallbackIfNeeded(); | |
527 } | |
528 | |
529 void RendererImpl::RunEndedCallbackIfNeeded() { | |
530 DVLOG(2) << __FUNCTION__; | |
531 DCHECK(task_runner_->BelongsToCurrentThread()); | |
532 | |
533 if (audio_renderer_ && !audio_ended_) | |
534 return; | |
535 | |
536 if (video_renderer_ && !video_ended_) | |
537 return; | |
538 | |
539 { | |
540 base::AutoLock auto_lock(interpolator_lock_); | |
541 PauseClockAndStopTicking_Locked(); | |
542 base::TimeDelta duration = get_duration_cb_.Run(); | |
543 interpolator_->SetBounds(duration, duration); | |
544 } | |
545 | |
546 ended_cb_.Run(); | |
547 } | |
548 | |
549 void RendererImpl::OnError(PipelineStatus error) { | |
550 DVLOG(2) << __FUNCTION__; | |
551 DCHECK(task_runner_->BelongsToCurrentThread()); | |
552 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; | |
553 | |
554 state_ = STATE_ERROR; | |
555 | |
556 // Pipeline will destroy |this| as the result of error. | |
557 base::ResetAndReturn(&error_cb_).Run(error); | |
558 | |
559 FireAllPendingCallbacks(); | |
560 } | |
561 | |
562 void RendererImpl::FireAllPendingCallbacks() { | |
563 DCHECK(task_runner_->BelongsToCurrentThread()); | |
564 | |
565 if (!init_cb_.is_null()) | |
566 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT); | |
567 | |
568 if (!flush_cb_.is_null()) | |
569 base::ResetAndReturn(&flush_cb_).Run(); | |
570 } | |
571 | |
572 } // namespace media | |
OLD | NEW |