1 // Copyright (c) 2012 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 "content/renderer/pepper/pepper_platform_audio_input.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "build/build_config.h"
11 #include "content/child/child_process.h"
12 #include "content/renderer/media/audio_input_message_filter.h"
13 #include "content/renderer/pepper/pepper_audio_input_host.h"
14 #include "content/renderer/pepper/pepper_media_device_manager.h"
15 #include "content/renderer/render_frame_impl.h"
16 #include "content/renderer/render_thread_impl.h"
17 #include "content/renderer/render_view_impl.h"
18 #include "media/audio/audio_manager_base.h"
19 #include "ppapi/shared_impl/ppb_audio_config_shared.h"
25 PepperPlatformAudioInput
* PepperPlatformAudioInput::Create(
27 const std::string
& device_id
,
28 const GURL
& document_url
,
30 int frames_per_buffer
,
31 PepperAudioInputHost
* client
) {
32 scoped_refptr
<PepperPlatformAudioInput
> audio_input(
33 new PepperPlatformAudioInput());
34 if (audio_input
->Initialize(render_frame_id
,
40 // Balanced by Release invoked in
41 // PepperPlatformAudioInput::ShutDownOnIOThread().
42 audio_input
->AddRef();
43 return audio_input
.get();
48 void PepperPlatformAudioInput::StartCapture() {
49 DCHECK(main_message_loop_proxy_
->BelongsToCurrentThread());
51 io_message_loop_proxy_
->PostTask(
53 base::Bind(&PepperPlatformAudioInput::StartCaptureOnIOThread
, this));
56 void PepperPlatformAudioInput::StopCapture() {
57 DCHECK(main_message_loop_proxy_
->BelongsToCurrentThread());
59 io_message_loop_proxy_
->PostTask(
61 base::Bind(&PepperPlatformAudioInput::StopCaptureOnIOThread
, this));
64 void PepperPlatformAudioInput::ShutDown() {
65 DCHECK(main_message_loop_proxy_
->BelongsToCurrentThread());
67 // Make sure we don't call shutdown more than once.
71 // Called on the main thread to stop all audio callbacks. We must only change
72 // the client on the main thread, and the delegates from the I/O thread.
74 io_message_loop_proxy_
->PostTask(
76 base::Bind(&PepperPlatformAudioInput::ShutDownOnIOThread
, this));
79 void PepperPlatformAudioInput::OnStreamCreated(
80 base::SharedMemoryHandle handle
,
81 base::SyncSocket::Handle socket_handle
,
86 DCHECK(socket_handle
);
88 DCHECK_NE(-1, handle
.fd
);
89 DCHECK_NE(-1, socket_handle
);
92 // TODO(yzshen): Make use of circular buffer scheme. crbug.com/181449.
93 DCHECK_EQ(1, total_segments
);
95 if (base::MessageLoopProxy::current().get() !=
96 main_message_loop_proxy_
.get()) {
97 // If shutdown has occurred, |client_| will be NULL and the handles will be
98 // cleaned up on the main thread.
99 main_message_loop_proxy_
->PostTask(
101 base::Bind(&PepperPlatformAudioInput::OnStreamCreated
,
108 // Must dereference the client only on the main thread. Shutdown may have
109 // occurred while the request was in-flight, so we need to NULL check.
111 client_
->StreamCreated(handle
, length
, socket_handle
);
113 // Clean up the handles.
114 base::SyncSocket
temp_socket(socket_handle
);
115 base::SharedMemory
temp_shared_memory(handle
, false);
120 void PepperPlatformAudioInput::OnVolume(double volume
) {}
122 void PepperPlatformAudioInput::OnStateChanged(
123 media::AudioInputIPCDelegate::State state
) {}
125 void PepperPlatformAudioInput::OnIPCClosed() { ipc_
.reset(); }
127 PepperPlatformAudioInput::~PepperPlatformAudioInput() {
128 // Make sure we have been shut down. Warning: this may happen on the I/O
130 // Although these members should be accessed on a specific thread (either the
131 // main thread or the I/O thread), it should be fine to examine their value
135 DCHECK(label_
.empty());
136 DCHECK(!pending_open_device_
);
139 PepperPlatformAudioInput::PepperPlatformAudioInput()
141 main_message_loop_proxy_(base::MessageLoopProxy::current()),
142 io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()),
143 render_frame_id_(MSG_ROUTING_NONE
),
144 create_stream_sent_(false),
145 pending_open_device_(false),
146 pending_open_device_id_(-1) {}
148 bool PepperPlatformAudioInput::Initialize(
150 const std::string
& device_id
,
151 const GURL
& document_url
,
153 int frames_per_buffer
,
154 PepperAudioInputHost
* client
) {
155 DCHECK(main_message_loop_proxy_
->BelongsToCurrentThread());
157 RenderFrameImpl
* const render_frame
=
158 RenderFrameImpl::FromRoutingID(render_frame_id
);
159 if (!render_frame
|| !client
)
162 render_frame_id_
= render_frame_id
;
165 if (!GetMediaDeviceManager())
168 ipc_
= RenderThreadImpl::current()
169 ->audio_input_message_filter()
170 ->CreateAudioInputIPC(render_frame
->render_view()->GetRoutingID());
172 params_
.Reset(media::AudioParameters::AUDIO_PCM_LINEAR
,
173 media::CHANNEL_LAYOUT_MONO
,
174 ppapi::kAudioInputChannels
,
176 ppapi::kBitsPerAudioInputSample
,
179 // We need to open the device and obtain the label and session ID before
181 pending_open_device_id_
= GetMediaDeviceManager()->OpenDevice(
182 PP_DEVICETYPE_DEV_AUDIOCAPTURE
,
183 device_id
.empty() ? media::AudioManagerBase::kDefaultDeviceId
: device_id
,
185 base::Bind(&PepperPlatformAudioInput::OnDeviceOpened
, this));
186 pending_open_device_
= true;
191 void PepperPlatformAudioInput::InitializeOnIOThread(int session_id
) {
192 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
197 // We will be notified by OnStreamCreated().
198 create_stream_sent_
= true;
199 ipc_
->CreateStream(this, session_id
, params_
, false, 1);
202 void PepperPlatformAudioInput::StartCaptureOnIOThread() {
203 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
206 ipc_
->RecordStream();
209 void PepperPlatformAudioInput::StopCaptureOnIOThread() {
210 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
212 // TODO(yzshen): We cannot re-start capturing if the stream is closed.
213 if (ipc_
&& create_stream_sent_
) {
219 void PepperPlatformAudioInput::ShutDownOnIOThread() {
220 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
222 StopCaptureOnIOThread();
224 main_message_loop_proxy_
->PostTask(
225 FROM_HERE
, base::Bind(&PepperPlatformAudioInput::CloseDevice
, this));
227 Release(); // Release for the delegate, balances out the reference taken in
228 // PepperPlatformAudioInput::Create.
231 void PepperPlatformAudioInput::OnDeviceOpened(int request_id
,
233 const std::string
& label
) {
234 DCHECK(main_message_loop_proxy_
->BelongsToCurrentThread());
236 pending_open_device_
= false;
237 pending_open_device_id_
= -1;
239 PepperMediaDeviceManager
* const device_manager
= GetMediaDeviceManager();
240 if (succeeded
&& device_manager
) {
241 DCHECK(!label
.empty());
245 int session_id
= device_manager
->GetSessionID(
246 PP_DEVICETYPE_DEV_AUDIOCAPTURE
, label
);
247 io_message_loop_proxy_
->PostTask(
249 base::Bind(&PepperPlatformAudioInput::InitializeOnIOThread
,
253 // Shutdown has occurred.
257 NotifyStreamCreationFailed();
261 void PepperPlatformAudioInput::CloseDevice() {
262 DCHECK(main_message_loop_proxy_
->BelongsToCurrentThread());
264 if (!label_
.empty()) {
265 PepperMediaDeviceManager
* const device_manager
= GetMediaDeviceManager();
267 device_manager
->CloseDevice(label_
);
270 if (pending_open_device_
) {
271 PepperMediaDeviceManager
* const device_manager
= GetMediaDeviceManager();
273 device_manager
->CancelOpenDevice(pending_open_device_id_
);
274 pending_open_device_
= false;
275 pending_open_device_id_
= -1;
279 void PepperPlatformAudioInput::NotifyStreamCreationFailed() {
280 DCHECK(main_message_loop_proxy_
->BelongsToCurrentThread());
283 client_
->StreamCreationFailed();
286 PepperMediaDeviceManager
* PepperPlatformAudioInput::GetMediaDeviceManager() {
287 DCHECK(main_message_loop_proxy_
->BelongsToCurrentThread());
289 RenderFrameImpl
* const render_frame
=
290 RenderFrameImpl::FromRoutingID(render_frame_id_
);
291 return render_frame
?
292 PepperMediaDeviceManager::GetForRenderFrame(render_frame
).get() : NULL
;
295 } // namespace content