blob: 40aa644cd74f9c09552615776292b3993a7a1962 [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
Peter Boströmfb60ea02021-04-05 21:06:127#include <memory>
Max Morin56fe26e82018-01-28 13:40:428#include <string>
9
[email protected]77b55502012-11-08 22:20:2010#include "base/bind.h"
Hans Wennborg708fa822020-04-27 17:23:1511#include "base/check_op.h"
brettw669d47b12015-02-13 21:17:3812#include "base/numerics/safe_conversions.h"
[email protected]77b55502012-11-08 22:20:2013#include "ipc/ipc_platform_file.h"
[email protected]5955c9f2014-07-10 16:44:0114#include "media/base/audio_bus.h"
jrummell37a54c02016-04-22 19:54:2715#include "media/base/audio_parameters.h"
[email protected]77b55502012-11-08 22:20:2016#include "ppapi/c/pp_errors.h"
17#include "ppapi/proxy/ppapi_messages.h"
18#include "ppapi/proxy/resource_message_params.h"
[email protected]eb5960da2013-01-16 23:23:5319#include "ppapi/proxy/serialized_handle.h"
[email protected]77b55502012-11-08 22:20:2020#include "ppapi/shared_impl/ppapi_globals.h"
[email protected]c90ffec72013-07-03 11:57:4521#include "ppapi/shared_impl/ppb_audio_config_shared.h"
[email protected]77b55502012-11-08 22:20:2022#include "ppapi/shared_impl/resource_tracker.h"
23#include "ppapi/shared_impl/tracked_callback.h"
24#include "ppapi/thunk/enter.h"
25#include "ppapi/thunk/ppb_audio_config_api.h"
26
27namespace ppapi {
28namespace proxy {
29
[email protected]5955c9f2014-07-10 16:44:0130AudioInputResource::AudioInputResource(Connection connection,
31 PP_Instance instance)
[email protected]77b55502012-11-08 22:20:2032 : PluginResource(connection, instance),
33 open_state_(BEFORE_OPEN),
34 capturing_(false),
35 shared_memory_size_(0),
[email protected]a23089d02014-01-06 23:51:4036 audio_input_callback_0_3_(NULL),
[email protected]77b55502012-11-08 22:20:2037 audio_input_callback_(NULL),
38 user_data_(NULL),
[email protected]c90ffec72013-07-03 11:57:4539 enumeration_helper_(this),
[email protected]5955c9f2014-07-10 16:44:0140 bytes_per_second_(0),
41 sample_frame_count_(0),
42 client_buffer_size_bytes_(0) {
[email protected]77b55502012-11-08 22:20:2043 SendCreate(RENDERER, PpapiHostMsg_AudioInput_Create());
44}
45
46AudioInputResource::~AudioInputResource() {
47 Close();
48}
49
50thunk::PPB_AudioInput_API* AudioInputResource::AsPPB_AudioInput_API() {
51 return this;
52}
53
[email protected]4f01c762012-12-05 02:44:1854void AudioInputResource::OnReplyReceived(
55 const ResourceMessageReplyParams& params,
56 const IPC::Message& msg) {
57 if (!enumeration_helper_.HandleReply(params, msg))
58 PluginResource::OnReplyReceived(params, msg);
59}
60
[email protected]4f01c762012-12-05 02:44:1861int32_t AudioInputResource::EnumerateDevices(
62 const PP_ArrayOutput& output,
63 scoped_refptr<TrackedCallback> callback) {
64 return enumeration_helper_.EnumerateDevices(output, callback);
65}
66
67int32_t AudioInputResource::MonitorDeviceChange(
68 PP_MonitorDeviceChangeCallback callback,
69 void* user_data) {
70 return enumeration_helper_.MonitorDeviceChange(callback, user_data);
[email protected]77b55502012-11-08 22:20:2071}
72
[email protected]a23089d02014-01-06 23:51:4073int32_t AudioInputResource::Open0_3(
[email protected]c90ffec72013-07-03 11:57:4574 PP_Resource device_ref,
75 PP_Resource config,
[email protected]a23089d02014-01-06 23:51:4076 PPB_AudioInput_Callback_0_3 audio_input_callback_0_3,
[email protected]c90ffec72013-07-03 11:57:4577 void* user_data,
78 scoped_refptr<TrackedCallback> callback) {
[email protected]a23089d02014-01-06 23:51:4079 return CommonOpen(device_ref, config, audio_input_callback_0_3, NULL,
[email protected]c90ffec72013-07-03 11:57:4580 user_data, callback);
81}
82
[email protected]8917a672013-02-25 17:18:0483int32_t AudioInputResource::Open(PP_Resource device_ref,
[email protected]77b55502012-11-08 22:20:2084 PP_Resource config,
85 PPB_AudioInput_Callback audio_input_callback,
86 void* user_data,
87 scoped_refptr<TrackedCallback> callback) {
[email protected]c90ffec72013-07-03 11:57:4588 return CommonOpen(device_ref, config, NULL, audio_input_callback, user_data,
89 callback);
[email protected]77b55502012-11-08 22:20:2090}
91
92PP_Resource AudioInputResource::GetCurrentConfig() {
93 // AddRef for the caller.
94 if (config_.get())
95 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
96 return config_;
97}
98
99PP_Bool AudioInputResource::StartCapture() {
100 if (open_state_ == CLOSED || (open_state_ == BEFORE_OPEN &&
101 !TrackedCallback::IsPending(open_callback_))) {
102 return PP_FALSE;
103 }
104 if (capturing_)
105 return PP_TRUE;
106
107 capturing_ = true;
108 // Return directly if the audio input device hasn't been opened. Capturing
109 // will be started once the open operation is completed.
110 if (open_state_ == BEFORE_OPEN)
111 return PP_TRUE;
112
113 StartThread();
114
115 Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(true));
116 return PP_TRUE;
117}
118
119PP_Bool AudioInputResource::StopCapture() {
120 if (open_state_ == CLOSED)
121 return PP_FALSE;
122 if (!capturing_)
123 return PP_TRUE;
124
125 // If the audio input device hasn't been opened, set |capturing_| to false and
126 // return directly.
127 if (open_state_ == BEFORE_OPEN) {
128 capturing_ = false;
129 return PP_TRUE;
130 }
131
132 Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(false));
133
134 StopThread();
135 capturing_ = false;
136
137 return PP_TRUE;
138}
139
140void AudioInputResource::Close() {
141 if (open_state_ == CLOSED)
142 return;
143
144 open_state_ = CLOSED;
145 Post(RENDERER, PpapiHostMsg_AudioInput_Close());
146 StopThread();
147
148 if (TrackedCallback::IsPending(open_callback_))
149 open_callback_->PostAbort();
150}
151
[email protected]4f01c762012-12-05 02:44:18152void AudioInputResource::LastPluginRefWasDeleted() {
153 enumeration_helper_.LastPluginRefWasDeleted();
[email protected]77b55502012-11-08 22:20:20154}
155
156void AudioInputResource::OnPluginMsgOpenReply(
157 const ResourceMessageReplyParams& params) {
158 if (open_state_ == BEFORE_OPEN && params.result() == PP_OK) {
159 IPC::PlatformFileForTransit socket_handle_for_transit =
160 IPC::InvalidPlatformFileForTransit();
161 params.TakeSocketHandleAtIndex(0, &socket_handle_for_transit);
162 base::SyncSocket::Handle socket_handle =
163 IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit);
164 CHECK(socket_handle != base::SyncSocket::kInvalidHandle);
165
166 SerializedHandle serialized_shared_memory_handle =
Alexandr Ilin002a9a22018-06-01 13:32:18167 params.TakeHandleOfTypeAtIndex(1,
168 SerializedHandle::SHARED_MEMORY_REGION);
[email protected]77b55502012-11-08 22:20:20169 CHECK(serialized_shared_memory_handle.IsHandleValid());
170
[email protected]77b55502012-11-08 22:20:20171 open_state_ = OPENED;
Alexandr Ilin002a9a22018-06-01 13:32:18172 SetStreamInfo(base::ReadOnlySharedMemoryRegion::Deserialize(
173 serialized_shared_memory_handle.TakeSharedMemoryRegion()),
[email protected]77b55502012-11-08 22:20:20174 socket_handle);
175 } else {
176 capturing_ = false;
177 }
178
179 // The callback may have been aborted by Close().
180 if (TrackedCallback::IsPending(open_callback_))
181 open_callback_->Run(params.result());
182}
183
184void AudioInputResource::SetStreamInfo(
Alexandr Ilin002a9a22018-06-01 13:32:18185 base::ReadOnlySharedMemoryRegion shared_memory_region,
[email protected]77b55502012-11-08 22:20:20186 base::SyncSocket::Handle socket_handle) {
Peter Boströmfb60ea02021-04-05 21:06:12187 socket_ = std::make_unique<base::CancelableSyncSocket>(socket_handle);
Alexandr Ilin002a9a22018-06-01 13:32:18188 DCHECK(!shared_memory_mapping_.IsValid());
[email protected]77b55502012-11-08 22:20:20189
Max Morin30996fb2017-09-01 13:28:35190 // Ensure that the allocated memory is enough for the audio bus and buffer
191 // parameters. Note that there might be slightly more allocated memory as
192 // some shared memory implementations round up to the closest 2^n when
193 // allocating.
194 // Example: DCHECK_GE(8208, 8192 + 16) for |sample_frame_count_| = 2048.
195 shared_memory_size_ = media::ComputeAudioInputBufferSize(
196 kAudioInputChannels, sample_frame_count_, 1u);
Alexandr Ilin002a9a22018-06-01 13:32:18197 DCHECK_GE(shared_memory_region.GetSize(), shared_memory_size_);
Max Morin30996fb2017-09-01 13:28:35198
[email protected]5955c9f2014-07-10 16:44:01199 // If we fail to map the shared memory into the caller's address space we
200 // might as well fail here since nothing will work if this is the case.
Alexandr Ilin002a9a22018-06-01 13:32:18201 shared_memory_mapping_ = shared_memory_region.MapAt(0, shared_memory_size_);
202 CHECK(shared_memory_mapping_.IsValid());
[email protected]5955c9f2014-07-10 16:44:01203
204 // Create a new audio bus and wrap the audio data section in shared memory.
Alexandr Ilin002a9a22018-06-01 13:32:18205 const media::AudioInputBuffer* buffer =
206 static_cast<const media::AudioInputBuffer*>(
207 shared_memory_mapping_.memory());
208 audio_bus_ = media::AudioBus::WrapReadOnlyMemory(
[email protected]5955c9f2014-07-10 16:44:01209 kAudioInputChannels, sample_frame_count_, buffer->audio);
210
[email protected]5955c9f2014-07-10 16:44:01211 // Create an extra integer audio buffer for user audio data callbacks.
212 // Data in shared memory will be copied to this buffer, after interleaving
213 // and truncation, before each input callback to match the format expected
214 // by the client.
215 client_buffer_size_bytes_ = audio_bus_->frames() * audio_bus_->channels() *
216 kBitsPerAudioInputSample / 8;
217 client_buffer_.reset(new uint8_t[client_buffer_size_bytes_]);
[email protected]77b55502012-11-08 22:20:20218
219 // There is a pending capture request before SetStreamInfo().
220 if (capturing_) {
221 // Set |capturing_| to false so that the state looks consistent to
222 // StartCapture(), which will reset it to true.
223 capturing_ = false;
224 StartCapture();
225 }
226}
227
228void AudioInputResource::StartThread() {
229 // Don't start the thread unless all our state is set up correctly.
[email protected]a23089d02014-01-06 23:51:40230 if ((!audio_input_callback_0_3_ && !audio_input_callback_) ||
Alexandr Ilin002a9a22018-06-01 13:32:18231 !socket_.get() || !capturing_ || !shared_memory_mapping_.memory() ||
[email protected]5955c9f2014-07-10 16:44:01232 !audio_bus_.get() || !client_buffer_.get()) {
[email protected]77b55502012-11-08 22:20:20233 return;
234 }
235 DCHECK(!audio_input_thread_.get());
Peter Boströmfb60ea02021-04-05 21:06:12236 audio_input_thread_ = std::make_unique<base::DelegateSimpleThread>(
237 this, "plugin_audio_input_thread");
[email protected]77b55502012-11-08 22:20:20238 audio_input_thread_->Start();
239}
240
241void AudioInputResource::StopThread() {
242 // Shut down the socket to escape any hanging |Receive|s.
243 if (socket_.get())
244 socket_->Shutdown();
245 if (audio_input_thread_.get()) {
246 audio_input_thread_->Join();
247 audio_input_thread_.reset();
248 }
249}
250
251void AudioInputResource::Run() {
252 // The shared memory represents AudioInputBufferParameters and the actual data
[email protected]5955c9f2014-07-10 16:44:01253 // buffer stored as an audio bus.
Alexandr Ilin002a9a22018-06-01 13:32:18254 const media::AudioInputBuffer* buffer =
255 static_cast<const media::AudioInputBuffer*>(
256 shared_memory_mapping_.memory());
[email protected]5955c9f2014-07-10 16:44:01257 const uint32_t audio_bus_size_bytes =
brettw669d47b12015-02-13 21:17:38258 base::checked_cast<uint32_t>(shared_memory_size_ -
259 sizeof(media::AudioInputBufferParameters));
[email protected]77b55502012-11-08 22:20:20260
grunell95e37d62015-10-13 07:39:58261 // This is a constantly increasing counter that is used to verify on the
262 // browser side that buffers are in sync.
avie029c4132015-12-23 06:45:22263 uint32_t buffer_index = 0;
grunell95e37d62015-10-13 07:39:58264
[email protected]5955c9f2014-07-10 16:44:01265 while (true) {
266 int pending_data = 0;
267 size_t bytes_read = socket_->Receive(&pending_data, sizeof(pending_data));
268 if (bytes_read != sizeof(pending_data)) {
269 DCHECK_EQ(bytes_read, 0U);
270 break;
271 }
272 if (pending_data < 0)
273 break;
274
275 // Convert an AudioBus from deinterleaved float to interleaved integer data.
276 // Store the result in a preallocated |client_buffer_|.
Raul Tambreb1da2442019-04-07 18:18:03277 static_assert(kBitsPerAudioInputSample == 16,
278 "ToInterleaved expects 2 bytes.");
279 audio_bus_->ToInterleaved<media::SignedInt16SampleTypeTraits>(
280 audio_bus_->frames(), reinterpret_cast<int16_t*>(client_buffer_.get()));
[email protected]5955c9f2014-07-10 16:44:01281
grunell95e37d62015-10-13 07:39:58282 // Inform other side that we have read the data from the shared memory.
283 ++buffer_index;
284 size_t bytes_sent = socket_->Send(&buffer_index, sizeof(buffer_index));
285 if (bytes_sent != sizeof(buffer_index)) {
286 DCHECK_EQ(bytes_sent, 0U);
287 break;
288 }
289
[email protected]77b55502012-11-08 22:20:20290 // While closing the stream, we may receive buffers whose size is different
291 // from |data_buffer_size|.
[email protected]5955c9f2014-07-10 16:44:01292 CHECK_LE(buffer->params.size, audio_bus_size_bytes);
[email protected]c90ffec72013-07-03 11:57:45293 if (buffer->params.size > 0) {
294 if (audio_input_callback_) {
295 PP_TimeDelta latency =
296 static_cast<double>(pending_data) / bytes_per_second_;
[email protected]5955c9f2014-07-10 16:44:01297 audio_input_callback_(client_buffer_.get(),
298 client_buffer_size_bytes_,
299 latency,
[email protected]c90ffec72013-07-03 11:57:45300 user_data_);
301 } else {
[email protected]5955c9f2014-07-10 16:44:01302 audio_input_callback_0_3_(
303 client_buffer_.get(), client_buffer_size_bytes_, user_data_);
[email protected]c90ffec72013-07-03 11:57:45304 }
305 }
[email protected]77b55502012-11-08 22:20:20306 }
307}
308
[email protected]c90ffec72013-07-03 11:57:45309int32_t AudioInputResource::CommonOpen(
310 PP_Resource device_ref,
311 PP_Resource config,
[email protected]a23089d02014-01-06 23:51:40312 PPB_AudioInput_Callback_0_3 audio_input_callback_0_3,
[email protected]c90ffec72013-07-03 11:57:45313 PPB_AudioInput_Callback audio_input_callback,
314 void* user_data,
315 scoped_refptr<TrackedCallback> callback) {
316 std::string device_id;
317 // |device_id| remains empty if |device_ref| is 0, which means the default
318 // device.
319 if (device_ref != 0) {
320 thunk::EnterResourceNoLock<thunk::PPB_DeviceRef_API> enter_device_ref(
321 device_ref, true);
322 if (enter_device_ref.failed())
323 return PP_ERROR_BADRESOURCE;
324 device_id = enter_device_ref.object()->GetDeviceRefData().id;
325 }
326
327 if (TrackedCallback::IsPending(open_callback_))
328 return PP_ERROR_INPROGRESS;
329 if (open_state_ != BEFORE_OPEN)
330 return PP_ERROR_FAILED;
331
[email protected]a23089d02014-01-06 23:51:40332 if (!audio_input_callback_0_3 && !audio_input_callback)
[email protected]c90ffec72013-07-03 11:57:45333 return PP_ERROR_BADARGUMENT;
334 thunk::EnterResourceNoLock<thunk::PPB_AudioConfig_API> enter_config(config,
335 true);
336 if (enter_config.failed())
337 return PP_ERROR_BADARGUMENT;
338
339 config_ = config;
[email protected]a23089d02014-01-06 23:51:40340 audio_input_callback_0_3_ = audio_input_callback_0_3;
[email protected]c90ffec72013-07-03 11:57:45341 audio_input_callback_ = audio_input_callback;
342 user_data_ = user_data;
343 open_callback_ = callback;
344 bytes_per_second_ = kAudioInputChannels * (kBitsPerAudioInputSample / 8) *
345 enter_config.object()->GetSampleRate();
[email protected]5955c9f2014-07-10 16:44:01346 sample_frame_count_ = enter_config.object()->GetSampleFrameCount();
[email protected]c90ffec72013-07-03 11:57:45347
348 PpapiHostMsg_AudioInput_Open msg(
349 device_id, enter_config.object()->GetSampleRate(),
350 enter_config.object()->GetSampleFrameCount());
351 Call<PpapiPluginMsg_AudioInput_OpenReply>(
352 RENDERER, msg,
Anand K Mistry9182c2a72021-03-17 04:40:08353 base::BindOnce(&AudioInputResource::OnPluginMsgOpenReply,
354 base::Unretained(this)));
[email protected]c90ffec72013-07-03 11:57:45355 return PP_OK_COMPLETIONPENDING;
356}
[email protected]77b55502012-11-08 22:20:20357} // namespace proxy
358} // namespace ppapi