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