Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / renderer / pepper / pepper_platform_audio_input.cc
blob390e9cd6039659b0e7d28d63faf9b5abe7ef54ff
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/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"
22 #include "url/gurl.h"
24 namespace content {
26 // static
27 PepperPlatformAudioInput* PepperPlatformAudioInput::Create(
28 int render_frame_id,
29 const std::string& device_id,
30 const GURL& document_url,
31 int sample_rate,
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,
37 device_id,
38 document_url,
39 sample_rate,
40 frames_per_buffer,
41 client)) {
42 // Balanced by Release invoked in
43 // PepperPlatformAudioInput::ShutDownOnIOThread().
44 audio_input->AddRef();
45 return audio_input.get();
47 return NULL;
50 void PepperPlatformAudioInput::StartCapture() {
51 DCHECK(main_task_runner_->BelongsToCurrentThread());
53 io_task_runner_->PostTask(
54 FROM_HERE,
55 base::Bind(&PepperPlatformAudioInput::StartCaptureOnIOThread, this));
58 void PepperPlatformAudioInput::StopCapture() {
59 DCHECK(main_task_runner_->BelongsToCurrentThread());
61 io_task_runner_->PostTask(
62 FROM_HERE,
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.
70 if (!client_)
71 return;
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.
75 client_ = NULL;
76 io_task_runner_->PostTask(
77 FROM_HERE,
78 base::Bind(&PepperPlatformAudioInput::ShutDownOnIOThread, this));
81 void PepperPlatformAudioInput::OnStreamCreated(
82 base::SharedMemoryHandle handle,
83 base::SyncSocket::Handle socket_handle,
84 int length,
85 int total_segments) {
86 #if defined(OS_WIN)
87 DCHECK(handle);
88 DCHECK(socket_handle);
89 #else
90 DCHECK(base::SharedMemory::IsHandleValid(handle));
91 DCHECK_NE(-1, socket_handle);
92 #endif
93 DCHECK(length);
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));
103 } else {
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.
106 if (client_) {
107 client_->StreamCreated(handle, length, socket_handle);
108 } else {
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
125 // thread!
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
128 // here.
129 DCHECK(!ipc_);
130 DCHECK(!client_);
131 DCHECK(label_.empty());
132 DCHECK(!pending_open_device_);
135 PepperPlatformAudioInput::PepperPlatformAudioInput()
136 : client_(NULL),
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(
146 int render_frame_id,
147 const std::string& device_id,
148 const GURL& document_url,
149 int sample_rate,
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)
157 return false;
159 render_frame_id_ = render_frame_id;
160 client_ = client;
162 if (!GetMediaDeviceManager())
163 return false;
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,
171 sample_rate,
172 ppapi::kBitsPerAudioInputSample,
173 frames_per_buffer);
175 // We need to open the device and obtain the label and session ID before
176 // initializing.
177 pending_open_device_id_ = GetMediaDeviceManager()->OpenDevice(
178 PP_DEVICETYPE_DEV_AUDIOCAPTURE,
179 device_id.empty() ? media::AudioManagerBase::kDefaultDeviceId : device_id,
180 document_url,
181 base::Bind(&PepperPlatformAudioInput::OnDeviceOpened, this));
182 pending_open_device_ = true;
184 return true;
187 void PepperPlatformAudioInput::InitializeOnIOThread(int session_id) {
188 DCHECK(io_task_runner_->BelongsToCurrentThread());
190 if (!ipc_)
191 return;
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());
201 if (ipc_)
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_) {
210 ipc_->CloseStream();
212 ipc_.reset();
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,
228 bool succeeded,
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());
238 label_ = label;
240 if (client_) {
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,
245 this, session_id));
246 } else {
247 // Shutdown has occurred.
248 CloseDevice();
250 } else {
251 NotifyStreamCreationFailed();
255 void PepperPlatformAudioInput::CloseDevice() {
256 DCHECK(main_task_runner_->BelongsToCurrentThread());
258 if (!label_.empty()) {
259 PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
260 if (device_manager)
261 device_manager->CloseDevice(label_);
262 label_.clear();
264 if (pending_open_device_) {
265 PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
266 if (device_manager)
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());
276 if (client_)
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