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/base/audio_bus.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(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_3_(NULL),
     33       audio_input_callback_(NULL),
     34       user_data_(NULL),
     35       enumeration_helper_(this),
     36       bytes_per_second_(0),
     37       sample_frame_count_(0),
     38       client_buffer_size_bytes_(0) {
     39   SendCreate(RENDERER, PpapiHostMsg_AudioInput_Create());
     40 }
     41 
     42 AudioInputResource::~AudioInputResource() {
     43   Close();
     44 }
     45 
     46 thunk::PPB_AudioInput_API* AudioInputResource::AsPPB_AudioInput_API() {
     47   return this;
     48 }
     49 
     50 void AudioInputResource::OnReplyReceived(
     51     const ResourceMessageReplyParams& params,
     52     const IPC::Message& msg) {
     53   if (!enumeration_helper_.HandleReply(params, msg))
     54     PluginResource::OnReplyReceived(params, msg);
     55 }
     56 
     57 int32_t AudioInputResource::EnumerateDevices(
     58     const PP_ArrayOutput& output,
     59     scoped_refptr<TrackedCallback> callback) {
     60   return enumeration_helper_.EnumerateDevices(output, callback);
     61 }
     62 
     63 int32_t AudioInputResource::MonitorDeviceChange(
     64     PP_MonitorDeviceChangeCallback callback,
     65     void* user_data) {
     66   return enumeration_helper_.MonitorDeviceChange(callback, user_data);
     67 }
     68 
     69 int32_t AudioInputResource::Open0_3(
     70     PP_Resource device_ref,
     71     PP_Resource config,
     72     PPB_AudioInput_Callback_0_3 audio_input_callback_0_3,
     73     void* user_data,
     74     scoped_refptr<TrackedCallback> callback) {
     75   return CommonOpen(device_ref, config, audio_input_callback_0_3, NULL,
     76                     user_data, callback);
     77 }
     78 
     79 int32_t AudioInputResource::Open(PP_Resource device_ref,
     80                                  PP_Resource config,
     81                                  PPB_AudioInput_Callback audio_input_callback,
     82                                  void* user_data,
     83                                  scoped_refptr<TrackedCallback> callback) {
     84   return CommonOpen(device_ref, config, NULL, audio_input_callback, user_data,
     85                     callback);
     86 }
     87 
     88 PP_Resource AudioInputResource::GetCurrentConfig() {
     89   // AddRef for the caller.
     90   if (config_.get())
     91     PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
     92   return config_;
     93 }
     94 
     95 PP_Bool AudioInputResource::StartCapture() {
     96   if (open_state_ == CLOSED || (open_state_ == BEFORE_OPEN &&
     97                                 !TrackedCallback::IsPending(open_callback_))) {
     98     return PP_FALSE;
     99   }
    100   if (capturing_)
    101     return PP_TRUE;
    102 
    103   capturing_ = true;
    104   // Return directly if the audio input device hasn't been opened. Capturing
    105   // will be started once the open operation is completed.
    106   if (open_state_ == BEFORE_OPEN)
    107     return PP_TRUE;
    108 
    109   StartThread();
    110 
    111   Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(true));
    112   return PP_TRUE;
    113 }
    114 
    115 PP_Bool AudioInputResource::StopCapture() {
    116   if (open_state_ == CLOSED)
    117     return PP_FALSE;
    118   if (!capturing_)
    119     return PP_TRUE;
    120 
    121   // If the audio input device hasn't been opened, set |capturing_| to false and
    122   // return directly.
    123   if (open_state_ == BEFORE_OPEN) {
    124     capturing_ = false;
    125     return PP_TRUE;
    126   }
    127 
    128   Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(false));
    129 
    130   StopThread();
    131   capturing_ = false;
    132 
    133   return PP_TRUE;
    134 }
    135 
    136 void AudioInputResource::Close() {
    137   if (open_state_ == CLOSED)
    138     return;
    139 
    140   open_state_ = CLOSED;
    141   Post(RENDERER, PpapiHostMsg_AudioInput_Close());
    142   StopThread();
    143 
    144   if (TrackedCallback::IsPending(open_callback_))
    145     open_callback_->PostAbort();
    146 }
    147 
    148 void AudioInputResource::LastPluginRefWasDeleted() {
    149   enumeration_helper_.LastPluginRefWasDeleted();
    150 }
    151 
    152 void AudioInputResource::OnPluginMsgOpenReply(
    153     const ResourceMessageReplyParams& params) {
    154   if (open_state_ == BEFORE_OPEN && params.result() == PP_OK) {
    155     IPC::PlatformFileForTransit socket_handle_for_transit =
    156         IPC::InvalidPlatformFileForTransit();
    157     params.TakeSocketHandleAtIndex(0, &socket_handle_for_transit);
    158     base::SyncSocket::Handle socket_handle =
    159         IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit);
    160     CHECK(socket_handle != base::SyncSocket::kInvalidHandle);
    161 
    162     SerializedHandle serialized_shared_memory_handle =
    163         params.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY);
    164     CHECK(serialized_shared_memory_handle.IsHandleValid());
    165 
    166     open_state_ = OPENED;
    167     SetStreamInfo(serialized_shared_memory_handle.shmem(),
    168                   serialized_shared_memory_handle.size(),
    169                   socket_handle);
    170   } else {
    171     capturing_ = false;
    172   }
    173 
    174   // The callback may have been aborted by Close().
    175   if (TrackedCallback::IsPending(open_callback_))
    176     open_callback_->Run(params.result());
    177 }
    178 
    179 void AudioInputResource::SetStreamInfo(
    180     base::SharedMemoryHandle shared_memory_handle,
    181     size_t shared_memory_size,
    182     base::SyncSocket::Handle socket_handle) {
    183   socket_.reset(new base::CancelableSyncSocket(socket_handle));
    184   shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false));
    185   shared_memory_size_ = shared_memory_size;
    186   DCHECK(!shared_memory_->memory());
    187 
    188   // If we fail to map the shared memory into the caller's address space we
    189   // might as well fail here since nothing will work if this is the case.
    190   CHECK(shared_memory_->Map(shared_memory_size_));
    191 
    192   // Create a new audio bus and wrap the audio data section in shared memory.
    193   media::AudioInputBuffer* buffer =
    194       static_cast<media::AudioInputBuffer*>(shared_memory_->memory());
    195   audio_bus_ = media::AudioBus::WrapMemory(
    196       kAudioInputChannels, sample_frame_count_, buffer->audio);
    197 
    198   // Ensure that the size of the created audio bus matches the allocated
    199   // size in shared memory.
    200   // Example: DCHECK_EQ(8208 - 16, 8192) for |sample_frame_count_| = 2048.
    201   const uint32_t audio_bus_size_bytes = media::AudioBus::CalculateMemorySize(
    202       audio_bus_->channels(), audio_bus_->frames());
    203   DCHECK_EQ(shared_memory_size_ - sizeof(media::AudioInputBufferParameters),
    204             audio_bus_size_bytes);
    205 
    206   // Create an extra integer audio buffer for user audio data callbacks.
    207   // Data in shared memory will be copied to this buffer, after interleaving
    208   // and truncation, before each input callback to match the format expected
    209   // by the client.
    210   client_buffer_size_bytes_ = audio_bus_->frames() * audio_bus_->channels() *
    211       kBitsPerAudioInputSample / 8;
    212   client_buffer_.reset(new uint8_t[client_buffer_size_bytes_]);
    213 
    214   // There is a pending capture request before SetStreamInfo().
    215   if (capturing_) {
    216     // Set |capturing_| to false so that the state looks consistent to
    217     // StartCapture(), which will reset it to true.
    218     capturing_ = false;
    219     StartCapture();
    220   }
    221 }
    222 
    223 void AudioInputResource::StartThread() {
    224   // Don't start the thread unless all our state is set up correctly.
    225   if ((!audio_input_callback_0_3_ && !audio_input_callback_) ||
    226       !socket_.get() || !capturing_ || !shared_memory_->memory() ||
    227       !audio_bus_.get() || !client_buffer_.get()) {
    228     return;
    229   }
    230   DCHECK(!audio_input_thread_.get());
    231   audio_input_thread_.reset(new base::DelegateSimpleThread(
    232       this, "plugin_audio_input_thread"));
    233   audio_input_thread_->Start();
    234 }
    235 
    236 void AudioInputResource::StopThread() {
    237   // Shut down the socket to escape any hanging |Receive|s.
    238   if (socket_.get())
    239     socket_->Shutdown();
    240   if (audio_input_thread_.get()) {
    241     audio_input_thread_->Join();
    242     audio_input_thread_.reset();
    243   }
    244 }
    245 
    246 void AudioInputResource::Run() {
    247   // The shared memory represents AudioInputBufferParameters and the actual data
    248   // buffer stored as an audio bus.
    249   media::AudioInputBuffer* buffer =
    250       static_cast<media::AudioInputBuffer*>(shared_memory_->memory());
    251   const uint32_t audio_bus_size_bytes =
    252       shared_memory_size_ - sizeof(media::AudioInputBufferParameters);
    253 
    254   while (true) {
    255     int pending_data = 0;
    256     size_t bytes_read = socket_->Receive(&pending_data, sizeof(pending_data));
    257     if (bytes_read != sizeof(pending_data)) {
    258       DCHECK_EQ(bytes_read, 0U);
    259       break;
    260     }
    261     if (pending_data < 0)
    262       break;
    263 
    264     // Convert an AudioBus from deinterleaved float to interleaved integer data.
    265     // Store the result in a preallocated |client_buffer_|.
    266     audio_bus_->ToInterleaved(audio_bus_->frames(),
    267                               kBitsPerAudioInputSample / 8,
    268                               client_buffer_.get());
    269 
    270     // While closing the stream, we may receive buffers whose size is different
    271     // from |data_buffer_size|.
    272     CHECK_LE(buffer->params.size, audio_bus_size_bytes);
    273     if (buffer->params.size > 0) {
    274       if (audio_input_callback_) {
    275         PP_TimeDelta latency =
    276             static_cast<double>(pending_data) / bytes_per_second_;
    277         audio_input_callback_(client_buffer_.get(),
    278                               client_buffer_size_bytes_,
    279                               latency,
    280                               user_data_);
    281       } else {
    282         audio_input_callback_0_3_(
    283             client_buffer_.get(), client_buffer_size_bytes_, user_data_);
    284       }
    285     }
    286   }
    287 }
    288 
    289 int32_t AudioInputResource::CommonOpen(
    290     PP_Resource device_ref,
    291     PP_Resource config,
    292     PPB_AudioInput_Callback_0_3 audio_input_callback_0_3,
    293     PPB_AudioInput_Callback audio_input_callback,
    294     void* user_data,
    295     scoped_refptr<TrackedCallback> callback) {
    296   std::string device_id;
    297   // |device_id| remains empty if |device_ref| is 0, which means the default
    298   // device.
    299   if (device_ref != 0) {
    300     thunk::EnterResourceNoLock<thunk::PPB_DeviceRef_API> enter_device_ref(
    301         device_ref, true);
    302     if (enter_device_ref.failed())
    303       return PP_ERROR_BADRESOURCE;
    304     device_id = enter_device_ref.object()->GetDeviceRefData().id;
    305   }
    306 
    307   if (TrackedCallback::IsPending(open_callback_))
    308     return PP_ERROR_INPROGRESS;
    309   if (open_state_ != BEFORE_OPEN)
    310     return PP_ERROR_FAILED;
    311 
    312   if (!audio_input_callback_0_3 && !audio_input_callback)
    313     return PP_ERROR_BADARGUMENT;
    314   thunk::EnterResourceNoLock<thunk::PPB_AudioConfig_API> enter_config(config,
    315                                                                       true);
    316   if (enter_config.failed())
    317     return PP_ERROR_BADARGUMENT;
    318 
    319   config_ = config;
    320   audio_input_callback_0_3_ = audio_input_callback_0_3;
    321   audio_input_callback_ = audio_input_callback;
    322   user_data_ = user_data;
    323   open_callback_ = callback;
    324   bytes_per_second_ = kAudioInputChannels * (kBitsPerAudioInputSample / 8) *
    325                       enter_config.object()->GetSampleRate();
    326   sample_frame_count_ = enter_config.object()->GetSampleFrameCount();
    327 
    328   PpapiHostMsg_AudioInput_Open msg(
    329       device_id, enter_config.object()->GetSampleRate(),
    330       enter_config.object()->GetSampleFrameCount());
    331   Call<PpapiPluginMsg_AudioInput_OpenReply>(
    332       RENDERER, msg,
    333       base::Bind(&AudioInputResource::OnPluginMsgOpenReply,
    334                  base::Unretained(this)));
    335   return PP_OK_COMPLETIONPENDING;
    336 }
    337 }  // namespace proxy
    338 }  // namespace ppapi
    339