Elim cr-checkbox
[chromium-blink-merge.git] / ppapi / proxy / audio_input_resource.cc
blob4a24255226f9fca2ccba1ceeae1f76d197dfe0e8
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"
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/numerics/safe_conversions.h"
10 #include "ipc/ipc_platform_file.h"
11 #include "media/audio/audio_parameters.h"
12 #include "media/base/audio_bus.h"
13 #include "ppapi/c/pp_errors.h"
14 #include "ppapi/proxy/ppapi_messages.h"
15 #include "ppapi/proxy/resource_message_params.h"
16 #include "ppapi/proxy/serialized_handle.h"
17 #include "ppapi/shared_impl/ppapi_globals.h"
18 #include "ppapi/shared_impl/ppb_audio_config_shared.h"
19 #include "ppapi/shared_impl/resource_tracker.h"
20 #include "ppapi/shared_impl/tracked_callback.h"
21 #include "ppapi/thunk/enter.h"
22 #include "ppapi/thunk/ppb_audio_config_api.h"
24 namespace ppapi {
25 namespace proxy {
27 AudioInputResource::AudioInputResource(Connection connection,
28 PP_Instance instance)
29 : PluginResource(connection, instance),
30 open_state_(BEFORE_OPEN),
31 capturing_(false),
32 shared_memory_size_(0),
33 audio_input_callback_0_3_(NULL),
34 audio_input_callback_(NULL),
35 user_data_(NULL),
36 enumeration_helper_(this),
37 bytes_per_second_(0),
38 sample_frame_count_(0),
39 client_buffer_size_bytes_(0) {
40 SendCreate(RENDERER, PpapiHostMsg_AudioInput_Create());
43 AudioInputResource::~AudioInputResource() {
44 Close();
47 thunk::PPB_AudioInput_API* AudioInputResource::AsPPB_AudioInput_API() {
48 return this;
51 void AudioInputResource::OnReplyReceived(
52 const ResourceMessageReplyParams& params,
53 const IPC::Message& msg) {
54 if (!enumeration_helper_.HandleReply(params, msg))
55 PluginResource::OnReplyReceived(params, msg);
58 int32_t AudioInputResource::EnumerateDevices(
59 const PP_ArrayOutput& output,
60 scoped_refptr<TrackedCallback> callback) {
61 return enumeration_helper_.EnumerateDevices(output, callback);
64 int32_t AudioInputResource::MonitorDeviceChange(
65 PP_MonitorDeviceChangeCallback callback,
66 void* user_data) {
67 return enumeration_helper_.MonitorDeviceChange(callback, user_data);
70 int32_t AudioInputResource::Open0_3(
71 PP_Resource device_ref,
72 PP_Resource config,
73 PPB_AudioInput_Callback_0_3 audio_input_callback_0_3,
74 void* user_data,
75 scoped_refptr<TrackedCallback> callback) {
76 return CommonOpen(device_ref, config, audio_input_callback_0_3, NULL,
77 user_data, callback);
80 int32_t AudioInputResource::Open(PP_Resource device_ref,
81 PP_Resource config,
82 PPB_AudioInput_Callback audio_input_callback,
83 void* user_data,
84 scoped_refptr<TrackedCallback> callback) {
85 return CommonOpen(device_ref, config, NULL, audio_input_callback, user_data,
86 callback);
89 PP_Resource AudioInputResource::GetCurrentConfig() {
90 // AddRef for the caller.
91 if (config_.get())
92 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
93 return config_;
96 PP_Bool AudioInputResource::StartCapture() {
97 if (open_state_ == CLOSED || (open_state_ == BEFORE_OPEN &&
98 !TrackedCallback::IsPending(open_callback_))) {
99 return PP_FALSE;
101 if (capturing_)
102 return PP_TRUE;
104 capturing_ = true;
105 // Return directly if the audio input device hasn't been opened. Capturing
106 // will be started once the open operation is completed.
107 if (open_state_ == BEFORE_OPEN)
108 return PP_TRUE;
110 StartThread();
112 Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(true));
113 return PP_TRUE;
116 PP_Bool AudioInputResource::StopCapture() {
117 if (open_state_ == CLOSED)
118 return PP_FALSE;
119 if (!capturing_)
120 return PP_TRUE;
122 // If the audio input device hasn't been opened, set |capturing_| to false and
123 // return directly.
124 if (open_state_ == BEFORE_OPEN) {
125 capturing_ = false;
126 return PP_TRUE;
129 Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(false));
131 StopThread();
132 capturing_ = false;
134 return PP_TRUE;
137 void AudioInputResource::Close() {
138 if (open_state_ == CLOSED)
139 return;
141 open_state_ = CLOSED;
142 Post(RENDERER, PpapiHostMsg_AudioInput_Close());
143 StopThread();
145 if (TrackedCallback::IsPending(open_callback_))
146 open_callback_->PostAbort();
149 void AudioInputResource::LastPluginRefWasDeleted() {
150 enumeration_helper_.LastPluginRefWasDeleted();
153 void AudioInputResource::OnPluginMsgOpenReply(
154 const ResourceMessageReplyParams& params) {
155 if (open_state_ == BEFORE_OPEN && params.result() == PP_OK) {
156 IPC::PlatformFileForTransit socket_handle_for_transit =
157 IPC::InvalidPlatformFileForTransit();
158 params.TakeSocketHandleAtIndex(0, &socket_handle_for_transit);
159 base::SyncSocket::Handle socket_handle =
160 IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit);
161 CHECK(socket_handle != base::SyncSocket::kInvalidHandle);
163 SerializedHandle serialized_shared_memory_handle =
164 params.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY);
165 CHECK(serialized_shared_memory_handle.IsHandleValid());
167 open_state_ = OPENED;
168 SetStreamInfo(serialized_shared_memory_handle.shmem(),
169 serialized_shared_memory_handle.size(),
170 socket_handle);
171 } else {
172 capturing_ = false;
175 // The callback may have been aborted by Close().
176 if (TrackedCallback::IsPending(open_callback_))
177 open_callback_->Run(params.result());
180 void AudioInputResource::SetStreamInfo(
181 base::SharedMemoryHandle shared_memory_handle,
182 size_t shared_memory_size,
183 base::SyncSocket::Handle socket_handle) {
184 socket_.reset(new base::CancelableSyncSocket(socket_handle));
185 shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false));
186 shared_memory_size_ = shared_memory_size;
187 DCHECK(!shared_memory_->memory());
189 // If we fail to map the shared memory into the caller's address space we
190 // might as well fail here since nothing will work if this is the case.
191 CHECK(shared_memory_->Map(shared_memory_size_));
193 // Create a new audio bus and wrap the audio data section in shared memory.
194 media::AudioInputBuffer* buffer =
195 static_cast<media::AudioInputBuffer*>(shared_memory_->memory());
196 audio_bus_ = media::AudioBus::WrapMemory(
197 kAudioInputChannels, sample_frame_count_, buffer->audio);
199 // Ensure that the size of the created audio bus matches the allocated
200 // size in shared memory.
201 // Example: DCHECK_EQ(8208 - 16, 8192) for |sample_frame_count_| = 2048.
202 const uint32_t audio_bus_size_bytes = media::AudioBus::CalculateMemorySize(
203 audio_bus_->channels(), audio_bus_->frames());
204 DCHECK_EQ(shared_memory_size_ - sizeof(media::AudioInputBufferParameters),
205 audio_bus_size_bytes);
207 // Create an extra integer audio buffer for user audio data callbacks.
208 // Data in shared memory will be copied to this buffer, after interleaving
209 // and truncation, before each input callback to match the format expected
210 // by the client.
211 client_buffer_size_bytes_ = audio_bus_->frames() * audio_bus_->channels() *
212 kBitsPerAudioInputSample / 8;
213 client_buffer_.reset(new uint8_t[client_buffer_size_bytes_]);
215 // There is a pending capture request before SetStreamInfo().
216 if (capturing_) {
217 // Set |capturing_| to false so that the state looks consistent to
218 // StartCapture(), which will reset it to true.
219 capturing_ = false;
220 StartCapture();
224 void AudioInputResource::StartThread() {
225 // Don't start the thread unless all our state is set up correctly.
226 if ((!audio_input_callback_0_3_ && !audio_input_callback_) ||
227 !socket_.get() || !capturing_ || !shared_memory_->memory() ||
228 !audio_bus_.get() || !client_buffer_.get()) {
229 return;
231 DCHECK(!audio_input_thread_.get());
232 audio_input_thread_.reset(new base::DelegateSimpleThread(
233 this, "plugin_audio_input_thread"));
234 audio_input_thread_->Start();
237 void AudioInputResource::StopThread() {
238 // Shut down the socket to escape any hanging |Receive|s.
239 if (socket_.get())
240 socket_->Shutdown();
241 if (audio_input_thread_.get()) {
242 audio_input_thread_->Join();
243 audio_input_thread_.reset();
247 void AudioInputResource::Run() {
248 // The shared memory represents AudioInputBufferParameters and the actual data
249 // buffer stored as an audio bus.
250 media::AudioInputBuffer* buffer =
251 static_cast<media::AudioInputBuffer*>(shared_memory_->memory());
252 const uint32_t audio_bus_size_bytes =
253 base::checked_cast<uint32_t>(shared_memory_size_ -
254 sizeof(media::AudioInputBufferParameters));
256 while (true) {
257 int pending_data = 0;
258 size_t bytes_read = socket_->Receive(&pending_data, sizeof(pending_data));
259 if (bytes_read != sizeof(pending_data)) {
260 DCHECK_EQ(bytes_read, 0U);
261 break;
263 if (pending_data < 0)
264 break;
266 // Convert an AudioBus from deinterleaved float to interleaved integer data.
267 // Store the result in a preallocated |client_buffer_|.
268 audio_bus_->ToInterleaved(audio_bus_->frames(),
269 kBitsPerAudioInputSample / 8,
270 client_buffer_.get());
272 // While closing the stream, we may receive buffers whose size is different
273 // from |data_buffer_size|.
274 CHECK_LE(buffer->params.size, audio_bus_size_bytes);
275 if (buffer->params.size > 0) {
276 if (audio_input_callback_) {
277 PP_TimeDelta latency =
278 static_cast<double>(pending_data) / bytes_per_second_;
279 audio_input_callback_(client_buffer_.get(),
280 client_buffer_size_bytes_,
281 latency,
282 user_data_);
283 } else {
284 audio_input_callback_0_3_(
285 client_buffer_.get(), client_buffer_size_bytes_, user_data_);
291 int32_t AudioInputResource::CommonOpen(
292 PP_Resource device_ref,
293 PP_Resource config,
294 PPB_AudioInput_Callback_0_3 audio_input_callback_0_3,
295 PPB_AudioInput_Callback audio_input_callback,
296 void* user_data,
297 scoped_refptr<TrackedCallback> callback) {
298 std::string device_id;
299 // |device_id| remains empty if |device_ref| is 0, which means the default
300 // device.
301 if (device_ref != 0) {
302 thunk::EnterResourceNoLock<thunk::PPB_DeviceRef_API> enter_device_ref(
303 device_ref, true);
304 if (enter_device_ref.failed())
305 return PP_ERROR_BADRESOURCE;
306 device_id = enter_device_ref.object()->GetDeviceRefData().id;
309 if (TrackedCallback::IsPending(open_callback_))
310 return PP_ERROR_INPROGRESS;
311 if (open_state_ != BEFORE_OPEN)
312 return PP_ERROR_FAILED;
314 if (!audio_input_callback_0_3 && !audio_input_callback)
315 return PP_ERROR_BADARGUMENT;
316 thunk::EnterResourceNoLock<thunk::PPB_AudioConfig_API> enter_config(config,
317 true);
318 if (enter_config.failed())
319 return PP_ERROR_BADARGUMENT;
321 config_ = config;
322 audio_input_callback_0_3_ = audio_input_callback_0_3;
323 audio_input_callback_ = audio_input_callback;
324 user_data_ = user_data;
325 open_callback_ = callback;
326 bytes_per_second_ = kAudioInputChannels * (kBitsPerAudioInputSample / 8) *
327 enter_config.object()->GetSampleRate();
328 sample_frame_count_ = enter_config.object()->GetSampleFrameCount();
330 PpapiHostMsg_AudioInput_Open msg(
331 device_id, enter_config.object()->GetSampleRate(),
332 enter_config.object()->GetSampleFrameCount());
333 Call<PpapiPluginMsg_AudioInput_OpenReply>(
334 RENDERER, msg,
335 base::Bind(&AudioInputResource::OnPluginMsgOpenReply,
336 base::Unretained(this)));
337 return PP_OK_COMPLETIONPENDING;
339 } // namespace proxy
340 } // namespace ppapi