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/location.h"
9 #include "base/logging.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "build/build_config.h"
13 #include "content/child/child_process.h"
14 #include "content/renderer/media/audio_input_message_filter.h"
15 #include "content/renderer/pepper/pepper_audio_input_host.h"
16 #include "content/renderer/pepper/pepper_media_device_manager.h"
17 #include "content/renderer/render_frame_impl.h"
18 #include "content/renderer/render_thread_impl.h"
19 #include "content/renderer/render_view_impl.h"
20 #include "media/audio/audio_manager_base.h"
21 #include "ppapi/shared_impl/ppb_audio_config_shared.h"
27 PepperPlatformAudioInput
* PepperPlatformAudioInput::Create(
29 const std::string
& device_id
,
30 const GURL
& document_url
,
32 int frames_per_buffer
,
33 PepperAudioInputHost
* client
) {
34 scoped_refptr
<PepperPlatformAudioInput
> audio_input(
35 new PepperPlatformAudioInput());
36 if (audio_input
->Initialize(render_frame_id
,
42 // Balanced by Release invoked in
43 // PepperPlatformAudioInput::ShutDownOnIOThread().
44 audio_input
->AddRef();
45 return audio_input
.get();
50 void PepperPlatformAudioInput::StartCapture() {
51 DCHECK(main_task_runner_
->BelongsToCurrentThread());
53 io_task_runner_
->PostTask(
55 base::Bind(&PepperPlatformAudioInput::StartCaptureOnIOThread
, this));
58 void PepperPlatformAudioInput::StopCapture() {
59 DCHECK(main_task_runner_
->BelongsToCurrentThread());
61 io_task_runner_
->PostTask(
63 base::Bind(&PepperPlatformAudioInput::StopCaptureOnIOThread
, this));
66 void PepperPlatformAudioInput::ShutDown() {
67 DCHECK(main_task_runner_
->BelongsToCurrentThread());
69 // Make sure we don't call shutdown more than once.
73 // Called on the main thread to stop all audio callbacks. We must only change
74 // the client on the main thread, and the delegates from the I/O thread.
76 io_task_runner_
->PostTask(
78 base::Bind(&PepperPlatformAudioInput::ShutDownOnIOThread
, this));
81 void PepperPlatformAudioInput::OnStreamCreated(
82 base::SharedMemoryHandle handle
,
83 base::SyncSocket::Handle socket_handle
,
88 DCHECK(socket_handle
);
90 DCHECK(base::SharedMemory::IsHandleValid(handle
));
91 DCHECK_NE(-1, socket_handle
);
94 // TODO(yzshen): Make use of circular buffer scheme. crbug.com/181449.
95 DCHECK_EQ(1, total_segments
);
97 if (base::ThreadTaskRunnerHandle::Get().get() != main_task_runner_
.get()) {
98 // If shutdown has occurred, |client_| will be NULL and the handles will be
99 // cleaned up on the main thread.
100 main_task_runner_
->PostTask(
101 FROM_HERE
, base::Bind(&PepperPlatformAudioInput::OnStreamCreated
, this,
102 handle
, socket_handle
, length
, total_segments
));
104 // Must dereference the client only on the main thread. Shutdown may have
105 // occurred while the request was in-flight, so we need to NULL check.
107 client_
->StreamCreated(handle
, length
, socket_handle
);
109 // Clean up the handles.
110 base::SyncSocket
temp_socket(socket_handle
);
111 base::SharedMemory
temp_shared_memory(handle
, false);
116 void PepperPlatformAudioInput::OnVolume(double volume
) {}
118 void PepperPlatformAudioInput::OnStateChanged(
119 media::AudioInputIPCDelegateState state
) {}
121 void PepperPlatformAudioInput::OnIPCClosed() { ipc_
.reset(); }
123 PepperPlatformAudioInput::~PepperPlatformAudioInput() {
124 // Make sure we have been shut down. Warning: this may happen on the I/O
126 // Although these members should be accessed on a specific thread (either the
127 // main thread or the I/O thread), it should be fine to examine their value
131 DCHECK(label_
.empty());
132 DCHECK(!pending_open_device_
);
135 PepperPlatformAudioInput::PepperPlatformAudioInput()
137 main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
138 io_task_runner_(ChildProcess::current()->io_task_runner()),
139 render_frame_id_(MSG_ROUTING_NONE
),
140 create_stream_sent_(false),
141 pending_open_device_(false),
142 pending_open_device_id_(-1) {
145 bool PepperPlatformAudioInput::Initialize(
147 const std::string
& device_id
,
148 const GURL
& document_url
,
150 int frames_per_buffer
,
151 PepperAudioInputHost
* client
) {
152 DCHECK(main_task_runner_
->BelongsToCurrentThread());
154 RenderFrameImpl
* const render_frame
=
155 RenderFrameImpl::FromRoutingID(render_frame_id
);
156 if (!render_frame
|| !client
)
159 render_frame_id_
= render_frame_id
;
162 if (!GetMediaDeviceManager())
165 ipc_
= RenderThreadImpl::current()
166 ->audio_input_message_filter()
167 ->CreateAudioInputIPC(render_frame_id
);
169 params_
.Reset(media::AudioParameters::AUDIO_PCM_LINEAR
,
170 media::CHANNEL_LAYOUT_MONO
,
172 ppapi::kBitsPerAudioInputSample
,
175 // We need to open the device and obtain the label and session ID before
177 pending_open_device_id_
= GetMediaDeviceManager()->OpenDevice(
178 PP_DEVICETYPE_DEV_AUDIOCAPTURE
,
179 device_id
.empty() ? media::AudioManagerBase::kDefaultDeviceId
: device_id
,
181 base::Bind(&PepperPlatformAudioInput::OnDeviceOpened
, this));
182 pending_open_device_
= true;
187 void PepperPlatformAudioInput::InitializeOnIOThread(int session_id
) {
188 DCHECK(io_task_runner_
->BelongsToCurrentThread());
193 // We will be notified by OnStreamCreated().
194 create_stream_sent_
= true;
195 ipc_
->CreateStream(this, session_id
, params_
, false, 1);
198 void PepperPlatformAudioInput::StartCaptureOnIOThread() {
199 DCHECK(io_task_runner_
->BelongsToCurrentThread());
202 ipc_
->RecordStream();
205 void PepperPlatformAudioInput::StopCaptureOnIOThread() {
206 DCHECK(io_task_runner_
->BelongsToCurrentThread());
208 // TODO(yzshen): We cannot re-start capturing if the stream is closed.
209 if (ipc_
&& create_stream_sent_
) {
215 void PepperPlatformAudioInput::ShutDownOnIOThread() {
216 DCHECK(io_task_runner_
->BelongsToCurrentThread());
218 StopCaptureOnIOThread();
220 main_task_runner_
->PostTask(
221 FROM_HERE
, base::Bind(&PepperPlatformAudioInput::CloseDevice
, this));
223 Release(); // Release for the delegate, balances out the reference taken in
224 // PepperPlatformAudioInput::Create.
227 void PepperPlatformAudioInput::OnDeviceOpened(int request_id
,
229 const std::string
& label
) {
230 DCHECK(main_task_runner_
->BelongsToCurrentThread());
232 pending_open_device_
= false;
233 pending_open_device_id_
= -1;
235 PepperMediaDeviceManager
* const device_manager
= GetMediaDeviceManager();
236 if (succeeded
&& device_manager
) {
237 DCHECK(!label
.empty());
241 int session_id
= device_manager
->GetSessionID(
242 PP_DEVICETYPE_DEV_AUDIOCAPTURE
, label
);
243 io_task_runner_
->PostTask(
244 FROM_HERE
, base::Bind(&PepperPlatformAudioInput::InitializeOnIOThread
,
247 // Shutdown has occurred.
251 NotifyStreamCreationFailed();
255 void PepperPlatformAudioInput::CloseDevice() {
256 DCHECK(main_task_runner_
->BelongsToCurrentThread());
258 if (!label_
.empty()) {
259 PepperMediaDeviceManager
* const device_manager
= GetMediaDeviceManager();
261 device_manager
->CloseDevice(label_
);
264 if (pending_open_device_
) {
265 PepperMediaDeviceManager
* const device_manager
= GetMediaDeviceManager();
267 device_manager
->CancelOpenDevice(pending_open_device_id_
);
268 pending_open_device_
= false;
269 pending_open_device_id_
= -1;
273 void PepperPlatformAudioInput::NotifyStreamCreationFailed() {
274 DCHECK(main_task_runner_
->BelongsToCurrentThread());
277 client_
->StreamCreationFailed();
280 PepperMediaDeviceManager
* PepperPlatformAudioInput::GetMediaDeviceManager() {
281 DCHECK(main_task_runner_
->BelongsToCurrentThread());
283 RenderFrameImpl
* const render_frame
=
284 RenderFrameImpl::FromRoutingID(render_frame_id_
);
285 return render_frame
?
286 PepperMediaDeviceManager::GetForRenderFrame(render_frame
).get() : NULL
;
289 } // namespace content