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 "media/base/audio_bus.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/ppb_audio_config_shared.h"
18 #include "ppapi/shared_impl/resource_tracker.h"
19 #include "ppapi/shared_impl/tracked_callback.h"
20 #include "ppapi/thunk/enter.h"
21 #include "ppapi/thunk/ppb_audio_config_api.h"
26 AudioInputResource::AudioInputResource(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),
37 sample_frame_count_(0),
38 client_buffer_size_bytes_(0) {
39 SendCreate(RENDERER
, PpapiHostMsg_AudioInput_Create());
42 AudioInputResource::~AudioInputResource() {
46 thunk::PPB_AudioInput_API
* AudioInputResource::AsPPB_AudioInput_API() {
50 void AudioInputResource::OnReplyReceived(
51 const ResourceMessageReplyParams
& params
,
52 const IPC::Message
& msg
) {
53 if (!enumeration_helper_
.HandleReply(params
, msg
))
54 PluginResource::OnReplyReceived(params
, msg
);
57 int32_t AudioInputResource::EnumerateDevices(
58 const PP_ArrayOutput
& output
,
59 scoped_refptr
<TrackedCallback
> callback
) {
60 return enumeration_helper_
.EnumerateDevices(output
, callback
);
63 int32_t AudioInputResource::MonitorDeviceChange(
64 PP_MonitorDeviceChangeCallback callback
,
66 return enumeration_helper_
.MonitorDeviceChange(callback
, user_data
);
69 int32_t AudioInputResource::Open0_3(
70 PP_Resource device_ref
,
72 PPB_AudioInput_Callback_0_3 audio_input_callback_0_3
,
74 scoped_refptr
<TrackedCallback
> callback
) {
75 return CommonOpen(device_ref
, config
, audio_input_callback_0_3
, NULL
,
79 int32_t AudioInputResource::Open(PP_Resource device_ref
,
81 PPB_AudioInput_Callback audio_input_callback
,
83 scoped_refptr
<TrackedCallback
> callback
) {
84 return CommonOpen(device_ref
, config
, NULL
, audio_input_callback
, user_data
,
88 PP_Resource
AudioInputResource::GetCurrentConfig() {
89 // AddRef for the caller.
91 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_
);
95 PP_Bool
AudioInputResource::StartCapture() {
96 if (open_state_
== CLOSED
|| (open_state_
== BEFORE_OPEN
&&
97 !TrackedCallback::IsPending(open_callback_
))) {
104 // Return directly if the audio input device hasn't been opened. Capturing
105 // will be started once the open operation is completed.
106 if (open_state_
== BEFORE_OPEN
)
111 Post(RENDERER
, PpapiHostMsg_AudioInput_StartOrStop(true));
115 PP_Bool
AudioInputResource::StopCapture() {
116 if (open_state_
== CLOSED
)
121 // If the audio input device hasn't been opened, set |capturing_| to false and
123 if (open_state_
== BEFORE_OPEN
) {
128 Post(RENDERER
, PpapiHostMsg_AudioInput_StartOrStop(false));
136 void AudioInputResource::Close() {
137 if (open_state_
== CLOSED
)
140 open_state_
= CLOSED
;
141 Post(RENDERER
, PpapiHostMsg_AudioInput_Close());
144 if (TrackedCallback::IsPending(open_callback_
))
145 open_callback_
->PostAbort();
148 void AudioInputResource::LastPluginRefWasDeleted() {
149 enumeration_helper_
.LastPluginRefWasDeleted();
152 void AudioInputResource::OnPluginMsgOpenReply(
153 const ResourceMessageReplyParams
& params
) {
154 if (open_state_
== BEFORE_OPEN
&& params
.result() == PP_OK
) {
155 IPC::PlatformFileForTransit socket_handle_for_transit
=
156 IPC::InvalidPlatformFileForTransit();
157 params
.TakeSocketHandleAtIndex(0, &socket_handle_for_transit
);
158 base::SyncSocket::Handle socket_handle
=
159 IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit
);
160 CHECK(socket_handle
!= base::SyncSocket::kInvalidHandle
);
162 SerializedHandle serialized_shared_memory_handle
=
163 params
.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY
);
164 CHECK(serialized_shared_memory_handle
.IsHandleValid());
166 open_state_
= OPENED
;
167 SetStreamInfo(serialized_shared_memory_handle
.shmem(),
168 serialized_shared_memory_handle
.size(),
174 // The callback may have been aborted by Close().
175 if (TrackedCallback::IsPending(open_callback_
))
176 open_callback_
->Run(params
.result());
179 void AudioInputResource::SetStreamInfo(
180 base::SharedMemoryHandle shared_memory_handle
,
181 size_t shared_memory_size
,
182 base::SyncSocket::Handle socket_handle
) {
183 socket_
.reset(new base::CancelableSyncSocket(socket_handle
));
184 shared_memory_
.reset(new base::SharedMemory(shared_memory_handle
, false));
185 shared_memory_size_
= shared_memory_size
;
186 DCHECK(!shared_memory_
->memory());
188 // If we fail to map the shared memory into the caller's address space we
189 // might as well fail here since nothing will work if this is the case.
190 CHECK(shared_memory_
->Map(shared_memory_size_
));
192 // Create a new audio bus and wrap the audio data section in shared memory.
193 media::AudioInputBuffer
* buffer
=
194 static_cast<media::AudioInputBuffer
*>(shared_memory_
->memory());
195 audio_bus_
= media::AudioBus::WrapMemory(
196 kAudioInputChannels
, sample_frame_count_
, buffer
->audio
);
198 // Ensure that the size of the created audio bus matches the allocated
199 // size in shared memory.
200 // Example: DCHECK_EQ(8208 - 16, 8192) for |sample_frame_count_| = 2048.
201 const uint32_t audio_bus_size_bytes
= media::AudioBus::CalculateMemorySize(
202 audio_bus_
->channels(), audio_bus_
->frames());
203 DCHECK_EQ(shared_memory_size_
- sizeof(media::AudioInputBufferParameters
),
204 audio_bus_size_bytes
);
206 // Create an extra integer audio buffer for user audio data callbacks.
207 // Data in shared memory will be copied to this buffer, after interleaving
208 // and truncation, before each input callback to match the format expected
210 client_buffer_size_bytes_
= audio_bus_
->frames() * audio_bus_
->channels() *
211 kBitsPerAudioInputSample
/ 8;
212 client_buffer_
.reset(new uint8_t[client_buffer_size_bytes_
]);
214 // There is a pending capture request before SetStreamInfo().
216 // Set |capturing_| to false so that the state looks consistent to
217 // StartCapture(), which will reset it to true.
223 void AudioInputResource::StartThread() {
224 // Don't start the thread unless all our state is set up correctly.
225 if ((!audio_input_callback_0_3_
&& !audio_input_callback_
) ||
226 !socket_
.get() || !capturing_
|| !shared_memory_
->memory() ||
227 !audio_bus_
.get() || !client_buffer_
.get()) {
230 DCHECK(!audio_input_thread_
.get());
231 audio_input_thread_
.reset(new base::DelegateSimpleThread(
232 this, "plugin_audio_input_thread"));
233 audio_input_thread_
->Start();
236 void AudioInputResource::StopThread() {
237 // Shut down the socket to escape any hanging |Receive|s.
240 if (audio_input_thread_
.get()) {
241 audio_input_thread_
->Join();
242 audio_input_thread_
.reset();
246 void AudioInputResource::Run() {
247 // The shared memory represents AudioInputBufferParameters and the actual data
248 // buffer stored as an audio bus.
249 media::AudioInputBuffer
* buffer
=
250 static_cast<media::AudioInputBuffer
*>(shared_memory_
->memory());
251 const uint32_t audio_bus_size_bytes
=
252 shared_memory_size_
- sizeof(media::AudioInputBufferParameters
);
255 int pending_data
= 0;
256 size_t bytes_read
= socket_
->Receive(&pending_data
, sizeof(pending_data
));
257 if (bytes_read
!= sizeof(pending_data
)) {
258 DCHECK_EQ(bytes_read
, 0U);
261 if (pending_data
< 0)
264 // Convert an AudioBus from deinterleaved float to interleaved integer data.
265 // Store the result in a preallocated |client_buffer_|.
266 audio_bus_
->ToInterleaved(audio_bus_
->frames(),
267 kBitsPerAudioInputSample
/ 8,
268 client_buffer_
.get());
270 // While closing the stream, we may receive buffers whose size is different
271 // from |data_buffer_size|.
272 CHECK_LE(buffer
->params
.size
, audio_bus_size_bytes
);
273 if (buffer
->params
.size
> 0) {
274 if (audio_input_callback_
) {
275 PP_TimeDelta latency
=
276 static_cast<double>(pending_data
) / bytes_per_second_
;
277 audio_input_callback_(client_buffer_
.get(),
278 client_buffer_size_bytes_
,
282 audio_input_callback_0_3_(
283 client_buffer_
.get(), client_buffer_size_bytes_
, user_data_
);
289 int32_t AudioInputResource::CommonOpen(
290 PP_Resource device_ref
,
292 PPB_AudioInput_Callback_0_3 audio_input_callback_0_3
,
293 PPB_AudioInput_Callback audio_input_callback
,
295 scoped_refptr
<TrackedCallback
> callback
) {
296 std::string device_id
;
297 // |device_id| remains empty if |device_ref| is 0, which means the default
299 if (device_ref
!= 0) {
300 thunk::EnterResourceNoLock
<thunk::PPB_DeviceRef_API
> enter_device_ref(
302 if (enter_device_ref
.failed())
303 return PP_ERROR_BADRESOURCE
;
304 device_id
= enter_device_ref
.object()->GetDeviceRefData().id
;
307 if (TrackedCallback::IsPending(open_callback_
))
308 return PP_ERROR_INPROGRESS
;
309 if (open_state_
!= BEFORE_OPEN
)
310 return PP_ERROR_FAILED
;
312 if (!audio_input_callback_0_3
&& !audio_input_callback
)
313 return PP_ERROR_BADARGUMENT
;
314 thunk::EnterResourceNoLock
<thunk::PPB_AudioConfig_API
> enter_config(config
,
316 if (enter_config
.failed())
317 return PP_ERROR_BADARGUMENT
;
320 audio_input_callback_0_3_
= audio_input_callback_0_3
;
321 audio_input_callback_
= audio_input_callback
;
322 user_data_
= user_data
;
323 open_callback_
= callback
;
324 bytes_per_second_
= kAudioInputChannels
* (kBitsPerAudioInputSample
/ 8) *
325 enter_config
.object()->GetSampleRate();
326 sample_frame_count_
= enter_config
.object()->GetSampleFrameCount();
328 PpapiHostMsg_AudioInput_Open
msg(
329 device_id
, enter_config
.object()->GetSampleRate(),
330 enter_config
.object()->GetSampleFrameCount());
331 Call
<PpapiPluginMsg_AudioInput_OpenReply
>(
333 base::Bind(&AudioInputResource::OnPluginMsgOpenReply
,
334 base::Unretained(this)));
335 return PP_OK_COMPLETIONPENDING
;