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