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.
5 #include "ppapi/proxy/audio_input_resource.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"
25 AudioInputResource::AudioInputResource(
26 Connection connection
,
28 : PluginResource(connection
, instance
),
29 open_state_(BEFORE_OPEN
),
31 shared_memory_size_(0),
32 audio_input_callback_0_2_(NULL
),
33 audio_input_callback_(NULL
),
35 enumeration_helper_(this),
36 bytes_per_second_(0) {
37 SendCreate(RENDERER
, PpapiHostMsg_AudioInput_Create());
40 AudioInputResource::~AudioInputResource() {
44 thunk::PPB_AudioInput_API
* AudioInputResource::AsPPB_AudioInput_API() {
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
);
55 int32_t AudioInputResource::EnumerateDevices0_2(
57 scoped_refptr
<TrackedCallback
> callback
) {
58 return enumeration_helper_
.EnumerateDevices0_2(devices
, callback
);
61 int32_t AudioInputResource::EnumerateDevices(
62 const PP_ArrayOutput
& output
,
63 scoped_refptr
<TrackedCallback
> callback
) {
64 return enumeration_helper_
.EnumerateDevices(output
, callback
);
67 int32_t AudioInputResource::MonitorDeviceChange(
68 PP_MonitorDeviceChangeCallback callback
,
70 return enumeration_helper_
.MonitorDeviceChange(callback
, user_data
);
73 int32_t AudioInputResource::Open0_2(
74 PP_Resource device_ref
,
76 PPB_AudioInput_Callback_0_2 audio_input_callback_0_2
,
78 scoped_refptr
<TrackedCallback
> callback
) {
79 return CommonOpen(device_ref
, config
, audio_input_callback_0_2
, NULL
,
83 int32_t AudioInputResource::Open(PP_Resource device_ref
,
85 PPB_AudioInput_Callback audio_input_callback
,
87 scoped_refptr
<TrackedCallback
> callback
) {
88 return CommonOpen(device_ref
, config
, NULL
, audio_input_callback
, user_data
,
92 PP_Resource
AudioInputResource::GetCurrentConfig() {
93 // AddRef for the caller.
95 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_
);
99 PP_Bool
AudioInputResource::StartCapture() {
100 if (open_state_
== CLOSED
|| (open_state_
== BEFORE_OPEN
&&
101 !TrackedCallback::IsPending(open_callback_
))) {
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
)
115 Post(RENDERER
, PpapiHostMsg_AudioInput_StartOrStop(true));
119 PP_Bool
AudioInputResource::StopCapture() {
120 if (open_state_
== CLOSED
)
125 // If the audio input device hasn't been opened, set |capturing_| to false and
127 if (open_state_
== BEFORE_OPEN
) {
132 Post(RENDERER
, PpapiHostMsg_AudioInput_StartOrStop(false));
140 void AudioInputResource::Close() {
141 if (open_state_
== CLOSED
)
144 open_state_
= CLOSED
;
145 Post(RENDERER
, PpapiHostMsg_AudioInput_Close());
148 if (TrackedCallback::IsPending(open_callback_
))
149 open_callback_
->PostAbort();
152 void AudioInputResource::LastPluginRefWasDeleted() {
153 enumeration_helper_
.LastPluginRefWasDeleted();
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
);
166 SerializedHandle serialized_shared_memory_handle
=
167 params
.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY
);
168 CHECK(serialized_shared_memory_handle
.IsHandleValid());
170 open_state_
= OPENED
;
171 SetStreamInfo(serialized_shared_memory_handle
.shmem(),
172 serialized_shared_memory_handle
.size(),
178 // The callback may have been aborted by Close().
179 if (TrackedCallback::IsPending(open_callback_
))
180 open_callback_
->Run(params
.result());
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
;
191 if (!shared_memory_
->Map(shared_memory_size_
)) {
192 PpapiGlobals::Get()->LogWithSource(
196 "Failed to map shared memory for PPB_AudioInput_Shared.");
199 // There is a pending capture request before SetStreamInfo().
201 // Set |capturing_| to false so that the state looks consistent to
202 // StartCapture(), which will reset it to true.
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()) {
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();
220 void AudioInputResource::StopThread() {
221 // Shut down the socket to escape any hanging |Receive|s.
224 if (audio_input_thread_
.get()) {
225 audio_input_thread_
->Join();
226 audio_input_thread_
.reset();
230 void AudioInputResource::Run() {
231 // The shared memory represents AudioInputBufferParameters and the actual data
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
);
239 while (sizeof(pending_data
) == socket_
->Receive(&pending_data
,
240 sizeof(pending_data
)) &&
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
,
252 audio_input_callback_0_2_(&buffer
->audio
[0], buffer
->params
.size
,
259 int32_t AudioInputResource::CommonOpen(
260 PP_Resource device_ref
,
262 PPB_AudioInput_Callback_0_2 audio_input_callback_0_2
,
263 PPB_AudioInput_Callback audio_input_callback
,
265 scoped_refptr
<TrackedCallback
> callback
) {
266 std::string device_id
;
267 // |device_id| remains empty if |device_ref| is 0, which means the default
269 if (device_ref
!= 0) {
270 thunk::EnterResourceNoLock
<thunk::PPB_DeviceRef_API
> enter_device_ref(
272 if (enter_device_ref
.failed())
273 return PP_ERROR_BADRESOURCE
;
274 device_id
= enter_device_ref
.object()->GetDeviceRefData().id
;
277 if (TrackedCallback::IsPending(open_callback_
))
278 return PP_ERROR_INPROGRESS
;
279 if (open_state_
!= BEFORE_OPEN
)
280 return PP_ERROR_FAILED
;
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
,
286 if (enter_config
.failed())
287 return PP_ERROR_BADARGUMENT
;
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();
297 PpapiHostMsg_AudioInput_Open
msg(
298 device_id
, enter_config
.object()->GetSampleRate(),
299 enter_config
.object()->GetSampleFrameCount());
300 Call
<PpapiPluginMsg_AudioInput_OpenReply
>(
302 base::Bind(&AudioInputResource::OnPluginMsgOpenReply
,
303 base::Unretained(this)));
304 return PP_OK_COMPLETIONPENDING
;