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 21namespace content { 22 23const int AudioInputDeviceManager::kFakeOpenSessionId = 1; 24 25namespace { 26// Starting id for the first capture session. 27const int kFirstSessionId = AudioInputDeviceManager::kFakeOpenSessionId + 1; 28} 29 30AudioInputDeviceManager::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 41AudioInputDeviceManager::~AudioInputDeviceManager() { 42} 43 44const 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 54void 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 64void AudioInputDeviceManager::Unregister() { 65 DCHECK(listener_); 66 listener_ = NULL; 67} 68 69void 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 79int 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 91void 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 109void AudioInputDeviceManager::UseFakeDevice() { 110 DCHECK_CURRENTLY_ON(BrowserThread::IO); 111 use_fake_device_ = true; 112} 113 114bool AudioInputDeviceManager::ShouldUseFakeDevice() const { 115 DCHECK_CURRENTLY_ON(BrowserThread::IO); 116 return use_fake_device_; 117} 118 119#if defined(OS_CHROMEOS) 120void 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 139void 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 156void 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 191void 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 240void 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 249void 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 261void 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 268bool AudioInputDeviceManager::IsOnDeviceThread() const { 269 return device_task_runner_->BelongsToCurrentThread(); 270} 271 272AudioInputDeviceManager::StreamDeviceList::iterator 273AudioInputDeviceManager::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 283void 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) 298void 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