Home | History | Annotate | Download | only in media
      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/browser/renderer_host/media/audio_input_device_manager.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "content/public/browser/browser_thread.h"
     10 #include "content/public/common/media_stream_request.h"
     11 #include "media/audio/audio_device_name.h"
     12 #include "media/audio/audio_input_ipc.h"
     13 #include "media/audio/audio_manager_base.h"
     14 #include "media/audio/audio_parameters.h"
     15 #include "media/base/channel_layout.h"
     16 #include "media/base/scoped_histogram_timer.h"
     17 
     18 namespace content {
     19 
     20 const int AudioInputDeviceManager::kFakeOpenSessionId = 1;
     21 
     22 namespace {
     23 // Starting id for the first capture session.
     24 const int kFirstSessionId = AudioInputDeviceManager::kFakeOpenSessionId + 1;
     25 }
     26 
     27 AudioInputDeviceManager::AudioInputDeviceManager(
     28     media::AudioManager* audio_manager)
     29     : listener_(NULL),
     30       next_capture_session_id_(kFirstSessionId),
     31       use_fake_device_(false),
     32       audio_manager_(audio_manager) {
     33   // TODO(xians): Remove this fake_device after the unittests do not need it.
     34   StreamDeviceInfo fake_device(MEDIA_DEVICE_AUDIO_CAPTURE,
     35                                media::AudioManagerBase::kDefaultDeviceName,
     36                                media::AudioManagerBase::kDefaultDeviceId,
     37                                44100, media::CHANNEL_LAYOUT_STEREO, false);
     38   fake_device.session_id = kFakeOpenSessionId;
     39   devices_.push_back(fake_device);
     40 }
     41 
     42 AudioInputDeviceManager::~AudioInputDeviceManager() {
     43 }
     44 
     45 const StreamDeviceInfo* AudioInputDeviceManager::GetOpenedDeviceInfoById(
     46     int session_id) {
     47   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     48   StreamDeviceList::iterator device = GetDevice(session_id);
     49   if (device == devices_.end())
     50     return NULL;
     51 
     52   return &(*device);
     53 }
     54 
     55 void AudioInputDeviceManager::Register(
     56     MediaStreamProviderListener* listener,
     57     base::MessageLoopProxy* device_thread_loop) {
     58   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     59   DCHECK(!listener_);
     60   DCHECK(!device_loop_.get());
     61   listener_ = listener;
     62   device_loop_ = device_thread_loop;
     63 }
     64 
     65 void AudioInputDeviceManager::Unregister() {
     66   DCHECK(listener_);
     67   listener_ = NULL;
     68 }
     69 
     70 void AudioInputDeviceManager::EnumerateDevices(MediaStreamType stream_type) {
     71   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     72   DCHECK(listener_);
     73 
     74   device_loop_->PostTask(
     75       FROM_HERE,
     76       base::Bind(&AudioInputDeviceManager::EnumerateOnDeviceThread,
     77                  this, stream_type));
     78 }
     79 
     80 int AudioInputDeviceManager::Open(const StreamDeviceInfo& device) {
     81   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     82   // Generate a new id for this device.
     83   int session_id = next_capture_session_id_++;
     84   device_loop_->PostTask(
     85       FROM_HERE,
     86       base::Bind(&AudioInputDeviceManager::OpenOnDeviceThread,
     87                  this, session_id, device));
     88 
     89   return session_id;
     90 }
     91 
     92 void AudioInputDeviceManager::Close(int session_id) {
     93   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     94   DCHECK(listener_);
     95   StreamDeviceList::iterator device = GetDevice(session_id);
     96   if (device == devices_.end())
     97     return;
     98   const MediaStreamType stream_type = device->device.type;
     99   if (session_id != kFakeOpenSessionId)
    100     devices_.erase(device);
    101 
    102   // Post a callback through the listener on IO thread since
    103   // MediaStreamManager is expecting the callback asynchronously.
    104   BrowserThread::PostTask(BrowserThread::IO,
    105                           FROM_HERE,
    106                           base::Bind(&AudioInputDeviceManager::ClosedOnIOThread,
    107                                      this, stream_type, session_id));
    108 }
    109 
    110 void AudioInputDeviceManager::UseFakeDevice() {
    111   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    112   use_fake_device_ = true;
    113 }
    114 
    115 bool AudioInputDeviceManager::ShouldUseFakeDevice() const {
    116   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    117   return use_fake_device_;
    118 }
    119 
    120 void AudioInputDeviceManager::EnumerateOnDeviceThread(
    121     MediaStreamType stream_type) {
    122   SCOPED_UMA_HISTOGRAM_TIMER(
    123       "Media.AudioInputDeviceManager.EnumerateOnDeviceThreadTime");
    124   DCHECK(IsOnDeviceThread());
    125 
    126   media::AudioDeviceNames device_names;
    127 
    128   switch (stream_type) {
    129     case MEDIA_DEVICE_AUDIO_CAPTURE:
    130       // AudioManager is guaranteed to outlive MediaStreamManager in
    131       // BrowserMainloop.
    132       audio_manager_->GetAudioInputDeviceNames(&device_names);
    133       break;
    134 
    135     default:
    136       NOTREACHED();
    137       break;
    138   }
    139 
    140   scoped_ptr<StreamDeviceInfoArray> devices(new StreamDeviceInfoArray());
    141   for (media::AudioDeviceNames::iterator it = device_names.begin();
    142        it != device_names.end(); ++it) {
    143     // Add device information to device vector.
    144     devices->push_back(StreamDeviceInfo(
    145         stream_type, it->device_name, it->unique_id, false));
    146   }
    147 
    148   // If the |use_fake_device_| flag is on, inject the fake device if there is
    149   // no available device on the OS.
    150   if (use_fake_device_ && devices->empty()) {
    151     devices->push_back(StreamDeviceInfo(
    152         stream_type, media::AudioManagerBase::kDefaultDeviceName,
    153         media::AudioManagerBase::kDefaultDeviceId, false));
    154   }
    155 
    156   // Return the device list through the listener by posting a task on
    157   // IO thread since MediaStreamManager handles the callback asynchronously.
    158   BrowserThread::PostTask(
    159       BrowserThread::IO,
    160       FROM_HERE,
    161       base::Bind(&AudioInputDeviceManager::DevicesEnumeratedOnIOThread,
    162                  this, stream_type, base::Passed(&devices)));
    163 }
    164 
    165 void AudioInputDeviceManager::OpenOnDeviceThread(
    166     int session_id, const StreamDeviceInfo& info) {
    167   SCOPED_UMA_HISTOGRAM_TIMER(
    168       "Media.AudioInputDeviceManager.OpenOnDeviceThreadTime");
    169   DCHECK(IsOnDeviceThread());
    170 
    171   StreamDeviceInfo out(info.device.type, info.device.name, info.device.id,
    172                        0, 0, false);
    173   out.session_id = session_id;
    174   if (use_fake_device_) {
    175     // Don't need to query the hardware information if using fake device.
    176     out.device.sample_rate = 44100;
    177     out.device.channel_layout = media::CHANNEL_LAYOUT_STEREO;
    178   } else {
    179     // Get the preferred sample rate and channel configuration for the
    180     // audio device.
    181     media::AudioParameters params =
    182         audio_manager_->GetInputStreamParameters(info.device.id);
    183     out.device.sample_rate = params.sample_rate();
    184     out.device.channel_layout = params.channel_layout();
    185   }
    186 
    187   // Return the |session_id| through the listener by posting a task on
    188   // IO thread since MediaStreamManager handles the callback asynchronously.
    189   BrowserThread::PostTask(BrowserThread::IO,
    190                           FROM_HERE,
    191                           base::Bind(&AudioInputDeviceManager::OpenedOnIOThread,
    192                                      this, session_id, out));
    193 }
    194 
    195 void AudioInputDeviceManager::DevicesEnumeratedOnIOThread(
    196     MediaStreamType stream_type,
    197     scoped_ptr<StreamDeviceInfoArray> devices) {
    198   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    199   // Ensure that |devices| gets deleted on exit.
    200   if (listener_)
    201     listener_->DevicesEnumerated(stream_type, *devices);
    202 }
    203 
    204 void AudioInputDeviceManager::OpenedOnIOThread(int session_id,
    205                                                const StreamDeviceInfo& info) {
    206   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    207   DCHECK_EQ(session_id, info.session_id);
    208   DCHECK(GetDevice(session_id) == devices_.end());
    209   devices_.push_back(info);
    210 
    211   if (listener_)
    212     listener_->Opened(info.device.type, session_id);
    213 }
    214 
    215 void AudioInputDeviceManager::ClosedOnIOThread(MediaStreamType stream_type,
    216                                                int session_id) {
    217   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    218   if (listener_)
    219     listener_->Closed(stream_type, session_id);
    220 }
    221 
    222 bool AudioInputDeviceManager::IsOnDeviceThread() const {
    223   return device_loop_->BelongsToCurrentThread();
    224 }
    225 
    226 AudioInputDeviceManager::StreamDeviceList::iterator
    227 AudioInputDeviceManager::GetDevice(int session_id) {
    228   for (StreamDeviceList::iterator i(devices_.begin()); i != devices_.end();
    229        ++i) {
    230     if (i->session_id == session_id)
    231       return i;
    232   }
    233 
    234   return devices_.end();
    235 }
    236 
    237 }  // namespace content
    238