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 <string>
8
9 #include "base/bind.h"
10 #include "base/check_op.h"
11 #include "base/numerics/safe_conversions.h"
12 #include "ipc/ipc_platform_file.h"
13 #include "media/base/audio_bus.h"
14 #include "media/base/audio_parameters.h"
15 #include "ppapi/c/pp_errors.h"
16 #include "ppapi/proxy/ppapi_messages.h"
17 #include "ppapi/proxy/resource_message_params.h"
18 #include "ppapi/proxy/serialized_handle.h"
19 #include "ppapi/shared_impl/ppapi_globals.h"
20 #include "ppapi/shared_impl/ppb_audio_config_shared.h"
21 #include "ppapi/shared_impl/resource_tracker.h"
22 #include "ppapi/shared_impl/tracked_callback.h"
23 #include "ppapi/thunk/enter.h"
24 #include "ppapi/thunk/ppb_audio_config_api.h"
25
26 namespace ppapi {
27 namespace proxy {
28
AudioInputResource(Connection connection,PP_Instance instance)29 AudioInputResource::AudioInputResource(Connection connection,
30 PP_Instance instance)
31 : PluginResource(connection, instance),
32 open_state_(BEFORE_OPEN),
33 capturing_(false),
34 shared_memory_size_(0),
35 audio_input_callback_0_3_(NULL),
36 audio_input_callback_(NULL),
37 user_data_(NULL),
38 enumeration_helper_(this),
39 bytes_per_second_(0),
40 sample_frame_count_(0),
41 client_buffer_size_bytes_(0) {
42 SendCreate(RENDERER, PpapiHostMsg_AudioInput_Create());
43 }
44
~AudioInputResource()45 AudioInputResource::~AudioInputResource() {
46 Close();
47 }
48
AsPPB_AudioInput_API()49 thunk::PPB_AudioInput_API* AudioInputResource::AsPPB_AudioInput_API() {
50 return this;
51 }
52
OnReplyReceived(const ResourceMessageReplyParams & params,const IPC::Message & msg)53 void AudioInputResource::OnReplyReceived(
54 const ResourceMessageReplyParams& params,
55 const IPC::Message& msg) {
56 if (!enumeration_helper_.HandleReply(params, msg))
57 PluginResource::OnReplyReceived(params, msg);
58 }
59
EnumerateDevices(const PP_ArrayOutput & output,scoped_refptr<TrackedCallback> callback)60 int32_t AudioInputResource::EnumerateDevices(
61 const PP_ArrayOutput& output,
62 scoped_refptr<TrackedCallback> callback) {
63 return enumeration_helper_.EnumerateDevices(output, callback);
64 }
65
MonitorDeviceChange(PP_MonitorDeviceChangeCallback callback,void * user_data)66 int32_t AudioInputResource::MonitorDeviceChange(
67 PP_MonitorDeviceChangeCallback callback,
68 void* user_data) {
69 return enumeration_helper_.MonitorDeviceChange(callback, user_data);
70 }
71
Open0_3(PP_Resource device_ref,PP_Resource config,PPB_AudioInput_Callback_0_3 audio_input_callback_0_3,void * user_data,scoped_refptr<TrackedCallback> callback)72 int32_t AudioInputResource::Open0_3(
73 PP_Resource device_ref,
74 PP_Resource config,
75 PPB_AudioInput_Callback_0_3 audio_input_callback_0_3,
76 void* user_data,
77 scoped_refptr<TrackedCallback> callback) {
78 return CommonOpen(device_ref, config, audio_input_callback_0_3, NULL,
79 user_data, callback);
80 }
81
Open(PP_Resource device_ref,PP_Resource config,PPB_AudioInput_Callback audio_input_callback,void * user_data,scoped_refptr<TrackedCallback> callback)82 int32_t AudioInputResource::Open(PP_Resource device_ref,
83 PP_Resource config,
84 PPB_AudioInput_Callback audio_input_callback,
85 void* user_data,
86 scoped_refptr<TrackedCallback> callback) {
87 return CommonOpen(device_ref, config, NULL, audio_input_callback, user_data,
88 callback);
89 }
90
GetCurrentConfig()91 PP_Resource AudioInputResource::GetCurrentConfig() {
92 // AddRef for the caller.
93 if (config_.get())
94 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
95 return config_;
96 }
97
StartCapture()98 PP_Bool AudioInputResource::StartCapture() {
99 if (open_state_ == CLOSED || (open_state_ == BEFORE_OPEN &&
100 !TrackedCallback::IsPending(open_callback_))) {
101 return PP_FALSE;
102 }
103 if (capturing_)
104 return PP_TRUE;
105
106 capturing_ = true;
107 // Return directly if the audio input device hasn't been opened. Capturing
108 // will be started once the open operation is completed.
109 if (open_state_ == BEFORE_OPEN)
110 return PP_TRUE;
111
112 StartThread();
113
114 Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(true));
115 return PP_TRUE;
116 }
117
StopCapture()118 PP_Bool AudioInputResource::StopCapture() {
119 if (open_state_ == CLOSED)
120 return PP_FALSE;
121 if (!capturing_)
122 return PP_TRUE;
123
124 // If the audio input device hasn't been opened, set |capturing_| to false and
125 // return directly.
126 if (open_state_ == BEFORE_OPEN) {
127 capturing_ = false;
128 return PP_TRUE;
129 }
130
131 Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(false));
132
133 StopThread();
134 capturing_ = false;
135
136 return PP_TRUE;
137 }
138
Close()139 void AudioInputResource::Close() {
140 if (open_state_ == CLOSED)
141 return;
142
143 open_state_ = CLOSED;
144 Post(RENDERER, PpapiHostMsg_AudioInput_Close());
145 StopThread();
146
147 if (TrackedCallback::IsPending(open_callback_))
148 open_callback_->PostAbort();
149 }
150
LastPluginRefWasDeleted()151 void AudioInputResource::LastPluginRefWasDeleted() {
152 enumeration_helper_.LastPluginRefWasDeleted();
153 }
154
OnPluginMsgOpenReply(const ResourceMessageReplyParams & params)155 void AudioInputResource::OnPluginMsgOpenReply(
156 const ResourceMessageReplyParams& params) {
157 if (open_state_ == BEFORE_OPEN && params.result() == PP_OK) {
158 IPC::PlatformFileForTransit socket_handle_for_transit =
159 IPC::InvalidPlatformFileForTransit();
160 params.TakeSocketHandleAtIndex(0, &socket_handle_for_transit);
161 base::SyncSocket::Handle socket_handle =
162 IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit);
163 CHECK(socket_handle != base::SyncSocket::kInvalidHandle);
164
165 SerializedHandle serialized_shared_memory_handle =
166 params.TakeHandleOfTypeAtIndex(1,
167 SerializedHandle::SHARED_MEMORY_REGION);
168 CHECK(serialized_shared_memory_handle.IsHandleValid());
169
170 open_state_ = OPENED;
171 SetStreamInfo(base::ReadOnlySharedMemoryRegion::Deserialize(
172 serialized_shared_memory_handle.TakeSharedMemoryRegion()),
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::ReadOnlySharedMemoryRegion shared_memory_region,base::SyncSocket::Handle socket_handle)183 void AudioInputResource::SetStreamInfo(
184 base::ReadOnlySharedMemoryRegion shared_memory_region,
185 base::SyncSocket::Handle socket_handle) {
186 socket_.reset(new base::CancelableSyncSocket(socket_handle));
187 DCHECK(!shared_memory_mapping_.IsValid());
188
189 // Ensure that the allocated memory is enough for the audio bus and buffer
190 // parameters. Note that there might be slightly more allocated memory as
191 // some shared memory implementations round up to the closest 2^n when
192 // allocating.
193 // Example: DCHECK_GE(8208, 8192 + 16) for |sample_frame_count_| = 2048.
194 shared_memory_size_ = media::ComputeAudioInputBufferSize(
195 kAudioInputChannels, sample_frame_count_, 1u);
196 DCHECK_GE(shared_memory_region.GetSize(), shared_memory_size_);
197
198 // If we fail to map the shared memory into the caller's address space we
199 // might as well fail here since nothing will work if this is the case.
200 shared_memory_mapping_ = shared_memory_region.MapAt(0, shared_memory_size_);
201 CHECK(shared_memory_mapping_.IsValid());
202
203 // Create a new audio bus and wrap the audio data section in shared memory.
204 const media::AudioInputBuffer* buffer =
205 static_cast<const media::AudioInputBuffer*>(
206 shared_memory_mapping_.memory());
207 audio_bus_ = media::AudioBus::WrapReadOnlyMemory(
208 kAudioInputChannels, sample_frame_count_, buffer->audio);
209
210 // Create an extra integer audio buffer for user audio data callbacks.
211 // Data in shared memory will be copied to this buffer, after interleaving
212 // and truncation, before each input callback to match the format expected
213 // by the client.
214 client_buffer_size_bytes_ = audio_bus_->frames() * audio_bus_->channels() *
215 kBitsPerAudioInputSample / 8;
216 client_buffer_.reset(new uint8_t[client_buffer_size_bytes_]);
217
218 // There is a pending capture request before SetStreamInfo().
219 if (capturing_) {
220 // Set |capturing_| to false so that the state looks consistent to
221 // StartCapture(), which will reset it to true.
222 capturing_ = false;
223 StartCapture();
224 }
225 }
226
StartThread()227 void AudioInputResource::StartThread() {
228 // Don't start the thread unless all our state is set up correctly.
229 if ((!audio_input_callback_0_3_ && !audio_input_callback_) ||
230 !socket_.get() || !capturing_ || !shared_memory_mapping_.memory() ||
231 !audio_bus_.get() || !client_buffer_.get()) {
232 return;
233 }
234 DCHECK(!audio_input_thread_.get());
235 audio_input_thread_.reset(new base::DelegateSimpleThread(
236 this, "plugin_audio_input_thread"));
237 audio_input_thread_->Start();
238 }
239
StopThread()240 void AudioInputResource::StopThread() {
241 // Shut down the socket to escape any hanging |Receive|s.
242 if (socket_.get())
243 socket_->Shutdown();
244 if (audio_input_thread_.get()) {
245 audio_input_thread_->Join();
246 audio_input_thread_.reset();
247 }
248 }
249
Run()250 void AudioInputResource::Run() {
251 // The shared memory represents AudioInputBufferParameters and the actual data
252 // buffer stored as an audio bus.
253 const media::AudioInputBuffer* buffer =
254 static_cast<const media::AudioInputBuffer*>(
255 shared_memory_mapping_.memory());
256 const uint32_t audio_bus_size_bytes =
257 base::checked_cast<uint32_t>(shared_memory_size_ -
258 sizeof(media::AudioInputBufferParameters));
259
260 // This is a constantly increasing counter that is used to verify on the
261 // browser side that buffers are in sync.
262 uint32_t buffer_index = 0;
263
264 while (true) {
265 int pending_data = 0;
266 size_t bytes_read = socket_->Receive(&pending_data, sizeof(pending_data));
267 if (bytes_read != sizeof(pending_data)) {
268 DCHECK_EQ(bytes_read, 0U);
269 break;
270 }
271 if (pending_data < 0)
272 break;
273
274 // Convert an AudioBus from deinterleaved float to interleaved integer data.
275 // Store the result in a preallocated |client_buffer_|.
276 static_assert(kBitsPerAudioInputSample == 16,
277 "ToInterleaved expects 2 bytes.");
278 audio_bus_->ToInterleaved<media::SignedInt16SampleTypeTraits>(
279 audio_bus_->frames(), reinterpret_cast<int16_t*>(client_buffer_.get()));
280
281 // Inform other side that we have read the data from the shared memory.
282 ++buffer_index;
283 size_t bytes_sent = socket_->Send(&buffer_index, sizeof(buffer_index));
284 if (bytes_sent != sizeof(buffer_index)) {
285 DCHECK_EQ(bytes_sent, 0U);
286 break;
287 }
288
289 // While closing the stream, we may receive buffers whose size is different
290 // from |data_buffer_size|.
291 CHECK_LE(buffer->params.size, audio_bus_size_bytes);
292 if (buffer->params.size > 0) {
293 if (audio_input_callback_) {
294 PP_TimeDelta latency =
295 static_cast<double>(pending_data) / bytes_per_second_;
296 audio_input_callback_(client_buffer_.get(),
297 client_buffer_size_bytes_,
298 latency,
299 user_data_);
300 } else {
301 audio_input_callback_0_3_(
302 client_buffer_.get(), client_buffer_size_bytes_, user_data_);
303 }
304 }
305 }
306 }
307
CommonOpen(PP_Resource device_ref,PP_Resource config,PPB_AudioInput_Callback_0_3 audio_input_callback_0_3,PPB_AudioInput_Callback audio_input_callback,void * user_data,scoped_refptr<TrackedCallback> callback)308 int32_t AudioInputResource::CommonOpen(
309 PP_Resource device_ref,
310 PP_Resource config,
311 PPB_AudioInput_Callback_0_3 audio_input_callback_0_3,
312 PPB_AudioInput_Callback audio_input_callback,
313 void* user_data,
314 scoped_refptr<TrackedCallback> callback) {
315 std::string device_id;
316 // |device_id| remains empty if |device_ref| is 0, which means the default
317 // device.
318 if (device_ref != 0) {
319 thunk::EnterResourceNoLock<thunk::PPB_DeviceRef_API> enter_device_ref(
320 device_ref, true);
321 if (enter_device_ref.failed())
322 return PP_ERROR_BADRESOURCE;
323 device_id = enter_device_ref.object()->GetDeviceRefData().id;
324 }
325
326 if (TrackedCallback::IsPending(open_callback_))
327 return PP_ERROR_INPROGRESS;
328 if (open_state_ != BEFORE_OPEN)
329 return PP_ERROR_FAILED;
330
331 if (!audio_input_callback_0_3 && !audio_input_callback)
332 return PP_ERROR_BADARGUMENT;
333 thunk::EnterResourceNoLock<thunk::PPB_AudioConfig_API> enter_config(config,
334 true);
335 if (enter_config.failed())
336 return PP_ERROR_BADARGUMENT;
337
338 config_ = config;
339 audio_input_callback_0_3_ = audio_input_callback_0_3;
340 audio_input_callback_ = audio_input_callback;
341 user_data_ = user_data;
342 open_callback_ = callback;
343 bytes_per_second_ = kAudioInputChannels * (kBitsPerAudioInputSample / 8) *
344 enter_config.object()->GetSampleRate();
345 sample_frame_count_ = enter_config.object()->GetSampleFrameCount();
346
347 PpapiHostMsg_AudioInput_Open msg(
348 device_id, enter_config.object()->GetSampleRate(),
349 enter_config.object()->GetSampleFrameCount());
350 Call<PpapiPluginMsg_AudioInput_OpenReply>(
351 RENDERER, msg,
352 base::Bind(&AudioInputResource::OnPluginMsgOpenReply,
353 base::Unretained(this)));
354 return PP_OK_COMPLETIONPENDING;
355 }
356 } // namespace proxy
357 } // namespace ppapi
358