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