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/audio_input_resource.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/logging.h"
      9 #include "ipc/ipc_platform_file.h"
     10 #include "media/audio/audio_parameters.h"
     11 #include "ppapi/c/pp_errors.h"
     12 #include "ppapi/proxy/ppapi_messages.h"
     13 #include "ppapi/proxy/resource_message_params.h"
     14 #include "ppapi/proxy/serialized_handle.h"
     15 #include "ppapi/shared_impl/ppapi_globals.h"
     16 #include "ppapi/shared_impl/ppb_audio_config_shared.h"
     17 #include "ppapi/shared_impl/resource_tracker.h"
     18 #include "ppapi/shared_impl/tracked_callback.h"
     19 #include "ppapi/thunk/enter.h"
     20 #include "ppapi/thunk/ppb_audio_config_api.h"
     21 
     22 namespace ppapi {
     23 namespace proxy {
     24 
     25 AudioInputResource::AudioInputResource(
     26     Connection connection,
     27     PP_Instance instance)
     28     : PluginResource(connection, instance),
     29       open_state_(BEFORE_OPEN),
     30       capturing_(false),
     31       shared_memory_size_(0),
     32       audio_input_callback_0_2_(NULL),
     33       audio_input_callback_(NULL),
     34       user_data_(NULL),
     35       enumeration_helper_(this),
     36       bytes_per_second_(0) {
     37   SendCreate(RENDERER, PpapiHostMsg_AudioInput_Create());
     38 }
     39 
     40 AudioInputResource::~AudioInputResource() {
     41   Close();
     42 }
     43 
     44 thunk::PPB_AudioInput_API* AudioInputResource::AsPPB_AudioInput_API() {
     45   return this;
     46 }
     47 
     48 void AudioInputResource::OnReplyReceived(
     49     const ResourceMessageReplyParams& params,
     50     const IPC::Message& msg) {
     51   if (!enumeration_helper_.HandleReply(params, msg))
     52     PluginResource::OnReplyReceived(params, msg);
     53 }
     54 
     55 int32_t AudioInputResource::EnumerateDevices0_2(
     56     PP_Resource* devices,
     57     scoped_refptr<TrackedCallback> callback) {
     58   return enumeration_helper_.EnumerateDevices0_2(devices, callback);
     59 }
     60 
     61 int32_t AudioInputResource::EnumerateDevices(
     62     const PP_ArrayOutput& output,
     63     scoped_refptr<TrackedCallback> callback) {
     64   return enumeration_helper_.EnumerateDevices(output, callback);
     65 }
     66 
     67 int32_t AudioInputResource::MonitorDeviceChange(
     68     PP_MonitorDeviceChangeCallback callback,
     69     void* user_data) {
     70   return enumeration_helper_.MonitorDeviceChange(callback, user_data);
     71 }
     72 
     73 int32_t AudioInputResource::Open0_2(
     74     PP_Resource device_ref,
     75     PP_Resource config,
     76     PPB_AudioInput_Callback_0_2 audio_input_callback_0_2,
     77     void* user_data,
     78     scoped_refptr<TrackedCallback> callback) {
     79   return CommonOpen(device_ref, config, audio_input_callback_0_2, NULL,
     80                     user_data, callback);
     81 }
     82 
     83 int32_t AudioInputResource::Open(PP_Resource device_ref,
     84                                  PP_Resource config,
     85                                  PPB_AudioInput_Callback audio_input_callback,
     86                                  void* user_data,
     87                                  scoped_refptr<TrackedCallback> callback) {
     88   return CommonOpen(device_ref, config, NULL, audio_input_callback, user_data,
     89                     callback);
     90 }
     91 
     92 PP_Resource AudioInputResource::GetCurrentConfig() {
     93   // AddRef for the caller.
     94   if (config_.get())
     95     PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
     96   return config_;
     97 }
     98 
     99 PP_Bool AudioInputResource::StartCapture() {
    100   if (open_state_ == CLOSED || (open_state_ == BEFORE_OPEN &&
    101                                 !TrackedCallback::IsPending(open_callback_))) {
    102     return PP_FALSE;
    103   }
    104   if (capturing_)
    105     return PP_TRUE;
    106 
    107   capturing_ = true;
    108   // Return directly if the audio input device hasn't been opened. Capturing
    109   // will be started once the open operation is completed.
    110   if (open_state_ == BEFORE_OPEN)
    111     return PP_TRUE;
    112 
    113   StartThread();
    114 
    115   Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(true));
    116   return PP_TRUE;
    117 }
    118 
    119 PP_Bool AudioInputResource::StopCapture() {
    120   if (open_state_ == CLOSED)
    121     return PP_FALSE;
    122   if (!capturing_)
    123     return PP_TRUE;
    124 
    125   // If the audio input device hasn't been opened, set |capturing_| to false and
    126   // return directly.
    127   if (open_state_ == BEFORE_OPEN) {
    128     capturing_ = false;
    129     return PP_TRUE;
    130   }
    131 
    132   Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(false));
    133 
    134   StopThread();
    135   capturing_ = false;
    136 
    137   return PP_TRUE;
    138 }
    139 
    140 void AudioInputResource::Close() {
    141   if (open_state_ == CLOSED)
    142     return;
    143 
    144   open_state_ = CLOSED;
    145   Post(RENDERER, PpapiHostMsg_AudioInput_Close());
    146   StopThread();
    147 
    148   if (TrackedCallback::IsPending(open_callback_))
    149     open_callback_->PostAbort();
    150 }
    151 
    152 void AudioInputResource::LastPluginRefWasDeleted() {
    153   enumeration_helper_.LastPluginRefWasDeleted();
    154 }
    155 
    156 void AudioInputResource::OnPluginMsgOpenReply(
    157     const ResourceMessageReplyParams& params) {
    158   if (open_state_ == BEFORE_OPEN && params.result() == PP_OK) {
    159     IPC::PlatformFileForTransit socket_handle_for_transit =
    160         IPC::InvalidPlatformFileForTransit();
    161     params.TakeSocketHandleAtIndex(0, &socket_handle_for_transit);
    162     base::SyncSocket::Handle socket_handle =
    163         IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit);
    164     CHECK(socket_handle != base::SyncSocket::kInvalidHandle);
    165 
    166     SerializedHandle serialized_shared_memory_handle =
    167         params.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY);
    168     CHECK(serialized_shared_memory_handle.IsHandleValid());
    169 
    170     open_state_ = OPENED;
    171     SetStreamInfo(serialized_shared_memory_handle.shmem(),
    172                   serialized_shared_memory_handle.size(),
    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(
    184     base::SharedMemoryHandle shared_memory_handle,
    185     size_t shared_memory_size,
    186     base::SyncSocket::Handle socket_handle) {
    187   socket_.reset(new base::CancelableSyncSocket(socket_handle));
    188   shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false));
    189   shared_memory_size_ = shared_memory_size;
    190 
    191   if (!shared_memory_->Map(shared_memory_size_)) {
    192     PpapiGlobals::Get()->LogWithSource(
    193         pp_instance(),
    194         PP_LOGLEVEL_WARNING,
    195         std::string(),
    196         "Failed to map shared memory for PPB_AudioInput_Shared.");
    197   }
    198 
    199   // There is a pending capture request before SetStreamInfo().
    200   if (capturing_) {
    201     // Set |capturing_| to false so that the state looks consistent to
    202     // StartCapture(), which will reset it to true.
    203     capturing_ = false;
    204     StartCapture();
    205   }
    206 }
    207 
    208 void AudioInputResource::StartThread() {
    209   // Don't start the thread unless all our state is set up correctly.
    210   if ((!audio_input_callback_0_2_ && !audio_input_callback_) ||
    211       !socket_.get() || !capturing_ || !shared_memory_->memory()) {
    212     return;
    213   }
    214   DCHECK(!audio_input_thread_.get());
    215   audio_input_thread_.reset(new base::DelegateSimpleThread(
    216       this, "plugin_audio_input_thread"));
    217   audio_input_thread_->Start();
    218 }
    219 
    220 void AudioInputResource::StopThread() {
    221   // Shut down the socket to escape any hanging |Receive|s.
    222   if (socket_.get())
    223     socket_->Shutdown();
    224   if (audio_input_thread_.get()) {
    225     audio_input_thread_->Join();
    226     audio_input_thread_.reset();
    227   }
    228 }
    229 
    230 void AudioInputResource::Run() {
    231   // The shared memory represents AudioInputBufferParameters and the actual data
    232   // buffer.
    233   media::AudioInputBuffer* buffer =
    234       static_cast<media::AudioInputBuffer*>(shared_memory_->memory());
    235   uint32_t data_buffer_size =
    236       shared_memory_size_ - sizeof(media::AudioInputBufferParameters);
    237   int pending_data;
    238 
    239   while (sizeof(pending_data) == socket_->Receive(&pending_data,
    240                                                   sizeof(pending_data)) &&
    241          pending_data >= 0) {
    242     // While closing the stream, we may receive buffers whose size is different
    243     // from |data_buffer_size|.
    244     CHECK_LE(buffer->params.size, data_buffer_size);
    245     if (buffer->params.size > 0) {
    246       if (audio_input_callback_) {
    247         PP_TimeDelta latency =
    248             static_cast<double>(pending_data) / bytes_per_second_;
    249         audio_input_callback_(&buffer->audio[0], buffer->params.size, latency,
    250                               user_data_);
    251       } else {
    252         audio_input_callback_0_2_(&buffer->audio[0], buffer->params.size,
    253                                   user_data_);
    254       }
    255     }
    256   }
    257 }
    258 
    259 int32_t AudioInputResource::CommonOpen(
    260     PP_Resource device_ref,
    261     PP_Resource config,
    262     PPB_AudioInput_Callback_0_2 audio_input_callback_0_2,
    263     PPB_AudioInput_Callback audio_input_callback,
    264     void* user_data,
    265     scoped_refptr<TrackedCallback> callback) {
    266   std::string device_id;
    267   // |device_id| remains empty if |device_ref| is 0, which means the default
    268   // device.
    269   if (device_ref != 0) {
    270     thunk::EnterResourceNoLock<thunk::PPB_DeviceRef_API> enter_device_ref(
    271         device_ref, true);
    272     if (enter_device_ref.failed())
    273       return PP_ERROR_BADRESOURCE;
    274     device_id = enter_device_ref.object()->GetDeviceRefData().id;
    275   }
    276 
    277   if (TrackedCallback::IsPending(open_callback_))
    278     return PP_ERROR_INPROGRESS;
    279   if (open_state_ != BEFORE_OPEN)
    280     return PP_ERROR_FAILED;
    281 
    282   if (!audio_input_callback_0_2 && !audio_input_callback)
    283     return PP_ERROR_BADARGUMENT;
    284   thunk::EnterResourceNoLock<thunk::PPB_AudioConfig_API> enter_config(config,
    285                                                                       true);
    286   if (enter_config.failed())
    287     return PP_ERROR_BADARGUMENT;
    288 
    289   config_ = config;
    290   audio_input_callback_0_2_ = audio_input_callback_0_2;
    291   audio_input_callback_ = audio_input_callback;
    292   user_data_ = user_data;
    293   open_callback_ = callback;
    294   bytes_per_second_ = kAudioInputChannels * (kBitsPerAudioInputSample / 8) *
    295                       enter_config.object()->GetSampleRate();
    296 
    297   PpapiHostMsg_AudioInput_Open msg(
    298       device_id, enter_config.object()->GetSampleRate(),
    299       enter_config.object()->GetSampleFrameCount());
    300   Call<PpapiPluginMsg_AudioInput_OpenReply>(
    301       RENDERER, msg,
    302       base::Bind(&AudioInputResource::OnPluginMsgOpenReply,
    303                  base::Unretained(this)));
    304   return PP_OK_COMPLETIONPENDING;
    305 }
    306 }  // namespace proxy
    307 }  // namespace ppapi
    308