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

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

Powered by Google App Engine
This is Rietveld 408576698