3110eaf08b77d868b70b2077cf79e38f3717f5d1
[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_thread_impl.h"
16 #include "content/renderer/render_view_impl.h"
17 #include "media/audio/audio_manager_base.h"
18 #include "ppapi/shared_impl/ppb_audio_config_shared.h"
19 #include "url/gurl.h"
20
21 namespace content {
22
23 // static
24 PepperPlatformAudioInput* PepperPlatformAudioInput::Create(
25     const base::WeakPtr<RenderViewImpl>& render_view,
26     const std::string& device_id,
27     const GURL& document_url,
28     int sample_rate,
29     int frames_per_buffer,
30     PepperAudioInputHost* client) {
31   scoped_refptr<PepperPlatformAudioInput> audio_input(
32       new PepperPlatformAudioInput());
33   if (audio_input->Initialize(render_view, device_id, document_url,
34                               sample_rate, frames_per_buffer, client)) {
35     // Balanced by Release invoked in
36     // PepperPlatformAudioInput::ShutDownOnIOThread().
37     audio_input->AddRef();
38     return audio_input.get();
39   }
40   return NULL;
41 }
42
43 void PepperPlatformAudioInput::StartCapture() {
44   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
45
46   io_message_loop_proxy_->PostTask(
47       FROM_HERE,
48       base::Bind(&PepperPlatformAudioInput::StartCaptureOnIOThread, this));
49 }
50
51 void PepperPlatformAudioInput::StopCapture() {
52   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
53
54   io_message_loop_proxy_->PostTask(
55       FROM_HERE,
56       base::Bind(&PepperPlatformAudioInput::StopCaptureOnIOThread, this));
57 }
58
59 void PepperPlatformAudioInput::ShutDown() {
60   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
61
62   // Make sure we don't call shutdown more than once.
63   if (!client_)
64     return;
65
66   // Called on the main thread to stop all audio callbacks. We must only change
67   // the client on the main thread, and the delegates from the I/O thread.
68   client_ = NULL;
69   io_message_loop_proxy_->PostTask(
70       FROM_HERE,
71       base::Bind(&PepperPlatformAudioInput::ShutDownOnIOThread, this));
72 }
73
74 void PepperPlatformAudioInput::OnStreamCreated(
75     base::SharedMemoryHandle handle,
76     base::SyncSocket::Handle socket_handle,
77     int length,
78     int total_segments) {
79 #if defined(OS_WIN)
80   DCHECK(handle);
81   DCHECK(socket_handle);
82 #else
83   DCHECK_NE(-1, handle.fd);
84   DCHECK_NE(-1, socket_handle);
85 #endif
86   DCHECK(length);
87   // TODO(yzshen): Make use of circular buffer scheme. crbug.com/181449.
88   DCHECK_EQ(1, total_segments);
89
90   if (base::MessageLoopProxy::current().get() !=
91       main_message_loop_proxy_.get()) {
92     // If shutdown has occurred, |client_| will be NULL and the handles will be
93     // cleaned up on the main thread.
94     main_message_loop_proxy_->PostTask(
95         FROM_HERE,
96         base::Bind(&PepperPlatformAudioInput::OnStreamCreated, this,
97                    handle, socket_handle, length, total_segments));
98   } else {
99     // Must dereference the client only on the main thread. Shutdown may have
100     // occurred while the request was in-flight, so we need to NULL check.
101     if (client_) {
102       client_->StreamCreated(handle, length, socket_handle);
103     } else {
104       // Clean up the handles.
105       base::SyncSocket temp_socket(socket_handle);
106       base::SharedMemory temp_shared_memory(handle, false);
107     }
108   }
109 }
110
111 void PepperPlatformAudioInput::OnVolume(double volume) {}
112
113 void PepperPlatformAudioInput::OnStateChanged(
114     media::AudioInputIPCDelegate::State state) {
115 }
116
117 void PepperPlatformAudioInput::OnIPCClosed() {
118   ipc_.reset();
119 }
120
121 PepperPlatformAudioInput::~PepperPlatformAudioInput() {
122   // Make sure we have been shut down. Warning: this may happen on the I/O
123   // thread!
124   // Although these members should be accessed on a specific thread (either the
125   // main thread or the I/O thread), it should be fine to examine their value
126   // here.
127   DCHECK(!ipc_);
128   DCHECK(!client_);
129   DCHECK(label_.empty());
130   DCHECK(!pending_open_device_);
131 }
132
133 PepperPlatformAudioInput::PepperPlatformAudioInput()
134     : client_(NULL),
135       main_message_loop_proxy_(base::MessageLoopProxy::current()),
136       io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()),
137       create_stream_sent_(false),
138       pending_open_device_(false),
139       pending_open_device_id_(-1) {
140 }
141
142 bool PepperPlatformAudioInput::Initialize(
143     const base::WeakPtr<RenderViewImpl>& render_view,
144     const std::string& device_id,
145     const GURL& document_url,
146     int sample_rate,
147     int frames_per_buffer,
148     PepperAudioInputHost* client) {
149   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
150
151   if (!render_view.get() || !client)
152     return false;
153
154   ipc_ = RenderThreadImpl::current()->audio_input_message_filter()->
155       CreateAudioInputIPC(render_view->GetRoutingID());
156
157   render_view_ = render_view;
158   client_ = client;
159
160   params_.Reset(media::AudioParameters::AUDIO_PCM_LINEAR,
161                 media::CHANNEL_LAYOUT_MONO, ppapi::kAudioInputChannels, 0,
162                 sample_rate, ppapi::kBitsPerAudioInputSample,
163                 frames_per_buffer);
164
165   // We need to open the device and obtain the label and session ID before
166   // initializing.
167   pending_open_device_id_ = GetMediaDeviceManager()->OpenDevice(
168       PP_DEVICETYPE_DEV_AUDIOCAPTURE,
169       device_id.empty() ? media::AudioManagerBase::kDefaultDeviceId : device_id,
170       document_url,
171       base::Bind(&PepperPlatformAudioInput::OnDeviceOpened, this));
172   pending_open_device_ = true;
173
174   return true;
175 }
176
177 void PepperPlatformAudioInput::InitializeOnIOThread(int session_id) {
178   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
179
180   if (!ipc_)
181     return;
182
183   // We will be notified by OnStreamCreated().
184   create_stream_sent_ = true;
185   ipc_->CreateStream(this, session_id, params_, false, 1);
186 }
187
188 void PepperPlatformAudioInput::StartCaptureOnIOThread() {
189   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
190
191   if (ipc_)
192     ipc_->RecordStream();
193 }
194
195 void PepperPlatformAudioInput::StopCaptureOnIOThread() {
196   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
197
198   // TODO(yzshen): We cannot re-start capturing if the stream is closed.
199   if (ipc_ && create_stream_sent_) {
200     ipc_->CloseStream();
201   }
202   ipc_.reset();
203 }
204
205 void PepperPlatformAudioInput::ShutDownOnIOThread() {
206   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
207
208   StopCaptureOnIOThread();
209
210   main_message_loop_proxy_->PostTask(
211       FROM_HERE,
212       base::Bind(&PepperPlatformAudioInput::CloseDevice, this));
213
214   Release();  // Release for the delegate, balances out the reference taken in
215               // PepperPlatformAudioInput::Create.
216 }
217
218 void PepperPlatformAudioInput::OnDeviceOpened(int request_id,
219                                               bool succeeded,
220                                               const std::string& label) {
221   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
222
223   pending_open_device_ = false;
224   pending_open_device_id_ = -1;
225
226   if (succeeded && render_view_.get()) {
227     DCHECK(!label.empty());
228     label_ = label;
229
230     if (client_) {
231       int session_id = GetMediaDeviceManager()->GetSessionID(
232           PP_DEVICETYPE_DEV_AUDIOCAPTURE, label);
233       io_message_loop_proxy_->PostTask(
234           FROM_HERE,
235           base::Bind(&PepperPlatformAudioInput::InitializeOnIOThread,
236                      this, session_id));
237     } else {
238       // Shutdown has occurred.
239       CloseDevice();
240     }
241   } else {
242     NotifyStreamCreationFailed();
243   }
244 }
245
246 void PepperPlatformAudioInput::CloseDevice() {
247   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
248
249   if (render_view_.get()) {
250     if (!label_.empty()) {
251       GetMediaDeviceManager()->CloseDevice(label_);
252       label_.clear();
253     }
254     if (pending_open_device_) {
255       GetMediaDeviceManager()->CancelOpenDevice(pending_open_device_id_);
256       pending_open_device_ = false;
257       pending_open_device_id_ = -1;
258     }
259   }
260 }
261
262 void PepperPlatformAudioInput::NotifyStreamCreationFailed() {
263   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
264
265   if (client_)
266     client_->StreamCreationFailed();
267 }
268
269 PepperMediaDeviceManager* PepperPlatformAudioInput::GetMediaDeviceManager() {
270   return PepperMediaDeviceManager::GetForRenderView(render_view_.get());
271 }
272
273 }  // namespace content