| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromecast/browser/media/cast_renderer.h" |
| |
| #include "base/bind.h" |
| #include "base/single_thread_task_runner.h" |
| #include "chromecast/base/task_runner_impl.h" |
| #include "chromecast/media/cma/base/balanced_media_task_runner_factory.h" |
| #include "chromecast/media/cma/base/cma_logging.h" |
| #include "chromecast/media/cma/base/demuxer_stream_adapter.h" |
| #include "chromecast/media/cma/pipeline/media_pipeline_impl.h" |
| #include "chromecast/media/cma/pipeline/video_pipeline_client.h" |
| #include "chromecast/public/media/media_pipeline_backend.h" |
| #include "chromecast/public/media/media_pipeline_device_params.h" |
| #include "media/base/audio_decoder_config.h" |
| #include "media/base/demuxer_stream.h" |
| #include "media/base/media_log.h" |
| |
| namespace chromecast { |
| namespace media { |
| |
| namespace { |
| // Maximum difference between audio frame PTS and video frame PTS |
| // for frames read from the DemuxerStream. |
| const base::TimeDelta kMaxDeltaFetcher(base::TimeDelta::FromMilliseconds(2000)); |
| } // namespace |
| |
| CastRenderer::CastRenderer( |
| const CreateMediaPipelineBackendCB& create_backend_cb, |
| const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) |
| : create_backend_cb_(create_backend_cb), |
| task_runner_(task_runner), |
| media_task_runner_factory_( |
| new BalancedMediaTaskRunnerFactory(kMaxDeltaFetcher)) { |
| CMALOG(kLogControl) << __FUNCTION__ << ": " << this; |
| } |
| |
| CastRenderer::~CastRenderer() { |
| CMALOG(kLogControl) << __FUNCTION__ << ": " << this; |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| } |
| |
| void CastRenderer::Initialize( |
| ::media::DemuxerStreamProvider* demuxer_stream_provider, |
| const ::media::PipelineStatusCB& init_cb, |
| const ::media::StatisticsCB& statistics_cb, |
| const ::media::BufferingStateCB& buffering_state_cb, |
| const base::Closure& ended_cb, |
| const ::media::PipelineStatusCB& error_cb, |
| const base::Closure& waiting_for_decryption_key_cb) { |
| CMALOG(kLogControl) << __FUNCTION__ << ": " << this; |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| |
| // Create pipeline backend. |
| backend_task_runner_.reset(new TaskRunnerImpl()); |
| // TODO(erickung): crbug.com/443956. Need to provide right LoadType. |
| LoadType load_type = kLoadTypeMediaSource; |
| MediaPipelineDeviceParams::MediaSyncType sync_type = |
| (load_type == kLoadTypeMediaStream) |
| ? MediaPipelineDeviceParams::kModeIgnorePts |
| : MediaPipelineDeviceParams::kModeSyncPts; |
| MediaPipelineDeviceParams params(sync_type, backend_task_runner_.get()); |
| std::unique_ptr<MediaPipelineBackend> backend = |
| create_backend_cb_.Run(params); |
| |
| // Create pipeline. |
| MediaPipelineClient pipeline_client; |
| pipeline_client.error_cb = error_cb; |
| pipeline_client.buffering_state_cb = buffering_state_cb; |
| pipeline_.reset(new MediaPipelineImpl); |
| pipeline_->SetClient(pipeline_client); |
| pipeline_->Initialize(load_type, std::move(backend)); |
| |
| // Initialize audio. |
| ::media::DemuxerStream* audio_stream = |
| demuxer_stream_provider->GetStream(::media::DemuxerStream::AUDIO); |
| if (audio_stream) { |
| AvPipelineClient audio_client; |
| audio_client.wait_for_key_cb = waiting_for_decryption_key_cb; |
| audio_client.eos_cb = |
| base::Bind(&CastRenderer::OnEos, base::Unretained(this), STREAM_AUDIO); |
| audio_client.playback_error_cb = error_cb; |
| audio_client.statistics_cb = statistics_cb; |
| std::unique_ptr<CodedFrameProvider> frame_provider(new DemuxerStreamAdapter( |
| task_runner_, media_task_runner_factory_, audio_stream)); |
| ::media::PipelineStatus status = |
| pipeline_->InitializeAudio(audio_stream->audio_decoder_config(), |
| audio_client, std::move(frame_provider)); |
| if (status != ::media::PIPELINE_OK) { |
| init_cb.Run(status); |
| return; |
| } |
| audio_stream->EnableBitstreamConverter(); |
| } |
| |
| // Initialize video. |
| ::media::DemuxerStream* video_stream = |
| demuxer_stream_provider->GetStream(::media::DemuxerStream::VIDEO); |
| if (video_stream) { |
| VideoPipelineClient video_client; |
| // TODO(alokp): Set VideoPipelineClient::natural_size_changed_cb. |
| video_client.av_pipeline_client.wait_for_key_cb = |
| waiting_for_decryption_key_cb; |
| video_client.av_pipeline_client.eos_cb = |
| base::Bind(&CastRenderer::OnEos, base::Unretained(this), STREAM_VIDEO); |
| video_client.av_pipeline_client.playback_error_cb = error_cb; |
| video_client.av_pipeline_client.statistics_cb = statistics_cb; |
| // TODO(alokp): Change MediaPipelineImpl API to accept a single config |
| // after CmaRenderer is deprecated. |
| std::vector<::media::VideoDecoderConfig> video_configs; |
| video_configs.push_back(video_stream->video_decoder_config()); |
| std::unique_ptr<CodedFrameProvider> frame_provider(new DemuxerStreamAdapter( |
| task_runner_, media_task_runner_factory_, video_stream)); |
| ::media::PipelineStatus status = pipeline_->InitializeVideo( |
| video_configs, video_client, std::move(frame_provider)); |
| if (status != ::media::PIPELINE_OK) { |
| init_cb.Run(status); |
| return; |
| } |
| video_stream->EnableBitstreamConverter(); |
| } |
| |
| ended_cb_ = ended_cb; |
| init_cb.Run(::media::PIPELINE_OK); |
| } |
| |
| void CastRenderer::SetCdm(::media::CdmContext* cdm_context, |
| const ::media::CdmAttachedCB& cdm_attached_cb) { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| NOTIMPLEMENTED(); |
| } |
| |
| void CastRenderer::Flush(const base::Closure& flush_cb) { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| pipeline_->Flush(flush_cb); |
| } |
| |
| void CastRenderer::StartPlayingFrom(base::TimeDelta time) { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| |
| eos_[STREAM_AUDIO] = !pipeline_->HasAudio(); |
| eos_[STREAM_VIDEO] = !pipeline_->HasVideo(); |
| pipeline_->StartPlayingFrom(time); |
| } |
| |
| void CastRenderer::SetPlaybackRate(double playback_rate) { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| pipeline_->SetPlaybackRate(playback_rate); |
| } |
| |
| void CastRenderer::SetVolume(float volume) { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| pipeline_->SetVolume(volume); |
| } |
| |
| base::TimeDelta CastRenderer::GetMediaTime() { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| return pipeline_->GetMediaTime(); |
| } |
| |
| bool CastRenderer::HasAudio() { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| return pipeline_->HasAudio(); |
| } |
| |
| bool CastRenderer::HasVideo() { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| return pipeline_->HasVideo(); |
| } |
| |
| void CastRenderer::OnEos(Stream stream) { |
| DCHECK(!eos_[stream]); |
| eos_[stream] = true; |
| CMALOG(kLogControl) << __FUNCTION__ << ": eos_audio=" << eos_[STREAM_AUDIO] |
| << " eos_video=" << eos_[STREAM_VIDEO]; |
| if (eos_[STREAM_AUDIO] && eos_[STREAM_VIDEO]) |
| ended_cb_.Run(); |
| } |
| |
| } // namespace media |
| } // namespace chromecast |