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