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/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_structs.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"
25 AudioInputResource::AudioInputResource(
26 Connection connection
,
28 : PluginResource(connection
, instance
),
29 open_state_(BEFORE_OPEN
),
31 shared_memory_size_(0),
32 audio_input_callback_(NULL
),
34 ALLOW_THIS_IN_INITIALIZER_LIST(enumeration_helper_(this)) {
35 SendCreate(RENDERER
, PpapiHostMsg_AudioInput_Create());
38 AudioInputResource::~AudioInputResource() {
42 thunk::PPB_AudioInput_API
* AudioInputResource::AsPPB_AudioInput_API() {
46 void AudioInputResource::OnReplyReceived(
47 const ResourceMessageReplyParams
& params
,
48 const IPC::Message
& msg
) {
49 if (!enumeration_helper_
.HandleReply(params
, msg
))
50 PluginResource::OnReplyReceived(params
, msg
);
53 int32_t AudioInputResource::EnumerateDevices0_2(
55 scoped_refptr
<TrackedCallback
> callback
) {
56 return enumeration_helper_
.EnumerateDevices0_2(devices
, callback
);
59 int32_t AudioInputResource::EnumerateDevices(
60 const PP_ArrayOutput
& output
,
61 scoped_refptr
<TrackedCallback
> callback
) {
62 return enumeration_helper_
.EnumerateDevices(output
, callback
);
65 int32_t AudioInputResource::MonitorDeviceChange(
66 PP_MonitorDeviceChangeCallback callback
,
68 return enumeration_helper_
.MonitorDeviceChange(callback
, user_data
);
71 int32_t AudioInputResource::Open(const std::string
& device_id
,
73 PPB_AudioInput_Callback audio_input_callback
,
75 scoped_refptr
<TrackedCallback
> callback
) {
76 if (TrackedCallback::IsPending(open_callback_
))
77 return PP_ERROR_INPROGRESS
;
78 if (open_state_
!= BEFORE_OPEN
)
79 return PP_ERROR_FAILED
;
81 if (!audio_input_callback
)
82 return PP_ERROR_BADARGUMENT
;
83 thunk::EnterResourceNoLock
<thunk::PPB_AudioConfig_API
> enter_config(config
,
85 if (enter_config
.failed())
86 return PP_ERROR_BADARGUMENT
;
89 audio_input_callback_
= audio_input_callback
;
90 user_data_
= user_data
;
91 open_callback_
= callback
;
93 PpapiHostMsg_AudioInput_Open
msg(
94 device_id
, enter_config
.object()->GetSampleRate(),
95 enter_config
.object()->GetSampleFrameCount());
96 Call
<PpapiPluginMsg_AudioInput_OpenReply
>(
98 base::Bind(&AudioInputResource::OnPluginMsgOpenReply
,
99 base::Unretained(this)));
100 return PP_OK_COMPLETIONPENDING
;
103 PP_Resource
AudioInputResource::GetCurrentConfig() {
104 // AddRef for the caller.
106 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_
);
110 PP_Bool
AudioInputResource::StartCapture() {
111 if (open_state_
== CLOSED
|| (open_state_
== BEFORE_OPEN
&&
112 !TrackedCallback::IsPending(open_callback_
))) {
119 // Return directly if the audio input device hasn't been opened. Capturing
120 // will be started once the open operation is completed.
121 if (open_state_
== BEFORE_OPEN
)
126 Post(RENDERER
, PpapiHostMsg_AudioInput_StartOrStop(true));
130 PP_Bool
AudioInputResource::StopCapture() {
131 if (open_state_
== CLOSED
)
136 // If the audio input device hasn't been opened, set |capturing_| to false and
138 if (open_state_
== BEFORE_OPEN
) {
143 Post(RENDERER
, PpapiHostMsg_AudioInput_StartOrStop(false));
151 void AudioInputResource::Close() {
152 if (open_state_
== CLOSED
)
155 open_state_
= CLOSED
;
156 Post(RENDERER
, PpapiHostMsg_AudioInput_Close());
159 if (TrackedCallback::IsPending(open_callback_
))
160 open_callback_
->PostAbort();
163 void AudioInputResource::LastPluginRefWasDeleted() {
164 enumeration_helper_
.LastPluginRefWasDeleted();
167 void AudioInputResource::OnPluginMsgOpenReply(
168 const ResourceMessageReplyParams
& params
) {
169 if (open_state_
== BEFORE_OPEN
&& params
.result() == PP_OK
) {
170 IPC::PlatformFileForTransit socket_handle_for_transit
=
171 IPC::InvalidPlatformFileForTransit();
172 params
.TakeSocketHandleAtIndex(0, &socket_handle_for_transit
);
173 base::SyncSocket::Handle socket_handle
=
174 IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit
);
175 CHECK(socket_handle
!= base::SyncSocket::kInvalidHandle
);
177 SerializedHandle serialized_shared_memory_handle
=
178 params
.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY
);
179 CHECK(serialized_shared_memory_handle
.IsHandleValid());
181 // See the comment in pepper_audio_input_host.cc about how we must call
182 // TotalSharedMemorySizeInBytes to get the actual size of the buffer. Here,
183 // we must call PacketSizeInBytes to get back the size of the audio buffer,
184 // excluding the bytes that audio uses for book-keeping.
185 size_t shared_memory_size
= media::PacketSizeInBytes(
186 serialized_shared_memory_handle
.size());
188 open_state_
= OPENED
;
189 SetStreamInfo(serialized_shared_memory_handle
.shmem(), shared_memory_size
,
195 // The callback may have been aborted by Close().
196 if (TrackedCallback::IsPending(open_callback_
))
197 open_callback_
->Run(params
.result());
200 void AudioInputResource::SetStreamInfo(
201 base::SharedMemoryHandle shared_memory_handle
,
202 size_t shared_memory_size
,
203 base::SyncSocket::Handle socket_handle
) {
204 socket_
.reset(new base::CancelableSyncSocket(socket_handle
));
205 shared_memory_
.reset(new base::SharedMemory(shared_memory_handle
, false));
206 shared_memory_size_
= shared_memory_size
;
208 if (!shared_memory_
->Map(shared_memory_size_
)) {
209 PpapiGlobals::Get()->LogWithSource(pp_instance(), PP_LOGLEVEL_WARNING
, "",
210 "Failed to map shared memory for PPB_AudioInput_Shared.");
213 // There is a pending capture request before SetStreamInfo().
215 // Set |capturing_| to false so that the state looks consistent to
216 // StartCapture(), which will reset it to true.
222 void AudioInputResource::StartThread() {
223 // Don't start the thread unless all our state is set up correctly.
224 if (!audio_input_callback_
|| !socket_
.get() || !capturing_
||
225 !shared_memory_
->memory()) {
228 DCHECK(!audio_input_thread_
.get());
229 audio_input_thread_
.reset(new base::DelegateSimpleThread(
230 this, "plugin_audio_input_thread"));
231 audio_input_thread_
->Start();
234 void AudioInputResource::StopThread() {
235 // Shut down the socket to escape any hanging |Receive|s.
238 if (audio_input_thread_
.get()) {
239 audio_input_thread_
->Join();
240 audio_input_thread_
.reset();
244 void AudioInputResource::Run() {
245 // The shared memory represents AudioInputBufferParameters and the actual data
247 media::AudioInputBuffer
* buffer
=
248 static_cast<media::AudioInputBuffer
*>(shared_memory_
->memory());
249 uint32_t data_buffer_size
=
250 shared_memory_size_
- sizeof(media::AudioInputBufferParameters
);
253 while (sizeof(pending_data
) == socket_
->Receive(&pending_data
,
254 sizeof(pending_data
)) &&
256 // While closing the stream, we may receive buffers whose size is different
257 // from |data_buffer_size|.
258 CHECK_LE(buffer
->params
.size
, data_buffer_size
);
259 if (buffer
->params
.size
> 0)
260 audio_input_callback_(&buffer
->audio
[0], buffer
->params
.size
, user_data_
);