audio_input_resource.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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 "ppapi/proxy/audio_input_resource.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "ipc/ipc_platform_file.h"
10#include "media/audio/audio_parameters.h"
11#include "media/audio/shared_memory_util.h"
12#include "ppapi/c/pp_errors.h"
13#include "ppapi/proxy/ppapi_messages.h"
14#include "ppapi/proxy/resource_message_params.h"
15#include "ppapi/proxy/serialized_handle.h"
16#include "ppapi/shared_impl/ppapi_globals.h"
17#include "ppapi/shared_impl/resource_tracker.h"
18#include "ppapi/shared_impl/tracked_callback.h"
19#include "ppapi/thunk/enter.h"
20#include "ppapi/thunk/ppb_audio_config_api.h"
21
22namespace ppapi {
23namespace proxy {
24
25AudioInputResource::AudioInputResource(
26    Connection connection,
27    PP_Instance instance)
28    : PluginResource(connection, instance),
29      open_state_(BEFORE_OPEN),
30      capturing_(false),
31      shared_memory_size_(0),
32      audio_input_callback_(NULL),
33      user_data_(NULL),
34      enumeration_helper_(this) {
35  SendCreate(RENDERER, PpapiHostMsg_AudioInput_Create());
36}
37
38AudioInputResource::~AudioInputResource() {
39  Close();
40}
41
42thunk::PPB_AudioInput_API* AudioInputResource::AsPPB_AudioInput_API() {
43  return this;
44}
45
46void AudioInputResource::OnReplyReceived(
47    const ResourceMessageReplyParams& params,
48    const IPC::Message& msg) {
49  if (!enumeration_helper_.HandleReply(params, msg))
50    PluginResource::OnReplyReceived(params, msg);
51}
52
53int32_t AudioInputResource::EnumerateDevices0_2(
54    PP_Resource* devices,
55    scoped_refptr<TrackedCallback> callback) {
56  return enumeration_helper_.EnumerateDevices0_2(devices, callback);
57}
58
59int32_t AudioInputResource::EnumerateDevices(
60    const PP_ArrayOutput& output,
61    scoped_refptr<TrackedCallback> callback) {
62  return enumeration_helper_.EnumerateDevices(output, callback);
63}
64
65int32_t AudioInputResource::MonitorDeviceChange(
66    PP_MonitorDeviceChangeCallback callback,
67    void* user_data) {
68  return enumeration_helper_.MonitorDeviceChange(callback, user_data);
69}
70
71int32_t AudioInputResource::Open(PP_Resource device_ref,
72                                 PP_Resource config,
73                                 PPB_AudioInput_Callback audio_input_callback,
74                                 void* user_data,
75                                 scoped_refptr<TrackedCallback> callback) {
76  std::string device_id;
77  // |device_id| remains empty if |device_ref| is 0, which means the default
78  // device.
79  if (device_ref != 0) {
80    thunk::EnterResourceNoLock<thunk::PPB_DeviceRef_API> enter_device_ref(
81        device_ref, true);
82    if (enter_device_ref.failed())
83      return PP_ERROR_BADRESOURCE;
84    device_id = enter_device_ref.object()->GetDeviceRefData().id;
85  }
86
87  if (TrackedCallback::IsPending(open_callback_))
88    return PP_ERROR_INPROGRESS;
89  if (open_state_ != BEFORE_OPEN)
90    return PP_ERROR_FAILED;
91
92  if (!audio_input_callback)
93    return PP_ERROR_BADARGUMENT;
94  thunk::EnterResourceNoLock<thunk::PPB_AudioConfig_API> enter_config(config,
95                                                                      true);
96  if (enter_config.failed())
97    return PP_ERROR_BADARGUMENT;
98
99  config_ = config;
100  audio_input_callback_ = audio_input_callback;
101  user_data_ = user_data;
102  open_callback_ = callback;
103
104  PpapiHostMsg_AudioInput_Open msg(
105      device_id, enter_config.object()->GetSampleRate(),
106      enter_config.object()->GetSampleFrameCount());
107  Call<PpapiPluginMsg_AudioInput_OpenReply>(
108      RENDERER, msg,
109      base::Bind(&AudioInputResource::OnPluginMsgOpenReply,
110                 base::Unretained(this)));
111  return PP_OK_COMPLETIONPENDING;
112}
113
114PP_Resource AudioInputResource::GetCurrentConfig() {
115  // AddRef for the caller.
116  if (config_.get())
117    PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
118  return config_;
119}
120
121PP_Bool AudioInputResource::StartCapture() {
122  if (open_state_ == CLOSED || (open_state_ == BEFORE_OPEN &&
123                                !TrackedCallback::IsPending(open_callback_))) {
124    return PP_FALSE;
125  }
126  if (capturing_)
127    return PP_TRUE;
128
129  capturing_ = true;
130  // Return directly if the audio input device hasn't been opened. Capturing
131  // will be started once the open operation is completed.
132  if (open_state_ == BEFORE_OPEN)
133    return PP_TRUE;
134
135  StartThread();
136
137  Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(true));
138  return PP_TRUE;
139}
140
141PP_Bool AudioInputResource::StopCapture() {
142  if (open_state_ == CLOSED)
143    return PP_FALSE;
144  if (!capturing_)
145    return PP_TRUE;
146
147  // If the audio input device hasn't been opened, set |capturing_| to false and
148  // return directly.
149  if (open_state_ == BEFORE_OPEN) {
150    capturing_ = false;
151    return PP_TRUE;
152  }
153
154  Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(false));
155
156  StopThread();
157  capturing_ = false;
158
159  return PP_TRUE;
160}
161
162void AudioInputResource::Close() {
163  if (open_state_ == CLOSED)
164    return;
165
166  open_state_ = CLOSED;
167  Post(RENDERER, PpapiHostMsg_AudioInput_Close());
168  StopThread();
169
170  if (TrackedCallback::IsPending(open_callback_))
171    open_callback_->PostAbort();
172}
173
174void AudioInputResource::LastPluginRefWasDeleted() {
175  enumeration_helper_.LastPluginRefWasDeleted();
176}
177
178void AudioInputResource::OnPluginMsgOpenReply(
179    const ResourceMessageReplyParams& params) {
180  if (open_state_ == BEFORE_OPEN && params.result() == PP_OK) {
181    IPC::PlatformFileForTransit socket_handle_for_transit =
182        IPC::InvalidPlatformFileForTransit();
183    params.TakeSocketHandleAtIndex(0, &socket_handle_for_transit);
184    base::SyncSocket::Handle socket_handle =
185        IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit);
186    CHECK(socket_handle != base::SyncSocket::kInvalidHandle);
187
188    SerializedHandle serialized_shared_memory_handle =
189        params.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY);
190    CHECK(serialized_shared_memory_handle.IsHandleValid());
191
192    // See the comment in pepper_audio_input_host.cc about how we must call
193    // TotalSharedMemorySizeInBytes to get the actual size of the buffer. Here,
194    // we must call PacketSizeInBytes to get back the size of the audio buffer,
195    // excluding the bytes that audio uses for book-keeping.
196    size_t shared_memory_size = media::PacketSizeInBytes(
197        serialized_shared_memory_handle.size());
198
199    open_state_ = OPENED;
200    SetStreamInfo(serialized_shared_memory_handle.shmem(), shared_memory_size,
201                  socket_handle);
202  } else {
203    capturing_ = false;
204  }
205
206  // The callback may have been aborted by Close().
207  if (TrackedCallback::IsPending(open_callback_))
208    open_callback_->Run(params.result());
209}
210
211void AudioInputResource::SetStreamInfo(
212    base::SharedMemoryHandle shared_memory_handle,
213    size_t shared_memory_size,
214    base::SyncSocket::Handle socket_handle) {
215  socket_.reset(new base::CancelableSyncSocket(socket_handle));
216  shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false));
217  shared_memory_size_ = shared_memory_size;
218
219  if (!shared_memory_->Map(shared_memory_size_)) {
220    PpapiGlobals::Get()->LogWithSource(
221        pp_instance(),
222        PP_LOGLEVEL_WARNING,
223        std::string(),
224        "Failed to map shared memory for PPB_AudioInput_Shared.");
225  }
226
227  // There is a pending capture request before SetStreamInfo().
228  if (capturing_) {
229    // Set |capturing_| to false so that the state looks consistent to
230    // StartCapture(), which will reset it to true.
231    capturing_ = false;
232    StartCapture();
233  }
234}
235
236void AudioInputResource::StartThread() {
237  // Don't start the thread unless all our state is set up correctly.
238  if (!audio_input_callback_ || !socket_.get() || !capturing_ ||
239      !shared_memory_->memory()) {
240    return;
241  }
242  DCHECK(!audio_input_thread_.get());
243  audio_input_thread_.reset(new base::DelegateSimpleThread(
244      this, "plugin_audio_input_thread"));
245  audio_input_thread_->Start();
246}
247
248void AudioInputResource::StopThread() {
249  // Shut down the socket to escape any hanging |Receive|s.
250  if (socket_.get())
251    socket_->Shutdown();
252  if (audio_input_thread_.get()) {
253    audio_input_thread_->Join();
254    audio_input_thread_.reset();
255  }
256}
257
258void AudioInputResource::Run() {
259  // The shared memory represents AudioInputBufferParameters and the actual data
260  // buffer.
261  media::AudioInputBuffer* buffer =
262      static_cast<media::AudioInputBuffer*>(shared_memory_->memory());
263  uint32_t data_buffer_size =
264      shared_memory_size_ - sizeof(media::AudioInputBufferParameters);
265  int pending_data;
266
267  while (sizeof(pending_data) == socket_->Receive(&pending_data,
268                                                  sizeof(pending_data)) &&
269         pending_data >= 0) {
270    // While closing the stream, we may receive buffers whose size is different
271    // from |data_buffer_size|.
272    CHECK_LE(buffer->params.size, data_buffer_size);
273    if (buffer->params.size > 0)
274      audio_input_callback_(&buffer->audio[0], buffer->params.size, user_data_);
275  }
276}
277
278}  // namespace proxy
279}  // namespace ppapi
280