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