OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Native Client 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 "native_client/src/shared/ppapi_proxy/plugin_ppb_audio.h" |
| 6 |
| 7 #include <pthread.h> |
| 8 #include <stdio.h> |
| 9 #include <string.h> |
| 10 #include <sys/errno.h> |
| 11 #include <sys/mman.h> |
| 12 #include "native_client/src/include/nacl_scoped_ptr.h" |
| 13 #include "native_client/src/include/portability.h" |
| 14 #include "native_client/src/shared/ppapi_proxy/plugin_globals.h" |
| 15 #include "native_client/src/shared/ppapi_proxy/plugin_resource.h" |
| 16 #include "native_client/src/shared/ppapi_proxy/utility.h" |
| 17 #include "native_client/src/shared/srpc/nacl_srpc.h" |
| 18 #include "native_client/src/untrusted/nacl/syscall_bindings_trampoline.h" |
| 19 #include "ppapi/c/ppb_audio.h" |
| 20 #include "ppapi/c/ppb_audio_config.h" |
| 21 #include "ppapi/cpp/module_impl.h" |
| 22 #include "srpcgen/ppb_rpc.h" |
| 23 #include "srpcgen/ppp_rpc.h" |
| 24 |
| 25 namespace ppapi_proxy { |
| 26 namespace { |
| 27 |
| 28 // round size up to next 64k |
| 29 size_t ceil64k(size_t n) { |
| 30 return (n + 0xFFFF) & (~0xFFFF); |
| 31 } |
| 32 |
| 33 } // namespace |
| 34 |
| 35 PluginAudio::PluginAudio() : |
| 36 resource_(kInvalidResourceId), |
| 37 socket_(-1), |
| 38 shm_(-1), |
| 39 shm_size_(0), |
| 40 shm_buffer_(NULL), |
| 41 state_(AUDIO_INCOMPLETE), |
| 42 thread_id_(), |
| 43 thread_active_(false), |
| 44 user_callback_(NULL), |
| 45 user_data_(NULL) { |
| 46 DebugPrintf("PluginAudio::PluginAudio\n"); |
| 47 } |
| 48 |
| 49 PluginAudio::~PluginAudio() { |
| 50 DebugPrintf("PluginAudio::~PluginAudio\n"); |
| 51 // Ensure audio thread is not active. |
| 52 if (resource_ != kInvalidResourceId) |
| 53 GetInterface()->StopPlayback(resource_); |
| 54 // Unmap the shared memory buffer, if present. |
| 55 if (shm_buffer_) { |
| 56 munmap(shm_buffer_, ceil64k(shm_size_)); |
| 57 shm_buffer_ = NULL; |
| 58 shm_size_ = 0; |
| 59 } |
| 60 // Close the handles. |
| 61 if (shm_ != -1) { |
| 62 close(shm_); |
| 63 shm_ = -1; |
| 64 } |
| 65 if (socket_ != -1) { |
| 66 close(socket_); |
| 67 socket_ = -1; |
| 68 } |
| 69 } |
| 70 |
| 71 bool PluginAudio::InitFromBrowserResource(PP_Resource resource) { |
| 72 DebugPrintf("PluginAudio::InitFromBrowserResource: resource=%"NACL_PRIu32"\n", |
| 73 resource); |
| 74 resource_ = resource; |
| 75 return true; |
| 76 } |
| 77 |
| 78 void PluginAudio::AudioThread(void* self) { |
| 79 PluginAudio* audio = static_cast<PluginAudio*>(self); |
| 80 DebugPrintf("PluginAudio::AudioThread: self=%p\n", self); |
| 81 while (true) { |
| 82 int32_t sync_value; |
| 83 // block on socket read |
| 84 ssize_t r = read(audio->socket_, &sync_value, sizeof(sync_value)); |
| 85 // StopPlayback() will send a value of -1 over the sync_socket |
| 86 if ((sizeof(sync_value) != r) || (-1 == sync_value)) |
| 87 break; |
| 88 // invoke user callback, get next buffer of audio data |
| 89 audio->user_callback_(audio->shm_buffer_, |
| 90 audio->shm_size_, |
| 91 audio->user_data_); |
| 92 } |
| 93 } |
| 94 |
| 95 void PluginAudio::StreamCreated(NaClSrpcImcDescType socket, |
| 96 NaClSrpcImcDescType shm, size_t shm_size) { |
| 97 DebugPrintf("PluginAudio::StreamCreated: shm=%"NACL_PRIu32"" |
| 98 " shm_size=%"NACL_PRIuS"\n", shm, shm_size); |
| 99 socket_ = socket; |
| 100 shm_ = shm; |
| 101 shm_size_ = shm_size; |
| 102 shm_buffer_ = mmap(NULL, |
| 103 ceil64k(shm_size), |
| 104 PROT_READ | PROT_WRITE, |
| 105 MAP_SHARED, |
| 106 shm, |
| 107 0); |
| 108 if (MAP_FAILED != shm_buffer_) { |
| 109 if (state() == AUDIO_PENDING) { |
| 110 StartAudioThread(); |
| 111 } else { |
| 112 set_state(AUDIO_READY); |
| 113 } |
| 114 } else { |
| 115 shm_buffer_ = NULL; |
| 116 } |
| 117 } |
| 118 |
| 119 bool PluginAudio::StartAudioThread() { |
| 120 // clear contents of shm buffer before spinning up audio thread |
| 121 DebugPrintf("PluginAudio::StartAudioThread\n"); |
| 122 memset(shm_buffer_, 0, shm_size_); |
| 123 const struct PP_ThreadFunctions* thread_funcs = GetThreadCreator(); |
| 124 if (NULL == thread_funcs->thread_create || |
| 125 NULL == thread_funcs->thread_join) { |
| 126 return false; |
| 127 } |
| 128 int ret = thread_funcs->thread_create(&thread_id_, AudioThread, this); |
| 129 if (0 == ret) { |
| 130 thread_active_ = true; |
| 131 set_state(AUDIO_PLAYING); |
| 132 return true; |
| 133 } |
| 134 return false; |
| 135 } |
| 136 |
| 137 bool PluginAudio::StopAudioThread() { |
| 138 DebugPrintf("PluginAudio::StopAudioThread\n"); |
| 139 if (thread_active_) { |
| 140 int ret = GetThreadCreator()->thread_join(thread_id_); |
| 141 if (0 == ret) { |
| 142 thread_active_ = false; |
| 143 set_state(AUDIO_READY); |
| 144 return true; |
| 145 } |
| 146 } |
| 147 return false; |
| 148 } |
| 149 |
| 150 // Start of untrusted PPB_Audio functions |
| 151 namespace { |
| 152 |
| 153 PP_Resource Create(PP_Instance instance, |
| 154 PP_Resource config, |
| 155 PPB_Audio_Callback user_callback, |
| 156 void* user_data) { |
| 157 DebugPrintf("PPB_Audio::Create: instance=%"NACL_PRIu32" config=%"NACL_PRIu32 |
| 158 " user_callback=%p user_data=%p\n", |
| 159 instance, config, user_callback, user_data); |
| 160 PP_Resource audio_resource; |
| 161 // Proxy to browser Create, get audio PP_Resource |
| 162 NaClSrpcError srpc_result = PpbAudioRpcClient::PPB_Audio_Create( |
| 163 GetMainSrpcChannel(), |
| 164 instance, |
| 165 config, |
| 166 &audio_resource); |
| 167 DebugPrintf("PPB_Audio::Create: %s\n", NaClSrpcErrorString(srpc_result)); |
| 168 if (NACL_SRPC_RESULT_OK != srpc_result) { |
| 169 return kInvalidResourceId; |
| 170 } |
| 171 if (kInvalidResourceId == audio_resource) { |
| 172 return kInvalidResourceId; |
| 173 } |
| 174 scoped_refptr<PluginAudio> audio = |
| 175 PluginResource::AdoptAs<PluginAudio>(audio_resource); |
| 176 if (audio.get()) { |
| 177 audio->set_callback(user_callback, user_data); |
| 178 return audio_resource; |
| 179 } |
| 180 return kInvalidResourceId; |
| 181 } |
| 182 |
| 183 PP_Bool IsAudio(PP_Resource resource) { |
| 184 int32_t success; |
| 185 DebugPrintf("PPB_Audio::IsAudio: resource=%"NACL_PRIu32"\n", resource); |
| 186 NaClSrpcError srpc_result = |
| 187 PpbAudioRpcClient::PPB_Audio_IsAudio( |
| 188 GetMainSrpcChannel(), |
| 189 resource, |
| 190 &success); |
| 191 DebugPrintf("PPB_Audio::IsAudio: %s\n", NaClSrpcErrorString(srpc_result)); |
| 192 if (NACL_SRPC_RESULT_OK != srpc_result) { |
| 193 return PP_FALSE; |
| 194 } |
| 195 return PP_FromBool(success); |
| 196 } |
| 197 |
| 198 PP_Resource GetCurrentConfig(PP_Resource audio) { |
| 199 DebugPrintf("PPB_Audio::GetCurrentConfig: audio=%"NACL_PRIu32"\n", audio); |
| 200 PP_Resource config_resource; |
| 201 NaClSrpcError srpc_result = |
| 202 PpbAudioRpcClient::PPB_Audio_GetCurrentConfig( |
| 203 GetMainSrpcChannel(), |
| 204 audio, |
| 205 &config_resource); |
| 206 DebugPrintf("PPB_Audio::GetCurrentConfig: %s\n", |
| 207 NaClSrpcErrorString(srpc_result)); |
| 208 if (NACL_SRPC_RESULT_OK != srpc_result) { |
| 209 return kInvalidResourceId; |
| 210 } |
| 211 return config_resource; |
| 212 } |
| 213 |
| 214 PP_Bool StartPlayback(PP_Resource audio_resource) { |
| 215 DebugPrintf("PPB_Audio::StartPlayback: audio_resource=%"NACL_PRIu32"\n", |
| 216 audio_resource); |
| 217 scoped_refptr<PluginAudio> audio = |
| 218 PluginResource::GetAs<PluginAudio>(audio_resource); |
| 219 if (NULL == audio.get()) { |
| 220 return PP_FALSE; |
| 221 } |
| 222 if (audio->state() == AUDIO_INCOMPLETE) { |
| 223 audio->set_state(AUDIO_PENDING); |
| 224 } |
| 225 if (audio->state() == AUDIO_READY) { |
| 226 if (!audio->StartAudioThread()) { |
| 227 return PP_FALSE; |
| 228 } |
| 229 } |
| 230 int32_t success; |
| 231 NaClSrpcError srpc_result = |
| 232 PpbAudioRpcClient::PPB_Audio_StartPlayback( |
| 233 GetMainSrpcChannel(), |
| 234 audio_resource, |
| 235 &success); |
| 236 DebugPrintf("PPB_Audio::StartPlayback: %s\n", |
| 237 NaClSrpcErrorString(srpc_result)); |
| 238 if (NACL_SRPC_RESULT_OK != srpc_result || !success) { |
| 239 return PP_FALSE; |
| 240 } |
| 241 return PP_TRUE; |
| 242 } |
| 243 |
| 244 PP_Bool StopPlayback(PP_Resource audio_resource) { |
| 245 DebugPrintf("PPB_Audio::StopPlayback: audio_resource=%"NACL_PRIu32"\n", |
| 246 audio_resource); |
| 247 scoped_refptr<PluginAudio> audio = |
| 248 PluginResource::GetAs<PluginAudio>(audio_resource); |
| 249 if (NULL == audio.get()) { |
| 250 return PP_FALSE; |
| 251 } |
| 252 if (audio->state() == AUDIO_PENDING) { |
| 253 // audio is pending to start, but StreamCreated() hasn't occurred yet... |
| 254 audio->set_state(AUDIO_INCOMPLETE); |
| 255 } |
| 256 // RPC to trusted side |
| 257 int32_t success; |
| 258 NaClSrpcError srpc_result = |
| 259 PpbAudioRpcClient::PPB_Audio_StopPlayback( |
| 260 GetMainSrpcChannel(), |
| 261 audio_resource, |
| 262 &success); |
| 263 DebugPrintf("PPB_Audio::StopPlayback: %s\n", |
| 264 NaClSrpcErrorString(srpc_result)); |
| 265 if (NACL_SRPC_RESULT_OK != srpc_result) { |
| 266 return PP_FALSE; |
| 267 } |
| 268 if (audio->state() != AUDIO_PLAYING) { |
| 269 return PP_FromBool(success); |
| 270 } |
| 271 // stop and join the audio thread |
| 272 return PP_FromBool(audio->StopAudioThread()); |
| 273 } |
| 274 } // namespace |
| 275 |
| 276 const PPB_Audio* PluginAudio::GetInterface() { |
| 277 DebugPrintf("PluginAudio::GetInterface\n"); |
| 278 static const PPB_Audio audio_interface = { |
| 279 Create, |
| 280 IsAudio, |
| 281 GetCurrentConfig, |
| 282 StartPlayback, |
| 283 StopPlayback, |
| 284 }; |
| 285 return &audio_interface; |
| 286 } |
| 287 } // namespace ppapi_proxy |
| 288 |
| 289 using ppapi_proxy::DebugPrintf; |
| 290 |
| 291 // PppAudioRpcServer::PPP_Audio_StreamCreated() must be in global |
| 292 // namespace. This function receives handles for the socket and shared |
| 293 // memory, provided by the trusted audio implementation. |
| 294 void PppAudioRpcServer::PPP_Audio_StreamCreated( |
| 295 NaClSrpcRpc* rpc, |
| 296 NaClSrpcClosure* done, |
| 297 PP_Resource audio_resource, |
| 298 NaClSrpcImcDescType shm, |
| 299 int32_t shm_size, |
| 300 NaClSrpcImcDescType sync_socket) { |
| 301 NaClSrpcClosureRunner runner(done); |
| 302 rpc->result = NACL_SRPC_RESULT_APP_ERROR; |
| 303 DebugPrintf("PPP_Audio::StreamCreated: audio_resource=%"NACL_PRIu32 |
| 304 " shm=%"NACL_PRIx32" shm_size=%"NACL_PRIuS |
| 305 " sync_socket=%"NACL_PRIx32"\n", |
| 306 audio_resource, shm, shm_size, sync_socket); |
| 307 scoped_refptr<ppapi_proxy::PluginAudio> audio = |
| 308 ppapi_proxy::PluginResource:: |
| 309 GetAs<ppapi_proxy::PluginAudio>(audio_resource); |
| 310 if (NULL == audio.get()) { |
| 311 // Ignore if no audio_resource -> audio_instance mapping exists, |
| 312 // the app may have shutdown audio before StreamCreated() invoked. |
| 313 rpc->result = NACL_SRPC_RESULT_OK; |
| 314 return; |
| 315 } |
| 316 audio->StreamCreated(sync_socket, shm, shm_size); |
| 317 rpc->result = NACL_SRPC_RESULT_OK; |
| 318 } |
OLD | NEW |