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

Side by Side Diff: media/filters/renderer_impl.cc

Issue 418143005: media: Introduce Renderer interface and RendererImpl. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase only Created 6 years, 4 months 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 | Annotate | Revision Log
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 "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;
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.
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_);
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_, 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();
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698