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