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