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 |