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_stream_provider.h"
15 #include "media/base/time_source.h"
16 #include "media/base/video_renderer.h"
17 #include "media/base/wall_clock_time_source.h"
18
19 namespace media {
20
RendererImpl(const scoped_refptr<base::SingleThreadTaskRunner> & task_runner,DemuxerStreamProvider * demuxer_stream_provider,scoped_ptr<AudioRenderer> audio_renderer,scoped_ptr<VideoRenderer> video_renderer)21 RendererImpl::RendererImpl(
22 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
23 DemuxerStreamProvider* demuxer_stream_provider,
24 scoped_ptr<AudioRenderer> audio_renderer,
25 scoped_ptr<VideoRenderer> video_renderer)
26 : state_(STATE_UNINITIALIZED),
27 task_runner_(task_runner),
28 demuxer_stream_provider_(demuxer_stream_provider),
29 audio_renderer_(audio_renderer.Pass()),
30 video_renderer_(video_renderer.Pass()),
31 time_source_(NULL),
32 time_ticking_(false),
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 clockless_video_playback_enabled_for_testing_(false),
39 weak_factory_(this),
40 weak_this_(weak_factory_.GetWeakPtr()) {
41 DVLOG(1) << __FUNCTION__;
42 }
43
~RendererImpl()44 RendererImpl::~RendererImpl() {
45 DVLOG(1) << __FUNCTION__;
46 DCHECK(task_runner_->BelongsToCurrentThread());
47
48 // Tear down in opposite order of construction as |video_renderer_| can still
49 // need |time_source_| (which can be |audio_renderer_|) to be alive.
50 video_renderer_.reset();
51 audio_renderer_.reset();
52
53 FireAllPendingCallbacks();
54 }
55
Initialize(const base::Closure & init_cb,const StatisticsCB & statistics_cb,const base::Closure & ended_cb,const PipelineStatusCB & error_cb,const BufferingStateCB & buffering_state_cb)56 void RendererImpl::Initialize(const base::Closure& 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 DVLOG(1) << __FUNCTION__;
62 DCHECK(task_runner_->BelongsToCurrentThread());
63 DCHECK_EQ(state_, STATE_UNINITIALIZED) << state_;
64 DCHECK(!init_cb.is_null());
65 DCHECK(!statistics_cb.is_null());
66 DCHECK(!ended_cb.is_null());
67 DCHECK(!error_cb.is_null());
68 DCHECK(!buffering_state_cb.is_null());
69 DCHECK(demuxer_stream_provider_->GetStream(DemuxerStream::AUDIO) ||
70 demuxer_stream_provider_->GetStream(DemuxerStream::VIDEO));
71
72 statistics_cb_ = statistics_cb;
73 ended_cb_ = ended_cb;
74 error_cb_ = error_cb;
75 buffering_state_cb_ = buffering_state_cb;
76
77 init_cb_ = init_cb;
78 state_ = STATE_INITIALIZING;
79 InitializeAudioRenderer();
80 }
81
Flush(const base::Closure & flush_cb)82 void RendererImpl::Flush(const base::Closure& flush_cb) {
83 DVLOG(1) << __FUNCTION__;
84 DCHECK(task_runner_->BelongsToCurrentThread());
85 DCHECK_EQ(state_, STATE_PLAYING) << state_;
86 DCHECK(flush_cb_.is_null());
87
88 flush_cb_ = flush_cb;
89 state_ = STATE_FLUSHING;
90
91 if (time_ticking_)
92 PausePlayback();
93
94 FlushAudioRenderer();
95 }
96
StartPlayingFrom(base::TimeDelta time)97 void RendererImpl::StartPlayingFrom(base::TimeDelta time) {
98 DVLOG(1) << __FUNCTION__;
99 DCHECK(task_runner_->BelongsToCurrentThread());
100 DCHECK_EQ(state_, STATE_PLAYING) << state_;
101
102 time_source_->SetMediaTime(time);
103
104 if (audio_renderer_)
105 audio_renderer_->StartPlaying();
106 if (video_renderer_)
107 video_renderer_->StartPlayingFrom(time);
108 }
109
SetPlaybackRate(float playback_rate)110 void RendererImpl::SetPlaybackRate(float playback_rate) {
111 DVLOG(1) << __FUNCTION__ << "(" << playback_rate << ")";
112 DCHECK(task_runner_->BelongsToCurrentThread());
113
114 // Playback rate changes are only carried out while playing.
115 if (state_ != STATE_PLAYING)
116 return;
117
118 time_source_->SetPlaybackRate(playback_rate);
119 }
120
SetVolume(float volume)121 void RendererImpl::SetVolume(float volume) {
122 DVLOG(1) << __FUNCTION__;
123 DCHECK(task_runner_->BelongsToCurrentThread());
124
125 if (audio_renderer_)
126 audio_renderer_->SetVolume(volume);
127 }
128
GetMediaTime()129 base::TimeDelta RendererImpl::GetMediaTime() {
130 // No BelongsToCurrentThread() checking because this can be called from other
131 // threads.
132 return time_source_->CurrentMediaTime();
133 }
134
HasAudio()135 bool RendererImpl::HasAudio() {
136 DCHECK(task_runner_->BelongsToCurrentThread());
137 return audio_renderer_ != NULL;
138 }
139
HasVideo()140 bool RendererImpl::HasVideo() {
141 DCHECK(task_runner_->BelongsToCurrentThread());
142 return video_renderer_ != NULL;
143 }
144
SetCdm(MediaKeys * cdm)145 void RendererImpl::SetCdm(MediaKeys* cdm) {
146 DVLOG(1) << __FUNCTION__;
147 DCHECK(task_runner_->BelongsToCurrentThread());
148 // TODO(xhwang): Explore to possibility to move CDM setting from
149 // WebMediaPlayerImpl to this class. See http://crbug.com/401264
150 NOTREACHED();
151 }
152
DisableUnderflowForTesting()153 void RendererImpl::DisableUnderflowForTesting() {
154 DVLOG(1) << __FUNCTION__;
155 DCHECK(task_runner_->BelongsToCurrentThread());
156 DCHECK_EQ(state_, STATE_UNINITIALIZED);
157
158 underflow_disabled_for_testing_ = true;
159 }
160
EnableClocklessVideoPlaybackForTesting()161 void RendererImpl::EnableClocklessVideoPlaybackForTesting() {
162 DVLOG(1) << __FUNCTION__;
163 DCHECK(task_runner_->BelongsToCurrentThread());
164 DCHECK_EQ(state_, STATE_UNINITIALIZED);
165 DCHECK(underflow_disabled_for_testing_)
166 << "Underflow must be disabled for clockless video playback";
167
168 clockless_video_playback_enabled_for_testing_ = true;
169 }
170
GetMediaTimeForSyncingVideo()171 base::TimeDelta RendererImpl::GetMediaTimeForSyncingVideo() {
172 // No BelongsToCurrentThread() checking because this can be called from other
173 // threads.
174 //
175 // TODO(scherkus): Currently called from VideoRendererImpl's internal thread,
176 // which should go away at some point http://crbug.com/110814
177 if (clockless_video_playback_enabled_for_testing_)
178 return base::TimeDelta::Max();
179
180 return time_source_->CurrentMediaTimeForSyncingVideo();
181 }
182
InitializeAudioRenderer()183 void RendererImpl::InitializeAudioRenderer() {
184 DVLOG(1) << __FUNCTION__;
185 DCHECK(task_runner_->BelongsToCurrentThread());
186 DCHECK_EQ(state_, STATE_INITIALIZING) << state_;
187 DCHECK(!init_cb_.is_null());
188
189 PipelineStatusCB done_cb =
190 base::Bind(&RendererImpl::OnAudioRendererInitializeDone, weak_this_);
191
192 if (!demuxer_stream_provider_->GetStream(DemuxerStream::AUDIO)) {
193 audio_renderer_.reset();
194 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK));
195 return;
196 }
197
198 audio_renderer_->Initialize(
199 demuxer_stream_provider_->GetStream(DemuxerStream::AUDIO),
200 done_cb,
201 base::Bind(&RendererImpl::OnUpdateStatistics, weak_this_),
202 base::Bind(&RendererImpl::OnBufferingStateChanged, weak_this_,
203 &audio_buffering_state_),
204 base::Bind(&RendererImpl::OnAudioRendererEnded, weak_this_),
205 base::Bind(&RendererImpl::OnError, weak_this_));
206 }
207
OnAudioRendererInitializeDone(PipelineStatus status)208 void RendererImpl::OnAudioRendererInitializeDone(PipelineStatus status) {
209 DVLOG(1) << __FUNCTION__ << ": " << status;
210 DCHECK(task_runner_->BelongsToCurrentThread());
211 DCHECK_EQ(state_, STATE_INITIALIZING) << state_;
212 DCHECK(!init_cb_.is_null());
213
214 if (status != PIPELINE_OK) {
215 audio_renderer_.reset();
216 OnError(status);
217 return;
218 }
219
220 InitializeVideoRenderer();
221 }
222
InitializeVideoRenderer()223 void RendererImpl::InitializeVideoRenderer() {
224 DVLOG(1) << __FUNCTION__;
225 DCHECK(task_runner_->BelongsToCurrentThread());
226 DCHECK_EQ(state_, STATE_INITIALIZING) << state_;
227 DCHECK(!init_cb_.is_null());
228
229 PipelineStatusCB done_cb =
230 base::Bind(&RendererImpl::OnVideoRendererInitializeDone, weak_this_);
231
232 if (!demuxer_stream_provider_->GetStream(DemuxerStream::VIDEO)) {
233 video_renderer_.reset();
234 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK));
235 return;
236 }
237
238 video_renderer_->Initialize(
239 demuxer_stream_provider_->GetStream(DemuxerStream::VIDEO),
240 demuxer_stream_provider_->GetLiveness() ==
241 DemuxerStreamProvider::LIVENESS_LIVE,
242 done_cb,
243 base::Bind(&RendererImpl::OnUpdateStatistics, weak_this_),
244 base::Bind(&RendererImpl::OnBufferingStateChanged,
245 weak_this_,
246 &video_buffering_state_),
247 base::Bind(&RendererImpl::OnVideoRendererEnded, weak_this_),
248 base::Bind(&RendererImpl::OnError, weak_this_),
249 base::Bind(&RendererImpl::GetMediaTimeForSyncingVideo,
250 base::Unretained(this)));
251 }
252
OnVideoRendererInitializeDone(PipelineStatus status)253 void RendererImpl::OnVideoRendererInitializeDone(PipelineStatus status) {
254 DVLOG(1) << __FUNCTION__ << ": " << status;
255 DCHECK(task_runner_->BelongsToCurrentThread());
256 DCHECK_EQ(state_, STATE_INITIALIZING) << state_;
257 DCHECK(!init_cb_.is_null());
258
259 if (status != PIPELINE_OK) {
260 audio_renderer_.reset();
261 video_renderer_.reset();
262 OnError(status);
263 return;
264 }
265
266 if (audio_renderer_) {
267 time_source_ = audio_renderer_->GetTimeSource();
268 } else {
269 wall_clock_time_source_.reset(new WallClockTimeSource());
270 time_source_ = wall_clock_time_source_.get();
271 }
272
273 state_ = STATE_PLAYING;
274 DCHECK(time_source_);
275 DCHECK(audio_renderer_ || video_renderer_);
276 base::ResetAndReturn(&init_cb_).Run();
277 }
278
FlushAudioRenderer()279 void RendererImpl::FlushAudioRenderer() {
280 DVLOG(1) << __FUNCTION__;
281 DCHECK(task_runner_->BelongsToCurrentThread());
282 DCHECK_EQ(state_, STATE_FLUSHING) << state_;
283 DCHECK(!flush_cb_.is_null());
284
285 if (!audio_renderer_) {
286 OnAudioRendererFlushDone();
287 return;
288 }
289
290 audio_renderer_->Flush(
291 base::Bind(&RendererImpl::OnAudioRendererFlushDone, weak_this_));
292 }
293
OnAudioRendererFlushDone()294 void RendererImpl::OnAudioRendererFlushDone() {
295 DVLOG(1) << __FUNCTION__;
296 DCHECK(task_runner_->BelongsToCurrentThread());
297
298 if (state_ == STATE_ERROR) {
299 DCHECK(flush_cb_.is_null());
300 return;
301 }
302
303 DCHECK_EQ(state_, STATE_FLUSHING) << state_;
304 DCHECK(!flush_cb_.is_null());
305
306 DCHECK_EQ(audio_buffering_state_, BUFFERING_HAVE_NOTHING);
307 audio_ended_ = false;
308 FlushVideoRenderer();
309 }
310
FlushVideoRenderer()311 void RendererImpl::FlushVideoRenderer() {
312 DVLOG(1) << __FUNCTION__;
313 DCHECK(task_runner_->BelongsToCurrentThread());
314 DCHECK_EQ(state_, STATE_FLUSHING) << state_;
315 DCHECK(!flush_cb_.is_null());
316
317 if (!video_renderer_) {
318 OnVideoRendererFlushDone();
319 return;
320 }
321
322 video_renderer_->Flush(
323 base::Bind(&RendererImpl::OnVideoRendererFlushDone, weak_this_));
324 }
325
OnVideoRendererFlushDone()326 void RendererImpl::OnVideoRendererFlushDone() {
327 DVLOG(1) << __FUNCTION__;
328 DCHECK(task_runner_->BelongsToCurrentThread());
329
330 if (state_ == STATE_ERROR) {
331 DCHECK(flush_cb_.is_null());
332 return;
333 }
334
335 DCHECK_EQ(state_, STATE_FLUSHING) << state_;
336 DCHECK(!flush_cb_.is_null());
337
338 DCHECK_EQ(video_buffering_state_, BUFFERING_HAVE_NOTHING);
339 video_ended_ = false;
340 state_ = STATE_PLAYING;
341 base::ResetAndReturn(&flush_cb_).Run();
342 }
343
OnUpdateStatistics(const PipelineStatistics & stats)344 void RendererImpl::OnUpdateStatistics(const PipelineStatistics& stats) {
345 DCHECK(task_runner_->BelongsToCurrentThread());
346 statistics_cb_.Run(stats);
347 }
348
OnBufferingStateChanged(BufferingState * buffering_state,BufferingState new_buffering_state)349 void RendererImpl::OnBufferingStateChanged(BufferingState* buffering_state,
350 BufferingState new_buffering_state) {
351 DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", "
352 << new_buffering_state << ") "
353 << (buffering_state == &audio_buffering_state_ ? "audio" : "video");
354 DCHECK(task_runner_->BelongsToCurrentThread());
355 bool was_waiting_for_enough_data = WaitingForEnoughData();
356
357 *buffering_state = new_buffering_state;
358
359 // Disable underflow by ignoring updates that renderers have ran out of data.
360 if (state_ == STATE_PLAYING && underflow_disabled_for_testing_ &&
361 time_ticking_) {
362 DVLOG(1) << "Update ignored because underflow is disabled for testing.";
363 return;
364 }
365
366 // Renderer underflowed.
367 if (!was_waiting_for_enough_data && WaitingForEnoughData()) {
368 PausePlayback();
369
370 // TODO(scherkus): Fire BUFFERING_HAVE_NOTHING callback to alert clients of
371 // underflow state http://crbug.com/144683
372 return;
373 }
374
375 // Renderer prerolled.
376 if (was_waiting_for_enough_data && !WaitingForEnoughData()) {
377 StartPlayback();
378 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH);
379 return;
380 }
381 }
382
WaitingForEnoughData() const383 bool RendererImpl::WaitingForEnoughData() const {
384 DCHECK(task_runner_->BelongsToCurrentThread());
385 if (state_ != STATE_PLAYING)
386 return false;
387 if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH)
388 return true;
389 if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH)
390 return true;
391 return false;
392 }
393
PausePlayback()394 void RendererImpl::PausePlayback() {
395 DVLOG(1) << __FUNCTION__;
396 DCHECK(task_runner_->BelongsToCurrentThread());
397 DCHECK(time_ticking_);
398 switch (state_) {
399 case STATE_PLAYING:
400 DCHECK(PlaybackHasEnded() || WaitingForEnoughData())
401 << "Playback should only pause due to ending or underflowing";
402 break;
403
404 case STATE_FLUSHING:
405 // It's OK to pause playback when flushing.
406 break;
407
408 case STATE_UNINITIALIZED:
409 case STATE_INITIALIZING:
410 case STATE_ERROR:
411 NOTREACHED() << "Invalid state: " << state_;
412 break;
413 }
414
415 time_ticking_ = false;
416 time_source_->StopTicking();
417 }
418
StartPlayback()419 void RendererImpl::StartPlayback() {
420 DVLOG(1) << __FUNCTION__;
421 DCHECK(task_runner_->BelongsToCurrentThread());
422 DCHECK_EQ(state_, STATE_PLAYING);
423 DCHECK(!time_ticking_);
424 DCHECK(!WaitingForEnoughData());
425
426 time_ticking_ = true;
427 time_source_->StartTicking();
428 }
429
OnAudioRendererEnded()430 void RendererImpl::OnAudioRendererEnded() {
431 DVLOG(1) << __FUNCTION__;
432 DCHECK(task_runner_->BelongsToCurrentThread());
433
434 if (state_ != STATE_PLAYING)
435 return;
436
437 DCHECK(!audio_ended_);
438 audio_ended_ = true;
439
440 RunEndedCallbackIfNeeded();
441 }
442
OnVideoRendererEnded()443 void RendererImpl::OnVideoRendererEnded() {
444 DVLOG(1) << __FUNCTION__;
445 DCHECK(task_runner_->BelongsToCurrentThread());
446
447 if (state_ != STATE_PLAYING)
448 return;
449
450 DCHECK(!video_ended_);
451 video_ended_ = true;
452
453 RunEndedCallbackIfNeeded();
454 }
455
PlaybackHasEnded() const456 bool RendererImpl::PlaybackHasEnded() const {
457 DVLOG(1) << __FUNCTION__;
458 DCHECK(task_runner_->BelongsToCurrentThread());
459
460 if (audio_renderer_ && !audio_ended_)
461 return false;
462
463 if (video_renderer_ && !video_ended_)
464 return false;
465
466 return true;
467 }
468
RunEndedCallbackIfNeeded()469 void RendererImpl::RunEndedCallbackIfNeeded() {
470 DVLOG(1) << __FUNCTION__;
471 DCHECK(task_runner_->BelongsToCurrentThread());
472
473 if (!PlaybackHasEnded())
474 return;
475
476 if (time_ticking_)
477 PausePlayback();
478
479 ended_cb_.Run();
480 }
481
OnError(PipelineStatus error)482 void RendererImpl::OnError(PipelineStatus error) {
483 DVLOG(1) << __FUNCTION__ << "(" << error << ")";
484 DCHECK(task_runner_->BelongsToCurrentThread());
485 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
486
487 state_ = STATE_ERROR;
488
489 // Pipeline will destroy |this| as the result of error.
490 base::ResetAndReturn(&error_cb_).Run(error);
491
492 FireAllPendingCallbacks();
493 }
494
FireAllPendingCallbacks()495 void RendererImpl::FireAllPendingCallbacks() {
496 DCHECK(task_runner_->BelongsToCurrentThread());
497
498 if (!init_cb_.is_null())
499 base::ResetAndReturn(&init_cb_).Run();
500
501 if (!flush_cb_.is_null())
502 base::ResetAndReturn(&flush_cb_).Run();
503 }
504
505 } // namespace media
506