Index: chromecast/renderer/media/video_pipeline_proxy.cc |
diff --git a/chromecast/renderer/media/video_pipeline_proxy.cc b/chromecast/renderer/media/video_pipeline_proxy.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6c924cd4e2e4e39e01547a40273e1bf8bc16d234 |
--- /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 |