OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chromecast/renderer/media/media_pipeline_proxy.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/callback_helpers.h" |
| 9 #include "base/location.h" |
| 10 #include "base/logging.h" |
| 11 #include "base/message_loop/message_loop_proxy.h" |
| 12 #include "chromecast/common/media/cma_messages.h" |
| 13 #include "chromecast/media/cma/base/coded_frame_provider.h" |
| 14 #include "chromecast/renderer/media/audio_pipeline_proxy.h" |
| 15 #include "chromecast/renderer/media/media_channel_proxy.h" |
| 16 #include "chromecast/renderer/media/video_pipeline_proxy.h" |
| 17 |
| 18 namespace chromecast { |
| 19 namespace media { |
| 20 |
| 21 // MediaPipelineProxyInternal - |
| 22 // This class is not thread safe and should run on the same thread |
| 23 // as the media channel proxy. |
| 24 class MediaPipelineProxyInternal { |
| 25 public: |
| 26 static void Release(scoped_ptr<MediaPipelineProxyInternal> proxy); |
| 27 |
| 28 explicit MediaPipelineProxyInternal( |
| 29 scoped_refptr<MediaChannelProxy> media_channel_proxy); |
| 30 virtual ~MediaPipelineProxyInternal(); |
| 31 |
| 32 void SetClient(const MediaPipelineClient& client); |
| 33 void SetCdm(int render_frame_id, int cdm_id); |
| 34 void StartPlayingFrom(const base::TimeDelta& time); |
| 35 void Flush(const ::media::PipelineStatusCB& status_cb); |
| 36 void Stop(); |
| 37 void SetPlaybackRate(float playback_rate); |
| 38 |
| 39 private: |
| 40 void Shutdown(); |
| 41 |
| 42 // Callbacks for CmaMessageFilterHost::MediaDelegate. |
| 43 void OnStateChanged(::media::PipelineStatus status); |
| 44 |
| 45 base::ThreadChecker thread_checker_; |
| 46 |
| 47 scoped_refptr<MediaChannelProxy> media_channel_proxy_; |
| 48 |
| 49 MediaPipelineClient client_; |
| 50 |
| 51 // Store the callback for a pending state transition. |
| 52 ::media::PipelineStatusCB status_cb_; |
| 53 |
| 54 DISALLOW_COPY_AND_ASSIGN(MediaPipelineProxyInternal); |
| 55 }; |
| 56 |
| 57 // static |
| 58 void MediaPipelineProxyInternal::Release( |
| 59 scoped_ptr<MediaPipelineProxyInternal> proxy) { |
| 60 proxy->Shutdown(); |
| 61 } |
| 62 |
| 63 MediaPipelineProxyInternal::MediaPipelineProxyInternal( |
| 64 scoped_refptr<MediaChannelProxy> media_channel_proxy) |
| 65 : media_channel_proxy_(media_channel_proxy) { |
| 66 DCHECK(media_channel_proxy.get()); |
| 67 |
| 68 // Creation can be done on a different thread. |
| 69 thread_checker_.DetachFromThread(); |
| 70 } |
| 71 |
| 72 MediaPipelineProxyInternal::~MediaPipelineProxyInternal() { |
| 73 } |
| 74 |
| 75 void MediaPipelineProxyInternal::Shutdown() { |
| 76 DCHECK(thread_checker_.CalledOnValidThread()); |
| 77 |
| 78 // Remove any callback on VideoPipelineProxyInternal. |
| 79 media_channel_proxy_->SetMediaDelegate( |
| 80 CmaMessageFilterProxy::MediaDelegate()); |
| 81 } |
| 82 |
| 83 void MediaPipelineProxyInternal::SetClient( |
| 84 const MediaPipelineClient& client) { |
| 85 DCHECK(thread_checker_.CalledOnValidThread()); |
| 86 DCHECK(!client.error_cb.is_null()); |
| 87 DCHECK(!client.buffering_state_cb.is_null()); |
| 88 client_ = client; |
| 89 |
| 90 CmaMessageFilterProxy::MediaDelegate delegate; |
| 91 delegate.state_changed_cb = |
| 92 base::Bind(&MediaPipelineProxyInternal::OnStateChanged, |
| 93 base::Unretained(this)); |
| 94 delegate.client = client; |
| 95 bool success = media_channel_proxy_->SetMediaDelegate(delegate); |
| 96 CHECK(success); |
| 97 } |
| 98 |
| 99 void MediaPipelineProxyInternal::SetCdm(int render_frame_id, int cdm_id) { |
| 100 DCHECK(thread_checker_.CalledOnValidThread()); |
| 101 bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( |
| 102 new CmaHostMsg_SetCdm(media_channel_proxy_->GetId(), |
| 103 render_frame_id, |
| 104 cdm_id))); |
| 105 LOG_IF(ERROR, !success) << "Failed to send SetCdm=" << cdm_id; |
| 106 } |
| 107 |
| 108 void MediaPipelineProxyInternal::Flush( |
| 109 const ::media::PipelineStatusCB& status_cb) { |
| 110 DCHECK(thread_checker_.CalledOnValidThread()); |
| 111 bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( |
| 112 new CmaHostMsg_Flush(media_channel_proxy_->GetId()))); |
| 113 if (!success) { |
| 114 status_cb.Run(::media::PIPELINE_ERROR_ABORT); |
| 115 return; |
| 116 } |
| 117 DCHECK(status_cb_.is_null()); |
| 118 status_cb_ = status_cb; |
| 119 } |
| 120 |
| 121 void MediaPipelineProxyInternal::Stop() { |
| 122 DCHECK(thread_checker_.CalledOnValidThread()); |
| 123 bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( |
| 124 new CmaHostMsg_Stop(media_channel_proxy_->GetId()))); |
| 125 if (!success) |
| 126 client_.error_cb.Run(::media::PIPELINE_ERROR_ABORT); |
| 127 } |
| 128 |
| 129 void MediaPipelineProxyInternal::StartPlayingFrom(const base::TimeDelta& time) { |
| 130 DCHECK(thread_checker_.CalledOnValidThread()); |
| 131 bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( |
| 132 new CmaHostMsg_StartPlayingFrom( |
| 133 media_channel_proxy_->GetId(), time))); |
| 134 if (!success) |
| 135 client_.error_cb.Run(::media::PIPELINE_ERROR_ABORT); |
| 136 } |
| 137 |
| 138 void MediaPipelineProxyInternal::SetPlaybackRate(float playback_rate) { |
| 139 DCHECK(thread_checker_.CalledOnValidThread()); |
| 140 media_channel_proxy_->Send(scoped_ptr<IPC::Message>( |
| 141 new CmaHostMsg_SetPlaybackRate( |
| 142 media_channel_proxy_->GetId(), playback_rate))); |
| 143 } |
| 144 |
| 145 void MediaPipelineProxyInternal::OnStateChanged( |
| 146 ::media::PipelineStatus status) { |
| 147 DCHECK(thread_checker_.CalledOnValidThread()); |
| 148 DCHECK(!status_cb_.is_null()); |
| 149 base::ResetAndReturn(&status_cb_).Run(status); |
| 150 } |
| 151 |
| 152 |
| 153 // A macro runs current member function on |io_message_loop_proxy_| thread. |
| 154 #define FORWARD_ON_IO_THREAD(param_fn, ...) \ |
| 155 io_message_loop_proxy_->PostTask( \ |
| 156 FROM_HERE, \ |
| 157 base::Bind(&MediaPipelineProxyInternal::param_fn, \ |
| 158 base::Unretained(proxy_.get()), ##__VA_ARGS__)) |
| 159 |
| 160 MediaPipelineProxy::MediaPipelineProxy( |
| 161 int render_frame_id, |
| 162 scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy, |
| 163 LoadType load_type) |
| 164 : io_message_loop_proxy_(io_message_loop_proxy), |
| 165 render_frame_id_(render_frame_id), |
| 166 media_channel_proxy_(new MediaChannelProxy), |
| 167 proxy_(new MediaPipelineProxyInternal(media_channel_proxy_)), |
| 168 has_audio_(false), |
| 169 has_video_(false), |
| 170 audio_pipeline_(new AudioPipelineProxy( |
| 171 io_message_loop_proxy, media_channel_proxy_)), |
| 172 video_pipeline_(new VideoPipelineProxy( |
| 173 io_message_loop_proxy, media_channel_proxy_)), |
| 174 weak_factory_(this) { |
| 175 weak_this_ = weak_factory_.GetWeakPtr(); |
| 176 io_message_loop_proxy_->PostTask( |
| 177 FROM_HERE, |
| 178 base::Bind(&MediaChannelProxy::Open, media_channel_proxy_, |
| 179 load_type)); |
| 180 thread_checker_.DetachFromThread(); |
| 181 } |
| 182 |
| 183 MediaPipelineProxy::~MediaPipelineProxy() { |
| 184 io_message_loop_proxy_->PostTask( |
| 185 FROM_HERE, |
| 186 base::Bind(&MediaPipelineProxyInternal::Release, base::Passed(&proxy_))); |
| 187 io_message_loop_proxy_->PostTask( |
| 188 FROM_HERE, |
| 189 base::Bind(&MediaChannelProxy::Close, media_channel_proxy_)); |
| 190 } |
| 191 |
| 192 void MediaPipelineProxy::SetClient( |
| 193 const MediaPipelineClient& client) { |
| 194 DCHECK(thread_checker_.CalledOnValidThread()); |
| 195 FORWARD_ON_IO_THREAD(SetClient, client); |
| 196 } |
| 197 |
| 198 void MediaPipelineProxy::SetCdm(int cdm_id) { |
| 199 DCHECK(thread_checker_.CalledOnValidThread()); |
| 200 FORWARD_ON_IO_THREAD(SetCdm, render_frame_id_, cdm_id); |
| 201 } |
| 202 |
| 203 AudioPipeline* MediaPipelineProxy::GetAudioPipeline() const { |
| 204 return audio_pipeline_.get(); |
| 205 } |
| 206 |
| 207 VideoPipeline* MediaPipelineProxy::GetVideoPipeline() const { |
| 208 return video_pipeline_.get(); |
| 209 } |
| 210 |
| 211 void MediaPipelineProxy::InitializeAudio( |
| 212 const ::media::AudioDecoderConfig& config, |
| 213 scoped_ptr<CodedFrameProvider> frame_provider, |
| 214 const ::media::PipelineStatusCB& status_cb) { |
| 215 DCHECK(thread_checker_.CalledOnValidThread()); |
| 216 has_audio_ = true; |
| 217 audio_pipeline_->Initialize(config, frame_provider.Pass(), status_cb); |
| 218 } |
| 219 |
| 220 void MediaPipelineProxy::InitializeVideo( |
| 221 const ::media::VideoDecoderConfig& config, |
| 222 scoped_ptr<CodedFrameProvider> frame_provider, |
| 223 const ::media::PipelineStatusCB& status_cb) { |
| 224 DCHECK(thread_checker_.CalledOnValidThread()); |
| 225 has_video_ = true; |
| 226 video_pipeline_->Initialize(config, frame_provider.Pass(), status_cb); |
| 227 } |
| 228 |
| 229 void MediaPipelineProxy::StartPlayingFrom(base::TimeDelta time) { |
| 230 DCHECK(thread_checker_.CalledOnValidThread()); |
| 231 if (has_audio_) |
| 232 audio_pipeline_->StartFeeding(); |
| 233 if (has_video_) |
| 234 video_pipeline_->StartFeeding(); |
| 235 FORWARD_ON_IO_THREAD(StartPlayingFrom, time); |
| 236 } |
| 237 |
| 238 void MediaPipelineProxy::Flush(const ::media::PipelineStatusCB& status_cb) { |
| 239 DCHECK(thread_checker_.CalledOnValidThread()); |
| 240 DCHECK(has_audio_ || has_video_); |
| 241 |
| 242 ::media::SerialRunner::Queue bound_fns; |
| 243 if (has_audio_) { |
| 244 bound_fns.Push(base::Bind(&AudioPipelineProxy::Flush, |
| 245 base::Unretained(audio_pipeline_.get()))); |
| 246 } |
| 247 if (has_video_) { |
| 248 bound_fns.Push(base::Bind(&VideoPipelineProxy::Flush, |
| 249 base::Unretained(video_pipeline_.get()))); |
| 250 } |
| 251 ::media::PipelineStatusCB cb = |
| 252 base::Bind(&MediaPipelineProxy::OnProxyFlushDone, weak_this_, status_cb); |
| 253 pending_callbacks_ = ::media::SerialRunner::Run(bound_fns, cb); |
| 254 } |
| 255 |
| 256 void MediaPipelineProxy::OnProxyFlushDone( |
| 257 const ::media::PipelineStatusCB& status_cb, |
| 258 ::media::PipelineStatus status) { |
| 259 DCHECK(thread_checker_.CalledOnValidThread()); |
| 260 DCHECK_EQ(status, ::media::PIPELINE_OK); |
| 261 pending_callbacks_.reset(); |
| 262 FORWARD_ON_IO_THREAD(Flush, status_cb); |
| 263 } |
| 264 |
| 265 void MediaPipelineProxy::Stop() { |
| 266 DCHECK(thread_checker_.CalledOnValidThread()); |
| 267 DCHECK(has_audio_ || has_video_); |
| 268 |
| 269 if (has_audio_) |
| 270 audio_pipeline_->Stop(); |
| 271 if (has_video_) |
| 272 video_pipeline_->Stop(); |
| 273 |
| 274 FORWARD_ON_IO_THREAD(Stop); |
| 275 } |
| 276 |
| 277 void MediaPipelineProxy::SetPlaybackRate(float playback_rate) { |
| 278 DCHECK(thread_checker_.CalledOnValidThread()); |
| 279 FORWARD_ON_IO_THREAD(SetPlaybackRate, playback_rate); |
| 280 } |
| 281 |
| 282 } // namespace cma |
| 283 } // namespace chromecast |
OLD | NEW |