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.
5 #include "chromecast/renderer/media/audio_pipeline_proxy.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_messages.h"
13 #include "chromecast/common/media/shared_memory_chunk.h"
14 #include "chromecast/media/cma/base/buffering_defs.h"
15 #include "chromecast/media/cma/base/cma_logging.h"
16 #include "chromecast/media/cma/base/coded_frame_provider.h"
17 #include "chromecast/media/cma/ipc/media_message_fifo.h"
18 #include "chromecast/media/cma/ipc_streamer/av_streamer_proxy.h"
19 #include "chromecast/media/cma/pipeline/av_pipeline_client.h"
20 #include "chromecast/renderer/media/cma_message_filter_proxy.h"
21 #include "chromecast/renderer/media/media_channel_proxy.h"
22 #include "media/base/bind_to_current_loop.h"
23 #include "media/base/pipeline_status.h"
25 namespace chromecast
{
35 // AudioPipelineProxyInternal -
36 // This class is not thread safe and should run on the same thread
37 // as the media channel proxy.
38 class AudioPipelineProxyInternal
{
40 typedef base::Callback
<void(scoped_ptr
<base::SharedMemory
>)> SharedMemCB
;
42 static void Release(scoped_ptr
<AudioPipelineProxyInternal
> proxy
);
44 explicit AudioPipelineProxyInternal(
45 scoped_refptr
<MediaChannelProxy
> media_channel_proxy
);
46 virtual ~AudioPipelineProxyInternal();
48 // Notify the other side (browser process) of some activity on the audio pipe.
49 // TODO(erickung): either send an IPC message or write a byte on the
51 void NotifyPipeWrite();
53 // These functions are almost a one to one correspondence with AudioPipeline
54 // but this is an internal class and there is no reason to derive from
56 void SetClient(const base::Closure
& pipe_read_cb
,
57 const AvPipelineClient
& client
);
58 void CreateAvPipe(const SharedMemCB
& shared_mem_cb
);
59 void Initialize(const ::media::AudioDecoderConfig
& config
,
60 const ::media::PipelineStatusCB
& status_cb
);
61 void SetVolume(float volume
);
66 // Callbacks for CmaMessageFilterHost::AudioDelegate.
67 void OnAvPipeCreated(bool status
,
68 base::SharedMemoryHandle shared_mem_handle
,
69 base::FileDescriptor socket
);
70 void OnStateChanged(::media::PipelineStatus status
);
72 base::ThreadChecker thread_checker_
;
74 scoped_refptr
<MediaChannelProxy
> media_channel_proxy_
;
76 // Store the callback for a pending state transition.
77 ::media::PipelineStatusCB status_cb_
;
79 SharedMemCB shared_mem_cb_
;
81 DISALLOW_COPY_AND_ASSIGN(AudioPipelineProxyInternal
);
85 void AudioPipelineProxyInternal::Release(
86 scoped_ptr
<AudioPipelineProxyInternal
> proxy
) {
90 AudioPipelineProxyInternal::AudioPipelineProxyInternal(
91 scoped_refptr
<MediaChannelProxy
> media_channel_proxy
)
92 : media_channel_proxy_(media_channel_proxy
) {
93 DCHECK(media_channel_proxy
.get());
95 // Creation can be done on a different thread.
96 thread_checker_
.DetachFromThread();
99 AudioPipelineProxyInternal::~AudioPipelineProxyInternal() {
102 void AudioPipelineProxyInternal::Shutdown() {
103 DCHECK(thread_checker_
.CalledOnValidThread());
105 // Remove any callback on AudioPipelineProxyInternal.
106 media_channel_proxy_
->SetAudioDelegate(
107 CmaMessageFilterProxy::AudioDelegate());
110 void AudioPipelineProxyInternal::NotifyPipeWrite() {
111 DCHECK(thread_checker_
.CalledOnValidThread());
113 // TODO(erickung): An alternative way would be to use a dedicated socket for
115 bool success
= media_channel_proxy_
->Send(scoped_ptr
<IPC::Message
>(
116 new CmaHostMsg_NotifyPipeWrite(
117 media_channel_proxy_
->GetId(), kAudioTrackId
)));
118 VLOG_IF(4, !success
) << "Sending msg failed";
121 void AudioPipelineProxyInternal::SetClient(
122 const base::Closure
& pipe_read_cb
,
123 const AvPipelineClient
& client
) {
124 DCHECK(thread_checker_
.CalledOnValidThread());
126 CmaMessageFilterProxy::AudioDelegate delegate
;
127 delegate
.av_pipe_cb
=
128 base::Bind(&AudioPipelineProxyInternal::OnAvPipeCreated
,
129 base::Unretained(this));
130 delegate
.state_changed_cb
=
131 base::Bind(&AudioPipelineProxyInternal::OnStateChanged
,
132 base::Unretained(this));
133 delegate
.pipe_read_cb
= pipe_read_cb
;
134 delegate
.client
= client
;
135 bool success
= media_channel_proxy_
->SetAudioDelegate(delegate
);
139 void AudioPipelineProxyInternal::CreateAvPipe(
140 const SharedMemCB
& shared_mem_cb
) {
141 DCHECK(thread_checker_
.CalledOnValidThread());
142 DCHECK(shared_mem_cb_
.is_null());
143 bool success
= media_channel_proxy_
->Send(scoped_ptr
<IPC::Message
>(
144 new CmaHostMsg_CreateAvPipe(
145 media_channel_proxy_
->GetId(), kAudioTrackId
, kAppAudioBufferSize
)));
147 shared_mem_cb
.Run(scoped_ptr
<base::SharedMemory
>());
150 shared_mem_cb_
= shared_mem_cb
;
153 void AudioPipelineProxyInternal::OnAvPipeCreated(
155 base::SharedMemoryHandle shared_mem_handle
,
156 base::FileDescriptor socket
) {
157 DCHECK(thread_checker_
.CalledOnValidThread());
158 DCHECK(!shared_mem_cb_
.is_null());
160 shared_mem_cb_
.Run(scoped_ptr
<base::SharedMemory
>());
164 CHECK(base::SharedMemory::IsHandleValid(shared_mem_handle
));
165 shared_mem_cb_
.Run(scoped_ptr
<base::SharedMemory
>(
166 new base::SharedMemory(shared_mem_handle
, false)));
169 void AudioPipelineProxyInternal::Initialize(
170 const ::media::AudioDecoderConfig
& config
,
171 const ::media::PipelineStatusCB
& status_cb
) {
172 DCHECK(thread_checker_
.CalledOnValidThread());
173 bool success
= media_channel_proxy_
->Send(scoped_ptr
<IPC::Message
>(
174 new CmaHostMsg_AudioInitialize(
175 media_channel_proxy_
->GetId(), kAudioTrackId
, config
)));
177 status_cb
.Run( ::media::PIPELINE_ERROR_INITIALIZATION_FAILED
);
180 DCHECK(status_cb_
.is_null());
181 status_cb_
= status_cb
;
184 void AudioPipelineProxyInternal::SetVolume(float volume
) {
185 DCHECK(thread_checker_
.CalledOnValidThread());
186 media_channel_proxy_
->Send(scoped_ptr
<IPC::Message
>(
187 new CmaHostMsg_SetVolume(media_channel_proxy_
->GetId(),
188 kAudioTrackId
, volume
)));
191 void AudioPipelineProxyInternal::OnStateChanged(
192 ::media::PipelineStatus status
) {
193 DCHECK(thread_checker_
.CalledOnValidThread());
194 DCHECK(!status_cb_
.is_null());
195 base::ResetAndReturn(&status_cb_
).Run(status
);
199 // A macro runs current member function on |io_message_loop_proxy_| thread.
200 #define FORWARD_ON_IO_THREAD(param_fn, ...) \
201 io_message_loop_proxy_->PostTask( \
203 base::Bind(&AudioPipelineProxyInternal::param_fn, \
204 base::Unretained(proxy_.get()), ##__VA_ARGS__))
206 AudioPipelineProxy::AudioPipelineProxy(
207 scoped_refptr
<base::MessageLoopProxy
> io_message_loop_proxy
,
208 scoped_refptr
<MediaChannelProxy
> media_channel_proxy
)
209 : io_message_loop_proxy_(io_message_loop_proxy
),
210 proxy_(new AudioPipelineProxyInternal(media_channel_proxy
)),
211 audio_streamer_(new AvStreamerProxy()),
212 weak_factory_(this) {
213 DCHECK(io_message_loop_proxy_
.get());
214 weak_this_
= weak_factory_
.GetWeakPtr();
215 thread_checker_
.DetachFromThread();
218 AudioPipelineProxy::~AudioPipelineProxy() {
219 DCHECK(thread_checker_
.CalledOnValidThread());
220 // Release the underlying object on the right thread.
221 io_message_loop_proxy_
->PostTask(
223 base::Bind(&AudioPipelineProxyInternal::Release
, base::Passed(&proxy_
)));
226 void AudioPipelineProxy::SetClient(
227 const AvPipelineClient
& client
) {
228 DCHECK(thread_checker_
.CalledOnValidThread());
229 base::Closure pipe_read_cb
= ::media::BindToCurrentLoop(
230 base::Bind(&AudioPipelineProxy::OnPipeRead
, weak_this_
));
231 FORWARD_ON_IO_THREAD(SetClient
, pipe_read_cb
, client
);
234 void AudioPipelineProxy::Initialize(
235 const ::media::AudioDecoderConfig
& config
,
236 scoped_ptr
<CodedFrameProvider
> frame_provider
,
237 const ::media::PipelineStatusCB
& status_cb
) {
238 CMALOG(kLogControl
) << "AudioPipelineProxy::Initialize";
239 DCHECK(thread_checker_
.CalledOnValidThread());
240 audio_streamer_
->SetCodedFrameProvider(frame_provider
.Pass());
242 AudioPipelineProxyInternal::SharedMemCB shared_mem_cb
=
243 ::media::BindToCurrentLoop(base::Bind(
244 &AudioPipelineProxy::OnAvPipeCreated
, weak_this_
,
246 FORWARD_ON_IO_THREAD(CreateAvPipe
, shared_mem_cb
);
249 void AudioPipelineProxy::OnAvPipeCreated(
250 const ::media::AudioDecoderConfig
& config
,
251 const ::media::PipelineStatusCB
& status_cb
,
252 scoped_ptr
<base::SharedMemory
> shared_memory
) {
253 CMALOG(kLogControl
) << "AudioPipelineProxy::OnAvPipeCreated";
254 DCHECK(thread_checker_
.CalledOnValidThread());
255 if (!shared_memory
||
256 !shared_memory
->Map(kAppAudioBufferSize
)) {
257 status_cb
.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED
);
260 CHECK(shared_memory
->memory());
262 scoped_ptr
<MediaMemoryChunk
> shared_memory_chunk(
263 new SharedMemoryChunk(shared_memory
.Pass(), kAppAudioBufferSize
));
264 scoped_ptr
<MediaMessageFifo
> audio_pipe(
265 new MediaMessageFifo(shared_memory_chunk
.Pass(), false));
266 audio_pipe
->ObserveWriteActivity(
267 base::Bind(&AudioPipelineProxy::OnPipeWrite
, weak_this_
));
269 audio_streamer_
->SetMediaMessageFifo(audio_pipe
.Pass());
271 // Now proceed to the decoder/renderer initialization.
272 FORWARD_ON_IO_THREAD(Initialize
, config
, status_cb
);
275 void AudioPipelineProxy::StartFeeding() {
276 DCHECK(thread_checker_
.CalledOnValidThread());
277 DCHECK(audio_streamer_
);
278 audio_streamer_
->Start();
281 void AudioPipelineProxy::Flush(const base::Closure
& done_cb
) {
282 DCHECK(thread_checker_
.CalledOnValidThread());
283 DCHECK(audio_streamer_
);
284 audio_streamer_
->StopAndFlush(done_cb
);
287 void AudioPipelineProxy::Stop() {
288 DCHECK(thread_checker_
.CalledOnValidThread());
289 if (!audio_streamer_
)
291 audio_streamer_
->StopAndFlush(base::Bind(&IgnoreResult
));
294 void AudioPipelineProxy::SetVolume(float volume
) {
295 DCHECK(thread_checker_
.CalledOnValidThread());
296 FORWARD_ON_IO_THREAD(SetVolume
, volume
);
299 void AudioPipelineProxy::OnPipeWrite() {
300 DCHECK(thread_checker_
.CalledOnValidThread());
301 FORWARD_ON_IO_THREAD(NotifyPipeWrite
);
304 void AudioPipelineProxy::OnPipeRead() {
305 DCHECK(thread_checker_
.CalledOnValidThread());
307 audio_streamer_
->OnFifoReadEvent();
311 } // namespace chromecast