summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chromecast/chromecast.gyp15
-rw-r--r--chromecast/common/chromecast_switches.cc3
-rw-r--r--chromecast/common/chromecast_switches.h2
-rw-r--r--chromecast/common/media/cma_ipc_common.h21
-rw-r--r--chromecast/common/media/cma_messages.h27
-rw-r--r--chromecast/common/media/cma_param_traits_macros.h7
-rw-r--r--chromecast/common/media/shared_memory_chunk.cc36
-rw-r--r--chromecast/common/media/shared_memory_chunk.h40
-rw-r--r--chromecast/renderer/DEPS1
-rw-r--r--chromecast/renderer/cast_content_renderer_client.cc15
-rw-r--r--chromecast/renderer/cast_content_renderer_client.h4
-rw-r--r--chromecast/renderer/media/audio_pipeline_proxy.cc311
-rw-r--r--chromecast/renderer/media/audio_pipeline_proxy.h79
-rw-r--r--chromecast/renderer/media/cma_media_renderer_factory.cc37
-rw-r--r--chromecast/renderer/media/cma_media_renderer_factory.h32
-rw-r--r--chromecast/renderer/media/cma_message_filter_proxy.cc272
-rw-r--r--chromecast/renderer/media/cma_message_filter_proxy.h135
-rw-r--r--chromecast/renderer/media/media_channel_proxy.cc79
-rw-r--r--chromecast/renderer/media/media_channel_proxy.h68
-rw-r--r--chromecast/renderer/media/media_pipeline_proxy.cc283
-rw-r--r--chromecast/renderer/media/media_pipeline_proxy.h85
-rw-r--r--chromecast/renderer/media/video_pipeline_proxy.cc299
-rw-r--r--chromecast/renderer/media/video_pipeline_proxy.h76
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