Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / renderer / pepper / pepper_platform_audio_input.cc
blob04c14a70543bdeda7b09649ffda1c91fc58e6960
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"
7 #include "base/bind.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"
20 #include "url/gurl.h"
22 namespace content {
24 // static
25 PepperPlatformAudioInput* PepperPlatformAudioInput::Create(
26 int render_frame_id,
27 const std::string& device_id,
28 const GURL& document_url,
29 int sample_rate,
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,
35 device_id,
36 document_url,
37 sample_rate,
38 frames_per_buffer,
39 client)) {
40 // Balanced by Release invoked in
41 // PepperPlatformAudioInput::ShutDownOnIOThread().
42 audio_input->AddRef();
43 return audio_input.get();
45 return NULL;
48 void PepperPlatformAudioInput::StartCapture() {
49 DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
51 io_message_loop_proxy_->PostTask(
52 FROM_HERE,
53 base::Bind(&PepperPlatformAudioInput::StartCaptureOnIOThread, this));
56 void PepperPlatformAudioInput::StopCapture() {
57 DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
59 io_message_loop_proxy_->PostTask(
60 FROM_HERE,
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.
68 if (!client_)
69 return;
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.
73 client_ = NULL;
74 io_message_loop_proxy_->PostTask(
75 FROM_HERE,
76 base::Bind(&PepperPlatformAudioInput::ShutDownOnIOThread, this));
79 void PepperPlatformAudioInput::OnStreamCreated(
80 base::SharedMemoryHandle handle,
81 base::SyncSocket::Handle socket_handle,
82 int length,
83 int total_segments) {
84 #if defined(OS_WIN)
85 DCHECK(handle);
86 DCHECK(socket_handle);
87 #else
88 DCHECK_NE(-1, handle.fd);
89 DCHECK_NE(-1, socket_handle);
90 #endif
91 DCHECK(length);
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(
100 FROM_HERE,
101 base::Bind(&PepperPlatformAudioInput::OnStreamCreated,
102 this,
103 handle,
104 socket_handle,
105 length,
106 total_segments));
107 } else {
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.
110 if (client_) {
111 client_->StreamCreated(handle, length, socket_handle);
112 } else {
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
129 // thread!
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
132 // here.
133 DCHECK(!ipc_);
134 DCHECK(!client_);
135 DCHECK(label_.empty());
136 DCHECK(!pending_open_device_);
139 PepperPlatformAudioInput::PepperPlatformAudioInput()
140 : client_(NULL),
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(
149 int render_frame_id,
150 const std::string& device_id,
151 const GURL& document_url,
152 int sample_rate,
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)
160 return false;
162 render_frame_id_ = render_frame_id;
163 client_ = client;
165 if (!GetMediaDeviceManager())
166 return false;
168 ipc_ = RenderThreadImpl::current()
169 ->audio_input_message_filter()
170 ->CreateAudioInputIPC(render_frame_id);
172 params_.Reset(media::AudioParameters::AUDIO_PCM_LINEAR,
173 media::CHANNEL_LAYOUT_MONO,
174 ppapi::kAudioInputChannels,
175 sample_rate,
176 ppapi::kBitsPerAudioInputSample,
177 frames_per_buffer);
179 // We need to open the device and obtain the label and session ID before
180 // initializing.
181 pending_open_device_id_ = GetMediaDeviceManager()->OpenDevice(
182 PP_DEVICETYPE_DEV_AUDIOCAPTURE,
183 device_id.empty() ? media::AudioManagerBase::kDefaultDeviceId : device_id,
184 document_url,
185 base::Bind(&PepperPlatformAudioInput::OnDeviceOpened, this));
186 pending_open_device_ = true;
188 return true;
191 void PepperPlatformAudioInput::InitializeOnIOThread(int session_id) {
192 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
194 if (!ipc_)
195 return;
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());
205 if (ipc_)
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_) {
214 ipc_->CloseStream();
216 ipc_.reset();
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,
232 bool succeeded,
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());
242 label_ = label;
244 if (client_) {
245 int session_id = device_manager->GetSessionID(
246 PP_DEVICETYPE_DEV_AUDIOCAPTURE, label);
247 io_message_loop_proxy_->PostTask(
248 FROM_HERE,
249 base::Bind(&PepperPlatformAudioInput::InitializeOnIOThread,
250 this,
251 session_id));
252 } else {
253 // Shutdown has occurred.
254 CloseDevice();
256 } else {
257 NotifyStreamCreationFailed();
261 void PepperPlatformAudioInput::CloseDevice() {
262 DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
264 if (!label_.empty()) {
265 PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
266 if (device_manager)
267 device_manager->CloseDevice(label_);
268 label_.clear();
270 if (pending_open_device_) {
271 PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
272 if (device_manager)
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());
282 if (client_)
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