Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / renderer / pepper / pepper_platform_audio_input.cc
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.
4
5 #include "content/renderer/pepper/pepper_platform_audio_input.h"
6
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"
21
22 namespace content {
23
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();
44   }
45   return NULL;
46 }
47
48 void PepperPlatformAudioInput::StartCapture() {
49   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
50
51   io_message_loop_proxy_->PostTask(
52       FROM_HERE,
53       base::Bind(&PepperPlatformAudioInput::StartCaptureOnIOThread, this));
54 }
55
56 void PepperPlatformAudioInput::StopCapture() {
57   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
58
59   io_message_loop_proxy_->PostTask(
60       FROM_HERE,
61       base::Bind(&PepperPlatformAudioInput::StopCaptureOnIOThread, this));
62 }
63
64 void PepperPlatformAudioInput::ShutDown() {
65   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
66
67   // Make sure we don't call shutdown more than once.
68   if (!client_)
69     return;
70
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));
77 }
78
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);
94
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);
116     }
117   }
118 }
119
120 void PepperPlatformAudioInput::OnVolume(double volume) {}
121
122 void PepperPlatformAudioInput::OnStateChanged(
123     media::AudioInputIPCDelegate::State state) {}
124
125 void PepperPlatformAudioInput::OnIPCClosed() { ipc_.reset(); }
126
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_);
137 }
138
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) {}
147
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());
156
157   RenderFrameImpl* const render_frame =
158       RenderFrameImpl::FromRoutingID(render_frame_id);
159   if (!render_frame || !client)
160     return false;
161
162   render_frame_id_ = render_frame_id;
163   client_ = client;
164
165   if (!GetMediaDeviceManager())
166     return false;
167
168   ipc_ = RenderThreadImpl::current()
169              ->audio_input_message_filter()
170              ->CreateAudioInputIPC(render_frame->render_view()->GetRoutingID());
171
172   params_.Reset(media::AudioParameters::AUDIO_PCM_LINEAR,
173                 media::CHANNEL_LAYOUT_MONO,
174                 ppapi::kAudioInputChannels,
175                 0,
176                 sample_rate,
177                 ppapi::kBitsPerAudioInputSample,
178                 frames_per_buffer);
179
180   // We need to open the device and obtain the label and session ID before
181   // initializing.
182   pending_open_device_id_ = GetMediaDeviceManager()->OpenDevice(
183       PP_DEVICETYPE_DEV_AUDIOCAPTURE,
184       device_id.empty() ? media::AudioManagerBase::kDefaultDeviceId : device_id,
185       document_url,
186       base::Bind(&PepperPlatformAudioInput::OnDeviceOpened, this));
187   pending_open_device_ = true;
188
189   return true;
190 }
191
192 void PepperPlatformAudioInput::InitializeOnIOThread(int session_id) {
193   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
194
195   if (!ipc_)
196     return;
197
198   // We will be notified by OnStreamCreated().
199   create_stream_sent_ = true;
200   ipc_->CreateStream(this, session_id, params_, false, 1);
201 }
202
203 void PepperPlatformAudioInput::StartCaptureOnIOThread() {
204   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
205
206   if (ipc_)
207     ipc_->RecordStream();
208 }
209
210 void PepperPlatformAudioInput::StopCaptureOnIOThread() {
211   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
212
213   // TODO(yzshen): We cannot re-start capturing if the stream is closed.
214   if (ipc_ && create_stream_sent_) {
215     ipc_->CloseStream();
216   }
217   ipc_.reset();
218 }
219
220 void PepperPlatformAudioInput::ShutDownOnIOThread() {
221   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
222
223   StopCaptureOnIOThread();
224
225   main_message_loop_proxy_->PostTask(
226       FROM_HERE, base::Bind(&PepperPlatformAudioInput::CloseDevice, this));
227
228   Release();  // Release for the delegate, balances out the reference taken in
229               // PepperPlatformAudioInput::Create.
230 }
231
232 void PepperPlatformAudioInput::OnDeviceOpened(int request_id,
233                                               bool succeeded,
234                                               const std::string& label) {
235   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
236
237   pending_open_device_ = false;
238   pending_open_device_id_ = -1;
239
240   PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
241   if (succeeded && device_manager) {
242     DCHECK(!label.empty());
243     label_ = label;
244
245     if (client_) {
246       int session_id = device_manager->GetSessionID(
247           PP_DEVICETYPE_DEV_AUDIOCAPTURE, label);
248       io_message_loop_proxy_->PostTask(
249           FROM_HERE,
250           base::Bind(&PepperPlatformAudioInput::InitializeOnIOThread,
251                      this,
252                      session_id));
253     } else {
254       // Shutdown has occurred.
255       CloseDevice();
256     }
257   } else {
258     NotifyStreamCreationFailed();
259   }
260 }
261
262 void PepperPlatformAudioInput::CloseDevice() {
263   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
264
265   if (!label_.empty()) {
266     PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
267     if (device_manager)
268       device_manager->CloseDevice(label_);
269     label_.clear();
270   }
271   if (pending_open_device_) {
272     PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
273     if (device_manager)
274       device_manager->CancelOpenDevice(pending_open_device_id_);
275     pending_open_device_ = false;
276     pending_open_device_id_ = -1;
277   }
278 }
279
280 void PepperPlatformAudioInput::NotifyStreamCreationFailed() {
281   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
282
283   if (client_)
284     client_->StreamCreationFailed();
285 }
286
287 PepperMediaDeviceManager* PepperPlatformAudioInput::GetMediaDeviceManager() {
288   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
289
290   RenderFrameImpl* const render_frame =
291       RenderFrameImpl::FromRoutingID(render_frame_id_);
292   return render_frame ?
293       PepperMediaDeviceManager::GetForRenderFrame(render_frame) : NULL;
294 }
295
296 }  // namespace content