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