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/audio/shared_memory_util.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 23namespace ppapi { 24namespace proxy { 25 26AudioInputResource::AudioInputResource( 27 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_2_(NULL), 34 audio_input_callback_(NULL), 35 user_data_(NULL), 36 enumeration_helper_(this), 37 bytes_per_second_(0) { 38 SendCreate(RENDERER, PpapiHostMsg_AudioInput_Create()); 39} 40 41AudioInputResource::~AudioInputResource() { 42 Close(); 43} 44 45thunk::PPB_AudioInput_API* AudioInputResource::AsPPB_AudioInput_API() { 46 return this; 47} 48 49void AudioInputResource::OnReplyReceived( 50 const ResourceMessageReplyParams& params, 51 const IPC::Message& msg) { 52 if (!enumeration_helper_.HandleReply(params, msg)) 53 PluginResource::OnReplyReceived(params, msg); 54} 55 56int32_t AudioInputResource::EnumerateDevices0_2( 57 PP_Resource* devices, 58 scoped_refptr<TrackedCallback> callback) { 59 return enumeration_helper_.EnumerateDevices0_2(devices, callback); 60} 61 62int32_t AudioInputResource::EnumerateDevices( 63 const PP_ArrayOutput& output, 64 scoped_refptr<TrackedCallback> callback) { 65 return enumeration_helper_.EnumerateDevices(output, callback); 66} 67 68int32_t AudioInputResource::MonitorDeviceChange( 69 PP_MonitorDeviceChangeCallback callback, 70 void* user_data) { 71 return enumeration_helper_.MonitorDeviceChange(callback, user_data); 72} 73 74int32_t AudioInputResource::Open0_2( 75 PP_Resource device_ref, 76 PP_Resource config, 77 PPB_AudioInput_Callback_0_2 audio_input_callback_0_2, 78 void* user_data, 79 scoped_refptr<TrackedCallback> callback) { 80 return CommonOpen(device_ref, config, audio_input_callback_0_2, NULL, 81 user_data, callback); 82} 83 84int32_t AudioInputResource::Open(PP_Resource device_ref, 85 PP_Resource config, 86 PPB_AudioInput_Callback audio_input_callback, 87 void* user_data, 88 scoped_refptr<TrackedCallback> callback) { 89 return CommonOpen(device_ref, config, NULL, audio_input_callback, user_data, 90 callback); 91} 92 93PP_Resource AudioInputResource::GetCurrentConfig() { 94 // AddRef for the caller. 95 if (config_.get()) 96 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_); 97 return config_; 98} 99 100PP_Bool AudioInputResource::StartCapture() { 101 if (open_state_ == CLOSED || (open_state_ == BEFORE_OPEN && 102 !TrackedCallback::IsPending(open_callback_))) { 103 return PP_FALSE; 104 } 105 if (capturing_) 106 return PP_TRUE; 107 108 capturing_ = true; 109 // Return directly if the audio input device hasn't been opened. Capturing 110 // will be started once the open operation is completed. 111 if (open_state_ == BEFORE_OPEN) 112 return PP_TRUE; 113 114 StartThread(); 115 116 Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(true)); 117 return PP_TRUE; 118} 119 120PP_Bool AudioInputResource::StopCapture() { 121 if (open_state_ == CLOSED) 122 return PP_FALSE; 123 if (!capturing_) 124 return PP_TRUE; 125 126 // If the audio input device hasn't been opened, set |capturing_| to false and 127 // return directly. 128 if (open_state_ == BEFORE_OPEN) { 129 capturing_ = false; 130 return PP_TRUE; 131 } 132 133 Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(false)); 134 135 StopThread(); 136 capturing_ = false; 137 138 return PP_TRUE; 139} 140 141void AudioInputResource::Close() { 142 if (open_state_ == CLOSED) 143 return; 144 145 open_state_ = CLOSED; 146 Post(RENDERER, PpapiHostMsg_AudioInput_Close()); 147 StopThread(); 148 149 if (TrackedCallback::IsPending(open_callback_)) 150 open_callback_->PostAbort(); 151} 152 153void AudioInputResource::LastPluginRefWasDeleted() { 154 enumeration_helper_.LastPluginRefWasDeleted(); 155} 156 157void AudioInputResource::OnPluginMsgOpenReply( 158 const ResourceMessageReplyParams& params) { 159 if (open_state_ == BEFORE_OPEN && params.result() == PP_OK) { 160 IPC::PlatformFileForTransit socket_handle_for_transit = 161 IPC::InvalidPlatformFileForTransit(); 162 params.TakeSocketHandleAtIndex(0, &socket_handle_for_transit); 163 base::SyncSocket::Handle socket_handle = 164 IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit); 165 CHECK(socket_handle != base::SyncSocket::kInvalidHandle); 166 167 SerializedHandle serialized_shared_memory_handle = 168 params.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY); 169 CHECK(serialized_shared_memory_handle.IsHandleValid()); 170 171 // See the comment in pepper_audio_input_host.cc about how we must call 172 // TotalSharedMemorySizeInBytes to get the actual size of the buffer. Here, 173 // we must call PacketSizeInBytes to get back the size of the audio buffer, 174 // excluding the bytes that audio uses for book-keeping. 175 size_t shared_memory_size = media::PacketSizeInBytes( 176 serialized_shared_memory_handle.size()); 177 178 open_state_ = OPENED; 179 SetStreamInfo(serialized_shared_memory_handle.shmem(), shared_memory_size, 180 socket_handle); 181 } else { 182 capturing_ = false; 183 } 184 185 // The callback may have been aborted by Close(). 186 if (TrackedCallback::IsPending(open_callback_)) 187 open_callback_->Run(params.result()); 188} 189 190void AudioInputResource::SetStreamInfo( 191 base::SharedMemoryHandle shared_memory_handle, 192 size_t shared_memory_size, 193 base::SyncSocket::Handle socket_handle) { 194 socket_.reset(new base::CancelableSyncSocket(socket_handle)); 195 shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false)); 196 shared_memory_size_ = shared_memory_size; 197 198 if (!shared_memory_->Map(shared_memory_size_)) { 199 PpapiGlobals::Get()->LogWithSource( 200 pp_instance(), 201 PP_LOGLEVEL_WARNING, 202 std::string(), 203 "Failed to map shared memory for PPB_AudioInput_Shared."); 204 } 205 206 // There is a pending capture request before SetStreamInfo(). 207 if (capturing_) { 208 // Set |capturing_| to false so that the state looks consistent to 209 // StartCapture(), which will reset it to true. 210 capturing_ = false; 211 StartCapture(); 212 } 213} 214 215void AudioInputResource::StartThread() { 216 // Don't start the thread unless all our state is set up correctly. 217 if ((!audio_input_callback_0_2_ && !audio_input_callback_) || 218 !socket_.get() || !capturing_ || !shared_memory_->memory()) { 219 return; 220 } 221 DCHECK(!audio_input_thread_.get()); 222 audio_input_thread_.reset(new base::DelegateSimpleThread( 223 this, "plugin_audio_input_thread")); 224 audio_input_thread_->Start(); 225} 226 227void AudioInputResource::StopThread() { 228 // Shut down the socket to escape any hanging |Receive|s. 229 if (socket_.get()) 230 socket_->Shutdown(); 231 if (audio_input_thread_.get()) { 232 audio_input_thread_->Join(); 233 audio_input_thread_.reset(); 234 } 235} 236 237void AudioInputResource::Run() { 238 // The shared memory represents AudioInputBufferParameters and the actual data 239 // buffer. 240 media::AudioInputBuffer* buffer = 241 static_cast<media::AudioInputBuffer*>(shared_memory_->memory()); 242 uint32_t data_buffer_size = 243 shared_memory_size_ - sizeof(media::AudioInputBufferParameters); 244 int pending_data; 245 246 while (sizeof(pending_data) == socket_->Receive(&pending_data, 247 sizeof(pending_data)) && 248 pending_data >= 0) { 249 // While closing the stream, we may receive buffers whose size is different 250 // from |data_buffer_size|. 251 CHECK_LE(buffer->params.size, data_buffer_size); 252 if (buffer->params.size > 0) { 253 if (audio_input_callback_) { 254 PP_TimeDelta latency = 255 static_cast<double>(pending_data) / bytes_per_second_; 256 audio_input_callback_(&buffer->audio[0], buffer->params.size, latency, 257 user_data_); 258 } else { 259 audio_input_callback_0_2_(&buffer->audio[0], buffer->params.size, 260 user_data_); 261 } 262 } 263 } 264} 265 266int32_t AudioInputResource::CommonOpen( 267 PP_Resource device_ref, 268 PP_Resource config, 269 PPB_AudioInput_Callback_0_2 audio_input_callback_0_2, 270 PPB_AudioInput_Callback audio_input_callback, 271 void* user_data, 272 scoped_refptr<TrackedCallback> callback) { 273 std::string device_id; 274 // |device_id| remains empty if |device_ref| is 0, which means the default 275 // device. 276 if (device_ref != 0) { 277 thunk::EnterResourceNoLock<thunk::PPB_DeviceRef_API> enter_device_ref( 278 device_ref, true); 279 if (enter_device_ref.failed()) 280 return PP_ERROR_BADRESOURCE; 281 device_id = enter_device_ref.object()->GetDeviceRefData().id; 282 } 283 284 if (TrackedCallback::IsPending(open_callback_)) 285 return PP_ERROR_INPROGRESS; 286 if (open_state_ != BEFORE_OPEN) 287 return PP_ERROR_FAILED; 288 289 if (!audio_input_callback_0_2 && !audio_input_callback) 290 return PP_ERROR_BADARGUMENT; 291 thunk::EnterResourceNoLock<thunk::PPB_AudioConfig_API> enter_config(config, 292 true); 293 if (enter_config.failed()) 294 return PP_ERROR_BADARGUMENT; 295 296 config_ = config; 297 audio_input_callback_0_2_ = audio_input_callback_0_2; 298 audio_input_callback_ = audio_input_callback; 299 user_data_ = user_data; 300 open_callback_ = callback; 301 bytes_per_second_ = kAudioInputChannels * (kBitsPerAudioInputSample / 8) * 302 enter_config.object()->GetSampleRate(); 303 304 PpapiHostMsg_AudioInput_Open msg( 305 device_id, enter_config.object()->GetSampleRate(), 306 enter_config.object()->GetSampleFrameCount()); 307 Call<PpapiPluginMsg_AudioInput_OpenReply>( 308 RENDERER, msg, 309 base::Bind(&AudioInputResource::OnPluginMsgOpenReply, 310 base::Unretained(this))); 311 return PP_OK_COMPLETIONPENDING; 312} 313} // namespace proxy 314} // namespace ppapi 315