blob: 96d6cff99014ea6cb7161bceb0d722722b7d3ddf [file] [log] [blame]
[email protected]77b55502012-11-08 22:20:201// 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
Max Morin56fe26e82018-01-28 13:40:427#include <string>
8
[email protected]77b55502012-11-08 22:20:209#include "base/bind.h"
10#include "base/logging.h"
brettw669d47b12015-02-13 21:17:3811#include "base/numerics/safe_conversions.h"
[email protected]77b55502012-11-08 22:20:2012#include "ipc/ipc_platform_file.h"
[email protected]5955c9f2014-07-10 16:44:0113#include "media/base/audio_bus.h"
jrummell37a54c02016-04-22 19:54:2714#include "media/base/audio_parameters.h"
[email protected]77b55502012-11-08 22:20:2015#include "ppapi/c/pp_errors.h"
16#include "ppapi/proxy/ppapi_messages.h"
17#include "ppapi/proxy/resource_message_params.h"
[email protected]eb5960da2013-01-16 23:23:5318#include "ppapi/proxy/serialized_handle.h"
[email protected]77b55502012-11-08 22:20:2019#include "ppapi/shared_impl/ppapi_globals.h"
[email protected]c90ffec72013-07-03 11:57:4520#include "ppapi/shared_impl/ppb_audio_config_shared.h"
[email protected]77b55502012-11-08 22:20:2021#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
26namespace ppapi {
27namespace proxy {
28
[email protected]5955c9f2014-07-10 16:44:0129AudioInputResource::AudioInputResource(Connection connection,
30 PP_Instance instance)
[email protected]77b55502012-11-08 22:20:2031 : PluginResource(connection, instance),
32 open_state_(BEFORE_OPEN),
33 capturing_(false),
34 shared_memory_size_(0),
[email protected]a23089d02014-01-06 23:51:4035 audio_input_callback_0_3_(NULL),
[email protected]77b55502012-11-08 22:20:2036 audio_input_callback_(NULL),
37 user_data_(NULL),
[email protected]c90ffec72013-07-03 11:57:4538 enumeration_helper_(this),
[email protected]5955c9f2014-07-10 16:44:0139 bytes_per_second_(0),
40 sample_frame_count_(0),
41 client_buffer_size_bytes_(0) {
[email protected]77b55502012-11-08 22:20:2042 SendCreate(RENDERER, PpapiHostMsg_AudioInput_Create());
43}
44
45AudioInputResource::~AudioInputResource() {
46 Close();
47}
48
49thunk::PPB_AudioInput_API* AudioInputResource::AsPPB_AudioInput_API() {
50 return this;
51}
52
[email protected]4f01c762012-12-05 02:44:1853void 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
[email protected]4f01c762012-12-05 02:44:1860int32_t AudioInputResource::EnumerateDevices(
61 const PP_ArrayOutput& output,
62 scoped_refptr<TrackedCallback> callback) {
63 return enumeration_helper_.EnumerateDevices(output, callback);
64}
65
66int32_t AudioInputResource::MonitorDeviceChange(
67 PP_MonitorDeviceChangeCallback callback,
68 void* user_data) {
69 return enumeration_helper_.MonitorDeviceChange(callback, user_data);
[email protected]77b55502012-11-08 22:20:2070}
71
[email protected]a23089d02014-01-06 23:51:4072int32_t AudioInputResource::Open0_3(
[email protected]c90ffec72013-07-03 11:57:4573 PP_Resource device_ref,
74 PP_Resource config,
[email protected]a23089d02014-01-06 23:51:4075 PPB_AudioInput_Callback_0_3 audio_input_callback_0_3,
[email protected]c90ffec72013-07-03 11:57:4576 void* user_data,
77 scoped_refptr<TrackedCallback> callback) {
[email protected]a23089d02014-01-06 23:51:4078 return CommonOpen(device_ref, config, audio_input_callback_0_3, NULL,
[email protected]c90ffec72013-07-03 11:57:4579 user_data, callback);
80}
81
[email protected]8917a672013-02-25 17:18:0482int32_t AudioInputResource::Open(PP_Resource device_ref,
[email protected]77b55502012-11-08 22:20:2083 PP_Resource config,
84 PPB_AudioInput_Callback audio_input_callback,
85 void* user_data,
86 scoped_refptr<TrackedCallback> callback) {
[email protected]c90ffec72013-07-03 11:57:4587 return CommonOpen(device_ref, config, NULL, audio_input_callback, user_data,
88 callback);
[email protected]77b55502012-11-08 22:20:2089}
90
91PP_Resource AudioInputResource::GetCurrentConfig() {
92 // AddRef for the caller.
93 if (config_.get())
94 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
95 return config_;
96}
97
98PP_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
118PP_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
139void 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
[email protected]4f01c762012-12-05 02:44:18151void AudioInputResource::LastPluginRefWasDeleted() {
152 enumeration_helper_.LastPluginRefWasDeleted();
[email protected]77b55502012-11-08 22:20:20153}
154
155void 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 =
Alexandr Ilin002a9a22018-06-01 13:32:18166 params.TakeHandleOfTypeAtIndex(1,
167 SerializedHandle::SHARED_MEMORY_REGION);
[email protected]77b55502012-11-08 22:20:20168 CHECK(serialized_shared_memory_handle.IsHandleValid());
169
[email protected]77b55502012-11-08 22:20:20170 open_state_ = OPENED;
Alexandr Ilin002a9a22018-06-01 13:32:18171 SetStreamInfo(base::ReadOnlySharedMemoryRegion::Deserialize(
172 serialized_shared_memory_handle.TakeSharedMemoryRegion()),
[email protected]77b55502012-11-08 22:20:20173 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
183void AudioInputResource::SetStreamInfo(
Alexandr Ilin002a9a22018-06-01 13:32:18184 base::ReadOnlySharedMemoryRegion shared_memory_region,
[email protected]77b55502012-11-08 22:20:20185 base::SyncSocket::Handle socket_handle) {
186 socket_.reset(new base::CancelableSyncSocket(socket_handle));
Alexandr Ilin002a9a22018-06-01 13:32:18187 DCHECK(!shared_memory_mapping_.IsValid());
[email protected]77b55502012-11-08 22:20:20188
Max Morin30996fb2017-09-01 13:28:35189 // 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);
Alexandr Ilin002a9a22018-06-01 13:32:18196 DCHECK_GE(shared_memory_region.GetSize(), shared_memory_size_);
Max Morin30996fb2017-09-01 13:28:35197
[email protected]5955c9f2014-07-10 16:44:01198 // 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.
Alexandr Ilin002a9a22018-06-01 13:32:18200 shared_memory_mapping_ = shared_memory_region.MapAt(0, shared_memory_size_);
201 CHECK(shared_memory_mapping_.IsValid());
[email protected]5955c9f2014-07-10 16:44:01202
203 // Create a new audio bus and wrap the audio data section in shared memory.
Alexandr Ilin002a9a22018-06-01 13:32:18204 const media::AudioInputBuffer* buffer =
205 static_cast<const media::AudioInputBuffer*>(
206 shared_memory_mapping_.memory());
207 audio_bus_ = media::AudioBus::WrapReadOnlyMemory(
[email protected]5955c9f2014-07-10 16:44:01208 kAudioInputChannels, sample_frame_count_, buffer->audio);
209
[email protected]5955c9f2014-07-10 16:44:01210 // 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_]);
[email protected]77b55502012-11-08 22:20:20217
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
227void AudioInputResource::StartThread() {
228 // Don't start the thread unless all our state is set up correctly.
[email protected]a23089d02014-01-06 23:51:40229 if ((!audio_input_callback_0_3_ && !audio_input_callback_) ||
Alexandr Ilin002a9a22018-06-01 13:32:18230 !socket_.get() || !capturing_ || !shared_memory_mapping_.memory() ||
[email protected]5955c9f2014-07-10 16:44:01231 !audio_bus_.get() || !client_buffer_.get()) {
[email protected]77b55502012-11-08 22:20:20232 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
240void 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
250void AudioInputResource::Run() {
251 // The shared memory represents AudioInputBufferParameters and the actual data
[email protected]5955c9f2014-07-10 16:44:01252 // buffer stored as an audio bus.
Alexandr Ilin002a9a22018-06-01 13:32:18253 const media::AudioInputBuffer* buffer =
254 static_cast<const media::AudioInputBuffer*>(
255 shared_memory_mapping_.memory());
[email protected]5955c9f2014-07-10 16:44:01256 const uint32_t audio_bus_size_bytes =
brettw669d47b12015-02-13 21:17:38257 base::checked_cast<uint32_t>(shared_memory_size_ -
258 sizeof(media::AudioInputBufferParameters));
[email protected]77b55502012-11-08 22:20:20259
grunell95e37d62015-10-13 07:39:58260 // This is a constantly increasing counter that is used to verify on the
261 // browser side that buffers are in sync.
avie029c4132015-12-23 06:45:22262 uint32_t buffer_index = 0;
grunell95e37d62015-10-13 07:39:58263
[email protected]5955c9f2014-07-10 16:44:01264 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_|.
Raul Tambreb1da2442019-04-07 18:18:03276 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()));
[email protected]5955c9f2014-07-10 16:44:01280
grunell95e37d62015-10-13 07:39:58281 // 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
[email protected]77b55502012-11-08 22:20:20289 // While closing the stream, we may receive buffers whose size is different
290 // from |data_buffer_size|.
[email protected]5955c9f2014-07-10 16:44:01291 CHECK_LE(buffer->params.size, audio_bus_size_bytes);
[email protected]c90ffec72013-07-03 11:57:45292 if (buffer->params.size > 0) {
293 if (audio_input_callback_) {
294 PP_TimeDelta latency =
295 static_cast<double>(pending_data) / bytes_per_second_;
[email protected]5955c9f2014-07-10 16:44:01296 audio_input_callback_(client_buffer_.get(),
297 client_buffer_size_bytes_,
298 latency,
[email protected]c90ffec72013-07-03 11:57:45299 user_data_);
300 } else {
[email protected]5955c9f2014-07-10 16:44:01301 audio_input_callback_0_3_(
302 client_buffer_.get(), client_buffer_size_bytes_, user_data_);
[email protected]c90ffec72013-07-03 11:57:45303 }
304 }
[email protected]77b55502012-11-08 22:20:20305 }
306}
307
[email protected]c90ffec72013-07-03 11:57:45308int32_t AudioInputResource::CommonOpen(
309 PP_Resource device_ref,
310 PP_Resource config,
[email protected]a23089d02014-01-06 23:51:40311 PPB_AudioInput_Callback_0_3 audio_input_callback_0_3,
[email protected]c90ffec72013-07-03 11:57:45312 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
[email protected]a23089d02014-01-06 23:51:40331 if (!audio_input_callback_0_3 && !audio_input_callback)
[email protected]c90ffec72013-07-03 11:57:45332 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;
[email protected]a23089d02014-01-06 23:51:40339 audio_input_callback_0_3_ = audio_input_callback_0_3;
[email protected]c90ffec72013-07-03 11:57:45340 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();
[email protected]5955c9f2014-07-10 16:44:01345 sample_frame_count_ = enter_config.object()->GetSampleFrameCount();
[email protected]c90ffec72013-07-03 11:57:45346
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}
[email protected]77b55502012-11-08 22:20:20356} // namespace proxy
357} // namespace ppapi