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 "ppapi/c/pp_errors.h" 12 #include "ppapi/proxy/ppapi_messages.h" 13 #include "ppapi/proxy/resource_message_params.h" 14 #include "ppapi/proxy/serialized_handle.h" 15 #include "ppapi/shared_impl/ppapi_globals.h" 16 #include "ppapi/shared_impl/ppb_audio_config_shared.h" 17 #include "ppapi/shared_impl/resource_tracker.h" 18 #include "ppapi/shared_impl/tracked_callback.h" 19 #include "ppapi/thunk/enter.h" 20 #include "ppapi/thunk/ppb_audio_config_api.h" 21 22 namespace ppapi { 23 namespace proxy { 24 25 AudioInputResource::AudioInputResource( 26 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_2_(NULL), 33 audio_input_callback_(NULL), 34 user_data_(NULL), 35 enumeration_helper_(this), 36 bytes_per_second_(0) { 37 SendCreate(RENDERER, PpapiHostMsg_AudioInput_Create()); 38 } 39 40 AudioInputResource::~AudioInputResource() { 41 Close(); 42 } 43 44 thunk::PPB_AudioInput_API* AudioInputResource::AsPPB_AudioInput_API() { 45 return this; 46 } 47 48 void AudioInputResource::OnReplyReceived( 49 const ResourceMessageReplyParams& params, 50 const IPC::Message& msg) { 51 if (!enumeration_helper_.HandleReply(params, msg)) 52 PluginResource::OnReplyReceived(params, msg); 53 } 54 55 int32_t AudioInputResource::EnumerateDevices0_2( 56 PP_Resource* devices, 57 scoped_refptr<TrackedCallback> callback) { 58 return enumeration_helper_.EnumerateDevices0_2(devices, callback); 59 } 60 61 int32_t AudioInputResource::EnumerateDevices( 62 const PP_ArrayOutput& output, 63 scoped_refptr<TrackedCallback> callback) { 64 return enumeration_helper_.EnumerateDevices(output, callback); 65 } 66 67 int32_t AudioInputResource::MonitorDeviceChange( 68 PP_MonitorDeviceChangeCallback callback, 69 void* user_data) { 70 return enumeration_helper_.MonitorDeviceChange(callback, user_data); 71 } 72 73 int32_t AudioInputResource::Open0_2( 74 PP_Resource device_ref, 75 PP_Resource config, 76 PPB_AudioInput_Callback_0_2 audio_input_callback_0_2, 77 void* user_data, 78 scoped_refptr<TrackedCallback> callback) { 79 return CommonOpen(device_ref, config, audio_input_callback_0_2, NULL, 80 user_data, callback); 81 } 82 83 int32_t AudioInputResource::Open(PP_Resource device_ref, 84 PP_Resource config, 85 PPB_AudioInput_Callback audio_input_callback, 86 void* user_data, 87 scoped_refptr<TrackedCallback> callback) { 88 return CommonOpen(device_ref, config, NULL, audio_input_callback, user_data, 89 callback); 90 } 91 92 PP_Resource AudioInputResource::GetCurrentConfig() { 93 // AddRef for the caller. 94 if (config_.get()) 95 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_); 96 return config_; 97 } 98 99 PP_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 119 PP_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 140 void 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 152 void AudioInputResource::LastPluginRefWasDeleted() { 153 enumeration_helper_.LastPluginRefWasDeleted(); 154 } 155 156 void 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 = 167 params.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY); 168 CHECK(serialized_shared_memory_handle.IsHandleValid()); 169 170 open_state_ = OPENED; 171 SetStreamInfo(serialized_shared_memory_handle.shmem(), 172 serialized_shared_memory_handle.size(), 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 183 void AudioInputResource::SetStreamInfo( 184 base::SharedMemoryHandle shared_memory_handle, 185 size_t shared_memory_size, 186 base::SyncSocket::Handle socket_handle) { 187 socket_.reset(new base::CancelableSyncSocket(socket_handle)); 188 shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false)); 189 shared_memory_size_ = shared_memory_size; 190 191 if (!shared_memory_->Map(shared_memory_size_)) { 192 PpapiGlobals::Get()->LogWithSource( 193 pp_instance(), 194 PP_LOGLEVEL_WARNING, 195 std::string(), 196 "Failed to map shared memory for PPB_AudioInput_Shared."); 197 } 198 199 // There is a pending capture request before SetStreamInfo(). 200 if (capturing_) { 201 // Set |capturing_| to false so that the state looks consistent to 202 // StartCapture(), which will reset it to true. 203 capturing_ = false; 204 StartCapture(); 205 } 206 } 207 208 void AudioInputResource::StartThread() { 209 // Don't start the thread unless all our state is set up correctly. 210 if ((!audio_input_callback_0_2_ && !audio_input_callback_) || 211 !socket_.get() || !capturing_ || !shared_memory_->memory()) { 212 return; 213 } 214 DCHECK(!audio_input_thread_.get()); 215 audio_input_thread_.reset(new base::DelegateSimpleThread( 216 this, "plugin_audio_input_thread")); 217 audio_input_thread_->Start(); 218 } 219 220 void AudioInputResource::StopThread() { 221 // Shut down the socket to escape any hanging |Receive|s. 222 if (socket_.get()) 223 socket_->Shutdown(); 224 if (audio_input_thread_.get()) { 225 audio_input_thread_->Join(); 226 audio_input_thread_.reset(); 227 } 228 } 229 230 void AudioInputResource::Run() { 231 // The shared memory represents AudioInputBufferParameters and the actual data 232 // buffer. 233 media::AudioInputBuffer* buffer = 234 static_cast<media::AudioInputBuffer*>(shared_memory_->memory()); 235 uint32_t data_buffer_size = 236 shared_memory_size_ - sizeof(media::AudioInputBufferParameters); 237 int pending_data; 238 239 while (sizeof(pending_data) == socket_->Receive(&pending_data, 240 sizeof(pending_data)) && 241 pending_data >= 0) { 242 // While closing the stream, we may receive buffers whose size is different 243 // from |data_buffer_size|. 244 CHECK_LE(buffer->params.size, data_buffer_size); 245 if (buffer->params.size > 0) { 246 if (audio_input_callback_) { 247 PP_TimeDelta latency = 248 static_cast<double>(pending_data) / bytes_per_second_; 249 audio_input_callback_(&buffer->audio[0], buffer->params.size, latency, 250 user_data_); 251 } else { 252 audio_input_callback_0_2_(&buffer->audio[0], buffer->params.size, 253 user_data_); 254 } 255 } 256 } 257 } 258 259 int32_t AudioInputResource::CommonOpen( 260 PP_Resource device_ref, 261 PP_Resource config, 262 PPB_AudioInput_Callback_0_2 audio_input_callback_0_2, 263 PPB_AudioInput_Callback audio_input_callback, 264 void* user_data, 265 scoped_refptr<TrackedCallback> callback) { 266 std::string device_id; 267 // |device_id| remains empty if |device_ref| is 0, which means the default 268 // device. 269 if (device_ref != 0) { 270 thunk::EnterResourceNoLock<thunk::PPB_DeviceRef_API> enter_device_ref( 271 device_ref, true); 272 if (enter_device_ref.failed()) 273 return PP_ERROR_BADRESOURCE; 274 device_id = enter_device_ref.object()->GetDeviceRefData().id; 275 } 276 277 if (TrackedCallback::IsPending(open_callback_)) 278 return PP_ERROR_INPROGRESS; 279 if (open_state_ != BEFORE_OPEN) 280 return PP_ERROR_FAILED; 281 282 if (!audio_input_callback_0_2 && !audio_input_callback) 283 return PP_ERROR_BADARGUMENT; 284 thunk::EnterResourceNoLock<thunk::PPB_AudioConfig_API> enter_config(config, 285 true); 286 if (enter_config.failed()) 287 return PP_ERROR_BADARGUMENT; 288 289 config_ = config; 290 audio_input_callback_0_2_ = audio_input_callback_0_2; 291 audio_input_callback_ = audio_input_callback; 292 user_data_ = user_data; 293 open_callback_ = callback; 294 bytes_per_second_ = kAudioInputChannels * (kBitsPerAudioInputSample / 8) * 295 enter_config.object()->GetSampleRate(); 296 297 PpapiHostMsg_AudioInput_Open msg( 298 device_id, enter_config.object()->GetSampleRate(), 299 enter_config.object()->GetSampleFrameCount()); 300 Call<PpapiPluginMsg_AudioInput_OpenReply>( 301 RENDERER, msg, 302 base::Bind(&AudioInputResource::OnPluginMsgOpenReply, 303 base::Unretained(this))); 304 return PP_OK_COMPLETIONPENDING; 305 } 306 } // namespace proxy 307 } // namespace ppapi 308