OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2017 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_output_resource.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/numerics/safe_conversions.h" |
| 10 #include "ipc/ipc_platform_file.h" |
| 11 #include "media/base/audio_bus.h" |
| 12 #include "media/base/audio_parameters.h" |
| 13 #include "ppapi/c/pp_errors.h" |
| 14 #include "ppapi/proxy/ppapi_messages.h" |
| 15 #include "ppapi/proxy/resource_message_params.h" |
| 16 #include "ppapi/proxy/serialized_handle.h" |
| 17 #include "ppapi/shared_impl/ppapi_globals.h" |
| 18 #include "ppapi/shared_impl/ppb_audio_config_shared.h" |
| 19 #include "ppapi/shared_impl/resource_tracker.h" |
| 20 #include "ppapi/shared_impl/tracked_callback.h" |
| 21 #include "ppapi/thunk/enter.h" |
| 22 #include "ppapi/thunk/ppb_audio_config_api.h" |
| 23 |
| 24 namespace ppapi { |
| 25 namespace proxy { |
| 26 |
| 27 AudioOutputResource::AudioOutputResource(Connection connection, |
| 28 PP_Instance instance) |
| 29 : PluginResource(connection, instance), |
| 30 open_state_(BEFORE_OPEN), |
| 31 playing_(false), |
| 32 shared_memory_size_(0), |
| 33 audio_output_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_AudioOutput_Create()); |
| 40 } |
| 41 |
| 42 AudioOutputResource::~AudioOutputResource() { |
| 43 Close(); |
| 44 } |
| 45 |
| 46 thunk::PPB_AudioOutput_API* AudioOutputResource::AsPPB_AudioOutput_API() { |
| 47 return this; |
| 48 } |
| 49 |
| 50 void AudioOutputResource::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 AudioOutputResource::EnumerateDevices( |
| 58 const PP_ArrayOutput& output, |
| 59 scoped_refptr<TrackedCallback> callback) { |
| 60 return enumeration_helper_.EnumerateDevices(output, callback); |
| 61 } |
| 62 |
| 63 int32_t AudioOutputResource::MonitorDeviceChange( |
| 64 PP_MonitorDeviceChangeCallback callback, |
| 65 void* user_data) { |
| 66 return enumeration_helper_.MonitorDeviceChange(callback, user_data); |
| 67 } |
| 68 |
| 69 int32_t AudioOutputResource::Open( |
| 70 PP_Resource device_ref, |
| 71 PP_Resource config, |
| 72 PPB_AudioOutput_Callback audio_output_callback, |
| 73 void* user_data, |
| 74 scoped_refptr<TrackedCallback> callback) { |
| 75 return CommonOpen(device_ref, |
| 76 config, |
| 77 audio_output_callback, |
| 78 user_data, |
| 79 callback); |
| 80 } |
| 81 |
| 82 PP_Resource AudioOutputResource::GetCurrentConfig() { |
| 83 // AddRef for the caller. |
| 84 if (config_.get()) |
| 85 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_); |
| 86 return config_; |
| 87 } |
| 88 |
| 89 PP_Bool AudioOutputResource::StartPlayback() { |
| 90 if (open_state_ == CLOSED || (open_state_ == BEFORE_OPEN && |
| 91 !TrackedCallback::IsPending(open_callback_))) { |
| 92 return PP_FALSE; |
| 93 } |
| 94 if (playing_) |
| 95 return PP_TRUE; |
| 96 |
| 97 playing_ = true; |
| 98 |
| 99 StartThread(); |
| 100 |
| 101 Post(RENDERER, PpapiHostMsg_AudioOutput_StartOrStop(true)); |
| 102 return PP_TRUE; |
| 103 } |
| 104 |
| 105 PP_Bool AudioOutputResource::StopPlayback() { |
| 106 if (open_state_ == CLOSED) |
| 107 return PP_FALSE; |
| 108 if (!playing_) |
| 109 return PP_TRUE; |
| 110 |
| 111 // If the audio output device hasn't been opened, set |playing_| to false and |
| 112 // return directly. |
| 113 if (open_state_ == BEFORE_OPEN) { |
| 114 playing_ = false; |
| 115 return PP_TRUE; |
| 116 } |
| 117 |
| 118 Post(RENDERER, PpapiHostMsg_AudioOutput_StartOrStop(false)); |
| 119 |
| 120 StopThread(); |
| 121 playing_ = false; |
| 122 |
| 123 return PP_TRUE; |
| 124 } |
| 125 |
| 126 void AudioOutputResource::Close() { |
| 127 if (open_state_ == CLOSED) |
| 128 return; |
| 129 |
| 130 open_state_ = CLOSED; |
| 131 Post(RENDERER, PpapiHostMsg_AudioOutput_Close()); |
| 132 StopThread(); |
| 133 |
| 134 if (TrackedCallback::IsPending(open_callback_)) |
| 135 open_callback_->PostAbort(); |
| 136 } |
| 137 |
| 138 void AudioOutputResource::LastPluginRefWasDeleted() { |
| 139 enumeration_helper_.LastPluginRefWasDeleted(); |
| 140 } |
| 141 |
| 142 void AudioOutputResource::OnPluginMsgOpenReply( |
| 143 const ResourceMessageReplyParams& params) { |
| 144 if (open_state_ == BEFORE_OPEN && params.result() == PP_OK) { |
| 145 IPC::PlatformFileForTransit socket_handle_for_transit = |
| 146 IPC::InvalidPlatformFileForTransit(); |
| 147 params.TakeSocketHandleAtIndex(0, &socket_handle_for_transit); |
| 148 base::SyncSocket::Handle socket_handle = |
| 149 IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit); |
| 150 CHECK(socket_handle != base::SyncSocket::kInvalidHandle); |
| 151 |
| 152 SerializedHandle serialized_shared_memory_handle = |
| 153 params.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY); |
| 154 CHECK(serialized_shared_memory_handle.IsHandleValid()); |
| 155 |
| 156 open_state_ = OPENED; |
| 157 SetStreamInfo(serialized_shared_memory_handle.shmem(), |
| 158 serialized_shared_memory_handle.size(), |
| 159 socket_handle); |
| 160 } else { |
| 161 playing_ = false; |
| 162 } |
| 163 |
| 164 // The callback may have been aborted by Close(). |
| 165 if (TrackedCallback::IsPending(open_callback_)) |
| 166 open_callback_->Run(params.result()); |
| 167 } |
| 168 |
| 169 void AudioOutputResource::SetStreamInfo( |
| 170 base::SharedMemoryHandle shared_memory_handle, |
| 171 size_t shared_memory_size, |
| 172 base::SyncSocket::Handle socket_handle) { |
| 173 socket_.reset(new base::CancelableSyncSocket(socket_handle)); |
| 174 shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false)); |
| 175 shared_memory_size_ = shared_memory_size; |
| 176 DCHECK(!shared_memory_->memory()); |
| 177 |
| 178 // If we fail to map the shared memory into the caller's address space we |
| 179 // might as well fail here since nothing will work if this is the case. |
| 180 CHECK(shared_memory_->Map(shared_memory_size_)); |
| 181 |
| 182 // Create a new audio bus and wrap the audio data section in shared memory. |
| 183 media::AudioOutputBuffer* buffer = |
| 184 static_cast<media::AudioOutputBuffer*>(shared_memory_->memory()); |
| 185 audio_bus_ = media::AudioBus::WrapMemory( |
| 186 kAudioOutputChannels, sample_frame_count_, buffer->audio); |
| 187 |
| 188 // Ensure that the size of the created audio bus matches the allocated |
| 189 // size in shared memory. |
| 190 // Example: DCHECK_EQ(8208 - 16, 8192) for |sample_frame_count_| = 2048. |
| 191 const uint32_t audio_bus_size_bytes = media::AudioBus::CalculateMemorySize( |
| 192 audio_bus_->channels(), audio_bus_->frames()); |
| 193 DCHECK_EQ(shared_memory_size_ - sizeof(media::AudioOutputBufferParameters), |
| 194 audio_bus_size_bytes); |
| 195 |
| 196 // Setup integer audio buffer for user audio data |
| 197 client_buffer_size_bytes_ = audio_bus_->frames() * audio_bus_->channels() * |
| 198 kBitsPerAudioOutputSample / 8; |
| 199 client_buffer_.reset(new uint8_t[client_buffer_size_bytes_]); |
| 200 } |
| 201 |
| 202 void AudioOutputResource::StartThread() { |
| 203 // Don't start the thread unless all our state is set up correctly. |
| 204 if (!audio_output_callback_ || !socket_.get() || |
| 205 !shared_memory_->memory() || !audio_bus_.get() || !client_buffer_.get() || |
| 206 bytes_per_second_ == 0) |
| 207 return; |
| 208 |
| 209 // Clear contents of shm buffer before starting audio thread. This will |
| 210 // prevent a burst of static if for some reason the audio thread doesn't |
| 211 // start up quickly enough. |
| 212 memset(shared_memory_->memory(), 0, shared_memory_size_); |
| 213 memset(client_buffer_.get(), 0, client_buffer_size_bytes_); |
| 214 |
| 215 DCHECK(!audio_output_thread_.get()); |
| 216 audio_output_thread_.reset(new base::DelegateSimpleThread( |
| 217 this, "plugin_audio_output_thread")); |
| 218 audio_output_thread_->Start(); |
| 219 } |
| 220 |
| 221 void AudioOutputResource::StopThread() { |
| 222 // Shut down the socket to escape any hanging |Receive|s. |
| 223 if (socket_.get()) |
| 224 socket_->Shutdown(); |
| 225 if (audio_output_thread_.get()) { |
| 226 audio_output_thread_->Join(); |
| 227 audio_output_thread_.reset(); |
| 228 } |
| 229 } |
| 230 |
| 231 void AudioOutputResource::Run() { |
| 232 // The shared memory represents AudioOutputBufferParameters and the actual |
| 233 // data buffer stored as an audio bus. |
| 234 media::AudioOutputBuffer* buffer = |
| 235 static_cast<media::AudioOutputBuffer*>(shared_memory_->memory()); |
| 236 |
| 237 // This is a constantly increasing counter that is used to verify on the |
| 238 // browser side that buffers are in sync. |
| 239 uint32_t buffer_index = 0; |
| 240 |
| 241 while (true) { |
| 242 int pending_data = 0; |
| 243 size_t bytes_read = socket_->Receive(&pending_data, sizeof(pending_data)); |
| 244 if (bytes_read != sizeof(pending_data)) { |
| 245 DCHECK_EQ(bytes_read, 0U); |
| 246 break; |
| 247 } |
| 248 if (pending_data < 0) |
| 249 break; |
| 250 |
| 251 { |
| 252 base::TimeDelta delay = |
| 253 base::TimeDelta::FromMicroseconds(buffer->params.delay); |
| 254 |
| 255 audio_output_callback_(client_buffer_.get(), |
| 256 client_buffer_size_bytes_, |
| 257 delay.InSecondsF(), |
| 258 user_data_); |
| 259 } |
| 260 |
| 261 // Deinterleave the audio data into the shared memory as floats. |
| 262 audio_bus_->FromInterleaved(client_buffer_.get(), |
| 263 audio_bus_->frames(), |
| 264 kBitsPerAudioOutputSample / 8); |
| 265 |
| 266 // Inform other side that we have read the data from the shared memory. |
| 267 // Let the other end know which buffer we just filled. The buffer index is |
| 268 // used to ensure the other end is getting the buffer it expects. For more |
| 269 // details on how this works see AudioSyncReader::WaitUntilDataIsReady(). |
| 270 ++buffer_index; |
| 271 size_t bytes_sent = socket_->Send(&buffer_index, sizeof(buffer_index)); |
| 272 if (bytes_sent != sizeof(buffer_index)) { |
| 273 DCHECK_EQ(bytes_sent, 0U); |
| 274 break; |
| 275 } |
| 276 } |
| 277 } |
| 278 |
| 279 int32_t AudioOutputResource::CommonOpen( |
| 280 PP_Resource device_ref, |
| 281 PP_Resource config, |
| 282 PPB_AudioOutput_Callback audio_output_callback, |
| 283 void* user_data, |
| 284 scoped_refptr<TrackedCallback> callback) { |
| 285 std::string device_id; |
| 286 // |device_id| remains empty if |device_ref| is 0, which means the default |
| 287 // device. |
| 288 if (device_ref != 0) { |
| 289 thunk::EnterResourceNoLock<thunk::PPB_DeviceRef_API> enter_device_ref( |
| 290 device_ref, true); |
| 291 if (enter_device_ref.failed()) |
| 292 return PP_ERROR_BADRESOURCE; |
| 293 device_id = enter_device_ref.object()->GetDeviceRefData().id; |
| 294 } |
| 295 |
| 296 if (TrackedCallback::IsPending(open_callback_)) |
| 297 return PP_ERROR_INPROGRESS; |
| 298 if (open_state_ != BEFORE_OPEN) |
| 299 return PP_ERROR_FAILED; |
| 300 |
| 301 if (!audio_output_callback) |
| 302 return PP_ERROR_BADARGUMENT; |
| 303 thunk::EnterResourceNoLock<thunk::PPB_AudioConfig_API> enter_config(config, |
| 304 true); |
| 305 if (enter_config.failed()) |
| 306 return PP_ERROR_BADARGUMENT; |
| 307 |
| 308 config_ = config; |
| 309 audio_output_callback_ = audio_output_callback; |
| 310 user_data_ = user_data; |
| 311 open_callback_ = callback; |
| 312 bytes_per_second_ = kAudioOutputChannels * (kBitsPerAudioOutputSample / 8) * |
| 313 enter_config.object()->GetSampleRate(); |
| 314 sample_frame_count_ = enter_config.object()->GetSampleFrameCount(); |
| 315 |
| 316 PpapiHostMsg_AudioOutput_Open msg( |
| 317 device_id, enter_config.object()->GetSampleRate(), |
| 318 enter_config.object()->GetSampleFrameCount()); |
| 319 Call<PpapiPluginMsg_AudioOutput_OpenReply>( |
| 320 RENDERER, msg, |
| 321 base::Bind(&AudioOutputResource::OnPluginMsgOpenReply, |
| 322 base::Unretained(this))); |
| 323 return PP_OK_COMPLETIONPENDING; |
| 324 } |
| 325 } // namespace proxy |
| 326 } // namespace ppapi |
OLD | NEW |