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