Chromium Code Reviews| 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/audio_pipeline_proxy.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/callback_helpers.h" | |
| 9 #include "base/memory/shared_memory.h" | |
| 10 #include "base/message_loop/message_loop.h" | |
| 11 #include "base/threading/thread_checker.h" | |
| 12 #include "chromecast/common/media/cma_ipc_common.h" | |
| 13 #include "chromecast/common/media/cma_messages.h" | |
| 14 #include "chromecast/common/media/shared_memory_chunk.h" | |
| 15 #include "chromecast/media/cma/base/buffering_defs.h" | |
| 16 #include "chromecast/media/cma/base/cma_logging.h" | |
| 17 #include "chromecast/media/cma/base/coded_frame_provider.h" | |
| 18 #include "chromecast/media/cma/ipc/media_message_fifo.h" | |
| 19 #include "chromecast/media/cma/ipc_streamer/av_streamer_proxy.h" | |
| 20 #include "chromecast/media/cma/pipeline/av_pipeline_client.h" | |
| 21 #include "chromecast/renderer/media/cma_message_filter_proxy.h" | |
| 22 #include "chromecast/renderer/media/media_channel_proxy.h" | |
| 23 #include "media/base/bind_to_current_loop.h" | |
| 24 #include "media/base/pipeline_status.h" | |
| 25 | |
| 26 namespace chromecast { | |
| 27 namespace cma { | |
| 28 | |
| 29 namespace { | |
| 30 | |
| 31 void Noop() { | |
|
gunsch
2014/12/20 22:41:33
probably rename to something like "IgnoreResult"
erickung1
2014/12/21 11:10:46
Done.
| |
| 32 } | |
| 33 | |
| 34 } // namespace | |
| 35 | |
| 36 // AudioPipelineProxyInternal - | |
| 37 // This class is not thread safe and should run on the same thread | |
| 38 // as the media channel proxy. | |
| 39 class AudioPipelineProxyInternal { | |
| 40 public: | |
| 41 typedef base::Callback<void(scoped_ptr<base::SharedMemory>)> SharedMemCB; | |
| 42 | |
| 43 static void Release(scoped_ptr<AudioPipelineProxyInternal> proxy); | |
| 44 | |
| 45 explicit AudioPipelineProxyInternal( | |
| 46 scoped_refptr<MediaChannelProxy> media_channel_proxy); | |
| 47 virtual ~AudioPipelineProxyInternal(); | |
| 48 | |
| 49 // Notify the other side (browser process) of some activity on the audio pipe. | |
| 50 // TODO(damienv): either send an IPC message or write a byte on the | |
|
gunsch
2014/12/20 22:41:33
we probably shouldn't be adding any TODO(damienv)
erickung1
2014/12/21 11:10:46
Change to my name first. I checked the code and it
| |
| 51 // SyncSocket. | |
| 52 void NotifyPipeWrite(); | |
| 53 | |
| 54 // These functions are almost a one to one correspondence with AudioPipeline | |
| 55 // but this is an internal class and there is no reason to derive from | |
| 56 // AudioPipeline. | |
| 57 void SetClient(const base::Closure& pipe_read_cb, | |
| 58 const chromecast::media::AvPipelineClient& client); | |
| 59 void CreateAvPipe(const SharedMemCB& shared_mem_cb); | |
| 60 void Initialize(const ::media::AudioDecoderConfig& config, | |
| 61 const ::media::PipelineStatusCB& status_cb); | |
| 62 void SetVolume(float volume); | |
| 63 | |
| 64 private: | |
| 65 void Shutdown(); | |
| 66 | |
| 67 // Callbacks for CmaMessageFilterHost::AudioDelegate. | |
| 68 void OnAvPipeCreated(bool status, | |
| 69 base::SharedMemoryHandle shared_mem_handle, | |
| 70 base::FileDescriptor socket); | |
| 71 void OnStateChanged(::media::PipelineStatus status); | |
| 72 | |
| 73 base::ThreadChecker thread_checker_; | |
| 74 | |
| 75 scoped_refptr<MediaChannelProxy> media_channel_proxy_; | |
| 76 | |
| 77 // Store the callback for a pending state transition. | |
| 78 ::media::PipelineStatusCB status_cb_; | |
| 79 | |
| 80 SharedMemCB shared_mem_cb_; | |
| 81 | |
| 82 DISALLOW_COPY_AND_ASSIGN(AudioPipelineProxyInternal); | |
|
gunsch
2014/12/20 22:41:33
include base/macros.h
erickung1
2014/12/21 11:10:46
probably don't need it since audio_pipeline_proxy.
| |
| 83 }; | |
| 84 | |
| 85 // static | |
| 86 void AudioPipelineProxyInternal::Release( | |
| 87 scoped_ptr<AudioPipelineProxyInternal> proxy) { | |
| 88 proxy->Shutdown(); | |
| 89 } | |
| 90 | |
| 91 AudioPipelineProxyInternal::AudioPipelineProxyInternal( | |
| 92 scoped_refptr<MediaChannelProxy> media_channel_proxy) | |
| 93 : media_channel_proxy_(media_channel_proxy) { | |
| 94 DCHECK(media_channel_proxy.get()); | |
| 95 | |
| 96 // Creation can be done on a different thread. | |
| 97 thread_checker_.DetachFromThread(); | |
| 98 } | |
| 99 | |
| 100 AudioPipelineProxyInternal::~AudioPipelineProxyInternal() { | |
| 101 } | |
| 102 | |
| 103 void AudioPipelineProxyInternal::Shutdown() { | |
| 104 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 105 | |
| 106 // Remove any callback on AudioPipelineProxyInternal. | |
| 107 media_channel_proxy_->SetAudioDelegate( | |
| 108 CmaMessageFilterProxy::AudioDelegate()); | |
| 109 } | |
| 110 | |
| 111 void AudioPipelineProxyInternal::NotifyPipeWrite() { | |
| 112 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 113 | |
| 114 // TODO(damienv): An alternative way would be to use a dedicated socket for | |
| 115 // this event. | |
| 116 bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( | |
| 117 new CmaHostMsg_NotifyPipeWrite( | |
| 118 media_channel_proxy_->GetId(), chromecast::media::kAudioTrackId))); | |
| 119 VLOG_IF(4, !success) << "Sending msg failed"; | |
| 120 } | |
| 121 | |
| 122 void AudioPipelineProxyInternal::SetClient( | |
| 123 const base::Closure& pipe_read_cb, | |
| 124 const chromecast::media::AvPipelineClient& client) { | |
| 125 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 126 | |
| 127 CmaMessageFilterProxy::AudioDelegate delegate; | |
| 128 delegate.av_pipe_cb = | |
| 129 base::Bind(&AudioPipelineProxyInternal::OnAvPipeCreated, | |
| 130 base::Unretained(this)); | |
| 131 delegate.state_changed_cb = | |
| 132 base::Bind(&AudioPipelineProxyInternal::OnStateChanged, | |
| 133 base::Unretained(this)); | |
| 134 delegate.pipe_read_cb = pipe_read_cb; | |
| 135 delegate.client = client; | |
| 136 bool success = media_channel_proxy_->SetAudioDelegate(delegate); | |
| 137 CHECK(success); | |
| 138 } | |
| 139 | |
| 140 void AudioPipelineProxyInternal::CreateAvPipe( | |
| 141 const SharedMemCB& shared_mem_cb) { | |
| 142 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 143 DCHECK(shared_mem_cb_.is_null()); | |
| 144 bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( | |
| 145 new CmaHostMsg_CreateAvPipe( | |
| 146 media_channel_proxy_->GetId(), chromecast::media::kAudioTrackId, | |
| 147 chromecast::media::kAppAudioBufferSize))); | |
| 148 if (!success) { | |
| 149 shared_mem_cb.Run(scoped_ptr<base::SharedMemory>()); | |
| 150 return; | |
| 151 } | |
| 152 shared_mem_cb_ = shared_mem_cb; | |
| 153 } | |
| 154 | |
| 155 void AudioPipelineProxyInternal::OnAvPipeCreated( | |
| 156 bool success, | |
| 157 base::SharedMemoryHandle shared_mem_handle, | |
| 158 base::FileDescriptor socket) { | |
| 159 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 160 DCHECK(!shared_mem_cb_.is_null()); | |
| 161 if (!success) { | |
| 162 shared_mem_cb_.Run(scoped_ptr<base::SharedMemory>()); | |
| 163 return; | |
| 164 } | |
| 165 | |
| 166 CHECK(base::SharedMemory::IsHandleValid(shared_mem_handle)); | |
| 167 shared_mem_cb_.Run(scoped_ptr<base::SharedMemory>( | |
| 168 new base::SharedMemory(shared_mem_handle, false))); | |
| 169 } | |
| 170 | |
| 171 #define DEFINE_STATE_TRANSITION_1(param_fn, param_msg, param_type1) \ | |
|
gunsch
2014/12/20 22:41:33
This #define is only used once; this would be clea
erickung1
2014/12/21 11:10:46
Done.
| |
| 172 void AudioPipelineProxyInternal::param_fn( \ | |
| 173 const param_type1& arg1, \ | |
| 174 const ::media::PipelineStatusCB& status_cb) { \ | |
| 175 DCHECK(thread_checker_.CalledOnValidThread()); \ | |
| 176 bool success = media_channel_proxy_->Send(scoped_ptr<IPC::Message>( \ | |
| 177 new param_msg( \ | |
| 178 media_channel_proxy_->GetId(), chromecast::media::kAudioTrackId, \ | |
| 179 arg1))); \ | |
| 180 if (!success) { \ | |
| 181 status_cb.Run( \ | |
| 182 ::media::PIPELINE_ERROR_INITIALIZATION_FAILED); \ | |
| 183 return; \ | |
| 184 } \ | |
| 185 DCHECK(status_cb_.is_null()); \ | |
| 186 status_cb_ = status_cb; \ | |
| 187 } | |
| 188 | |
| 189 DEFINE_STATE_TRANSITION_1(Initialize, CmaHostMsg_AudioInitialize, | |
| 190 ::media::AudioDecoderConfig) | |
| 191 | |
| 192 void AudioPipelineProxyInternal::SetVolume(float volume) { | |
| 193 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 194 media_channel_proxy_->Send(scoped_ptr<IPC::Message>( | |
| 195 new CmaHostMsg_SetVolume( | |
| 196 media_channel_proxy_->GetId(), | |
| 197 chromecast::media::kAudioTrackId, volume))); | |
| 198 } | |
| 199 | |
| 200 void AudioPipelineProxyInternal::OnStateChanged( | |
| 201 ::media::PipelineStatus status) { | |
| 202 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 203 DCHECK(!status_cb_.is_null()); | |
| 204 base::ResetAndReturn(&status_cb_).Run(status); | |
| 205 } | |
| 206 | |
| 207 | |
| 208 #define FORWARD_ON_IO_0(param_fn) \ | |
|
gunsch
2014/12/20 22:41:33
This #define is also only used once, just unroll i
erickung1
2014/12/21 11:10:46
Done.
| |
| 209 io_message_loop_proxy_->PostTask( \ | |
| 210 FROM_HERE, \ | |
| 211 base::Bind(&AudioPipelineProxyInternal::param_fn, \ | |
| 212 base::Unretained(proxy_.get()))) | |
| 213 #define FORWARD_ON_IO_1(param_fn, param_arg1) \ | |
| 214 io_message_loop_proxy_->PostTask( \ | |
| 215 FROM_HERE, \ | |
| 216 base::Bind(&AudioPipelineProxyInternal::param_fn, \ | |
| 217 base::Unretained(proxy_.get()), (param_arg1))) | |
| 218 #define FORWARD_ON_IO_2(param_fn, param_arg1, param_arg2) \ | |
| 219 io_message_loop_proxy_->PostTask( \ | |
| 220 FROM_HERE, \ | |
| 221 base::Bind(&AudioPipelineProxyInternal::param_fn, \ | |
| 222 base::Unretained(proxy_.get()), (param_arg1), (param_arg2))) | |
| 223 | |
| 224 AudioPipelineProxy::AudioPipelineProxy( | |
| 225 scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy, | |
| 226 scoped_refptr<MediaChannelProxy> media_channel_proxy) | |
| 227 : io_message_loop_proxy_(io_message_loop_proxy), | |
| 228 proxy_(new AudioPipelineProxyInternal(media_channel_proxy)), | |
| 229 audio_streamer_(new chromecast::media::AvStreamerProxy()), | |
| 230 weak_factory_(this) { | |
| 231 DCHECK(io_message_loop_proxy_.get()); | |
| 232 weak_this_ = weak_factory_.GetWeakPtr(); | |
| 233 thread_checker_.DetachFromThread(); | |
| 234 } | |
| 235 | |
| 236 AudioPipelineProxy::~AudioPipelineProxy() { | |
| 237 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 238 // Release the underlying object on the right thread. | |
| 239 io_message_loop_proxy_->PostTask( | |
| 240 FROM_HERE, | |
| 241 base::Bind(&AudioPipelineProxyInternal::Release, base::Passed(&proxy_))); | |
| 242 } | |
| 243 | |
| 244 void AudioPipelineProxy::SetClient( | |
| 245 const chromecast::media::AvPipelineClient& client) { | |
| 246 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 247 base::Closure pipe_read_cb = ::media::BindToCurrentLoop( | |
| 248 base::Bind(&AudioPipelineProxy::OnPipeRead, weak_this_)); | |
| 249 FORWARD_ON_IO_2(SetClient, pipe_read_cb, client); | |
| 250 } | |
| 251 | |
| 252 void AudioPipelineProxy::Initialize( | |
| 253 const ::media::AudioDecoderConfig& config, | |
| 254 scoped_ptr<chromecast::media::CodedFrameProvider> frame_provider, | |
| 255 const ::media::PipelineStatusCB& status_cb) { | |
| 256 CMALOG(chromecast::media::kLogControl) << "AudioPipelineProxy::Initialize"; | |
| 257 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 258 audio_streamer_->SetCodedFrameProvider(frame_provider.Pass()); | |
| 259 | |
| 260 AudioPipelineProxyInternal::SharedMemCB shared_mem_cb = | |
| 261 ::media::BindToCurrentLoop(base::Bind( | |
| 262 &AudioPipelineProxy::OnAvPipeCreated, weak_this_, | |
| 263 config, status_cb)); | |
| 264 FORWARD_ON_IO_1(CreateAvPipe, shared_mem_cb); | |
| 265 } | |
| 266 | |
| 267 void AudioPipelineProxy::OnAvPipeCreated( | |
| 268 const ::media::AudioDecoderConfig& config, | |
| 269 const ::media::PipelineStatusCB& status_cb, | |
| 270 scoped_ptr<base::SharedMemory> shared_memory) { | |
| 271 CMALOG(chromecast::media::kLogControl) | |
| 272 << "AudioPipelineProxy::OnAvPipeCreated"; | |
| 273 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 274 if (!shared_memory || | |
| 275 !shared_memory->Map(chromecast::media::kAppAudioBufferSize)) { | |
| 276 status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED); | |
| 277 return; | |
| 278 } | |
| 279 CHECK(shared_memory->memory()); | |
| 280 | |
| 281 scoped_ptr<chromecast::media::MediaMemoryChunk> shared_memory_chunk( | |
| 282 new chromecast::media::SharedMemoryChunk( | |
| 283 shared_memory.Pass(), chromecast::media::kAppAudioBufferSize)); | |
| 284 scoped_ptr<chromecast::media::MediaMessageFifo> audio_pipe( | |
| 285 new chromecast::media::MediaMessageFifo( | |
| 286 shared_memory_chunk.Pass(), false)); | |
| 287 audio_pipe->ObserveWriteActivity( | |
| 288 base::Bind(&AudioPipelineProxy::OnPipeWrite, weak_this_)); | |
| 289 | |
| 290 audio_streamer_->SetMediaMessageFifo(audio_pipe.Pass()); | |
| 291 | |
| 292 // Now proceed to the decoder/renderer initialization. | |
| 293 FORWARD_ON_IO_2(Initialize, config, status_cb); | |
| 294 } | |
| 295 | |
| 296 void AudioPipelineProxy::StartFeeding() { | |
| 297 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 298 DCHECK(audio_streamer_); | |
| 299 audio_streamer_->Start(); | |
| 300 } | |
| 301 | |
| 302 void AudioPipelineProxy::Flush(const base::Closure& done_cb) { | |
| 303 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 304 DCHECK(audio_streamer_); | |
| 305 audio_streamer_->StopAndFlush(done_cb); | |
| 306 } | |
| 307 | |
| 308 void AudioPipelineProxy::Stop() { | |
| 309 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 310 if (!audio_streamer_) | |
| 311 return; | |
| 312 audio_streamer_->StopAndFlush(base::Bind(&Noop)); | |
| 313 } | |
| 314 | |
| 315 void AudioPipelineProxy::SetVolume(float volume) { | |
| 316 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 317 FORWARD_ON_IO_1(SetVolume, volume); | |
| 318 } | |
| 319 | |
| 320 void AudioPipelineProxy::OnPipeWrite() { | |
| 321 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 322 FORWARD_ON_IO_0(NotifyPipeWrite); | |
| 323 } | |
| 324 | |
| 325 void AudioPipelineProxy::OnPipeRead() { | |
| 326 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 327 if (audio_streamer_) | |
| 328 audio_streamer_->OnFifoReadEvent(); | |
| 329 } | |
| 330 | |
| 331 } // namespace cma | |
| 332 } // namespace chromecast | |
| 333 | |
| OLD | NEW |