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_input_ipc.h"
     12 #include "media/audio/audio_manager_base.h"
     13 #include "media/audio/audio_parameters.h"
     14 #include "media/base/channel_layout.h"
     15 #include "media/base/scoped_histogram_timer.h"
     16 
     17 #if defined(OS_CHROMEOS)
     18 #include "chromeos/audio/cras_audio_handler.h"
     19 #endif
     20 
     21 namespace content {
     22 
     23 const int AudioInputDeviceManager::kFakeOpenSessionId = 1;
     24 
     25 namespace {
     26 // Starting id for the first capture session.
     27 const int kFirstSessionId = AudioInputDeviceManager::kFakeOpenSessionId + 1;
     28 }
     29 
     30 AudioInputDeviceManager::AudioInputDeviceManager(
     31     media::AudioManager* audio_manager)
     32     : listener_(NULL),
     33       next_capture_session_id_(kFirstSessionId),
     34       use_fake_device_(false),
     35 #if defined(OS_CHROMEOS)
     36       keyboard_mic_streams_count_(0),
     37 #endif
     38       audio_manager_(audio_manager) {
     39 }
     40 
     41 AudioInputDeviceManager::~AudioInputDeviceManager() {
     42 }
     43 
     44 const StreamDeviceInfo* AudioInputDeviceManager::GetOpenedDeviceInfoById(
     45     int session_id) {
     46   DCHECK_CURRENTLY_ON(BrowserThread::IO);
     47   StreamDeviceList::iterator device = GetDevice(session_id);
     48   if (device == devices_.end())
     49     return NULL;
     50 
     51   return &(*device);
     52 }
     53 
     54 void AudioInputDeviceManager::Register(
     55     MediaStreamProviderListener* listener,
     56     const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) {
     57   DCHECK_CURRENTLY_ON(BrowserThread::IO);
     58   DCHECK(!listener_);
     59   DCHECK(!device_task_runner_.get());
     60   listener_ = listener;
     61   device_task_runner_ = device_task_runner;
     62 }
     63 
     64 void AudioInputDeviceManager::Unregister() {
     65   DCHECK(listener_);
     66   listener_ = NULL;
     67 }
     68 
     69 void AudioInputDeviceManager::EnumerateDevices(MediaStreamType stream_type) {
     70   DCHECK_CURRENTLY_ON(BrowserThread::IO);
     71   DCHECK(listener_);
     72 
     73   device_task_runner_->PostTask(
     74       FROM_HERE,
     75       base::Bind(&AudioInputDeviceManager::EnumerateOnDeviceThread,
     76                  this, stream_type));
     77 }
     78 
     79 int AudioInputDeviceManager::Open(const StreamDeviceInfo& device) {
     80   DCHECK_CURRENTLY_ON(BrowserThread::IO);
     81   // Generate a new id for this device.
     82   int session_id = next_capture_session_id_++;
     83   device_task_runner_->PostTask(
     84       FROM_HERE,
     85       base::Bind(&AudioInputDeviceManager::OpenOnDeviceThread,
     86                  this, session_id, device));
     87 
     88   return session_id;
     89 }
     90 
     91 void AudioInputDeviceManager::Close(int session_id) {
     92   DCHECK_CURRENTLY_ON(BrowserThread::IO);
     93   DCHECK(listener_);
     94   StreamDeviceList::iterator device = GetDevice(session_id);
     95   if (device == devices_.end())
     96     return;
     97   const MediaStreamType stream_type = device->device.type;
     98   if (session_id != kFakeOpenSessionId)
     99     devices_.erase(device);
    100 
    101   // Post a callback through the listener on IO thread since
    102   // MediaStreamManager is expecting the callback asynchronously.
    103   BrowserThread::PostTask(BrowserThread::IO,
    104                           FROM_HERE,
    105                           base::Bind(&AudioInputDeviceManager::ClosedOnIOThread,
    106                                      this, stream_type, session_id));
    107 }
    108 
    109 void AudioInputDeviceManager::UseFakeDevice() {
    110   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    111   use_fake_device_ = true;
    112 }
    113 
    114 bool AudioInputDeviceManager::ShouldUseFakeDevice() const {
    115   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    116   return use_fake_device_;
    117 }
    118 
    119 #if defined(OS_CHROMEOS)
    120 void AudioInputDeviceManager::RegisterKeyboardMicStream(
    121     const base::Closure& callback) {
    122   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    123 
    124   ++keyboard_mic_streams_count_;
    125   if (keyboard_mic_streams_count_ == 1) {
    126     BrowserThread::PostTaskAndReply(
    127         BrowserThread::UI,
    128         FROM_HERE,
    129         base::Bind(
    130             &AudioInputDeviceManager::SetKeyboardMicStreamActiveOnUIThread,
    131             this,
    132             true),
    133         callback);
    134   } else {
    135     callback.Run();
    136   }
    137 }
    138 
    139 void AudioInputDeviceManager::UnregisterKeyboardMicStream() {
    140   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    141 
    142   --keyboard_mic_streams_count_;
    143   DCHECK_GE(keyboard_mic_streams_count_, 0);
    144   if (keyboard_mic_streams_count_ == 0) {
    145     BrowserThread::PostTask(
    146         BrowserThread::UI,
    147         FROM_HERE,
    148         base::Bind(
    149             &AudioInputDeviceManager::SetKeyboardMicStreamActiveOnUIThread,
    150             this,
    151             false));
    152   }
    153 }
    154 #endif
    155 
    156 void AudioInputDeviceManager::EnumerateOnDeviceThread(
    157     MediaStreamType stream_type) {
    158   SCOPED_UMA_HISTOGRAM_TIMER(
    159       "Media.AudioInputDeviceManager.EnumerateOnDeviceThreadTime");
    160   DCHECK(IsOnDeviceThread());
    161   DCHECK_EQ(MEDIA_DEVICE_AUDIO_CAPTURE, stream_type);
    162 
    163   media::AudioDeviceNames device_names;
    164   if (use_fake_device_) {
    165     // Use the fake devices.
    166     GetFakeDeviceNames(&device_names);
    167   } else {
    168     // Enumerate the devices on the OS.
    169     // AudioManager is guaranteed to outlive MediaStreamManager in
    170     // BrowserMainloop.
    171     audio_manager_->GetAudioInputDeviceNames(&device_names);
    172   }
    173 
    174   scoped_ptr<StreamDeviceInfoArray> devices(new StreamDeviceInfoArray());
    175   for (media::AudioDeviceNames::iterator it = device_names.begin();
    176        it != device_names.end(); ++it) {
    177     // Add device information to device vector.
    178     devices->push_back(StreamDeviceInfo(
    179         stream_type, it->device_name, it->unique_id));
    180   }
    181 
    182   // Return the device list through the listener by posting a task on
    183   // IO thread since MediaStreamManager handles the callback asynchronously.
    184   BrowserThread::PostTask(
    185       BrowserThread::IO,
    186       FROM_HERE,
    187       base::Bind(&AudioInputDeviceManager::DevicesEnumeratedOnIOThread,
    188                  this, stream_type, base::Passed(&devices)));
    189 }
    190 
    191 void AudioInputDeviceManager::OpenOnDeviceThread(
    192     int session_id, const StreamDeviceInfo& info) {
    193   SCOPED_UMA_HISTOGRAM_TIMER(
    194       "Media.AudioInputDeviceManager.OpenOnDeviceThreadTime");
    195   DCHECK(IsOnDeviceThread());
    196 
    197   StreamDeviceInfo out(info.device.type, info.device.name, info.device.id,
    198                        0, 0, 0);
    199   out.session_id = session_id;
    200 
    201   MediaStreamDevice::AudioDeviceParameters& input_params = out.device.input;
    202 
    203   if (use_fake_device_) {
    204     // Don't need to query the hardware information if using fake device.
    205     input_params.sample_rate = 44100;
    206     input_params.channel_layout = media::CHANNEL_LAYOUT_STEREO;
    207   } else {
    208     // Get the preferred sample rate and channel configuration for the
    209     // audio device.
    210     media::AudioParameters params =
    211         audio_manager_->GetInputStreamParameters(info.device.id);
    212     input_params.sample_rate = params.sample_rate();
    213     input_params.channel_layout = params.channel_layout();
    214     input_params.frames_per_buffer = params.frames_per_buffer();
    215     input_params.effects = params.effects();
    216 
    217     // Add preferred output device information if a matching output device
    218     // exists.
    219     out.device.matched_output_device_id =
    220         audio_manager_->GetAssociatedOutputDeviceID(info.device.id);
    221     if (!out.device.matched_output_device_id.empty()) {
    222       params = audio_manager_->GetOutputStreamParameters(
    223           out.device.matched_output_device_id);
    224       MediaStreamDevice::AudioDeviceParameters& matched_output_params =
    225           out.device.matched_output;
    226       matched_output_params.sample_rate = params.sample_rate();
    227       matched_output_params.channel_layout = params.channel_layout();
    228       matched_output_params.frames_per_buffer = params.frames_per_buffer();
    229     }
    230   }
    231 
    232   // Return the |session_id| through the listener by posting a task on
    233   // IO thread since MediaStreamManager handles the callback asynchronously.
    234   BrowserThread::PostTask(BrowserThread::IO,
    235                           FROM_HERE,
    236                           base::Bind(&AudioInputDeviceManager::OpenedOnIOThread,
    237                                      this, session_id, out));
    238 }
    239 
    240 void AudioInputDeviceManager::DevicesEnumeratedOnIOThread(
    241     MediaStreamType stream_type,
    242     scoped_ptr<StreamDeviceInfoArray> devices) {
    243   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    244   // Ensure that |devices| gets deleted on exit.
    245   if (listener_)
    246     listener_->DevicesEnumerated(stream_type, *devices);
    247 }
    248 
    249 void AudioInputDeviceManager::OpenedOnIOThread(int session_id,
    250                                                const StreamDeviceInfo& info) {
    251   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    252   DCHECK_EQ(session_id, info.session_id);
    253   DCHECK(GetDevice(session_id) == devices_.end());
    254 
    255   devices_.push_back(info);
    256 
    257   if (listener_)
    258     listener_->Opened(info.device.type, session_id);
    259 }
    260 
    261 void AudioInputDeviceManager::ClosedOnIOThread(MediaStreamType stream_type,
    262                                                int session_id) {
    263   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    264   if (listener_)
    265     listener_->Closed(stream_type, session_id);
    266 }
    267 
    268 bool AudioInputDeviceManager::IsOnDeviceThread() const {
    269   return device_task_runner_->BelongsToCurrentThread();
    270 }
    271 
    272 AudioInputDeviceManager::StreamDeviceList::iterator
    273 AudioInputDeviceManager::GetDevice(int session_id) {
    274   for (StreamDeviceList::iterator i(devices_.begin()); i != devices_.end();
    275        ++i) {
    276     if (i->session_id == session_id)
    277       return i;
    278   }
    279 
    280   return devices_.end();
    281 }
    282 
    283 void AudioInputDeviceManager::GetFakeDeviceNames(
    284     media::AudioDeviceNames* device_names) {
    285   static const char kFakeDeviceName1[] = "Fake Audio 1";
    286   static const char kFakeDeviceId1[] = "fake_audio_1";
    287   static const char kFakeDeviceName2[] = "Fake Audio 2";
    288   static const char kFakeDeviceId2[] = "fake_audio_2";
    289   DCHECK(device_names->empty());
    290   DCHECK(use_fake_device_);
    291   device_names->push_back(media::AudioDeviceName(kFakeDeviceName1,
    292                                                  kFakeDeviceId1));
    293   device_names->push_back(media::AudioDeviceName(kFakeDeviceName2,
    294                                                  kFakeDeviceId2));
    295 }
    296 
    297 #if defined(OS_CHROMEOS)
    298 void AudioInputDeviceManager::SetKeyboardMicStreamActiveOnUIThread(
    299     bool active) {
    300   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    301   chromeos::CrasAudioHandler::Get()->SetKeyboardMicActive(active);
    302 }
    303 #endif
    304 
    305 
    306 }  // namespace content
    307