OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chromecast/media/cma/filters/cma_renderer.h" | 5 #include "chromecast/renderer/media/cma_renderer.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/callback_helpers.h" | 8 #include "base/callback_helpers.h" |
9 #include "base/location.h" | 9 #include "base/location.h" |
10 #include "base/single_thread_task_runner.h" | 10 #include "base/single_thread_task_runner.h" |
11 #include "base/thread_task_runner_handle.h" | 11 #include "base/thread_task_runner_handle.h" |
12 #include "chromecast/media/cma/base/balanced_media_task_runner_factory.h" | 12 #include "chromecast/media/cma/base/balanced_media_task_runner_factory.h" |
13 #include "chromecast/media/cma/base/cma_logging.h" | 13 #include "chromecast/media/cma/base/cma_logging.h" |
14 #include "chromecast/media/cma/filters/demuxer_stream_adapter.h" | |
15 #include "chromecast/media/cma/filters/hole_frame_factory.h" | |
16 #include "chromecast/media/cma/pipeline/audio_pipeline.h" | |
17 #include "chromecast/media/cma/pipeline/av_pipeline_client.h" | 14 #include "chromecast/media/cma/pipeline/av_pipeline_client.h" |
18 #include "chromecast/media/cma/pipeline/media_pipeline.h" | |
19 #include "chromecast/media/cma/pipeline/media_pipeline_client.h" | 15 #include "chromecast/media/cma/pipeline/media_pipeline_client.h" |
20 #include "chromecast/media/cma/pipeline/video_pipeline.h" | |
21 #include "chromecast/media/cma/pipeline/video_pipeline_client.h" | 16 #include "chromecast/media/cma/pipeline/video_pipeline_client.h" |
| 17 #include "chromecast/renderer/media/audio_pipeline_proxy.h" |
| 18 #include "chromecast/renderer/media/demuxer_stream_adapter.h" |
| 19 #include "chromecast/renderer/media/hole_frame_factory.h" |
| 20 #include "chromecast/renderer/media/media_pipeline_proxy.h" |
| 21 #include "chromecast/renderer/media/video_pipeline_proxy.h" |
22 #include "media/base/bind_to_current_loop.h" | 22 #include "media/base/bind_to_current_loop.h" |
23 #include "media/base/demuxer_stream_provider.h" | 23 #include "media/base/demuxer_stream_provider.h" |
24 #include "media/base/pipeline_status.h" | 24 #include "media/base/pipeline_status.h" |
25 #include "media/base/time_delta_interpolator.h" | 25 #include "media/base/time_delta_interpolator.h" |
26 #include "media/base/video_renderer_sink.h" | 26 #include "media/base/video_renderer_sink.h" |
27 #include "media/renderers/gpu_video_accelerator_factories.h" | 27 #include "media/renderers/gpu_video_accelerator_factories.h" |
28 #include "ui/gfx/geometry/size.h" | 28 #include "ui/gfx/geometry/size.h" |
29 | 29 |
30 namespace chromecast { | 30 namespace chromecast { |
31 namespace media { | 31 namespace media { |
32 | 32 |
33 namespace { | 33 namespace { |
34 | 34 |
35 // Maximum difference between audio frame PTS and video frame PTS | 35 // Maximum difference between audio frame PTS and video frame PTS |
36 // for frames read from the DemuxerStream. | 36 // for frames read from the DemuxerStream. |
37 const base::TimeDelta kMaxDeltaFetcher( | 37 const base::TimeDelta kMaxDeltaFetcher(base::TimeDelta::FromMilliseconds(2000)); |
38 base::TimeDelta::FromMilliseconds(2000)); | |
39 | 38 |
40 void MediaPipelineClientDummyCallback() {} | 39 void MediaPipelineClientDummyCallback() { |
| 40 } |
41 | 41 |
42 } // namespace | 42 } // namespace |
43 | 43 |
44 CmaRenderer::CmaRenderer( | 44 CmaRenderer::CmaRenderer( |
45 scoped_ptr<MediaPipeline> media_pipeline, | 45 scoped_ptr<MediaPipelineProxy> media_pipeline, |
46 ::media::VideoRendererSink* video_renderer_sink, | 46 ::media::VideoRendererSink* video_renderer_sink, |
47 const scoped_refptr<::media::GpuVideoAcceleratorFactories>& gpu_factories) | 47 const scoped_refptr<::media::GpuVideoAcceleratorFactories>& gpu_factories) |
48 : media_task_runner_factory_( | 48 : media_task_runner_factory_( |
49 new BalancedMediaTaskRunnerFactory(kMaxDeltaFetcher)), | 49 new BalancedMediaTaskRunnerFactory(kMaxDeltaFetcher)), |
50 media_pipeline_(media_pipeline.Pass()), | 50 media_pipeline_(media_pipeline.Pass()), |
51 audio_pipeline_(media_pipeline_->GetAudioPipeline()), | 51 audio_pipeline_(media_pipeline_->GetAudioPipeline()), |
52 video_pipeline_(media_pipeline_->GetVideoPipeline()), | 52 video_pipeline_(media_pipeline_->GetVideoPipeline()), |
53 video_renderer_sink_(video_renderer_sink), | 53 video_renderer_sink_(video_renderer_sink), |
54 state_(kUninitialized), | 54 state_(kUninitialized), |
55 is_pending_transition_(false), | 55 is_pending_transition_(false), |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
115 waiting_for_decryption_key_cb_ = waiting_for_decryption_key_cb; | 115 waiting_for_decryption_key_cb_ = waiting_for_decryption_key_cb; |
116 | 116 |
117 MediaPipelineClient media_pipeline_client; | 117 MediaPipelineClient media_pipeline_client; |
118 media_pipeline_client.error_cb = error_cb_; | 118 media_pipeline_client.error_cb = error_cb_; |
119 media_pipeline_client.buffering_state_cb = ::media::BindToCurrentLoop( | 119 media_pipeline_client.buffering_state_cb = ::media::BindToCurrentLoop( |
120 base::Bind(&CmaRenderer::OnBufferingNotification, weak_this_)); | 120 base::Bind(&CmaRenderer::OnBufferingNotification, weak_this_)); |
121 media_pipeline_client.time_update_cb = ::media::BindToCurrentLoop( | 121 media_pipeline_client.time_update_cb = ::media::BindToCurrentLoop( |
122 base::Bind(&CmaRenderer::OnPlaybackTimeUpdated, weak_this_)); | 122 base::Bind(&CmaRenderer::OnPlaybackTimeUpdated, weak_this_)); |
123 media_pipeline_client.pipeline_backend_created_cb = | 123 media_pipeline_client.pipeline_backend_created_cb = |
124 base::Bind(&MediaPipelineClientDummyCallback); | 124 base::Bind(&MediaPipelineClientDummyCallback); |
125 media_pipeline_client.pipeline_backend_destroyed_cb | 125 media_pipeline_client.pipeline_backend_destroyed_cb = |
126 = base::Bind(&MediaPipelineClientDummyCallback); | 126 base::Bind(&MediaPipelineClientDummyCallback); |
127 media_pipeline_->SetClient(media_pipeline_client); | 127 media_pipeline_->SetClient(media_pipeline_client); |
128 | 128 |
129 init_cb_ = init_cb; | 129 init_cb_ = init_cb; |
130 InitializeAudioPipeline(); | 130 InitializeAudioPipeline(); |
131 } | 131 } |
132 | 132 |
133 void CmaRenderer::Flush(const base::Closure& flush_cb) { | 133 void CmaRenderer::Flush(const base::Closure& flush_cb) { |
134 CMALOG(kLogControl) << __FUNCTION__; | 134 CMALOG(kLogControl) << __FUNCTION__; |
135 DCHECK(thread_checker_.CalledOnValidThread()); | 135 DCHECK(thread_checker_.CalledOnValidThread()); |
136 BeginStateTransition(); | 136 BeginStateTransition(); |
137 | 137 |
138 DCHECK(flush_cb_.is_null()); | 138 DCHECK(flush_cb_.is_null()); |
139 flush_cb_ = flush_cb; | 139 flush_cb_ = flush_cb; |
140 | 140 |
141 if (state_ == kError) { | 141 if (state_ == kError) { |
142 OnError(::media::PIPELINE_ERROR_ABORT); | 142 OnError(::media::PIPELINE_ERROR_ABORT); |
143 return; | 143 return; |
144 } | 144 } |
145 | 145 |
146 DCHECK_EQ(state_, kPlaying) << state_; | 146 DCHECK_EQ(state_, kPlaying) << state_; |
147 media_pipeline_->Flush( | 147 media_pipeline_->Flush(::media::BindToCurrentLoop( |
148 ::media::BindToCurrentLoop( | 148 base::Bind(&CmaRenderer::OnFlushDone, weak_this_))); |
149 base::Bind(&CmaRenderer::OnFlushDone, weak_this_))); | |
150 | 149 |
151 { | 150 { |
152 base::AutoLock auto_lock(time_interpolator_lock_); | 151 base::AutoLock auto_lock(time_interpolator_lock_); |
153 time_interpolator_->StopInterpolating(); | 152 time_interpolator_->StopInterpolating(); |
154 } | 153 } |
155 } | 154 } |
156 | 155 |
157 void CmaRenderer::StartPlayingFrom(base::TimeDelta time) { | 156 void CmaRenderer::StartPlayingFrom(base::TimeDelta time) { |
158 CMALOG(kLogControl) << __FUNCTION__ << ": " << time.InMilliseconds(); | 157 CMALOG(kLogControl) << __FUNCTION__ << ": " << time.InMilliseconds(); |
159 DCHECK(thread_checker_.CalledOnValidThread()); | 158 DCHECK(thread_checker_.CalledOnValidThread()); |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
241 | 240 |
242 void CmaRenderer::InitializeAudioPipeline() { | 241 void CmaRenderer::InitializeAudioPipeline() { |
243 DCHECK(thread_checker_.CalledOnValidThread()); | 242 DCHECK(thread_checker_.CalledOnValidThread()); |
244 DCHECK_EQ(state_, kUninitialized) << state_; | 243 DCHECK_EQ(state_, kUninitialized) << state_; |
245 DCHECK(!init_cb_.is_null()); | 244 DCHECK(!init_cb_.is_null()); |
246 | 245 |
247 ::media::DemuxerStream* stream = | 246 ::media::DemuxerStream* stream = |
248 demuxer_stream_provider_->GetStream(::media::DemuxerStream::AUDIO); | 247 demuxer_stream_provider_->GetStream(::media::DemuxerStream::AUDIO); |
249 ::media::PipelineStatusCB audio_initialization_done_cb = | 248 ::media::PipelineStatusCB audio_initialization_done_cb = |
250 ::media::BindToCurrentLoop( | 249 ::media::BindToCurrentLoop( |
251 base::Bind(&CmaRenderer::OnAudioPipelineInitializeDone, weak_this_, | 250 base::Bind(&CmaRenderer::OnAudioPipelineInitializeDone, |
| 251 weak_this_, |
252 stream != nullptr)); | 252 stream != nullptr)); |
253 if (!stream) { | 253 if (!stream) { |
254 CMALOG(kLogControl) << __FUNCTION__ << ": no audio stream, skipping init."; | 254 CMALOG(kLogControl) << __FUNCTION__ << ": no audio stream, skipping init."; |
255 audio_initialization_done_cb.Run(::media::PIPELINE_OK); | 255 audio_initialization_done_cb.Run(::media::PIPELINE_OK); |
256 return; | 256 return; |
257 } | 257 } |
258 | 258 |
259 // Receive events from the audio pipeline. | 259 // Receive events from the audio pipeline. |
260 AvPipelineClient av_pipeline_client; | 260 AvPipelineClient av_pipeline_client; |
261 av_pipeline_client.eos_cb = ::media::BindToCurrentLoop( | 261 av_pipeline_client.eos_cb = ::media::BindToCurrentLoop( |
262 base::Bind(&CmaRenderer::OnEosReached, weak_this_, true)); | 262 base::Bind(&CmaRenderer::OnEosReached, weak_this_, true)); |
263 av_pipeline_client.playback_error_cb = ::media::BindToCurrentLoop( | 263 av_pipeline_client.playback_error_cb = |
264 base::Bind(&CmaRenderer::OnError, weak_this_)); | 264 ::media::BindToCurrentLoop(base::Bind(&CmaRenderer::OnError, weak_this_)); |
265 av_pipeline_client.statistics_cb = ::media::BindToCurrentLoop( | 265 av_pipeline_client.statistics_cb = ::media::BindToCurrentLoop( |
266 base::Bind(&CmaRenderer::OnStatisticsUpdated, weak_this_)); | 266 base::Bind(&CmaRenderer::OnStatisticsUpdated, weak_this_)); |
267 audio_pipeline_->SetClient(av_pipeline_client); | 267 audio_pipeline_->SetClient(av_pipeline_client); |
268 | 268 |
269 scoped_ptr<CodedFrameProvider> frame_provider(new DemuxerStreamAdapter( | 269 scoped_ptr<CodedFrameProvider> frame_provider(new DemuxerStreamAdapter( |
270 base::ThreadTaskRunnerHandle::Get(), media_task_runner_factory_, stream)); | 270 base::ThreadTaskRunnerHandle::Get(), media_task_runner_factory_, stream)); |
271 | 271 |
272 const ::media::AudioDecoderConfig& config = stream->audio_decoder_config(); | 272 const ::media::AudioDecoderConfig& config = stream->audio_decoder_config(); |
273 if (config.codec() == ::media::kCodecAAC) | 273 if (config.codec() == ::media::kCodecAAC) |
274 stream->EnableBitstreamConverter(); | 274 stream->EnableBitstreamConverter(); |
(...skipping 25 matching lines...) Expand all Loading... |
300 | 300 |
301 void CmaRenderer::InitializeVideoPipeline() { | 301 void CmaRenderer::InitializeVideoPipeline() { |
302 DCHECK(thread_checker_.CalledOnValidThread()); | 302 DCHECK(thread_checker_.CalledOnValidThread()); |
303 DCHECK_EQ(state_, kUninitialized) << state_; | 303 DCHECK_EQ(state_, kUninitialized) << state_; |
304 DCHECK(!init_cb_.is_null()); | 304 DCHECK(!init_cb_.is_null()); |
305 | 305 |
306 ::media::DemuxerStream* stream = | 306 ::media::DemuxerStream* stream = |
307 demuxer_stream_provider_->GetStream(::media::DemuxerStream::VIDEO); | 307 demuxer_stream_provider_->GetStream(::media::DemuxerStream::VIDEO); |
308 ::media::PipelineStatusCB video_initialization_done_cb = | 308 ::media::PipelineStatusCB video_initialization_done_cb = |
309 ::media::BindToCurrentLoop( | 309 ::media::BindToCurrentLoop( |
310 base::Bind(&CmaRenderer::OnVideoPipelineInitializeDone, weak_this_, | 310 base::Bind(&CmaRenderer::OnVideoPipelineInitializeDone, |
| 311 weak_this_, |
311 stream != nullptr)); | 312 stream != nullptr)); |
312 if (!stream) { | 313 if (!stream) { |
313 CMALOG(kLogControl) << __FUNCTION__ << ": no video stream, skipping init."; | 314 CMALOG(kLogControl) << __FUNCTION__ << ": no video stream, skipping init."; |
314 video_initialization_done_cb.Run(::media::PIPELINE_OK); | 315 video_initialization_done_cb.Run(::media::PIPELINE_OK); |
315 return; | 316 return; |
316 } | 317 } |
317 | 318 |
318 // Receive events from the video pipeline. | 319 // Receive events from the video pipeline. |
319 VideoPipelineClient client; | 320 VideoPipelineClient client; |
320 client.av_pipeline_client.eos_cb = ::media::BindToCurrentLoop( | 321 client.av_pipeline_client.eos_cb = ::media::BindToCurrentLoop( |
321 base::Bind(&CmaRenderer::OnEosReached, weak_this_, false)); | 322 base::Bind(&CmaRenderer::OnEosReached, weak_this_, false)); |
322 client.av_pipeline_client.playback_error_cb = ::media::BindToCurrentLoop( | 323 client.av_pipeline_client.playback_error_cb = |
323 base::Bind(&CmaRenderer::OnError, weak_this_)); | 324 ::media::BindToCurrentLoop(base::Bind(&CmaRenderer::OnError, weak_this_)); |
324 client.av_pipeline_client.statistics_cb = ::media::BindToCurrentLoop( | 325 client.av_pipeline_client.statistics_cb = ::media::BindToCurrentLoop( |
325 base::Bind(&CmaRenderer::OnStatisticsUpdated, weak_this_)); | 326 base::Bind(&CmaRenderer::OnStatisticsUpdated, weak_this_)); |
326 client.natural_size_changed_cb = ::media::BindToCurrentLoop( | 327 client.natural_size_changed_cb = ::media::BindToCurrentLoop( |
327 base::Bind(&CmaRenderer::OnNaturalSizeChanged, weak_this_)); | 328 base::Bind(&CmaRenderer::OnNaturalSizeChanged, weak_this_)); |
328 video_pipeline_->SetClient(client); | 329 video_pipeline_->SetClient(client); |
329 | 330 |
330 scoped_ptr<CodedFrameProvider> frame_provider(new DemuxerStreamAdapter( | 331 scoped_ptr<CodedFrameProvider> frame_provider(new DemuxerStreamAdapter( |
331 base::ThreadTaskRunnerHandle::Get(), media_task_runner_factory_, stream)); | 332 base::ThreadTaskRunnerHandle::Get(), media_task_runner_factory_, stream)); |
332 | 333 |
333 const ::media::VideoDecoderConfig& config = stream->video_decoder_config(); | 334 const ::media::VideoDecoderConfig& config = stream->video_decoder_config(); |
334 if (config.codec() == ::media::kCodecH264) | 335 if (config.codec() == ::media::kCodecH264) |
335 stream->EnableBitstreamConverter(); | 336 stream->EnableBitstreamConverter(); |
336 | 337 |
337 initial_natural_size_ = config.natural_size(); | 338 initial_natural_size_ = config.natural_size(); |
338 | 339 |
339 std::vector<::media::VideoDecoderConfig> configs; | 340 std::vector<::media::VideoDecoderConfig> configs; |
340 configs.push_back(config); | 341 configs.push_back(config); |
341 media_pipeline_->InitializeVideo( | 342 media_pipeline_->InitializeVideo( |
342 configs, | 343 configs, frame_provider.Pass(), video_initialization_done_cb); |
343 frame_provider.Pass(), | |
344 video_initialization_done_cb); | |
345 } | 344 } |
346 | 345 |
347 void CmaRenderer::OnVideoPipelineInitializeDone( | 346 void CmaRenderer::OnVideoPipelineInitializeDone( |
348 bool video_stream_present, | 347 bool video_stream_present, |
349 ::media::PipelineStatus status) { | 348 ::media::PipelineStatus status) { |
350 CMALOG(kLogControl) << __FUNCTION__ << ": state=" << state_; | 349 CMALOG(kLogControl) << __FUNCTION__ << ": state=" << state_; |
351 DCHECK(thread_checker_.CalledOnValidThread()); | 350 DCHECK(thread_checker_.CalledOnValidThread()); |
352 | 351 |
353 // OnError() may be fired at any time, even before initialization is complete. | 352 // OnError() may be fired at any time, even before initialization is complete. |
354 if (state_ == kError) | 353 if (state_ == kError) |
(...skipping 21 matching lines...) Expand all Loading... |
376 if (is_audio) { | 375 if (is_audio) { |
377 DCHECK(!received_audio_eos_); | 376 DCHECK(!received_audio_eos_); |
378 received_audio_eos_ = true; | 377 received_audio_eos_ = true; |
379 } else { | 378 } else { |
380 DCHECK(!received_video_eos_); | 379 DCHECK(!received_video_eos_); |
381 received_video_eos_ = true; | 380 received_video_eos_ = true; |
382 } | 381 } |
383 | 382 |
384 bool audio_finished = !has_audio_ || received_audio_eos_; | 383 bool audio_finished = !has_audio_ || received_audio_eos_; |
385 bool video_finished = !has_video_ || received_video_eos_; | 384 bool video_finished = !has_video_ || received_video_eos_; |
386 CMALOG(kLogControl) << __FUNCTION__ | 385 CMALOG(kLogControl) << __FUNCTION__ << " audio_finished=" << audio_finished |
387 << " audio_finished=" << audio_finished | |
388 << " video_finished=" << video_finished; | 386 << " video_finished=" << video_finished; |
389 if (audio_finished && video_finished) | 387 if (audio_finished && video_finished) |
390 ended_cb_.Run(); | 388 ended_cb_.Run(); |
391 } | 389 } |
392 | 390 |
393 void CmaRenderer::OnStatisticsUpdated( | 391 void CmaRenderer::OnStatisticsUpdated( |
394 const ::media::PipelineStatistics& stats) { | 392 const ::media::PipelineStatistics& stats) { |
395 DCHECK(thread_checker_.CalledOnValidThread()); | 393 DCHECK(thread_checker_.CalledOnValidThread()); |
396 statistics_cb_.Run(stats); | 394 statistics_cb_.Run(stats); |
397 } | 395 } |
398 | 396 |
399 void CmaRenderer::OnNaturalSizeChanged(const gfx::Size& size) { | 397 void CmaRenderer::OnNaturalSizeChanged(const gfx::Size& size) { |
400 DCHECK(thread_checker_.CalledOnValidThread()); | 398 DCHECK(thread_checker_.CalledOnValidThread()); |
401 video_renderer_sink_->PaintFrameUsingOldRenderingPath( | 399 video_renderer_sink_->PaintFrameUsingOldRenderingPath( |
402 hole_frame_factory_->CreateHoleFrame(size)); | 400 hole_frame_factory_->CreateHoleFrame(size)); |
403 } | 401 } |
404 | 402 |
405 void CmaRenderer::OnPlaybackTimeUpdated( | 403 void CmaRenderer::OnPlaybackTimeUpdated(base::TimeDelta time, |
406 base::TimeDelta time, | 404 base::TimeDelta max_time, |
407 base::TimeDelta max_time, | 405 base::TimeTicks capture_time) { |
408 base::TimeTicks capture_time) { | |
409 DCHECK(thread_checker_.CalledOnValidThread()); | 406 DCHECK(thread_checker_.CalledOnValidThread()); |
410 if (state_ != kPlaying) { | 407 if (state_ != kPlaying) { |
411 LOG(WARNING) << "Ignoring a late time update"; | 408 LOG(WARNING) << "Ignoring a late time update"; |
412 return; | 409 return; |
413 } | 410 } |
414 | 411 |
415 // TODO(halliwell): arguably, TimeDeltaInterpolator::SetBounds should perform | 412 // TODO(halliwell): arguably, TimeDeltaInterpolator::SetBounds should perform |
416 // this calculation to avoid calling TimeTicks::Now twice (it's slower and has | 413 // this calculation to avoid calling TimeTicks::Now twice (it's slower and has |
417 // potential accuracy problems). | 414 // potential accuracy problems). |
418 base::TimeDelta lower_bound = | 415 base::TimeDelta lower_bound = |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
477 is_pending_transition_ = true; | 474 is_pending_transition_ = true; |
478 } | 475 } |
479 | 476 |
480 void CmaRenderer::CompleteStateTransition(State new_state) { | 477 void CmaRenderer::CompleteStateTransition(State new_state) { |
481 state_ = new_state; | 478 state_ = new_state; |
482 is_pending_transition_ = false; | 479 is_pending_transition_ = false; |
483 } | 480 } |
484 | 481 |
485 } // namespace media | 482 } // namespace media |
486 } // namespace chromecast | 483 } // namespace chromecast |
OLD | NEW |