Add a virtual beamforming audio device on ChromeOS.
[chromium-blink-merge.git] / content / browser / renderer_host / media / audio_input_device_manager.cc
blob5b231b05555511e016a189aec0df2413f204ab74
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.
5 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
7 #include "base/bind.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/metrics/histogram_macros.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "content/public/common/media_stream_request.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"
17 #if defined(OS_CHROMEOS)
18 #include "chromeos/audio/cras_audio_handler.h"
19 #endif
21 namespace content {
23 const int AudioInputDeviceManager::kFakeOpenSessionId = 1;
25 namespace {
26 // Starting id for the first capture session.
27 const int kFirstSessionId = AudioInputDeviceManager::kFakeOpenSessionId + 1;
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) {
41 AudioInputDeviceManager::~AudioInputDeviceManager() {
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;
51 return &(*device);
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;
64 void AudioInputDeviceManager::Unregister() {
65 DCHECK(listener_);
66 listener_ = NULL;
69 void AudioInputDeviceManager::EnumerateDevices(MediaStreamType stream_type) {
70 DCHECK_CURRENTLY_ON(BrowserThread::IO);
71 DCHECK(listener_);
73 device_task_runner_->PostTask(
74 FROM_HERE,
75 base::Bind(&AudioInputDeviceManager::EnumerateOnDeviceThread,
76 this, stream_type));
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));
88 return session_id;
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);
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));
109 void AudioInputDeviceManager::UseFakeDevice() {
110 DCHECK_CURRENTLY_ON(BrowserThread::IO);
111 use_fake_device_ = true;
114 bool AudioInputDeviceManager::ShouldUseFakeDevice() const {
115 DCHECK_CURRENTLY_ON(BrowserThread::IO);
116 return use_fake_device_;
119 #if defined(OS_CHROMEOS)
120 void AudioInputDeviceManager::RegisterKeyboardMicStream(
121 const base::Closure& callback) {
122 DCHECK_CURRENTLY_ON(BrowserThread::IO);
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();
139 void AudioInputDeviceManager::UnregisterKeyboardMicStream() {
140 DCHECK_CURRENTLY_ON(BrowserThread::IO);
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));
154 #endif
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);
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);
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));
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)));
191 void AudioInputDeviceManager::OpenOnDeviceThread(
192 int session_id, const StreamDeviceInfo& info) {
193 SCOPED_UMA_HISTOGRAM_TIMER(
194 "Media.AudioInputDeviceManager.OpenOnDeviceThreadTime");
195 DCHECK(IsOnDeviceThread());
197 StreamDeviceInfo out(info.device.type, info.device.name, info.device.id,
198 0, 0, 0);
199 out.session_id = session_id;
201 MediaStreamDevice::AudioDeviceParameters& input_params = out.device.input;
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 input_params.mic_positions = params.mic_positions();
218 // Add preferred output device information if a matching output device
219 // exists.
220 out.device.matched_output_device_id =
221 audio_manager_->GetAssociatedOutputDeviceID(info.device.id);
222 if (!out.device.matched_output_device_id.empty()) {
223 params = audio_manager_->GetOutputStreamParameters(
224 out.device.matched_output_device_id);
225 MediaStreamDevice::AudioDeviceParameters& matched_output_params =
226 out.device.matched_output;
227 matched_output_params.sample_rate = params.sample_rate();
228 matched_output_params.channel_layout = params.channel_layout();
229 matched_output_params.frames_per_buffer = params.frames_per_buffer();
233 // Return the |session_id| through the listener by posting a task on
234 // IO thread since MediaStreamManager handles the callback asynchronously.
235 BrowserThread::PostTask(BrowserThread::IO,
236 FROM_HERE,
237 base::Bind(&AudioInputDeviceManager::OpenedOnIOThread,
238 this, session_id, out));
241 void AudioInputDeviceManager::DevicesEnumeratedOnIOThread(
242 MediaStreamType stream_type,
243 scoped_ptr<StreamDeviceInfoArray> devices) {
244 DCHECK_CURRENTLY_ON(BrowserThread::IO);
245 // Ensure that |devices| gets deleted on exit.
246 if (listener_)
247 listener_->DevicesEnumerated(stream_type, *devices);
250 void AudioInputDeviceManager::OpenedOnIOThread(int session_id,
251 const StreamDeviceInfo& info) {
252 DCHECK_CURRENTLY_ON(BrowserThread::IO);
253 DCHECK_EQ(session_id, info.session_id);
254 DCHECK(GetDevice(session_id) == devices_.end());
256 devices_.push_back(info);
258 if (listener_)
259 listener_->Opened(info.device.type, session_id);
262 void AudioInputDeviceManager::ClosedOnIOThread(MediaStreamType stream_type,
263 int session_id) {
264 DCHECK_CURRENTLY_ON(BrowserThread::IO);
265 if (listener_)
266 listener_->Closed(stream_type, session_id);
269 bool AudioInputDeviceManager::IsOnDeviceThread() const {
270 return device_task_runner_->BelongsToCurrentThread();
273 AudioInputDeviceManager::StreamDeviceList::iterator
274 AudioInputDeviceManager::GetDevice(int session_id) {
275 for (StreamDeviceList::iterator i(devices_.begin()); i != devices_.end();
276 ++i) {
277 if (i->session_id == session_id)
278 return i;
281 return devices_.end();
284 void AudioInputDeviceManager::GetFakeDeviceNames(
285 media::AudioDeviceNames* device_names) {
286 static const char kFakeDeviceName1[] = "Fake Audio 1";
287 static const char kFakeDeviceId1[] = "fake_audio_1";
288 static const char kFakeDeviceName2[] = "Fake Audio 2";
289 static const char kFakeDeviceId2[] = "fake_audio_2";
290 DCHECK(device_names->empty());
291 DCHECK(use_fake_device_);
292 device_names->push_back(media::AudioDeviceName(kFakeDeviceName1,
293 kFakeDeviceId1));
294 device_names->push_back(media::AudioDeviceName(kFakeDeviceName2,
295 kFakeDeviceId2));
298 #if defined(OS_CHROMEOS)
299 void AudioInputDeviceManager::SetKeyboardMicStreamActiveOnUIThread(
300 bool active) {
301 DCHECK_CURRENTLY_ON(BrowserThread::UI);
302 chromeos::CrasAudioHandler::Get()->SetKeyboardMicActive(active);
304 #endif
307 } // namespace content