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