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
22namespace content {
23
24// static
25PepperPlatformAudioInput* 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
48void 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
56void 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
64void 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
79void 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
120void PepperPlatformAudioInput::OnVolume(double volume) {}
121
122void PepperPlatformAudioInput::OnStateChanged(
123    media::AudioInputIPCDelegate::State state) {}
124
125void PepperPlatformAudioInput::OnIPCClosed() { ipc_.reset(); }
126
127PepperPlatformAudioInput::~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
139PepperPlatformAudioInput::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
148bool 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                sample_rate,
176                ppapi::kBitsPerAudioInputSample,
177                frames_per_buffer);
178
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;
187
188  return true;
189}
190
191void PepperPlatformAudioInput::InitializeOnIOThread(int session_id) {
192  DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
193
194  if (!ipc_)
195    return;
196
197  // We will be notified by OnStreamCreated().
198  create_stream_sent_ = true;
199  ipc_->CreateStream(this, session_id, params_, false, 1);
200}
201
202void PepperPlatformAudioInput::StartCaptureOnIOThread() {
203  DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
204
205  if (ipc_)
206    ipc_->RecordStream();
207}
208
209void PepperPlatformAudioInput::StopCaptureOnIOThread() {
210  DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
211
212  // TODO(yzshen): We cannot re-start capturing if the stream is closed.
213  if (ipc_ && create_stream_sent_) {
214    ipc_->CloseStream();
215  }
216  ipc_.reset();
217}
218
219void PepperPlatformAudioInput::ShutDownOnIOThread() {
220  DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
221
222  StopCaptureOnIOThread();
223
224  main_message_loop_proxy_->PostTask(
225      FROM_HERE, base::Bind(&PepperPlatformAudioInput::CloseDevice, this));
226
227  Release();  // Release for the delegate, balances out the reference taken in
228              // PepperPlatformAudioInput::Create.
229}
230
231void PepperPlatformAudioInput::OnDeviceOpened(int request_id,
232                                              bool succeeded,
233                                              const std::string& label) {
234  DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
235
236  pending_open_device_ = false;
237  pending_open_device_id_ = -1;
238
239  PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
240  if (succeeded && device_manager) {
241    DCHECK(!label.empty());
242    label_ = label;
243
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();
255    }
256  } else {
257    NotifyStreamCreationFailed();
258  }
259}
260
261void PepperPlatformAudioInput::CloseDevice() {
262  DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
263
264  if (!label_.empty()) {
265    PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
266    if (device_manager)
267      device_manager->CloseDevice(label_);
268    label_.clear();
269  }
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;
276  }
277}
278
279void PepperPlatformAudioInput::NotifyStreamCreationFailed() {
280  DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
281
282  if (client_)
283    client_->StreamCreationFailed();
284}
285
286PepperMediaDeviceManager* PepperPlatformAudioInput::GetMediaDeviceManager() {
287  DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
288
289  RenderFrameImpl* const render_frame =
290      RenderFrameImpl::FromRoutingID(render_frame_id_);
291  return render_frame ?
292      PepperMediaDeviceManager::GetForRenderFrame(render_frame).get() : NULL;
293}
294
295}  // namespace content
296