• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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