[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 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 | |
Max Morin | 56fe26e8 | 2018-01-28 13:40:42 | [diff] [blame] | 7 | #include <string> |
| 8 | |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 9 | #include "base/bind.h" |
| 10 | #include "base/logging.h" |
brettw | 669d47b1 | 2015-02-13 21:17:38 | [diff] [blame] | 11 | #include "base/numerics/safe_conversions.h" |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 12 | #include "ipc/ipc_platform_file.h" |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 13 | #include "media/base/audio_bus.h" |
jrummell | 37a54c0 | 2016-04-22 19:54:27 | [diff] [blame] | 14 | #include "media/base/audio_parameters.h" |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 15 | #include "ppapi/c/pp_errors.h" |
| 16 | #include "ppapi/proxy/ppapi_messages.h" |
| 17 | #include "ppapi/proxy/resource_message_params.h" |
[email protected] | eb5960da | 2013-01-16 23:23:53 | [diff] [blame] | 18 | #include "ppapi/proxy/serialized_handle.h" |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 19 | #include "ppapi/shared_impl/ppapi_globals.h" |
[email protected] | c90ffec7 | 2013-07-03 11:57:45 | [diff] [blame] | 20 | #include "ppapi/shared_impl/ppb_audio_config_shared.h" |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 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 | |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 29 | AudioInputResource::AudioInputResource(Connection connection, |
| 30 | PP_Instance instance) |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 31 | : PluginResource(connection, instance), |
| 32 | open_state_(BEFORE_OPEN), |
| 33 | capturing_(false), |
| 34 | shared_memory_size_(0), |
[email protected] | a23089d0 | 2014-01-06 23:51:40 | [diff] [blame] | 35 | audio_input_callback_0_3_(NULL), |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 36 | audio_input_callback_(NULL), |
| 37 | user_data_(NULL), |
[email protected] | c90ffec7 | 2013-07-03 11:57:45 | [diff] [blame] | 38 | enumeration_helper_(this), |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 39 | bytes_per_second_(0), |
| 40 | sample_frame_count_(0), |
| 41 | client_buffer_size_bytes_(0) { |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 42 | SendCreate(RENDERER, PpapiHostMsg_AudioInput_Create()); |
| 43 | } |
| 44 | |
| 45 | AudioInputResource::~AudioInputResource() { |
| 46 | Close(); |
| 47 | } |
| 48 | |
| 49 | thunk::PPB_AudioInput_API* AudioInputResource::AsPPB_AudioInput_API() { |
| 50 | return this; |
| 51 | } |
| 52 | |
[email protected] | 4f01c76 | 2012-12-05 02:44:18 | [diff] [blame] | 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 | |
[email protected] | 4f01c76 | 2012-12-05 02:44:18 | [diff] [blame] | 60 | int32_t AudioInputResource::EnumerateDevices( |
| 61 | const PP_ArrayOutput& output, |
| 62 | scoped_refptr<TrackedCallback> callback) { |
| 63 | return enumeration_helper_.EnumerateDevices(output, callback); |
| 64 | } |
| 65 | |
| 66 | int32_t AudioInputResource::MonitorDeviceChange( |
| 67 | PP_MonitorDeviceChangeCallback callback, |
| 68 | void* user_data) { |
| 69 | return enumeration_helper_.MonitorDeviceChange(callback, user_data); |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 70 | } |
| 71 | |
[email protected] | a23089d0 | 2014-01-06 23:51:40 | [diff] [blame] | 72 | int32_t AudioInputResource::Open0_3( |
[email protected] | c90ffec7 | 2013-07-03 11:57:45 | [diff] [blame] | 73 | PP_Resource device_ref, |
| 74 | PP_Resource config, |
[email protected] | a23089d0 | 2014-01-06 23:51:40 | [diff] [blame] | 75 | PPB_AudioInput_Callback_0_3 audio_input_callback_0_3, |
[email protected] | c90ffec7 | 2013-07-03 11:57:45 | [diff] [blame] | 76 | void* user_data, |
| 77 | scoped_refptr<TrackedCallback> callback) { |
[email protected] | a23089d0 | 2014-01-06 23:51:40 | [diff] [blame] | 78 | return CommonOpen(device_ref, config, audio_input_callback_0_3, NULL, |
[email protected] | c90ffec7 | 2013-07-03 11:57:45 | [diff] [blame] | 79 | user_data, callback); |
| 80 | } |
| 81 | |
[email protected] | 8917a67 | 2013-02-25 17:18:04 | [diff] [blame] | 82 | int32_t AudioInputResource::Open(PP_Resource device_ref, |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 83 | PP_Resource config, |
| 84 | PPB_AudioInput_Callback audio_input_callback, |
| 85 | void* user_data, |
| 86 | scoped_refptr<TrackedCallback> callback) { |
[email protected] | c90ffec7 | 2013-07-03 11:57:45 | [diff] [blame] | 87 | return CommonOpen(device_ref, config, NULL, audio_input_callback, user_data, |
| 88 | callback); |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 89 | } |
| 90 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
[email protected] | 4f01c76 | 2012-12-05 02:44:18 | [diff] [blame] | 151 | void AudioInputResource::LastPluginRefWasDeleted() { |
| 152 | enumeration_helper_.LastPluginRefWasDeleted(); |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 153 | } |
| 154 | |
| 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 = |
Alexandr Ilin | 002a9a2 | 2018-06-01 13:32:18 | [diff] [blame] | 166 | params.TakeHandleOfTypeAtIndex(1, |
| 167 | SerializedHandle::SHARED_MEMORY_REGION); |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 168 | CHECK(serialized_shared_memory_handle.IsHandleValid()); |
| 169 | |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 170 | open_state_ = OPENED; |
Alexandr Ilin | 002a9a2 | 2018-06-01 13:32:18 | [diff] [blame] | 171 | SetStreamInfo(base::ReadOnlySharedMemoryRegion::Deserialize( |
| 172 | serialized_shared_memory_handle.TakeSharedMemoryRegion()), |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 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( |
Alexandr Ilin | 002a9a2 | 2018-06-01 13:32:18 | [diff] [blame] | 184 | base::ReadOnlySharedMemoryRegion shared_memory_region, |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 185 | base::SyncSocket::Handle socket_handle) { |
| 186 | socket_.reset(new base::CancelableSyncSocket(socket_handle)); |
Alexandr Ilin | 002a9a2 | 2018-06-01 13:32:18 | [diff] [blame] | 187 | DCHECK(!shared_memory_mapping_.IsValid()); |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 188 | |
Max Morin | 30996fb | 2017-09-01 13:28:35 | [diff] [blame] | 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); |
Alexandr Ilin | 002a9a2 | 2018-06-01 13:32:18 | [diff] [blame] | 196 | DCHECK_GE(shared_memory_region.GetSize(), shared_memory_size_); |
Max Morin | 30996fb | 2017-09-01 13:28:35 | [diff] [blame] | 197 | |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 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. |
Alexandr Ilin | 002a9a2 | 2018-06-01 13:32:18 | [diff] [blame] | 200 | shared_memory_mapping_ = shared_memory_region.MapAt(0, shared_memory_size_); |
| 201 | CHECK(shared_memory_mapping_.IsValid()); |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 202 | |
| 203 | // Create a new audio bus and wrap the audio data section in shared memory. |
Alexandr Ilin | 002a9a2 | 2018-06-01 13:32:18 | [diff] [blame] | 204 | const media::AudioInputBuffer* buffer = |
| 205 | static_cast<const media::AudioInputBuffer*>( |
| 206 | shared_memory_mapping_.memory()); |
| 207 | audio_bus_ = media::AudioBus::WrapReadOnlyMemory( |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 208 | kAudioInputChannels, sample_frame_count_, buffer->audio); |
| 209 | |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 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_]); |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 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 | |
| 227 | void AudioInputResource::StartThread() { |
| 228 | // Don't start the thread unless all our state is set up correctly. |
[email protected] | a23089d0 | 2014-01-06 23:51:40 | [diff] [blame] | 229 | if ((!audio_input_callback_0_3_ && !audio_input_callback_) || |
Alexandr Ilin | 002a9a2 | 2018-06-01 13:32:18 | [diff] [blame] | 230 | !socket_.get() || !capturing_ || !shared_memory_mapping_.memory() || |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 231 | !audio_bus_.get() || !client_buffer_.get()) { |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 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 | |
| 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 | |
| 250 | void AudioInputResource::Run() { |
| 251 | // The shared memory represents AudioInputBufferParameters and the actual data |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 252 | // buffer stored as an audio bus. |
Alexandr Ilin | 002a9a2 | 2018-06-01 13:32:18 | [diff] [blame] | 253 | const media::AudioInputBuffer* buffer = |
| 254 | static_cast<const media::AudioInputBuffer*>( |
| 255 | shared_memory_mapping_.memory()); |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 256 | const uint32_t audio_bus_size_bytes = |
brettw | 669d47b1 | 2015-02-13 21:17:38 | [diff] [blame] | 257 | base::checked_cast<uint32_t>(shared_memory_size_ - |
| 258 | sizeof(media::AudioInputBufferParameters)); |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 259 | |
grunell | 95e37d6 | 2015-10-13 07:39:58 | [diff] [blame] | 260 | // This is a constantly increasing counter that is used to verify on the |
| 261 | // browser side that buffers are in sync. |
avi | e029c413 | 2015-12-23 06:45:22 | [diff] [blame] | 262 | uint32_t buffer_index = 0; |
grunell | 95e37d6 | 2015-10-13 07:39:58 | [diff] [blame] | 263 | |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 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_|. |
Raul Tambre | b1da244 | 2019-04-07 18:18:03 | [diff] [blame^] | 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())); |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 280 | |
grunell | 95e37d6 | 2015-10-13 07:39:58 | [diff] [blame] | 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 | |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 289 | // While closing the stream, we may receive buffers whose size is different |
| 290 | // from |data_buffer_size|. |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 291 | CHECK_LE(buffer->params.size, audio_bus_size_bytes); |
[email protected] | c90ffec7 | 2013-07-03 11:57:45 | [diff] [blame] | 292 | if (buffer->params.size > 0) { |
| 293 | if (audio_input_callback_) { |
| 294 | PP_TimeDelta latency = |
| 295 | static_cast<double>(pending_data) / bytes_per_second_; |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 296 | audio_input_callback_(client_buffer_.get(), |
| 297 | client_buffer_size_bytes_, |
| 298 | latency, |
[email protected] | c90ffec7 | 2013-07-03 11:57:45 | [diff] [blame] | 299 | user_data_); |
| 300 | } else { |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 301 | audio_input_callback_0_3_( |
| 302 | client_buffer_.get(), client_buffer_size_bytes_, user_data_); |
[email protected] | c90ffec7 | 2013-07-03 11:57:45 | [diff] [blame] | 303 | } |
| 304 | } |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 305 | } |
| 306 | } |
| 307 | |
[email protected] | c90ffec7 | 2013-07-03 11:57:45 | [diff] [blame] | 308 | int32_t AudioInputResource::CommonOpen( |
| 309 | PP_Resource device_ref, |
| 310 | PP_Resource config, |
[email protected] | a23089d0 | 2014-01-06 23:51:40 | [diff] [blame] | 311 | PPB_AudioInput_Callback_0_3 audio_input_callback_0_3, |
[email protected] | c90ffec7 | 2013-07-03 11:57:45 | [diff] [blame] | 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 | |
[email protected] | a23089d0 | 2014-01-06 23:51:40 | [diff] [blame] | 331 | if (!audio_input_callback_0_3 && !audio_input_callback) |
[email protected] | c90ffec7 | 2013-07-03 11:57:45 | [diff] [blame] | 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; |
[email protected] | a23089d0 | 2014-01-06 23:51:40 | [diff] [blame] | 339 | audio_input_callback_0_3_ = audio_input_callback_0_3; |
[email protected] | c90ffec7 | 2013-07-03 11:57:45 | [diff] [blame] | 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(); |
[email protected] | 5955c9f | 2014-07-10 16:44:01 | [diff] [blame] | 345 | sample_frame_count_ = enter_config.object()->GetSampleFrameCount(); |
[email protected] | c90ffec7 | 2013-07-03 11:57:45 | [diff] [blame] | 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 | } |
[email protected] | 77b5550 | 2012-11-08 22:20:20 | [diff] [blame] | 356 | } // namespace proxy |
| 357 | } // namespace ppapi |