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_3_(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::EnumerateDevices(
56 const PP_ArrayOutput
& output
,
57 scoped_refptr
<TrackedCallback
> callback
) {
58 return enumeration_helper_
.EnumerateDevices(output
, callback
);
61 int32_t AudioInputResource::MonitorDeviceChange(
62 PP_MonitorDeviceChangeCallback callback
,
64 return enumeration_helper_
.MonitorDeviceChange(callback
, user_data
);
67 int32_t AudioInputResource::Open0_3(
68 PP_Resource device_ref
,
70 PPB_AudioInput_Callback_0_3 audio_input_callback_0_3
,
72 scoped_refptr
<TrackedCallback
> callback
) {
73 return CommonOpen(device_ref
, config
, audio_input_callback_0_3
, NULL
,
77 int32_t AudioInputResource::Open(PP_Resource device_ref
,
79 PPB_AudioInput_Callback audio_input_callback
,
81 scoped_refptr
<TrackedCallback
> callback
) {
82 return CommonOpen(device_ref
, config
, NULL
, audio_input_callback
, user_data
,
86 PP_Resource
AudioInputResource::GetCurrentConfig() {
87 // AddRef for the caller.
89 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_
);
93 PP_Bool
AudioInputResource::StartCapture() {
94 if (open_state_
== CLOSED
|| (open_state_
== BEFORE_OPEN
&&
95 !TrackedCallback::IsPending(open_callback_
))) {
102 // Return directly if the audio input device hasn't been opened. Capturing
103 // will be started once the open operation is completed.
104 if (open_state_
== BEFORE_OPEN
)
109 Post(RENDERER
, PpapiHostMsg_AudioInput_StartOrStop(true));
113 PP_Bool
AudioInputResource::StopCapture() {
114 if (open_state_
== CLOSED
)
119 // If the audio input device hasn't been opened, set |capturing_| to false and
121 if (open_state_
== BEFORE_OPEN
) {
126 Post(RENDERER
, PpapiHostMsg_AudioInput_StartOrStop(false));
134 void AudioInputResource::Close() {
135 if (open_state_
== CLOSED
)
138 open_state_
= CLOSED
;
139 Post(RENDERER
, PpapiHostMsg_AudioInput_Close());
142 if (TrackedCallback::IsPending(open_callback_
))
143 open_callback_
->PostAbort();
146 void AudioInputResource::LastPluginRefWasDeleted() {
147 enumeration_helper_
.LastPluginRefWasDeleted();
150 void AudioInputResource::OnPluginMsgOpenReply(
151 const ResourceMessageReplyParams
& params
) {
152 if (open_state_
== BEFORE_OPEN
&& params
.result() == PP_OK
) {
153 IPC::PlatformFileForTransit socket_handle_for_transit
=
154 IPC::InvalidPlatformFileForTransit();
155 params
.TakeSocketHandleAtIndex(0, &socket_handle_for_transit
);
156 base::SyncSocket::Handle socket_handle
=
157 IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit
);
158 CHECK(socket_handle
!= base::SyncSocket::kInvalidHandle
);
160 SerializedHandle serialized_shared_memory_handle
=
161 params
.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY
);
162 CHECK(serialized_shared_memory_handle
.IsHandleValid());
164 open_state_
= OPENED
;
165 SetStreamInfo(serialized_shared_memory_handle
.shmem(),
166 serialized_shared_memory_handle
.size(),
172 // The callback may have been aborted by Close().
173 if (TrackedCallback::IsPending(open_callback_
))
174 open_callback_
->Run(params
.result());
177 void AudioInputResource::SetStreamInfo(
178 base::SharedMemoryHandle shared_memory_handle
,
179 size_t shared_memory_size
,
180 base::SyncSocket::Handle socket_handle
) {
181 socket_
.reset(new base::CancelableSyncSocket(socket_handle
));
182 shared_memory_
.reset(new base::SharedMemory(shared_memory_handle
, false));
183 shared_memory_size_
= shared_memory_size
;
185 if (!shared_memory_
->Map(shared_memory_size_
)) {
186 PpapiGlobals::Get()->LogWithSource(
190 "Failed to map shared memory for PPB_AudioInput_Shared.");
193 // There is a pending capture request before SetStreamInfo().
195 // Set |capturing_| to false so that the state looks consistent to
196 // StartCapture(), which will reset it to true.
202 void AudioInputResource::StartThread() {
203 // Don't start the thread unless all our state is set up correctly.
204 if ((!audio_input_callback_0_3_
&& !audio_input_callback_
) ||
205 !socket_
.get() || !capturing_
|| !shared_memory_
->memory()) {
208 DCHECK(!audio_input_thread_
.get());
209 audio_input_thread_
.reset(new base::DelegateSimpleThread(
210 this, "plugin_audio_input_thread"));
211 audio_input_thread_
->Start();
214 void AudioInputResource::StopThread() {
215 // Shut down the socket to escape any hanging |Receive|s.
218 if (audio_input_thread_
.get()) {
219 audio_input_thread_
->Join();
220 audio_input_thread_
.reset();
224 void AudioInputResource::Run() {
225 // The shared memory represents AudioInputBufferParameters and the actual data
227 media::AudioInputBuffer
* buffer
=
228 static_cast<media::AudioInputBuffer
*>(shared_memory_
->memory());
229 uint32_t data_buffer_size
=
230 shared_memory_size_
- sizeof(media::AudioInputBufferParameters
);
233 while (sizeof(pending_data
) == socket_
->Receive(&pending_data
,
234 sizeof(pending_data
)) &&
236 // While closing the stream, we may receive buffers whose size is different
237 // from |data_buffer_size|.
238 CHECK_LE(buffer
->params
.size
, data_buffer_size
);
239 if (buffer
->params
.size
> 0) {
240 if (audio_input_callback_
) {
241 PP_TimeDelta latency
=
242 static_cast<double>(pending_data
) / bytes_per_second_
;
243 audio_input_callback_(&buffer
->audio
[0], buffer
->params
.size
, latency
,
246 audio_input_callback_0_3_(&buffer
->audio
[0], buffer
->params
.size
,
253 int32_t AudioInputResource::CommonOpen(
254 PP_Resource device_ref
,
256 PPB_AudioInput_Callback_0_3 audio_input_callback_0_3
,
257 PPB_AudioInput_Callback audio_input_callback
,
259 scoped_refptr
<TrackedCallback
> callback
) {
260 std::string device_id
;
261 // |device_id| remains empty if |device_ref| is 0, which means the default
263 if (device_ref
!= 0) {
264 thunk::EnterResourceNoLock
<thunk::PPB_DeviceRef_API
> enter_device_ref(
266 if (enter_device_ref
.failed())
267 return PP_ERROR_BADRESOURCE
;
268 device_id
= enter_device_ref
.object()->GetDeviceRefData().id
;
271 if (TrackedCallback::IsPending(open_callback_
))
272 return PP_ERROR_INPROGRESS
;
273 if (open_state_
!= BEFORE_OPEN
)
274 return PP_ERROR_FAILED
;
276 if (!audio_input_callback_0_3
&& !audio_input_callback
)
277 return PP_ERROR_BADARGUMENT
;
278 thunk::EnterResourceNoLock
<thunk::PPB_AudioConfig_API
> enter_config(config
,
280 if (enter_config
.failed())
281 return PP_ERROR_BADARGUMENT
;
284 audio_input_callback_0_3_
= audio_input_callback_0_3
;
285 audio_input_callback_
= audio_input_callback
;
286 user_data_
= user_data
;
287 open_callback_
= callback
;
288 bytes_per_second_
= kAudioInputChannels
* (kBitsPerAudioInputSample
/ 8) *
289 enter_config
.object()->GetSampleRate();
291 PpapiHostMsg_AudioInput_Open
msg(
292 device_id
, enter_config
.object()->GetSampleRate(),
293 enter_config
.object()->GetSampleFrameCount());
294 Call
<PpapiPluginMsg_AudioInput_OpenReply
>(
296 base::Bind(&AudioInputResource::OnPluginMsgOpenReply
,
297 base::Unretained(this)));
298 return PP_OK_COMPLETIONPENDING
;