diff options
23 files changed, 1913 insertions, 14 deletions
diff --git a/chromecast/chromecast.gyp b/chromecast/chromecast.gyp index b5fd7bc..cf7ed71 100644 --- a/chromecast/chromecast.gyp +++ b/chromecast/chromecast.gyp @@ -419,11 +419,26 @@ '../ipc/ipc.gyp:ipc', ], 'sources': [ + 'common/media/cma_ipc_common.h', 'common/media/cma_messages.h', 'common/media/cma_message_generator.cc', 'common/media/cma_message_generator.h', 'common/media/cma_param_traits.cc', 'common/media/cma_param_traits.h', + 'common/media/shared_memory_chunk.cc', + 'common/media/shared_memory_chunk.h', + 'renderer/media/audio_pipeline_proxy.cc', + 'renderer/media/audio_pipeline_proxy.h', + 'renderer/media/cma_media_renderer_factory.cc', + 'renderer/media/cma_media_renderer_factory.h', + 'renderer/media/cma_message_filter_proxy.cc', + 'renderer/media/cma_message_filter_proxy.h', + 'renderer/media/media_channel_proxy.cc', + 'renderer/media/media_channel_proxy.h', + 'renderer/media/media_pipeline_proxy.cc', + 'renderer/media/media_pipeline_proxy.h', + 'renderer/media/video_pipeline_proxy.cc', + 'renderer/media/video_pipeline_proxy.h', ], }, # end of target 'cast_shell_media' # This target contains all of the primary code of |cast_shell|, except diff --git a/chromecast/common/chromecast_switches.cc b/chromecast/common/chromecast_switches.cc index 0ccbf7b..d66d2ff 100644 --- a/chromecast/common/chromecast_switches.cc +++ b/chromecast/common/chromecast_switches.cc @@ -6,6 +6,9 @@ namespace switches { +// Enable the CMA media pipeline. +const char kEnableCmaMediaPipeline[] = "enable-cma-media-pipeline"; + #if defined(OS_ANDROID) // Enable file accesses for debug. const char kEnableLocalFileAccesses[] = "enable-local-file-accesses"; diff --git a/chromecast/common/chromecast_switches.h b/chromecast/common/chromecast_switches.h index 4fc0e71..34b0f76 100644 --- a/chromecast/common/chromecast_switches.h +++ b/chromecast/common/chromecast_switches.h @@ -9,6 +9,8 @@ namespace switches { +extern const char kEnableCmaMediaPipeline[]; + #if defined(OS_ANDROID) // Content-implementation switches extern const char kEnableLocalFileAccesses[]; diff --git a/chromecast/common/media/cma_ipc_common.h b/chromecast/common/media/cma_ipc_common.h new file mode 100644 index 0000000..3431104 --- /dev/null +++ b/chromecast/common/media/cma_ipc_common.h @@ -0,0 +1,21 @@ +// Copyright 2014 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. + +#ifndef CHROMECAST_COMMON_MEDIA_CMA_IPC_COMMON_H_ +#define CHROMECAST_COMMON_MEDIA_CMA_IPC_COMMON_H_ + +namespace chromecast { +namespace media { + +enum TrackId { + kNoTrackId = -1, + kAudioTrackId = 0, + kVideoTrackId = 1, +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_COMMON_MEDIA_CMA_IPC_COMMON_H_ + diff --git a/chromecast/common/media/cma_messages.h b/chromecast/common/media/cma_messages.h index 279147a..8e2b88f 100644 --- a/chromecast/common/media/cma_messages.h +++ b/chromecast/common/media/cma_messages.h @@ -5,6 +5,7 @@ // IPC messages for the Cast Media Acceleration (CMA) pipeline. // Multiply-included message file, hence no include guard. +#include "chromecast/common/media/cma_ipc_common.h" #include "chromecast/common/media/cma_param_traits.h" #include "chromecast/common/media/cma_param_traits_macros.h" #include "chromecast/media/cma/pipeline/load_type.h" @@ -44,23 +45,23 @@ IPC_MESSAGE_CONTROL2(CmaHostMsg_SetPlaybackRate, IPC_MESSAGE_CONTROL3(CmaHostMsg_CreateAvPipe, int /* Media pipeline ID */, - int /* Track ID */, + chromecast::media::TrackId /* Track ID */, size_t /* Fifo size */); IPC_MESSAGE_CONTROL3(CmaHostMsg_AudioInitialize, int /* Media pipeline ID */, - int /* Track ID */, + chromecast::media::TrackId /* Track ID */, media::AudioDecoderConfig /* Audio config */) IPC_MESSAGE_CONTROL3(CmaHostMsg_VideoInitialize, int /* Media pipeline ID */, - int /* Track ID */, + chromecast::media::TrackId /* Track ID */, media::VideoDecoderConfig /* Video config */) IPC_MESSAGE_CONTROL3(CmaHostMsg_SetVolume, int /* Media pipeline ID */, - int /* Track ID */, + chromecast::media::TrackId /* Track ID */, float /* Volume */) IPC_MESSAGE_CONTROL2(CmaHostMsg_NotifyPipeWrite, int /* Media pipeline ID */, - int /* Track ID */) + chromecast::media::TrackId /* Track ID */) IPC_MESSAGE_CONTROL5(CmaHostMsg_NotifyExternalSurface, int /* Surface ID */, @@ -85,30 +86,30 @@ IPC_MESSAGE_CONTROL2(CmaMsg_BufferingNotification, IPC_MESSAGE_CONTROL5(CmaMsg_AvPipeCreated, int /* Media pipeline ID */, - int /* Track ID */, + chromecast::media::TrackId /* Track ID */, bool /* Status */, base::SharedMemoryHandle /* Shared memory */, base::FileDescriptor /* socket handle */) IPC_MESSAGE_CONTROL3(CmaMsg_TrackStateChanged, int /* Media pipeline ID */, - int /* Track ID */, + chromecast::media::TrackId /* Track ID */, media::PipelineStatus /* Status */) IPC_MESSAGE_CONTROL2(CmaMsg_NotifyPipeRead, int /* Media pipeline ID */, - int /* Track ID */) + chromecast::media::TrackId /* Track ID */) IPC_MESSAGE_CONTROL2(CmaMsg_Eos, int /* Media pipeline ID */, - int /* Track ID */) + chromecast::media::TrackId /* Track ID */) IPC_MESSAGE_CONTROL3(CmaMsg_PlaybackError, int /* Media pipeline ID */, - int /* Track ID */, + chromecast::media::TrackId /* Track ID */, media::PipelineStatus /* status */) IPC_MESSAGE_CONTROL3(CmaMsg_PlaybackStatistics, int /* Media pipeline ID */, - int /* Track ID */, + chromecast::media::TrackId /* Track ID */, media::PipelineStatistics /* status */) IPC_MESSAGE_CONTROL3(CmaMsg_NaturalSizeChanged, int /* Media pipeline ID */, - int /* Track ID */, - gfx::Size /* Size */) + chromecast::media::TrackId /* Track ID */, + gfx::Size /* Size */)
\ No newline at end of file diff --git a/chromecast/common/media/cma_param_traits_macros.h b/chromecast/common/media/cma_param_traits_macros.h index abde3ec..7268200 100644 --- a/chromecast/common/media/cma_param_traits_macros.h +++ b/chromecast/common/media/cma_param_traits_macros.h @@ -8,6 +8,7 @@ #ifndef CHROMECAST_COMMON_MEDIA_CMA_PARAM_TRAITS_MACROS_H_ #define CHROMECAST_COMMON_MEDIA_CMA_PARAM_TRAITS_MACROS_H_ +#include "chromecast/common/media/cma_ipc_common.h" #include "chromecast/media/cma/pipeline/load_type.h" #include "ipc/ipc_message_macros.h" #include "ipc/param_traits_macros.h" @@ -23,6 +24,10 @@ IPC_ENUM_TRAITS_MIN_MAX_VALUE(chromecast::media::LoadType, chromecast::media::kLoadTypeURL, chromecast::media::kLoadTypeMediaStream) +IPC_ENUM_TRAITS_MIN_MAX_VALUE(chromecast::media::TrackId, + chromecast::media::kNoTrackId, + chromecast::media::kVideoTrackId) + IPC_ENUM_TRAITS_MIN_MAX_VALUE(media::AudioCodec, media::AudioCodec::kUnknownAudioCodec, media::AudioCodec::kAudioCodecMax) @@ -46,4 +51,4 @@ IPC_STRUCT_TRAITS_BEGIN(media::PipelineStatistics) IPC_STRUCT_TRAITS_MEMBER(video_frames_dropped) IPC_STRUCT_TRAITS_END() -#endif // CHROMECAST_COMMON_MEDIA_CMA_PARAM_TRAITS_MACROS_H_ +#endif // CHROMECAST_COMMON_MEDIA_CMA_PARAM_TRAITS_MACROS_H_
\ No newline at end of file diff --git a/chromecast/common/media/shared_memory_chunk.cc b/chromecast/common/media/shared_memory_chunk.cc new file mode 100644 index 0000000..4053395 --- /dev/null +++ b/chromecast/common/media/shared_memory_chunk.cc @@ -0,0 +1,36 @@ +// Copyright 2014 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/common/media/shared_memory_chunk.h" + +#include "base/memory/shared_memory.h" + +namespace chromecast { +namespace media { + +SharedMemoryChunk::SharedMemoryChunk( + scoped_ptr<base::SharedMemory> shared_mem, + size_t size) + : shared_mem_(shared_mem.Pass()), + size_(size) { +} + +SharedMemoryChunk::~SharedMemoryChunk() { +} + +void* SharedMemoryChunk::data() const { + return shared_mem_->memory(); +} + +size_t SharedMemoryChunk::size() const { + return size_; +} + +bool SharedMemoryChunk::valid() const { + return true; +} + +} // namespace media +} // namespace chromecast + diff --git a/chromecast/common/media/shared_memory_chunk.h b/chromecast/common/media/shared_memory_chunk.h new file mode 100644 index 0000000..a8b71c2 --- /dev/null +++ b/chromecast/common/media/shared_memory_chunk.h @@ -0,0 +1,40 @@ +// Copyright 2014 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. + +#ifndef CHROMECAST_COMMON_MEDIA_SHARED_MEMORY_CHUNK_H_ +#define CHROMECAST_COMMON_MEDIA_SHARED_MEMORY_CHUNK_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "chromecast/media/cma/ipc/media_memory_chunk.h" + +namespace base { +class SharedMemory; +} + +namespace chromecast { +namespace media { + +class SharedMemoryChunk : public MediaMemoryChunk { + public: + SharedMemoryChunk(scoped_ptr<base::SharedMemory> shared_mem, + size_t size); + ~SharedMemoryChunk() override; + + // MediaMemoryChunk implementation. + void* data() const override; + size_t size() const override; + bool valid() const override; + + private: + scoped_ptr<base::SharedMemory> shared_mem_; + size_t size_; + + DISALLOW_COPY_AND_ASSIGN(SharedMemoryChunk); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_COMMON_MEDIA_SHARED_MEMORY_CHUNK_H_
\ No newline at end of file diff --git a/chromecast/renderer/DEPS b/chromecast/renderer/DEPS index b072a41..7e6b6e1 100644 --- a/chromecast/renderer/DEPS +++ b/chromecast/renderer/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+chromecast/common", "+chromecast/media", "+components/cdm/renderer", "+components/dns_prefetch/renderer", diff --git a/chromecast/renderer/cast_content_renderer_client.cc b/chromecast/renderer/cast_content_renderer_client.cc index beff1d3..c2d91de 100644 --- a/chromecast/renderer/cast_content_renderer_client.cc +++ b/chromecast/renderer/cast_content_renderer_client.cc @@ -8,9 +8,12 @@ #include "base/command_line.h" #include "base/memory/memory_pressure_listener.h" +#include "chromecast/common/chromecast_switches.h" #include "chromecast/renderer/key_systems_cast.h" +#include "chromecast/renderer/media/cma_media_renderer_factory.h" #include "components/dns_prefetch/renderer/prescient_networking_dispatcher.h" #include "content/public/common/content_switches.h" +#include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_view.h" #include "crypto/nss_util.h" #include "third_party/WebKit/public/platform/WebColor.h" @@ -69,6 +72,18 @@ void CastContentRendererClient::AddKeySystems( AddChromecastPlatformKeySystems(key_systems); } +scoped_ptr<::media::RendererFactory> +CastContentRendererClient::CreateMediaRendererFactory( + ::content::RenderFrame* render_frame) { + const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); + if (!cmd_line->HasSwitch(switches::kEnableCmaMediaPipeline)) + return nullptr; + + return scoped_ptr<::media::RendererFactory>( + new chromecast::media::CmaMediaRendererFactory( + render_frame->GetRoutingID())); +} + blink::WebPrescientNetworking* CastContentRendererClient::GetPrescientNetworking() { return prescient_networking_dispatcher_.get(); diff --git a/chromecast/renderer/cast_content_renderer_client.h b/chromecast/renderer/cast_content_renderer_client.h index 1f61b06..7b60e6e 100644 --- a/chromecast/renderer/cast_content_renderer_client.h +++ b/chromecast/renderer/cast_content_renderer_client.h @@ -5,6 +5,8 @@ #ifndef CHROMECAST_RENDERER_CAST_CONTENT_RENDERER_CLIENT_H_ #define CHROMECAST_RENDERER_CAST_CONTENT_RENDERER_CLIENT_H_ +#include <vector> + #include "base/macros.h" #include "content/public/renderer/content_renderer_client.h" @@ -25,6 +27,8 @@ class CastContentRendererClient : public content::ContentRendererClient { void RenderViewCreated(content::RenderView* render_view) override; void AddKeySystems( std::vector< ::media::KeySystemInfo>* key_systems) override; + scoped_ptr<media::RendererFactory> CreateMediaRendererFactory( + content::RenderFrame* render_frame) override; blink::WebPrescientNetworking* GetPrescientNetworking() override; private: diff --git a/chromecast/renderer/media/audio_pipeline_proxy.cc b/chromecast/renderer/media/audio_pipeline_proxy.cc new file mode 100644 index 0000000..30297a8 --- /dev/null +++ b/chromecast/renderer/media/audio_pipeline_proxy.cc @@ -0,0 +1,311 @@ +// Copyright 2014 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/renderer/media/audio_pipeline_proxy.h" + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/memory/shared_memory.h" +#include "base/message_loop/message_loop.h" +#include "base/threading/thread_checker.h" +#include "chromecast/common/media/cma_messages.h" +#include "chromecast/common/media/shared_memory_chunk.h" +#include "chromecast/media/cma/base/buffering_defs.h" +#include "chromecast/media/cma/base/cma_logging.h" +#include "chromecast/media/cma/base/coded_frame_provider.h" +#include "chromecast/media/cma/ipc/media_message_fifo.h" +#include "chromecast/media/cma/ipc_streamer/av_streamer_proxy.h" +#include "chromecast/media/cma/pipeline/av_pipeline_client.h" +#include "chromecast/renderer/media/cma_message_filter_proxy.h" +#include "chromecast/renderer/media/media_channel_proxy.h" +#include "media/base/bind_to_current_loop.h" +#include "media/base/pipeline_status.h" + +namespace chromecast { +namespace media { + +namespace { + +void IgnoreResult() { +} + +} // namespace + +// AudioPipelineProxyInternal - +// This class is not thread safe and should run on the same thread +// as the media channel proxy. +class AudioPipelineProxyInternal { + public: + typedef base::Callback<void(scoped_ptr<base::SharedMemory>)> SharedMemCB; + + static void Release(scoped_ptr<AudioPipelineProxyInternal> proxy); + + explicit AudioPipelineProxyInternal( + scoped_refptr<MediaChannelProxy> media_channel_proxy); + virtual ~AudioPipelineProxyInternal(); + + // Notify the other side (browser process) of some activity on the audio pipe. + // TODO(erickung): either send an IPC message or write a byte on the + // SyncSocket. + void NotifyPipeWrite(); + + // These functions are almost a one to one correspondence with AudioPipeline + // but this is an internal class and there is no reason to derive from + // AudioPipeline. + void SetClient(const base::Closure& pipe_read_cb, + const AvPipelineClient& client); + void CreateAvPipe(const SharedMemCB& shared_mem_cb); + void Initialize(const ::media::AudioDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb); + void SetVolume(float volume); + + private: + void Shutdown(); + + // Callbacks for CmaMessageFilterHost::AudioDelegate. + void OnAvPipeCreated(bool status, + base::SharedMemoryHandle shared_mem_handle, + base::FileDescriptor socket); + void OnStateChanged(::media::PipelineStatus status); + + base::ThreadChecker thread_checker_; + + scoped_refptr<MediaChannelProxy> media_channel_proxy_; + + // Store the callback for a pending state transition. + ::media::PipelineStatusCB status_cb_; + + SharedMemCB shared_mem_cb_; + + DISALLOW_COPY_AND_ASSIGN(AudioPipelineProxyInternal); +}; + +// static +void AudioPipelineProxyInternal::Release( + scoped_ptr<AudioPipelineProxyInternal> proxy) { + proxy->Shutdown(); +} + +AudioPipelineProxyInternal::AudioPipelineProxyInternal( + scoped_refptr<MediaChannelProxy> media_channel_proxy) + : media_channel_proxy_(media_channel_proxy) { + DCHECK(media_channel_proxy.get()); + + // Creation can be done on a different thread. + thread_checker_.DetachFromThread(); +} + +AudioPipelineProxyInternal::~AudioPipelineProxyInternal() { +} + +void AudioPipelineProxyInternal::Shutdown() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Remove any callback on AudioPipelineProxyInternal. + media_channel_proxy_->SetAudioDelegate( + CmaMessageFilterProxy::AudioDelegate()); +} + +void AudioPipelineProxyInternal::NotifyPipeWrite() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // TODO(erickung): An alternative way would be to use a dedicated socket for + // this event. + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_NotifyPipeWrite( + media_channel_proxy_->GetId(), kAudioTrackId))); + VLOG_IF(4, !success) << "Sending msg failed"; +} + +void AudioPipelineProxyInternal::SetClient( + const base::Closure& pipe_read_cb, + const AvPipelineClient& client) { + DCHECK(thread_checker_.CalledOnValidThread()); + + CmaMessageFilterProxy::AudioDelegate delegate; + delegate.av_pipe_cb = + base::Bind(&AudioPipelineProxyInternal::OnAvPipeCreated, + base::Unretained(this)); + delegate.state_changed_cb = + base::Bind(&AudioPipelineProxyInternal::OnStateChanged, + base::Unretained(this)); + delegate.pipe_read_cb = pipe_read_cb; + delegate.client = client; + bool success = media_channel_proxy_->SetAudioDelegate(delegate); + CHECK(success); +} + +void AudioPipelineProxyInternal::CreateAvPipe( + const SharedMemCB& shared_mem_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(shared_mem_cb_.is_null()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_CreateAvPipe( + media_channel_proxy_->GetId(), kAudioTrackId, kAppAudioBufferSize))); + if (!success) { + shared_mem_cb.Run(scoped_ptr<base::SharedMemory>()); + return; + } + shared_mem_cb_ = shared_mem_cb; +} + +void AudioPipelineProxyInternal::OnAvPipeCreated( + bool success, + base::SharedMemoryHandle shared_mem_handle, + base::FileDescriptor socket) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!shared_mem_cb_.is_null()); + if (!success) { + shared_mem_cb_.Run(scoped_ptr<base::SharedMemory>()); + return; + } + + CHECK(base::SharedMemory::IsHandleValid(shared_mem_handle)); + shared_mem_cb_.Run(scoped_ptr<base::SharedMemory>( + new base::SharedMemory(shared_mem_handle, false))); +} + +void AudioPipelineProxyInternal::Initialize( + const ::media::AudioDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_AudioInitialize( + media_channel_proxy_->GetId(), kAudioTrackId, config))); + if (!success) { + status_cb.Run( ::media::PIPELINE_ERROR_INITIALIZATION_FAILED); + return; + } + DCHECK(status_cb_.is_null()); + status_cb_ = status_cb; +} + +void AudioPipelineProxyInternal::SetVolume(float volume) { + DCHECK(thread_checker_.CalledOnValidThread()); + media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_SetVolume(media_channel_proxy_->GetId(), + kAudioTrackId, volume))); +} + +void AudioPipelineProxyInternal::OnStateChanged( + ::media::PipelineStatus status) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!status_cb_.is_null()); + base::ResetAndReturn(&status_cb_).Run(status); +} + + +// A macro runs current member function on |io_message_loop_proxy_| thread. +#define FORWARD_ON_IO_THREAD(param_fn, ...) \ + io_message_loop_proxy_->PostTask( \ + FROM_HERE, \ + base::Bind(&AudioPipelineProxyInternal::param_fn, \ + base::Unretained(proxy_.get()), ##__VA_ARGS__)) + +AudioPipelineProxy::AudioPipelineProxy( + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy, + scoped_refptr<MediaChannelProxy> media_channel_proxy) + : io_message_loop_proxy_(io_message_loop_proxy), + proxy_(new AudioPipelineProxyInternal(media_channel_proxy)), + audio_streamer_(new AvStreamerProxy()), + weak_factory_(this) { + DCHECK(io_message_loop_proxy_.get()); + weak_this_ = weak_factory_.GetWeakPtr(); + thread_checker_.DetachFromThread(); +} + +AudioPipelineProxy::~AudioPipelineProxy() { + DCHECK(thread_checker_.CalledOnValidThread()); + // Release the underlying object on the right thread. + io_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&AudioPipelineProxyInternal::Release, base::Passed(&proxy_))); +} + +void AudioPipelineProxy::SetClient( + const AvPipelineClient& client) { + DCHECK(thread_checker_.CalledOnValidThread()); + base::Closure pipe_read_cb = ::media::BindToCurrentLoop( + base::Bind(&AudioPipelineProxy::OnPipeRead, weak_this_)); + FORWARD_ON_IO_THREAD(SetClient, pipe_read_cb, client); +} + +void AudioPipelineProxy::Initialize( + const ::media::AudioDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) { + CMALOG(kLogControl) << "AudioPipelineProxy::Initialize"; + DCHECK(thread_checker_.CalledOnValidThread()); + audio_streamer_->SetCodedFrameProvider(frame_provider.Pass()); + + AudioPipelineProxyInternal::SharedMemCB shared_mem_cb = + ::media::BindToCurrentLoop(base::Bind( + &AudioPipelineProxy::OnAvPipeCreated, weak_this_, + config, status_cb)); + FORWARD_ON_IO_THREAD(CreateAvPipe, shared_mem_cb); +} + +void AudioPipelineProxy::OnAvPipeCreated( + const ::media::AudioDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb, + scoped_ptr<base::SharedMemory> shared_memory) { + CMALOG(kLogControl) << "AudioPipelineProxy::OnAvPipeCreated"; + DCHECK(thread_checker_.CalledOnValidThread()); + if (!shared_memory || + !shared_memory->Map(kAppAudioBufferSize)) { + status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED); + return; + } + CHECK(shared_memory->memory()); + + scoped_ptr<MediaMemoryChunk> shared_memory_chunk( + new SharedMemoryChunk(shared_memory.Pass(), kAppAudioBufferSize)); + scoped_ptr<MediaMessageFifo> audio_pipe( + new MediaMessageFifo(shared_memory_chunk.Pass(), false)); + audio_pipe->ObserveWriteActivity( + base::Bind(&AudioPipelineProxy::OnPipeWrite, weak_this_)); + + audio_streamer_->SetMediaMessageFifo(audio_pipe.Pass()); + + // Now proceed to the decoder/renderer initialization. + FORWARD_ON_IO_THREAD(Initialize, config, status_cb); +} + +void AudioPipelineProxy::StartFeeding() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(audio_streamer_); + audio_streamer_->Start(); +} + +void AudioPipelineProxy::Flush(const base::Closure& done_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(audio_streamer_); + audio_streamer_->StopAndFlush(done_cb); +} + +void AudioPipelineProxy::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!audio_streamer_) + return; + audio_streamer_->StopAndFlush(base::Bind(&IgnoreResult)); +} + +void AudioPipelineProxy::SetVolume(float volume) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_IO_THREAD(SetVolume, volume); +} + +void AudioPipelineProxy::OnPipeWrite() { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_IO_THREAD(NotifyPipeWrite); +} + +void AudioPipelineProxy::OnPipeRead() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (audio_streamer_) + audio_streamer_->OnFifoReadEvent(); +} + +} // namespace cma +} // namespace chromecast
\ No newline at end of file diff --git a/chromecast/renderer/media/audio_pipeline_proxy.h b/chromecast/renderer/media/audio_pipeline_proxy.h new file mode 100644 index 0000000..b319ee1 --- /dev/null +++ b/chromecast/renderer/media/audio_pipeline_proxy.h @@ -0,0 +1,79 @@ +// Copyright 2014 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. + +#ifndef CHROMECAST_RENDERER_MEDIA_AUDIO_PIPELINE_PROXY_H_ +#define CHROMECAST_RENDERER_MEDIA_AUDIO_PIPELINE_PROXY_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "chromecast/common/media/cma_ipc_common.h" +#include "chromecast/media/cma/pipeline/audio_pipeline.h" +#include "media/base/pipeline_status.h" + +namespace base { +class MessageLoopProxy; +class SharedMemory; +} + +namespace media { +class AudioDecoderConfig; +} + +namespace chromecast { +namespace media { +class AudioPipelineProxyInternal; +struct AvPipelineClient; +class AvStreamerProxy; +class CodedFrameProvider; +class MediaChannelProxy; + +class AudioPipelineProxy : public AudioPipeline { + public: + AudioPipelineProxy( + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy, + scoped_refptr<MediaChannelProxy> media_channel_proxy); + ~AudioPipelineProxy() override; + + void Initialize( + const ::media::AudioDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb); + void StartFeeding(); + void Flush(const base::Closure& done_cb); + void Stop(); + + // AudioPipeline implementation. + void SetClient(const AvPipelineClient& client) override; + void SetVolume(float volume) override; + + private: + base::ThreadChecker thread_checker_; + + void OnAvPipeCreated( + const ::media::AudioDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb, + scoped_ptr<base::SharedMemory> shared_memory); + void OnPipeWrite(); + void OnPipeRead(); + + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; + + // |proxy_| main goal is to convert function calls to IPC messages. + scoped_ptr<AudioPipelineProxyInternal> proxy_; + + scoped_ptr<AvStreamerProxy> audio_streamer_; + + base::WeakPtr<AudioPipelineProxy> weak_this_; + base::WeakPtrFactory<AudioPipelineProxy> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(AudioPipelineProxy); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_MEDIA_AUDIO_PIPELINE_PROXY_H_
\ No newline at end of file diff --git a/chromecast/renderer/media/cma_media_renderer_factory.cc b/chromecast/renderer/media/cma_media_renderer_factory.cc new file mode 100644 index 0000000..2a38149 --- /dev/null +++ b/chromecast/renderer/media/cma_media_renderer_factory.cc @@ -0,0 +1,37 @@ +// Copyright 2014 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/renderer/media/cma_media_renderer_factory.h" + +#include "base/command_line.h" +#include "chromecast/media/cma/filters/cma_renderer.h" +#include "chromecast/renderer/media/media_pipeline_proxy.h" +#include "content/public/renderer/render_thread.h" + +namespace chromecast { +namespace media { + +CmaMediaRendererFactory::CmaMediaRendererFactory(int render_frame_id) + : render_frame_id_(render_frame_id) { +} + +CmaMediaRendererFactory::~CmaMediaRendererFactory() { +} + +scoped_ptr< ::media::Renderer> CmaMediaRendererFactory::CreateRenderer( + const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, + ::media::AudioRendererSink* audio_renderer_sink) { + // TODO(erickung): crbug.com/443956. Need to provide right LoadType. + LoadType cma_load_type = kLoadTypeMediaSource; + scoped_ptr<MediaPipeline> cma_media_pipeline( + new MediaPipelineProxy( + render_frame_id_, + content::RenderThread::Get()->GetIOMessageLoopProxy(), + cma_load_type)); + return scoped_ptr< ::media::Renderer>( + new CmaRenderer(cma_media_pipeline.Pass())); +} + +} // namespace media +} // namespace chromecast
\ No newline at end of file diff --git a/chromecast/renderer/media/cma_media_renderer_factory.h b/chromecast/renderer/media/cma_media_renderer_factory.h new file mode 100644 index 0000000..b14b3d7 --- /dev/null +++ b/chromecast/renderer/media/cma_media_renderer_factory.h @@ -0,0 +1,32 @@ +// Copyright 2014 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. + +#ifndef CHROMECAST_RENDERER_MEDIA_CMA_MEDIA_RENDERER_FACTORY_H_ +#define CHROMECAST_RENDERER_MEDIA_CMA_MEDIA_RENDERER_FACTORY_H_ + +#include "base/macros.h" +#include "media/base/renderer_factory.h" + +namespace chromecast { +namespace media { + +class CmaMediaRendererFactory : public ::media::RendererFactory { + public: + explicit CmaMediaRendererFactory(int render_frame_id); + ~CmaMediaRendererFactory() final; + + // ::media::RendererFactory implementation. + scoped_ptr< ::media::Renderer> CreateRenderer( + const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, + ::media::AudioRendererSink* audio_renderer_sink) final; + + private: + int render_frame_id_; + DISALLOW_COPY_AND_ASSIGN(CmaMediaRendererFactory); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_MEDIA_CMA_MEDIA_RENDERER_FACTORY_H_
\ No newline at end of file diff --git a/chromecast/renderer/media/cma_message_filter_proxy.cc b/chromecast/renderer/media/cma_message_filter_proxy.cc new file mode 100644 index 0000000..f2701a9 --- /dev/null +++ b/chromecast/renderer/media/cma_message_filter_proxy.cc @@ -0,0 +1,272 @@ +// Copyright 2014 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/renderer/media/cma_message_filter_proxy.h" + +#include "base/bind.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/time/time.h" +#include "chromecast/common/media/cma_messages.h" +#include "ipc/ipc_logging.h" +#include "ipc/ipc_sender.h" + +namespace chromecast { +namespace media { + +CmaMessageFilterProxy::MediaDelegate::MediaDelegate() { +} + +CmaMessageFilterProxy::MediaDelegate::~MediaDelegate() { +} + +CmaMessageFilterProxy::AudioDelegate::AudioDelegate() { +} + +CmaMessageFilterProxy::AudioDelegate::~AudioDelegate() { +} + +CmaMessageFilterProxy::VideoDelegate::VideoDelegate() { +} + +CmaMessageFilterProxy::VideoDelegate::~VideoDelegate() { +} + +CmaMessageFilterProxy::DelegateEntry::DelegateEntry() { +} + +CmaMessageFilterProxy::DelegateEntry::~DelegateEntry() { +} + +CmaMessageFilterProxy* CmaMessageFilterProxy::filter_ = NULL; + +// static +CmaMessageFilterProxy* CmaMessageFilterProxy::Get() { + return filter_; +} + +CmaMessageFilterProxy::CmaMessageFilterProxy( + const scoped_refptr<base::MessageLoopProxy>& io_message_loop) + : sender_(NULL), + io_message_loop_(io_message_loop) { + DCHECK(!filter_); + filter_ = this; +} + +int CmaMessageFilterProxy::CreateChannel() { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + DelegateEntry* entry = new DelegateEntry(); + int id = delegates_.Add(entry); + return id; +} + +void CmaMessageFilterProxy::DestroyChannel(int id) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + delegates_.Remove(id); + delete entry; +} + +bool CmaMessageFilterProxy::SetMediaDelegate( + int id, const MediaDelegate& media_delegate) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return false; + entry->media_delegate = media_delegate; + return true; +} + +bool CmaMessageFilterProxy::SetAudioDelegate( + int id, const AudioDelegate& audio_delegate) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return false; + entry->audio_delegate = audio_delegate; + return true; +} + +bool CmaMessageFilterProxy::SetVideoDelegate( + int id, const VideoDelegate& video_delegate) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return false; + entry->video_delegate = video_delegate; + return true; +} + +bool CmaMessageFilterProxy::Send(scoped_ptr<IPC::Message> message) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + if (!sender_) + return false; + bool status = sender_->Send(message.release()); + return status; +} + +bool CmaMessageFilterProxy::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(CmaMessageFilterProxy, message) + IPC_MESSAGE_HANDLER(CmaMsg_AvPipeCreated, OnAvPipeCreated) + IPC_MESSAGE_HANDLER(CmaMsg_NotifyPipeRead, OnPipeRead) + IPC_MESSAGE_HANDLER(CmaMsg_MediaStateChanged, OnMediaStateChanged) + IPC_MESSAGE_HANDLER(CmaMsg_TrackStateChanged, OnTrackStateChanged) + IPC_MESSAGE_HANDLER(CmaMsg_Eos, OnEos) + IPC_MESSAGE_HANDLER(CmaMsg_TimeUpdate, OnTimeUpdate) + IPC_MESSAGE_HANDLER(CmaMsg_BufferingNotification, OnBufferingNotification) + IPC_MESSAGE_HANDLER(CmaMsg_PlaybackError, OnPlaybackError) + IPC_MESSAGE_HANDLER(CmaMsg_PlaybackStatistics, OnStatisticsUpdated) + IPC_MESSAGE_HANDLER(CmaMsg_NaturalSizeChanged, OnNaturalSizeChanged) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void CmaMessageFilterProxy::OnFilterAdded(IPC::Sender* sender) { + sender_ = sender; +} + +void CmaMessageFilterProxy::OnFilterRemoved() { + sender_ = NULL; +} + +void CmaMessageFilterProxy::OnChannelClosing() { + sender_ = NULL; +} + +CmaMessageFilterProxy::~CmaMessageFilterProxy() { + DCHECK(filter_); + filter_ = NULL; +} + +void CmaMessageFilterProxy::OnAvPipeCreated( + int id, + TrackId track_id, + bool status, + base::SharedMemoryHandle shared_memory_handle, + base::FileDescriptor socket_handle) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const AvPipeCB& cb = (track_id == kAudioTrackId) ? + entry->audio_delegate.av_pipe_cb : + entry->video_delegate.av_pipe_cb; + if (!cb.is_null()) + cb.Run(status, shared_memory_handle, socket_handle); +} + +void CmaMessageFilterProxy::OnPipeRead( + int id, TrackId track_id) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const base::Closure& cb = (track_id == kAudioTrackId) ? + entry->audio_delegate.pipe_read_cb : + entry->video_delegate.pipe_read_cb; + if (!cb.is_null()) + cb.Run(); +} + +void CmaMessageFilterProxy::OnMediaStateChanged( + int id, ::media::PipelineStatus status) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const ::media::PipelineStatusCB& cb = + entry->media_delegate.state_changed_cb; + if (!cb.is_null()) + cb.Run(status); +} + +void CmaMessageFilterProxy::OnTrackStateChanged( + int id, TrackId track_id, ::media::PipelineStatus status) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const ::media::PipelineStatusCB& cb = (track_id == kAudioTrackId) ? + entry->audio_delegate.state_changed_cb : + entry->video_delegate.state_changed_cb; + if (!cb.is_null()) + cb.Run(status); +} + +void CmaMessageFilterProxy::OnEos(int id, TrackId track_id) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const base::Closure& cb = (track_id == kAudioTrackId) ? + entry->audio_delegate.client.eos_cb : + entry->video_delegate.client.av_pipeline_client.eos_cb; + if (!cb.is_null()) + cb.Run(); +} + +void CmaMessageFilterProxy::OnTimeUpdate( + int id, + base::TimeDelta time, + base::TimeDelta max_time, + base::TimeTicks stc) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const MediaPipelineClient::TimeUpdateCB& cb = + entry->media_delegate.client.time_update_cb; + if (!cb.is_null()) + cb.Run(time, max_time, stc); +} + +void CmaMessageFilterProxy::OnBufferingNotification( + int id, ::media::BufferingState buffering_state) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const ::media::BufferingStateCB& cb = + entry->media_delegate.client.buffering_state_cb; + if (!cb.is_null()) + cb.Run(buffering_state); +} + +void CmaMessageFilterProxy::OnPlaybackError( + int id, TrackId track_id, ::media::PipelineStatus status) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const ::media::PipelineStatusCB& cb = + (track_id == kNoTrackId) ? entry->media_delegate.client.error_cb : + (track_id == kAudioTrackId) ? + entry->audio_delegate.client.playback_error_cb : + entry->video_delegate.client.av_pipeline_client.playback_error_cb; + if (!cb.is_null()) + cb.Run(status); +} + +void CmaMessageFilterProxy::OnStatisticsUpdated( + int id, TrackId track_id, const ::media::PipelineStatistics& stats) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + const ::media::StatisticsCB& cb = (track_id == kAudioTrackId) ? + entry->audio_delegate.client.statistics_cb : + entry->video_delegate.client.av_pipeline_client.statistics_cb; + if (!cb.is_null()) + cb.Run(stats); +} + +void CmaMessageFilterProxy::OnNaturalSizeChanged( + int id, TrackId track_id, const gfx::Size& size) { + DelegateEntry* entry = delegates_.Lookup(id); + if (!entry) + return; + if (track_id == kAudioTrackId) + return; + const VideoPipelineClient::NaturalSizeChangedCB& cb = + entry->video_delegate.client.natural_size_changed_cb; + if (!cb.is_null()) + cb.Run(size); +} + +} // namespace media +} // namespace chromecast
\ No newline at end of file diff --git a/chromecast/renderer/media/cma_message_filter_proxy.h b/chromecast/renderer/media/cma_message_filter_proxy.h new file mode 100644 index 0000000..0930f19 --- /dev/null +++ b/chromecast/renderer/media/cma_message_filter_proxy.h @@ -0,0 +1,135 @@ +// Copyright 2014 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. + +#ifndef CHROMECAST_RENDERER_MEDIA_CMA_MESSAGE_FILTER_PROXY_H_ +#define CHROMECAST_RENDERER_MEDIA_CMA_MESSAGE_FILTER_PROXY_H_ + +#include "base/id_map.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/shared_memory.h" +#include "base/sync_socket.h" +#include "chromecast/common/media/cma_ipc_common.h" +#include "chromecast/media/cma/pipeline/av_pipeline_client.h" +#include "chromecast/media/cma/pipeline/media_pipeline_client.h" +#include "chromecast/media/cma/pipeline/video_pipeline_client.h" +#include "ipc/message_filter.h" +#include "media/base/buffering_state.h" +#include "media/base/pipeline_status.h" + +namespace base { +class MessageLoopProxy; +} + +namespace chromecast { +namespace media { + +typedef base::Callback<void( + bool, base::SharedMemoryHandle, base::FileDescriptor)> AvPipeCB; + +class CmaMessageFilterProxy : public IPC::MessageFilter { + public: + struct MediaDelegate { + MediaDelegate(); + ~MediaDelegate(); + + ::media::PipelineStatusCB state_changed_cb; + MediaPipelineClient client; + }; + + struct AudioDelegate { + AudioDelegate(); + ~AudioDelegate(); + + AvPipeCB av_pipe_cb; + base::Closure pipe_read_cb; + ::media::PipelineStatusCB state_changed_cb; + AvPipelineClient client; + }; + + struct VideoDelegate { + VideoDelegate(); + ~VideoDelegate(); + + AvPipeCB av_pipe_cb; + base::Closure pipe_read_cb; + ::media::PipelineStatusCB state_changed_cb; + VideoPipelineClient client; + }; + + explicit CmaMessageFilterProxy( + const scoped_refptr<base::MessageLoopProxy>& io_message_loop); + + // Getter for the one CmaMessageFilterHost object. + static CmaMessageFilterProxy* Get(); + + int CreateChannel(); + void DestroyChannel(int id); + + // For adding/removing delegates. + bool SetMediaDelegate(int id, const MediaDelegate& media_delegate); + bool SetAudioDelegate(int id, const AudioDelegate& audio_delegate); + bool SetVideoDelegate(int id, const VideoDelegate& video_delegate); + + // Sends an IPC message using |channel_|. + bool Send(scoped_ptr<IPC::Message> message); + + // IPC::ChannelProxy::MessageFilter implementation. Called on IO thread. + bool OnMessageReceived(const IPC::Message& message) override; + void OnFilterAdded(IPC::Sender* sender) override; + void OnFilterRemoved() override; + void OnChannelClosing() override; + + protected: + ~CmaMessageFilterProxy() override; + + private: + struct DelegateEntry { + DelegateEntry(); + ~DelegateEntry(); + + MediaDelegate media_delegate; + AudioDelegate audio_delegate; + VideoDelegate video_delegate; + }; + + void OnAvPipeCreated(int id, + TrackId track_id, + bool status, + base::SharedMemoryHandle shared_memory_handle, + base::FileDescriptor socket_handle); + void OnPipeRead(int id, TrackId track_id); + void OnMediaStateChanged(int id, ::media::PipelineStatus status); + void OnTrackStateChanged(int id, TrackId track_id, + ::media::PipelineStatus status); + void OnEos(int id, TrackId track_id); + void OnTimeUpdate(int id, + base::TimeDelta time, + base::TimeDelta max_time, + base::TimeTicks stc); + void OnBufferingNotification(int id, ::media::BufferingState state); + void OnPlaybackError(int id, TrackId track_id, + ::media::PipelineStatus status); + void OnStatisticsUpdated(int id, TrackId track_id, + const ::media::PipelineStatistics& stats); + void OnNaturalSizeChanged(int id, TrackId track_id, + const gfx::Size& natural_size); + + // The singleton instance for this filter. + static CmaMessageFilterProxy* filter_; + + // A map of media ids to delegates. + IDMap<DelegateEntry> delegates_; + + IPC::Sender* sender_; + + scoped_refptr<base::MessageLoopProxy> const io_message_loop_; + + DISALLOW_COPY_AND_ASSIGN(CmaMessageFilterProxy); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_MEDIA_CMA_MESSAGE_FILTER_PROXY_H_ diff --git a/chromecast/renderer/media/media_channel_proxy.cc b/chromecast/renderer/media/media_channel_proxy.cc new file mode 100644 index 0000000..2ef1d25 --- /dev/null +++ b/chromecast/renderer/media/media_channel_proxy.cc @@ -0,0 +1,79 @@ +// Copyright 2014 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/renderer/media/media_channel_proxy.h" + +#include "base/logging.h" +#include "chromecast/common/media/cma_messages.h" + +namespace chromecast { +namespace media { + +MediaChannelProxy::MediaChannelProxy() + : is_open_(false), + id_(0) { + filter_ = CmaMessageFilterProxy::Get(); + DCHECK(filter_.get()); +} + +MediaChannelProxy::~MediaChannelProxy() { +} + +void MediaChannelProxy::Open(LoadType load_type) { + CHECK(!is_open_); + // Renderer side. + id_ = filter_->CreateChannel(); + is_open_ = true; + + // Browser side. + bool success = Send( + scoped_ptr<IPC::Message>(new CmaHostMsg_CreateMedia(id_, load_type))); + if (!success) { + is_open_ = false; + id_ = 0; + } +} + +void MediaChannelProxy::Close() { + if (!is_open_) + return; + + // Browser side. + Send(scoped_ptr<IPC::Message>(new CmaHostMsg_DestroyMedia(id_))); + + // Renderer side. + is_open_ = false; + filter_->DestroyChannel(id_); + id_ = 0; +} + +bool MediaChannelProxy::SetMediaDelegate( + const CmaMessageFilterProxy::MediaDelegate& media_delegate) { + if (!is_open_) + return false; + return filter_->SetMediaDelegate(id_, media_delegate); +} + +bool MediaChannelProxy::SetAudioDelegate( + const CmaMessageFilterProxy::AudioDelegate& audio_delegate) { + if (!is_open_) + return false; + return filter_->SetAudioDelegate(id_, audio_delegate); +} + +bool MediaChannelProxy::SetVideoDelegate( + const CmaMessageFilterProxy::VideoDelegate& video_delegate) { + if (!is_open_) + return false; + return filter_->SetVideoDelegate(id_, video_delegate); +} + +bool MediaChannelProxy::Send(scoped_ptr<IPC::Message> message) { + if (!is_open_) + return false; + return filter_->Send(message.Pass()); +} + +} // namespace media +} // namespace chromecast
\ No newline at end of file diff --git a/chromecast/renderer/media/media_channel_proxy.h b/chromecast/renderer/media/media_channel_proxy.h new file mode 100644 index 0000000..c7c4696 --- /dev/null +++ b/chromecast/renderer/media/media_channel_proxy.h @@ -0,0 +1,68 @@ +// Copyright 2014 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. + +#ifndef CHROMECAST_RENDERER_MEDIA_MEDIA_CHANNEL_PROXY_H_ +#define CHROMECAST_RENDERER_MEDIA_MEDIA_CHANNEL_PROXY_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "chromecast/media/cma/pipeline/load_type.h" +#include "chromecast/renderer/media/cma_message_filter_proxy.h" + +namespace IPC { +class Message; +} + +namespace chromecast { +namespace media { + +// MediaChannelProxy - Manage the lifetime of a CMA ipc channel. +// Must be invoked from the IO thread of the renderer process. +class MediaChannelProxy + : public base::RefCountedThreadSafe<MediaChannelProxy> { + public: + MediaChannelProxy(); + + // Opens a CMA ipc channel. + void Open(LoadType load_type); + + // Closes the ipc channel. + void Close(); + + // Returns the ID of the CMA ipc channel. + // Returns 0 or negative ID if no channel has been opened. + int GetId() { return id_; } + + // Manages delegates. + bool SetMediaDelegate( + const CmaMessageFilterProxy::MediaDelegate& media_delegate); + bool SetAudioDelegate( + const CmaMessageFilterProxy::AudioDelegate& audio_delegate); + bool SetVideoDelegate( + const CmaMessageFilterProxy::VideoDelegate& video_delegate); + + // Sends an IPC message over this CMA ipc channel. + bool Send(scoped_ptr<IPC::Message> message); + + private: + friend class base::RefCountedThreadSafe<MediaChannelProxy>; + virtual ~MediaChannelProxy(); + + // Message filter running on the renderer side. + scoped_refptr<CmaMessageFilterProxy> filter_; + + // Indicates whether the CMA channel is open. + bool is_open_; + + // Unique identifier per media pipeline. + int id_; + + DISALLOW_COPY_AND_ASSIGN(MediaChannelProxy); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_MEDIA_MEDIA_CHANNEL_PROXY_H_
\ No newline at end of file diff --git a/chromecast/renderer/media/media_pipeline_proxy.cc b/chromecast/renderer/media/media_pipeline_proxy.cc new file mode 100644 index 0000000..7a454b1 --- /dev/null +++ b/chromecast/renderer/media/media_pipeline_proxy.cc @@ -0,0 +1,283 @@ +// Copyright 2014 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/renderer/media/media_pipeline_proxy.h" + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/message_loop/message_loop_proxy.h" +#include "chromecast/common/media/cma_messages.h" +#include "chromecast/media/cma/base/coded_frame_provider.h" +#include "chromecast/renderer/media/audio_pipeline_proxy.h" +#include "chromecast/renderer/media/media_channel_proxy.h" +#include "chromecast/renderer/media/video_pipeline_proxy.h" + +namespace chromecast { +namespace media { + +// MediaPipelineProxyInternal - +// This class is not thread safe and should run on the same thread +// as the media channel proxy. +class MediaPipelineProxyInternal { + public: + static void Release(scoped_ptr<MediaPipelineProxyInternal> proxy); + + explicit MediaPipelineProxyInternal( + scoped_refptr<MediaChannelProxy> media_channel_proxy); + virtual ~MediaPipelineProxyInternal(); + + void SetClient(const MediaPipelineClient& client); + void SetCdm(int render_frame_id, int cdm_id); + void StartPlayingFrom(const base::TimeDelta& time); + void Flush(const ::media::PipelineStatusCB& status_cb); + void Stop(); + void SetPlaybackRate(float playback_rate); + + private: + void Shutdown(); + + // Callbacks for CmaMessageFilterHost::MediaDelegate. + void OnStateChanged(::media::PipelineStatus status); + + base::ThreadChecker thread_checker_; + + scoped_refptr<MediaChannelProxy> media_channel_proxy_; + + MediaPipelineClient client_; + + // Store the callback for a pending state transition. + ::media::PipelineStatusCB status_cb_; + + DISALLOW_COPY_AND_ASSIGN(MediaPipelineProxyInternal); +}; + +// static +void MediaPipelineProxyInternal::Release( + scoped_ptr<MediaPipelineProxyInternal> proxy) { + proxy->Shutdown(); +} + +MediaPipelineProxyInternal::MediaPipelineProxyInternal( + scoped_refptr<MediaChannelProxy> media_channel_proxy) + : media_channel_proxy_(media_channel_proxy) { + DCHECK(media_channel_proxy.get()); + + // Creation can be done on a different thread. + thread_checker_.DetachFromThread(); +} + +MediaPipelineProxyInternal::~MediaPipelineProxyInternal() { +} + +void MediaPipelineProxyInternal::Shutdown() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Remove any callback on VideoPipelineProxyInternal. + media_channel_proxy_->SetMediaDelegate( + CmaMessageFilterProxy::MediaDelegate()); +} + +void MediaPipelineProxyInternal::SetClient( + const MediaPipelineClient& client) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!client.error_cb.is_null()); + DCHECK(!client.buffering_state_cb.is_null()); + client_ = client; + + CmaMessageFilterProxy::MediaDelegate delegate; + delegate.state_changed_cb = + base::Bind(&MediaPipelineProxyInternal::OnStateChanged, + base::Unretained(this)); + delegate.client = client; + bool success = media_channel_proxy_->SetMediaDelegate(delegate); + CHECK(success); +} + +void MediaPipelineProxyInternal::SetCdm(int render_frame_id, int cdm_id) { + DCHECK(thread_checker_.CalledOnValidThread()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_SetCdm(media_channel_proxy_->GetId(), + render_frame_id, + cdm_id))); + LOG_IF(ERROR, !success) << "Failed to send SetCdm=" << cdm_id; +} + +void MediaPipelineProxyInternal::Flush( + const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_Flush(media_channel_proxy_->GetId()))); + if (!success) { + status_cb.Run(::media::PIPELINE_ERROR_ABORT); + return; + } + DCHECK(status_cb_.is_null()); + status_cb_ = status_cb; +} + +void MediaPipelineProxyInternal::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_Stop(media_channel_proxy_->GetId()))); + if (!success) + client_.error_cb.Run(::media::PIPELINE_ERROR_ABORT); +} + +void MediaPipelineProxyInternal::StartPlayingFrom(const base::TimeDelta& time) { + DCHECK(thread_checker_.CalledOnValidThread()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_StartPlayingFrom( + media_channel_proxy_->GetId(), time))); + if (!success) + client_.error_cb.Run(::media::PIPELINE_ERROR_ABORT); +} + +void MediaPipelineProxyInternal::SetPlaybackRate(float playback_rate) { + DCHECK(thread_checker_.CalledOnValidThread()); + media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_SetPlaybackRate( + media_channel_proxy_->GetId(), playback_rate))); +} + +void MediaPipelineProxyInternal::OnStateChanged( + ::media::PipelineStatus status) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!status_cb_.is_null()); + base::ResetAndReturn(&status_cb_).Run(status); +} + + +// A macro runs current member function on |io_message_loop_proxy_| thread. +#define FORWARD_ON_IO_THREAD(param_fn, ...) \ + io_message_loop_proxy_->PostTask( \ + FROM_HERE, \ + base::Bind(&MediaPipelineProxyInternal::param_fn, \ + base::Unretained(proxy_.get()), ##__VA_ARGS__)) + +MediaPipelineProxy::MediaPipelineProxy( + int render_frame_id, + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy, + LoadType load_type) + : io_message_loop_proxy_(io_message_loop_proxy), + render_frame_id_(render_frame_id), + media_channel_proxy_(new MediaChannelProxy), + proxy_(new MediaPipelineProxyInternal(media_channel_proxy_)), + has_audio_(false), + has_video_(false), + audio_pipeline_(new AudioPipelineProxy( + io_message_loop_proxy, media_channel_proxy_)), + video_pipeline_(new VideoPipelineProxy( + io_message_loop_proxy, media_channel_proxy_)), + weak_factory_(this) { + weak_this_ = weak_factory_.GetWeakPtr(); + io_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&MediaChannelProxy::Open, media_channel_proxy_, + load_type)); + thread_checker_.DetachFromThread(); +} + +MediaPipelineProxy::~MediaPipelineProxy() { + io_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&MediaPipelineProxyInternal::Release, base::Passed(&proxy_))); + io_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&MediaChannelProxy::Close, media_channel_proxy_)); +} + +void MediaPipelineProxy::SetClient( + const MediaPipelineClient& client) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_IO_THREAD(SetClient, client); +} + +void MediaPipelineProxy::SetCdm(int cdm_id) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_IO_THREAD(SetCdm, render_frame_id_, cdm_id); +} + +AudioPipeline* MediaPipelineProxy::GetAudioPipeline() const { + return audio_pipeline_.get(); +} + +VideoPipeline* MediaPipelineProxy::GetVideoPipeline() const { + return video_pipeline_.get(); +} + +void MediaPipelineProxy::InitializeAudio( + const ::media::AudioDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + has_audio_ = true; + audio_pipeline_->Initialize(config, frame_provider.Pass(), status_cb); +} + +void MediaPipelineProxy::InitializeVideo( + const ::media::VideoDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + has_video_ = true; + video_pipeline_->Initialize(config, frame_provider.Pass(), status_cb); +} + +void MediaPipelineProxy::StartPlayingFrom(base::TimeDelta time) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (has_audio_) + audio_pipeline_->StartFeeding(); + if (has_video_) + video_pipeline_->StartFeeding(); + FORWARD_ON_IO_THREAD(StartPlayingFrom, time); +} + +void MediaPipelineProxy::Flush(const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(has_audio_ || has_video_); + + ::media::SerialRunner::Queue bound_fns; + if (has_audio_) { + bound_fns.Push(base::Bind(&AudioPipelineProxy::Flush, + base::Unretained(audio_pipeline_.get()))); + } + if (has_video_) { + bound_fns.Push(base::Bind(&VideoPipelineProxy::Flush, + base::Unretained(video_pipeline_.get()))); + } + ::media::PipelineStatusCB cb = + base::Bind(&MediaPipelineProxy::OnProxyFlushDone, weak_this_, status_cb); + pending_callbacks_ = ::media::SerialRunner::Run(bound_fns, cb); +} + +void MediaPipelineProxy::OnProxyFlushDone( + const ::media::PipelineStatusCB& status_cb, + ::media::PipelineStatus status) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_EQ(status, ::media::PIPELINE_OK); + pending_callbacks_.reset(); + FORWARD_ON_IO_THREAD(Flush, status_cb); +} + +void MediaPipelineProxy::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(has_audio_ || has_video_); + + if (has_audio_) + audio_pipeline_->Stop(); + if (has_video_) + video_pipeline_->Stop(); + + FORWARD_ON_IO_THREAD(Stop); +} + +void MediaPipelineProxy::SetPlaybackRate(float playback_rate) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_IO_THREAD(SetPlaybackRate, playback_rate); +} + +} // namespace cma +} // namespace chromecast
\ No newline at end of file diff --git a/chromecast/renderer/media/media_pipeline_proxy.h b/chromecast/renderer/media/media_pipeline_proxy.h new file mode 100644 index 0000000..0d22795 --- /dev/null +++ b/chromecast/renderer/media/media_pipeline_proxy.h @@ -0,0 +1,85 @@ +// Copyright 2014 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. + +#ifndef CHROMECAST_RENDERER_MEDIA_MEDIA_PIPELINE_PROXY_H_ +#define CHROMECAST_RENDERER_MEDIA_MEDIA_PIPELINE_PROXY_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "chromecast/media/cma/pipeline/load_type.h" +#include "chromecast/media/cma/pipeline/media_pipeline.h" +#include "chromecast/media/cma/pipeline/media_pipeline_client.h" +#include "media/base/serial_runner.h" + +namespace base { +class MessageLoopProxy; +} + +namespace chromecast { +namespace media { +class AudioPipelineProxy; +class MediaChannelProxy; +class MediaPipelineProxyInternal; +class VideoPipelineProxy; + +class MediaPipelineProxy : public MediaPipeline { + public: + MediaPipelineProxy( + int render_frame_id, + scoped_refptr<base::MessageLoopProxy> message_loop_proxy, + LoadType load_type); + ~MediaPipelineProxy() override; + + // MediaPipeline implementation. + void SetClient(const MediaPipelineClient& client) override; + void SetCdm(int cdm_id) override; + AudioPipeline* GetAudioPipeline() const override; + VideoPipeline* GetVideoPipeline() const override; + void InitializeAudio( + const ::media::AudioDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) override; + void InitializeVideo( + const ::media::VideoDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) override; + void StartPlayingFrom(base::TimeDelta time) override; + void Flush(const ::media::PipelineStatusCB& status_cb) override; + void Stop() override; + void SetPlaybackRate(float playback_rate) override; + + private: + void OnProxyFlushDone(const ::media::PipelineStatusCB& status_cb, + ::media::PipelineStatus status); + + base::ThreadChecker thread_checker_; + + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; + + const int render_frame_id_; + + // CMA channel to convey IPC messages. + scoped_refptr<MediaChannelProxy> const media_channel_proxy_; + + scoped_ptr<MediaPipelineProxyInternal> proxy_; + + bool has_audio_; + bool has_video_; + scoped_ptr<AudioPipelineProxy> audio_pipeline_; + scoped_ptr<VideoPipelineProxy> video_pipeline_; + scoped_ptr< ::media::SerialRunner> pending_callbacks_; + + base::WeakPtr<MediaPipelineProxy> weak_this_; + base::WeakPtrFactory<MediaPipelineProxy> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(MediaPipelineProxy); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_MEDIA_MEDIA_PIPELINE_PROXY_H_
\ No newline at end of file diff --git a/chromecast/renderer/media/video_pipeline_proxy.cc b/chromecast/renderer/media/video_pipeline_proxy.cc new file mode 100644 index 0000000..6c924cd --- /dev/null +++ b/chromecast/renderer/media/video_pipeline_proxy.cc @@ -0,0 +1,299 @@ +// Copyright 2014 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/renderer/media/video_pipeline_proxy.h" + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/memory/shared_memory.h" +#include "base/message_loop/message_loop.h" +#include "base/threading/thread_checker.h" +#include "chromecast/common/media/cma_ipc_common.h" +#include "chromecast/common/media/cma_messages.h" +#include "chromecast/common/media/shared_memory_chunk.h" +#include "chromecast/media/cma/base/buffering_defs.h" +#include "chromecast/media/cma/base/cma_logging.h" +#include "chromecast/media/cma/base/coded_frame_provider.h" +#include "chromecast/media/cma/ipc/media_message_fifo.h" +#include "chromecast/media/cma/ipc_streamer/av_streamer_proxy.h" +#include "chromecast/renderer/media/cma_message_filter_proxy.h" +#include "chromecast/renderer/media/media_channel_proxy.h" +#include "media/base/bind_to_current_loop.h" +#include "media/base/pipeline_status.h" + +namespace chromecast { +namespace media { + +namespace { + +void IgnoreResult() { +} + +} // namespace + +// VideoPipelineProxyInternal - +// This class is not thread safe and should run on the same thread +// as the media channel proxy. +class VideoPipelineProxyInternal { + public: + typedef base::Callback<void(scoped_ptr<base::SharedMemory>)> SharedMemCB; + + static void Release(scoped_ptr<VideoPipelineProxyInternal> proxy); + + explicit VideoPipelineProxyInternal( + scoped_refptr<MediaChannelProxy> media_channel_proxy); + virtual ~VideoPipelineProxyInternal(); + + // Notify the other side (browser process) of some activity on the video pipe. + // TODO(erickung): either send an IPC message or write a byte on the + // SyncSocket. + void NotifyPipeWrite(); + + // These functions are almost a one to one correspondence with VideoPipeline + // but this is an internal class and there is no reason to derive from + // VideoPipeline. + void SetClient(const base::Closure& pipe_read_cb, + const VideoPipelineClient& client); + void CreateAvPipe(const SharedMemCB& shared_mem_cb); + void Initialize(const ::media::VideoDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb); + + private: + void Shutdown(); + + // Callbacks for CmaMessageFilterHost::VideoDelegate. + void OnAvPipeCreated(bool status, + base::SharedMemoryHandle shared_mem_handle, + base::FileDescriptor socket); + void OnStateChanged(::media::PipelineStatus status); + + base::ThreadChecker thread_checker_; + + scoped_refptr<MediaChannelProxy> media_channel_proxy_; + + // Store the callback for a pending state transition. + ::media::PipelineStatusCB status_cb_; + + SharedMemCB shared_mem_cb_; + + DISALLOW_COPY_AND_ASSIGN(VideoPipelineProxyInternal); +}; + +// static +void VideoPipelineProxyInternal::Release( + scoped_ptr<VideoPipelineProxyInternal> proxy) { + proxy->Shutdown(); +} + +VideoPipelineProxyInternal::VideoPipelineProxyInternal( + scoped_refptr<MediaChannelProxy> media_channel_proxy) + : media_channel_proxy_(media_channel_proxy) { + DCHECK(media_channel_proxy.get()); + + // Creation can be done on a different thread. + thread_checker_.DetachFromThread(); +} + +VideoPipelineProxyInternal::~VideoPipelineProxyInternal() { +} + +void VideoPipelineProxyInternal::Shutdown() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Remove any callback on VideoPipelineProxyInternal. + media_channel_proxy_->SetVideoDelegate( + CmaMessageFilterProxy::VideoDelegate()); +} + +void VideoPipelineProxyInternal::NotifyPipeWrite() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // TODO(damienv): An alternative way would be to use a dedicated socket for + // this event. + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_NotifyPipeWrite(media_channel_proxy_->GetId(), + kVideoTrackId))); + VLOG_IF(4, !success) << "Sending msg failed"; +} + +void VideoPipelineProxyInternal::SetClient( + const base::Closure& pipe_read_cb, + const VideoPipelineClient& video_client) { + DCHECK(thread_checker_.CalledOnValidThread()); + + CmaMessageFilterProxy::VideoDelegate delegate; + delegate.av_pipe_cb = + base::Bind(&VideoPipelineProxyInternal::OnAvPipeCreated, + base::Unretained(this)); + delegate.state_changed_cb = + base::Bind(&VideoPipelineProxyInternal::OnStateChanged, + base::Unretained(this)); + delegate.pipe_read_cb = pipe_read_cb; + delegate.client = video_client; + bool success = media_channel_proxy_->SetVideoDelegate(delegate); + CHECK(success); +} + +void VideoPipelineProxyInternal::CreateAvPipe( + const SharedMemCB& shared_mem_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(shared_mem_cb_.is_null()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_CreateAvPipe( + media_channel_proxy_->GetId(), kVideoTrackId, kAppVideoBufferSize))); + if (!success) { + shared_mem_cb.Run(scoped_ptr<base::SharedMemory>()); + return; + } + shared_mem_cb_ = shared_mem_cb; +} + +void VideoPipelineProxyInternal::OnAvPipeCreated( + bool success, + base::SharedMemoryHandle shared_mem_handle, + base::FileDescriptor socket) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!shared_mem_cb_.is_null()); + if (!success) { + shared_mem_cb_.Run(scoped_ptr<base::SharedMemory>()); + return; + } + + CHECK(base::SharedMemory::IsHandleValid(shared_mem_handle)); + shared_mem_cb_.Run(scoped_ptr<base::SharedMemory>( + new base::SharedMemory(shared_mem_handle, false))); +} + +void VideoPipelineProxyInternal::Initialize( + const ::media::VideoDecoderConfig& arg1, + const ::media::PipelineStatusCB& status_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( + new CmaHostMsg_VideoInitialize(media_channel_proxy_->GetId(), + kVideoTrackId, arg1))); + if (!success) { + status_cb.Run( ::media::PIPELINE_ERROR_INITIALIZATION_FAILED); + return; + } + DCHECK(status_cb_.is_null()); + status_cb_ = status_cb; +} + +void VideoPipelineProxyInternal::OnStateChanged( + ::media::PipelineStatus status) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!status_cb_.is_null()); + base::ResetAndReturn(&status_cb_).Run(status); +} + + +// A macro runs current member function on |io_message_loop_proxy_| thread. +#define FORWARD_ON_IO_THREAD(param_fn, ...) \ + io_message_loop_proxy_->PostTask( \ + FROM_HERE, \ + base::Bind(&VideoPipelineProxyInternal::param_fn, \ + base::Unretained(proxy_.get()), ##__VA_ARGS__)) + +VideoPipelineProxy::VideoPipelineProxy( + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy, + scoped_refptr<MediaChannelProxy> media_channel_proxy) + : io_message_loop_proxy_(io_message_loop_proxy), + proxy_(new VideoPipelineProxyInternal(media_channel_proxy)), + video_streamer_(new AvStreamerProxy()), + weak_factory_(this) { + DCHECK(io_message_loop_proxy_.get()); + weak_this_ = weak_factory_.GetWeakPtr(); + thread_checker_.DetachFromThread(); +} + +VideoPipelineProxy::~VideoPipelineProxy() { + DCHECK(thread_checker_.CalledOnValidThread()); + // Release the underlying object on the right thread. + io_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&VideoPipelineProxyInternal::Release, base::Passed(&proxy_))); +} + +void VideoPipelineProxy::SetClient( + const VideoPipelineClient& video_client) { + DCHECK(thread_checker_.CalledOnValidThread()); + base::Closure pipe_read_cb = + ::media::BindToCurrentLoop( + base::Bind(&VideoPipelineProxy::OnPipeRead, weak_this_)); + FORWARD_ON_IO_THREAD(SetClient, pipe_read_cb, video_client); +} + +void VideoPipelineProxy::Initialize( + const ::media::VideoDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb) { + CMALOG(kLogControl) << "VideoPipelineProxy::Initialize"; + DCHECK(thread_checker_.CalledOnValidThread()); + video_streamer_->SetCodedFrameProvider(frame_provider.Pass()); + + VideoPipelineProxyInternal::SharedMemCB shared_mem_cb = + ::media::BindToCurrentLoop(base::Bind( + &VideoPipelineProxy::OnAvPipeCreated, weak_this_, + config, status_cb)); + FORWARD_ON_IO_THREAD(CreateAvPipe, shared_mem_cb); +} + +void VideoPipelineProxy::OnAvPipeCreated( + const ::media::VideoDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb, + scoped_ptr<base::SharedMemory> shared_memory) { + CMALOG(kLogControl) << "VideoPipelineProxy::OnAvPipeCreated"; + DCHECK(thread_checker_.CalledOnValidThread()); + if (!shared_memory || + !shared_memory->Map(kAppVideoBufferSize)) { + status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED); + return; + } + CHECK(shared_memory->memory()); + + scoped_ptr<MediaMemoryChunk> shared_memory_chunk( + new SharedMemoryChunk(shared_memory.Pass(), kAppVideoBufferSize)); + scoped_ptr<MediaMessageFifo> video_pipe( + new MediaMessageFifo(shared_memory_chunk.Pass(), false)); + video_pipe->ObserveWriteActivity( + base::Bind(&VideoPipelineProxy::OnPipeWrite, weak_this_)); + + video_streamer_->SetMediaMessageFifo(video_pipe.Pass()); + + // Now proceed to the decoder/renderer initialization. + FORWARD_ON_IO_THREAD(Initialize, config, status_cb); +} + +void VideoPipelineProxy::StartFeeding() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(video_streamer_); + video_streamer_->Start(); +} + +void VideoPipelineProxy::Flush(const base::Closure& done_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(video_streamer_); + video_streamer_->StopAndFlush(done_cb); +} + +void VideoPipelineProxy::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!video_streamer_) + return; + video_streamer_->StopAndFlush(base::Bind(&IgnoreResult)); +} + +void VideoPipelineProxy::OnPipeWrite() { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_IO_THREAD(NotifyPipeWrite); +} + +void VideoPipelineProxy::OnPipeRead() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (video_streamer_) + video_streamer_->OnFifoReadEvent(); +} + +} // namespace media +} // namespace chromecast
\ No newline at end of file diff --git a/chromecast/renderer/media/video_pipeline_proxy.h b/chromecast/renderer/media/video_pipeline_proxy.h new file mode 100644 index 0000000..8874552 --- /dev/null +++ b/chromecast/renderer/media/video_pipeline_proxy.h @@ -0,0 +1,76 @@ +// Copyright 2014 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. + +#ifndef CHROMECAST_RENDERER_MEDIA_VIDEO_PIPELINE_PROXY_H_ +#define CHROMECAST_RENDERER_MEDIA_VIDEO_PIPELINE_PROXY_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "chromecast/media/cma/pipeline/video_pipeline.h" +#include "media/base/pipeline_status.h" + +namespace base { +class MessageLoopProxy; +class SharedMemory; +} + +namespace media { +class VideoDecoderConfig; +} + +namespace chromecast { +namespace media { +struct AvPipelineClient; +class AvStreamerProxy; +class CodedFrameProvider; +class VideoPipelineProxyInternal; +class MediaChannelProxy; + +class VideoPipelineProxy : public VideoPipeline { + public: + VideoPipelineProxy( + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy, + scoped_refptr<MediaChannelProxy> media_channel_proxy); + ~VideoPipelineProxy() override; + + void Initialize(const ::media::VideoDecoderConfig& config, + scoped_ptr<CodedFrameProvider> frame_provider, + const ::media::PipelineStatusCB& status_cb); + void StartFeeding(); + void Flush(const base::Closure& done_cb); + void Stop(); + + // VideoPipeline implementation. + void SetClient(const VideoPipelineClient& video_client) override; + + private: + base::ThreadChecker thread_checker_; + + void OnAvPipeCreated( + const ::media::VideoDecoderConfig& config, + const ::media::PipelineStatusCB& status_cb, + scoped_ptr<base::SharedMemory> shared_memory); + void OnPipeWrite(); + void OnPipeRead(); + + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; + + // |proxy_| main goal is to convert function calls to IPC messages. + scoped_ptr<VideoPipelineProxyInternal> proxy_; + + scoped_ptr<AvStreamerProxy> video_streamer_; + + base::WeakPtr<VideoPipelineProxy> weak_this_; + base::WeakPtrFactory<VideoPipelineProxy> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(VideoPipelineProxy); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_RENDERER_MEDIA_VIDEO_PIPELINE_PROXY_H_
\ No newline at end of file |