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"
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"
23 const int AudioInputDeviceManager::kFakeOpenSessionId
= 1;
26 // Starting id for the first capture session.
27 const int kFirstSessionId
= AudioInputDeviceManager::kFakeOpenSessionId
+ 1;
30 AudioInputDeviceManager::AudioInputDeviceManager(
31 media::AudioManager
* audio_manager
)
33 next_capture_session_id_(kFirstSessionId
),
34 use_fake_device_(false),
35 #if defined(OS_CHROMEOS)
36 keyboard_mic_streams_count_(0),
38 audio_manager_(audio_manager
) {
41 AudioInputDeviceManager::~AudioInputDeviceManager() {
44 const StreamDeviceInfo
* AudioInputDeviceManager::GetOpenedDeviceInfoById(
46 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
47 StreamDeviceList::iterator device
= GetDevice(session_id
);
48 if (device
== devices_
.end())
54 void AudioInputDeviceManager::Register(
55 MediaStreamProviderListener
* listener
,
56 const scoped_refptr
<base::SingleThreadTaskRunner
>& device_task_runner
) {
57 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
59 DCHECK(!device_task_runner_
.get());
61 device_task_runner_
= device_task_runner
;
64 void AudioInputDeviceManager::Unregister() {
69 void AudioInputDeviceManager::EnumerateDevices(MediaStreamType stream_type
) {
70 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
73 device_task_runner_
->PostTask(
75 base::Bind(&AudioInputDeviceManager::EnumerateOnDeviceThread
,
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(
85 base::Bind(&AudioInputDeviceManager::OpenOnDeviceThread
,
86 this, session_id
, device
));
91 void AudioInputDeviceManager::Close(int session_id
) {
92 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
94 StreamDeviceList::iterator device
= GetDevice(session_id
);
95 if (device
== devices_
.end())
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
,
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(
130 &AudioInputDeviceManager::SetKeyboardMicStreamActiveOnUIThread
,
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(
149 &AudioInputDeviceManager::SetKeyboardMicStreamActiveOnUIThread
,
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
);
168 // Enumerate the devices on the OS.
169 // AudioManager is guaranteed to outlive MediaStreamManager in
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(
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
,
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
;
208 // Get the preferred sample rate and channel configuration for the
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();
217 // Add preferred output device information if a matching output device
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();
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
,
236 base::Bind(&AudioInputDeviceManager::OpenedOnIOThread
,
237 this, session_id
, out
));
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.
246 listener_
->DevicesEnumerated(stream_type
, *devices
);
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());
255 devices_
.push_back(info
);
258 listener_
->Opened(info
.device
.type
, session_id
);
261 void AudioInputDeviceManager::ClosedOnIOThread(MediaStreamType stream_type
,
263 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
265 listener_
->Closed(stream_type
, session_id
);
268 bool AudioInputDeviceManager::IsOnDeviceThread() const {
269 return device_task_runner_
->BelongsToCurrentThread();
272 AudioInputDeviceManager::StreamDeviceList::iterator
273 AudioInputDeviceManager::GetDevice(int session_id
) {
274 for (StreamDeviceList::iterator
i(devices_
.begin()); i
!= devices_
.end();
276 if (i
->session_id
== session_id
)
280 return devices_
.end();
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
,
293 device_names
->push_back(media::AudioDeviceName(kFakeDeviceName2
,
297 #if defined(OS_CHROMEOS)
298 void AudioInputDeviceManager::SetKeyboardMicStreamActiveOnUIThread(
300 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
301 chromeos::CrasAudioHandler::Get()->SetKeyboardMicActive(active
);
306 } // namespace content