Home | History | Annotate | Download | only in proxy
      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/ppb_audio_proxy.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/threading/simple_thread.h"
      9 #include "media/audio/shared_memory_util.h"
     10 #include "ppapi/c/pp_errors.h"
     11 #include "ppapi/c/ppb_audio.h"
     12 #include "ppapi/c/ppb_audio_config.h"
     13 #include "ppapi/c/ppb_var.h"
     14 #include "ppapi/proxy/enter_proxy.h"
     15 #include "ppapi/proxy/plugin_dispatcher.h"
     16 #include "ppapi/proxy/ppapi_messages.h"
     17 #include "ppapi/shared_impl/api_id.h"
     18 #include "ppapi/shared_impl/platform_file.h"
     19 #include "ppapi/shared_impl/ppapi_globals.h"
     20 #include "ppapi/shared_impl/ppb_audio_shared.h"
     21 #include "ppapi/shared_impl/resource.h"
     22 #include "ppapi/thunk/ppb_audio_config_api.h"
     23 #include "ppapi/thunk/enter.h"
     24 #include "ppapi/thunk/resource_creation_api.h"
     25 #include "ppapi/thunk/thunk.h"
     26 
     27 using ppapi::IntToPlatformFile;
     28 using ppapi::proxy::SerializedHandle;
     29 using ppapi::thunk::EnterResourceNoLock;
     30 using ppapi::thunk::PPB_Audio_API;
     31 using ppapi::thunk::PPB_AudioConfig_API;
     32 
     33 namespace ppapi {
     34 namespace proxy {
     35 
     36 class Audio : public Resource, public PPB_Audio_Shared {
     37  public:
     38   Audio(const HostResource& audio_id,
     39         PP_Resource config_id,
     40         PPB_Audio_Callback callback,
     41         void* user_data);
     42   virtual ~Audio();
     43 
     44   // Resource overrides.
     45   virtual PPB_Audio_API* AsPPB_Audio_API();
     46 
     47   // PPB_Audio_API implementation.
     48   virtual PP_Resource GetCurrentConfig() OVERRIDE;
     49   virtual PP_Bool StartPlayback() OVERRIDE;
     50   virtual PP_Bool StopPlayback() OVERRIDE;
     51   virtual int32_t Open(
     52       PP_Resource config_id,
     53       scoped_refptr<TrackedCallback> create_callback) OVERRIDE;
     54   virtual int32_t GetSyncSocket(int* sync_socket) OVERRIDE;
     55   virtual int32_t GetSharedMemory(int* shm_handle, uint32_t* shm_size) OVERRIDE;
     56 
     57  private:
     58   // Owning reference to the current config object. This isn't actually used,
     59   // we just dish it out as requested by the plugin.
     60   PP_Resource config_;
     61 
     62   DISALLOW_COPY_AND_ASSIGN(Audio);
     63 };
     64 
     65 Audio::Audio(const HostResource& audio_id,
     66              PP_Resource config_id,
     67              PPB_Audio_Callback callback,
     68              void* user_data)
     69     : Resource(OBJECT_IS_PROXY, audio_id),
     70       config_(config_id) {
     71   SetCallback(callback, user_data);
     72   PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
     73 }
     74 
     75 Audio::~Audio() {
     76 #if defined(OS_NACL)
     77   // Invoke StopPlayback() to ensure audio back-end has a chance to send the
     78   // escape value over the sync socket, which will terminate the client side
     79   // audio callback loop.  This is required for NaCl Plugins that can't escape
     80   // by shutting down the sync_socket.
     81   StopPlayback();
     82 #endif
     83   PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(config_);
     84 }
     85 
     86 PPB_Audio_API* Audio::AsPPB_Audio_API() {
     87   return this;
     88 }
     89 
     90 PP_Resource Audio::GetCurrentConfig() {
     91   // AddRef for the caller.
     92   PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
     93   return config_;
     94 }
     95 
     96 PP_Bool Audio::StartPlayback() {
     97   if (playing())
     98     return PP_TRUE;
     99   SetStartPlaybackState();
    100   PluginDispatcher::GetForResource(this)->Send(
    101       new PpapiHostMsg_PPBAudio_StartOrStop(
    102           API_ID_PPB_AUDIO, host_resource(), true));
    103   return PP_TRUE;
    104 }
    105 
    106 PP_Bool Audio::StopPlayback() {
    107   if (!playing())
    108     return PP_TRUE;
    109   PluginDispatcher::GetForResource(this)->Send(
    110       new PpapiHostMsg_PPBAudio_StartOrStop(
    111           API_ID_PPB_AUDIO, host_resource(), false));
    112   SetStopPlaybackState();
    113   return PP_TRUE;
    114 }
    115 
    116 int32_t Audio::Open(PP_Resource config_id,
    117                     scoped_refptr<TrackedCallback> create_callback) {
    118   return PP_ERROR_NOTSUPPORTED;  // Don't proxy the trusted interface.
    119 }
    120 
    121 int32_t Audio::GetSyncSocket(int* sync_socket) {
    122   return PP_ERROR_NOTSUPPORTED;  // Don't proxy the trusted interface.
    123 }
    124 
    125 int32_t Audio::GetSharedMemory(int* shm_handle, uint32_t* shm_size) {
    126   return PP_ERROR_NOTSUPPORTED;  // Don't proxy the trusted interface.
    127 }
    128 
    129 PPB_Audio_Proxy::PPB_Audio_Proxy(Dispatcher* dispatcher)
    130     : InterfaceProxy(dispatcher),
    131       callback_factory_(this) {
    132 }
    133 
    134 PPB_Audio_Proxy::~PPB_Audio_Proxy() {
    135 }
    136 
    137 // static
    138 PP_Resource PPB_Audio_Proxy::CreateProxyResource(
    139     PP_Instance instance_id,
    140     PP_Resource config_id,
    141     PPB_Audio_Callback audio_callback,
    142     void* user_data) {
    143   PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance_id);
    144   if (!dispatcher)
    145     return 0;
    146 
    147   EnterResourceNoLock<PPB_AudioConfig_API> config(config_id, true);
    148   if (config.failed())
    149     return 0;
    150 
    151   if (!audio_callback)
    152     return 0;
    153 
    154   HostResource result;
    155   dispatcher->Send(new PpapiHostMsg_PPBAudio_Create(
    156       API_ID_PPB_AUDIO, instance_id,
    157       config.object()->GetSampleRate(), config.object()->GetSampleFrameCount(),
    158       &result));
    159   if (result.is_null())
    160     return 0;
    161 
    162   return (new Audio(result, config_id,
    163                     audio_callback, user_data))->GetReference();
    164 }
    165 
    166 bool PPB_Audio_Proxy::OnMessageReceived(const IPC::Message& msg) {
    167   bool handled = true;
    168   IPC_BEGIN_MESSAGE_MAP(PPB_Audio_Proxy, msg)
    169 // Don't build host side into NaCl IRT.
    170 #if !defined(OS_NACL)
    171     IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudio_Create, OnMsgCreate)
    172     IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudio_StartOrStop,
    173                         OnMsgStartOrStop)
    174 #endif
    175     IPC_MESSAGE_HANDLER(PpapiMsg_PPBAudio_NotifyAudioStreamCreated,
    176                         OnMsgNotifyAudioStreamCreated)
    177     IPC_MESSAGE_UNHANDLED(handled = false)
    178   IPC_END_MESSAGE_MAP()
    179   return handled;
    180 }
    181 
    182 #if !defined(OS_NACL)
    183 void PPB_Audio_Proxy::OnMsgCreate(PP_Instance instance_id,
    184                                   int32_t sample_rate,
    185                                   uint32_t sample_frame_count,
    186                                   HostResource* result) {
    187   thunk::EnterResourceCreation resource_creation(instance_id);
    188   if (resource_creation.failed())
    189     return;
    190 
    191   // Make the resource and get the API pointer to its trusted interface.
    192   result->SetHostResource(
    193       instance_id,
    194       resource_creation.functions()->CreateAudioTrusted(instance_id));
    195   if (result->is_null())
    196     return;
    197 
    198   // At this point, we've set the result resource, and this is a sync request.
    199   // Anything below this point must issue the AudioChannelConnected callback
    200   // to the browser. Since that's an async message, it will be issued back to
    201   // the plugin after the Create function returns (which is good because it
    202   // would be weird to get a connected message with a failure code for a
    203   // resource you haven't finished creating yet).
    204   //
    205   // The ...ForceCallback class will help ensure the callback is always called.
    206   // All error cases must call SetResult on this class.
    207   EnterHostFromHostResourceForceCallback<PPB_Audio_API> enter(
    208       *result, callback_factory_,
    209       &PPB_Audio_Proxy::AudioChannelConnected, *result);
    210   if (enter.failed())
    211     return;  // When enter fails, it will internally schedule the callback.
    212 
    213   // Make an audio config object.
    214   PP_Resource audio_config_res =
    215       resource_creation.functions()->CreateAudioConfig(
    216           instance_id, static_cast<PP_AudioSampleRate>(sample_rate),
    217           sample_frame_count);
    218   if (!audio_config_res) {
    219     enter.SetResult(PP_ERROR_FAILED);
    220     return;
    221   }
    222 
    223   // Initiate opening the audio object.
    224   enter.SetResult(enter.object()->Open(audio_config_res,
    225                                        enter.callback()));
    226 
    227   // Clean up the temporary audio config resource we made.
    228   const PPB_Core* core = static_cast<const PPB_Core*>(
    229       dispatcher()->local_get_interface()(PPB_CORE_INTERFACE));
    230   core->ReleaseResource(audio_config_res);
    231 }
    232 
    233 void PPB_Audio_Proxy::OnMsgStartOrStop(const HostResource& audio_id,
    234                                        bool play) {
    235   EnterHostFromHostResource<PPB_Audio_API> enter(audio_id);
    236   if (enter.failed())
    237     return;
    238   if (play)
    239     enter.object()->StartPlayback();
    240   else
    241     enter.object()->StopPlayback();
    242 }
    243 
    244 void PPB_Audio_Proxy::AudioChannelConnected(
    245     int32_t result,
    246     const HostResource& resource) {
    247   IPC::PlatformFileForTransit socket_handle =
    248       IPC::InvalidPlatformFileForTransit();
    249   base::SharedMemoryHandle shared_memory = IPC::InvalidPlatformFileForTransit();
    250   uint32_t audio_buffer_length = 0;
    251 
    252   int32_t result_code = result;
    253   if (result_code == PP_OK) {
    254     result_code = GetAudioConnectedHandles(resource, &socket_handle,
    255                                            &shared_memory,
    256                                            &audio_buffer_length);
    257   }
    258 
    259   // Send all the values, even on error. This simplifies some of our cleanup
    260   // code since the handles will be in the other process and could be
    261   // inconvenient to clean up. Our IPC code will automatically handle this for
    262   // us, as long as the remote side always closes the handles it receives
    263   // (in OnMsgNotifyAudioStreamCreated), even in the failure case.
    264   SerializedHandle fd_wrapper(SerializedHandle::SOCKET, socket_handle);
    265 
    266   // Note that we must call TotalSharedMemorySizeInBytes because
    267   // Audio allocates extra space in shared memory for book-keeping, so the
    268   // actual size of the shared memory buffer is larger than audio_buffer_length.
    269   // When sending to NaCl, NaClIPCAdapter expects this size to match the size
    270   // of the full shared memory buffer.
    271   SerializedHandle handle_wrapper(
    272       shared_memory,
    273       media::TotalSharedMemorySizeInBytes(audio_buffer_length));
    274   dispatcher()->Send(new PpapiMsg_PPBAudio_NotifyAudioStreamCreated(
    275       API_ID_PPB_AUDIO, resource, result_code, fd_wrapper, handle_wrapper));
    276 }
    277 
    278 int32_t PPB_Audio_Proxy::GetAudioConnectedHandles(
    279     const HostResource& resource,
    280     IPC::PlatformFileForTransit* foreign_socket_handle,
    281     base::SharedMemoryHandle* foreign_shared_memory_handle,
    282     uint32_t* shared_memory_length) {
    283   // Get the audio interface which will give us the handles.
    284   EnterHostFromHostResource<PPB_Audio_API> enter(resource);
    285   if (enter.failed())
    286     return PP_ERROR_NOINTERFACE;
    287 
    288   // Get the socket handle for signaling.
    289   int32_t socket_handle;
    290   int32_t result = enter.object()->GetSyncSocket(&socket_handle);
    291   if (result != PP_OK)
    292     return result;
    293 
    294   // socket_handle doesn't belong to us: don't close it.
    295   *foreign_socket_handle = dispatcher()->ShareHandleWithRemote(
    296       IntToPlatformFile(socket_handle), false);
    297   if (*foreign_socket_handle == IPC::InvalidPlatformFileForTransit())
    298     return PP_ERROR_FAILED;
    299 
    300   // Get the shared memory for the buffer.
    301   int shared_memory_handle;
    302   result = enter.object()->GetSharedMemory(&shared_memory_handle,
    303                                            shared_memory_length);
    304   if (result != PP_OK)
    305     return result;
    306 
    307   // shared_memory_handle doesn't belong to us: don't close it.
    308   *foreign_shared_memory_handle = dispatcher()->ShareHandleWithRemote(
    309       IntToPlatformFile(shared_memory_handle), false);
    310   if (*foreign_shared_memory_handle == IPC::InvalidPlatformFileForTransit())
    311     return PP_ERROR_FAILED;
    312 
    313   return PP_OK;
    314 }
    315 #endif  // !defined(OS_NACL)
    316 
    317 // Processed in the plugin (message from host).
    318 void PPB_Audio_Proxy::OnMsgNotifyAudioStreamCreated(
    319     const HostResource& audio_id,
    320     int32_t result_code,
    321     SerializedHandle socket_handle,
    322     SerializedHandle handle) {
    323   CHECK(socket_handle.is_socket());
    324   CHECK(handle.is_shmem());
    325   EnterPluginFromHostResource<PPB_Audio_API> enter(audio_id);
    326   if (enter.failed() || result_code != PP_OK) {
    327     // The caller may still have given us these handles in the failure case.
    328     // The easiest way to clean these up is to just put them in the objects
    329     // and then close them. This failure case is not performance critical.
    330     base::SyncSocket temp_socket(
    331         IPC::PlatformFileForTransitToPlatformFile(socket_handle.descriptor()));
    332     base::SharedMemory temp_mem(handle.shmem(), false);
    333   } else {
    334     EnterResourceNoLock<PPB_AudioConfig_API> config(
    335         static_cast<Audio*>(enter.object())->GetCurrentConfig(), true);
    336     // See the comment above about how we must call
    337     // TotalSharedMemorySizeInBytes to get the actual size of the buffer. Here,
    338     // we must call PacketSizeInBytes to get back the size of the audio buffer,
    339     // excluding the bytes that audio uses for book-keeping.
    340     static_cast<Audio*>(enter.object())->SetStreamInfo(
    341         enter.resource()->pp_instance(), handle.shmem(),
    342         media::PacketSizeInBytes(handle.size()),
    343         IPC::PlatformFileForTransitToPlatformFile(socket_handle.descriptor()),
    344         config.object()->GetSampleFrameCount());
    345   }
    346 }
    347 
    348 }  // namespace proxy
    349 }  // namespace ppapi
    350