Home | History | Annotate | Download | only in pepper
      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 "content/renderer/pepper/pepper_platform_audio_input.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/logging.h"
      9 #include "base/message_loop/message_loop_proxy.h"
     10 #include "build/build_config.h"
     11 #include "content/child/child_process.h"
     12 #include "content/renderer/media/audio_input_message_filter.h"
     13 #include "content/renderer/pepper/pepper_audio_input_host.h"
     14 #include "content/renderer/pepper/pepper_media_device_manager.h"
     15 #include "content/renderer/render_thread_impl.h"
     16 #include "content/renderer/render_view_impl.h"
     17 #include "media/audio/audio_manager_base.h"
     18 #include "ppapi/shared_impl/ppb_audio_config_shared.h"
     19 #include "url/gurl.h"
     20 
     21 namespace content {
     22 
     23 // static
     24 PepperPlatformAudioInput* PepperPlatformAudioInput::Create(
     25     const base::WeakPtr<RenderViewImpl>& render_view,
     26     const std::string& device_id,
     27     const GURL& document_url,
     28     int sample_rate,
     29     int frames_per_buffer,
     30     PepperAudioInputHost* client) {
     31   scoped_refptr<PepperPlatformAudioInput> audio_input(
     32       new PepperPlatformAudioInput());
     33   if (audio_input->Initialize(render_view,
     34                               device_id,
     35                               document_url,
     36                               sample_rate,
     37                               frames_per_buffer,
     38                               client)) {
     39     // Balanced by Release invoked in
     40     // PepperPlatformAudioInput::ShutDownOnIOThread().
     41     audio_input->AddRef();
     42     return audio_input.get();
     43   }
     44   return NULL;
     45 }
     46 
     47 void PepperPlatformAudioInput::StartCapture() {
     48   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
     49 
     50   io_message_loop_proxy_->PostTask(
     51       FROM_HERE,
     52       base::Bind(&PepperPlatformAudioInput::StartCaptureOnIOThread, this));
     53 }
     54 
     55 void PepperPlatformAudioInput::StopCapture() {
     56   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
     57 
     58   io_message_loop_proxy_->PostTask(
     59       FROM_HERE,
     60       base::Bind(&PepperPlatformAudioInput::StopCaptureOnIOThread, this));
     61 }
     62 
     63 void PepperPlatformAudioInput::ShutDown() {
     64   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
     65 
     66   // Make sure we don't call shutdown more than once.
     67   if (!client_)
     68     return;
     69 
     70   // Called on the main thread to stop all audio callbacks. We must only change
     71   // the client on the main thread, and the delegates from the I/O thread.
     72   client_ = NULL;
     73   io_message_loop_proxy_->PostTask(
     74       FROM_HERE,
     75       base::Bind(&PepperPlatformAudioInput::ShutDownOnIOThread, this));
     76 }
     77 
     78 void PepperPlatformAudioInput::OnStreamCreated(
     79     base::SharedMemoryHandle handle,
     80     base::SyncSocket::Handle socket_handle,
     81     int length,
     82     int total_segments) {
     83 #if defined(OS_WIN)
     84   DCHECK(handle);
     85   DCHECK(socket_handle);
     86 #else
     87   DCHECK_NE(-1, handle.fd);
     88   DCHECK_NE(-1, socket_handle);
     89 #endif
     90   DCHECK(length);
     91   // TODO(yzshen): Make use of circular buffer scheme. crbug.com/181449.
     92   DCHECK_EQ(1, total_segments);
     93 
     94   if (base::MessageLoopProxy::current().get() !=
     95       main_message_loop_proxy_.get()) {
     96     // If shutdown has occurred, |client_| will be NULL and the handles will be
     97     // cleaned up on the main thread.
     98     main_message_loop_proxy_->PostTask(
     99         FROM_HERE,
    100         base::Bind(&PepperPlatformAudioInput::OnStreamCreated,
    101                    this,
    102                    handle,
    103                    socket_handle,
    104                    length,
    105                    total_segments));
    106   } else {
    107     // Must dereference the client only on the main thread. Shutdown may have
    108     // occurred while the request was in-flight, so we need to NULL check.
    109     if (client_) {
    110       client_->StreamCreated(handle, length, socket_handle);
    111     } else {
    112       // Clean up the handles.
    113       base::SyncSocket temp_socket(socket_handle);
    114       base::SharedMemory temp_shared_memory(handle, false);
    115     }
    116   }
    117 }
    118 
    119 void PepperPlatformAudioInput::OnVolume(double volume) {}
    120 
    121 void PepperPlatformAudioInput::OnStateChanged(
    122     media::AudioInputIPCDelegate::State state) {}
    123 
    124 void PepperPlatformAudioInput::OnIPCClosed() { ipc_.reset(); }
    125 
    126 PepperPlatformAudioInput::~PepperPlatformAudioInput() {
    127   // Make sure we have been shut down. Warning: this may happen on the I/O
    128   // thread!
    129   // Although these members should be accessed on a specific thread (either the
    130   // main thread or the I/O thread), it should be fine to examine their value
    131   // here.
    132   DCHECK(!ipc_);
    133   DCHECK(!client_);
    134   DCHECK(label_.empty());
    135   DCHECK(!pending_open_device_);
    136 }
    137 
    138 PepperPlatformAudioInput::PepperPlatformAudioInput()
    139     : client_(NULL),
    140       main_message_loop_proxy_(base::MessageLoopProxy::current()),
    141       io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()),
    142       create_stream_sent_(false),
    143       pending_open_device_(false),
    144       pending_open_device_id_(-1) {}
    145 
    146 bool PepperPlatformAudioInput::Initialize(
    147     const base::WeakPtr<RenderViewImpl>& render_view,
    148     const std::string& device_id,
    149     const GURL& document_url,
    150     int sample_rate,
    151     int frames_per_buffer,
    152     PepperAudioInputHost* client) {
    153   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
    154 
    155   if (!render_view.get() || !client)
    156     return false;
    157 
    158   ipc_ = RenderThreadImpl::current()
    159              ->audio_input_message_filter()
    160              ->CreateAudioInputIPC(render_view->GetRoutingID());
    161 
    162   render_view_ = render_view;
    163   client_ = client;
    164 
    165   params_.Reset(media::AudioParameters::AUDIO_PCM_LINEAR,
    166                 media::CHANNEL_LAYOUT_MONO,
    167                 ppapi::kAudioInputChannels,
    168                 0,
    169                 sample_rate,
    170                 ppapi::kBitsPerAudioInputSample,
    171                 frames_per_buffer);
    172 
    173   // We need to open the device and obtain the label and session ID before
    174   // initializing.
    175   pending_open_device_id_ = GetMediaDeviceManager()->OpenDevice(
    176       PP_DEVICETYPE_DEV_AUDIOCAPTURE,
    177       device_id.empty() ? media::AudioManagerBase::kDefaultDeviceId : device_id,
    178       document_url,
    179       base::Bind(&PepperPlatformAudioInput::OnDeviceOpened, this));
    180   pending_open_device_ = true;
    181 
    182   return true;
    183 }
    184 
    185 void PepperPlatformAudioInput::InitializeOnIOThread(int session_id) {
    186   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
    187 
    188   if (!ipc_)
    189     return;
    190 
    191   // We will be notified by OnStreamCreated().
    192   create_stream_sent_ = true;
    193   ipc_->CreateStream(this, session_id, params_, false, 1);
    194 }
    195 
    196 void PepperPlatformAudioInput::StartCaptureOnIOThread() {
    197   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
    198 
    199   if (ipc_)
    200     ipc_->RecordStream();
    201 }
    202 
    203 void PepperPlatformAudioInput::StopCaptureOnIOThread() {
    204   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
    205 
    206   // TODO(yzshen): We cannot re-start capturing if the stream is closed.
    207   if (ipc_ && create_stream_sent_) {
    208     ipc_->CloseStream();
    209   }
    210   ipc_.reset();
    211 }
    212 
    213 void PepperPlatformAudioInput::ShutDownOnIOThread() {
    214   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
    215 
    216   StopCaptureOnIOThread();
    217 
    218   main_message_loop_proxy_->PostTask(
    219       FROM_HERE, base::Bind(&PepperPlatformAudioInput::CloseDevice, this));
    220 
    221   Release();  // Release for the delegate, balances out the reference taken in
    222               // PepperPlatformAudioInput::Create.
    223 }
    224 
    225 void PepperPlatformAudioInput::OnDeviceOpened(int request_id,
    226                                               bool succeeded,
    227                                               const std::string& label) {
    228   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
    229 
    230   pending_open_device_ = false;
    231   pending_open_device_id_ = -1;
    232 
    233   if (succeeded && render_view_.get()) {
    234     DCHECK(!label.empty());
    235     label_ = label;
    236 
    237     if (client_) {
    238       int session_id = GetMediaDeviceManager()->GetSessionID(
    239           PP_DEVICETYPE_DEV_AUDIOCAPTURE, label);
    240       io_message_loop_proxy_->PostTask(
    241           FROM_HERE,
    242           base::Bind(&PepperPlatformAudioInput::InitializeOnIOThread,
    243                      this,
    244                      session_id));
    245     } else {
    246       // Shutdown has occurred.
    247       CloseDevice();
    248     }
    249   } else {
    250     NotifyStreamCreationFailed();
    251   }
    252 }
    253 
    254 void PepperPlatformAudioInput::CloseDevice() {
    255   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
    256 
    257   if (render_view_.get()) {
    258     if (!label_.empty()) {
    259       GetMediaDeviceManager()->CloseDevice(label_);
    260       label_.clear();
    261     }
    262     if (pending_open_device_) {
    263       GetMediaDeviceManager()->CancelOpenDevice(pending_open_device_id_);
    264       pending_open_device_ = false;
    265       pending_open_device_id_ = -1;
    266     }
    267   }
    268 }
    269 
    270 void PepperPlatformAudioInput::NotifyStreamCreationFailed() {
    271   DCHECK(main_message_loop_proxy_->BelongsToCurrentThread());
    272 
    273   if (client_)
    274     client_->StreamCreationFailed();
    275 }
    276 
    277 PepperMediaDeviceManager* PepperPlatformAudioInput::GetMediaDeviceManager() {
    278   return PepperMediaDeviceManager::GetForRenderView(render_view_.get());
    279 }
    280 
    281 }  // namespace content
    282