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 "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"
19 const int AudioInputDeviceManager::kFakeOpenSessionId
= 1;
22 // Starting id for the first capture session.
23 const int kFirstSessionId
= AudioInputDeviceManager::kFakeOpenSessionId
+ 1;
26 AudioInputDeviceManager::AudioInputDeviceManager(
27 media::AudioManager
* audio_manager
)
29 next_capture_session_id_(kFirstSessionId
),
30 use_fake_device_(false),
31 audio_manager_(audio_manager
) {
34 AudioInputDeviceManager::~AudioInputDeviceManager() {
37 const StreamDeviceInfo
* AudioInputDeviceManager::GetOpenedDeviceInfoById(
39 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
40 StreamDeviceList::iterator device
= GetDevice(session_id
);
41 if (device
== devices_
.end())
47 void AudioInputDeviceManager::Register(
48 MediaStreamProviderListener
* listener
,
49 const scoped_refptr
<base::SingleThreadTaskRunner
>& device_task_runner
) {
50 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
52 DCHECK(!device_task_runner_
.get());
54 device_task_runner_
= device_task_runner
;
57 void AudioInputDeviceManager::Unregister() {
62 void AudioInputDeviceManager::EnumerateDevices(MediaStreamType stream_type
) {
63 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
66 device_task_runner_
->PostTask(
68 base::Bind(&AudioInputDeviceManager::EnumerateOnDeviceThread
,
72 int AudioInputDeviceManager::Open(const StreamDeviceInfo
& device
) {
73 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
74 // Generate a new id for this device.
75 int session_id
= next_capture_session_id_
++;
76 device_task_runner_
->PostTask(
78 base::Bind(&AudioInputDeviceManager::OpenOnDeviceThread
,
79 this, session_id
, device
));
84 void AudioInputDeviceManager::Close(int session_id
) {
85 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
87 StreamDeviceList::iterator device
= GetDevice(session_id
);
88 if (device
== devices_
.end())
90 const MediaStreamType stream_type
= device
->device
.type
;
91 if (session_id
!= kFakeOpenSessionId
)
92 devices_
.erase(device
);
94 // Post a callback through the listener on IO thread since
95 // MediaStreamManager is expecting the callback asynchronously.
96 BrowserThread::PostTask(BrowserThread::IO
,
98 base::Bind(&AudioInputDeviceManager::ClosedOnIOThread
,
99 this, stream_type
, session_id
));
102 void AudioInputDeviceManager::UseFakeDevice() {
103 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
104 use_fake_device_
= true;
107 bool AudioInputDeviceManager::ShouldUseFakeDevice() const {
108 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
109 return use_fake_device_
;
112 void AudioInputDeviceManager::EnumerateOnDeviceThread(
113 MediaStreamType stream_type
) {
114 SCOPED_UMA_HISTOGRAM_TIMER(
115 "Media.AudioInputDeviceManager.EnumerateOnDeviceThreadTime");
116 DCHECK(IsOnDeviceThread());
117 DCHECK_EQ(MEDIA_DEVICE_AUDIO_CAPTURE
, stream_type
);
119 media::AudioDeviceNames device_names
;
120 if (use_fake_device_
) {
121 // Use the fake devices.
122 GetFakeDeviceNames(&device_names
);
124 // Enumerate the devices on the OS.
125 // AudioManager is guaranteed to outlive MediaStreamManager in
127 audio_manager_
->GetAudioInputDeviceNames(&device_names
);
130 scoped_ptr
<StreamDeviceInfoArray
> devices(new StreamDeviceInfoArray());
131 for (media::AudioDeviceNames::iterator it
= device_names
.begin();
132 it
!= device_names
.end(); ++it
) {
133 // Add device information to device vector.
134 devices
->push_back(StreamDeviceInfo(
135 stream_type
, it
->device_name
, it
->unique_id
));
138 // Return the device list through the listener by posting a task on
139 // IO thread since MediaStreamManager handles the callback asynchronously.
140 BrowserThread::PostTask(
143 base::Bind(&AudioInputDeviceManager::DevicesEnumeratedOnIOThread
,
144 this, stream_type
, base::Passed(&devices
)));
147 void AudioInputDeviceManager::OpenOnDeviceThread(
148 int session_id
, const StreamDeviceInfo
& info
) {
149 SCOPED_UMA_HISTOGRAM_TIMER(
150 "Media.AudioInputDeviceManager.OpenOnDeviceThreadTime");
151 DCHECK(IsOnDeviceThread());
153 StreamDeviceInfo
out(info
.device
.type
, info
.device
.name
, info
.device
.id
,
155 out
.session_id
= session_id
;
157 MediaStreamDevice::AudioDeviceParameters
& input_params
= out
.device
.input
;
159 if (use_fake_device_
) {
160 // Don't need to query the hardware information if using fake device.
161 input_params
.sample_rate
= 44100;
162 input_params
.channel_layout
= media::CHANNEL_LAYOUT_STEREO
;
164 // Get the preferred sample rate and channel configuration for the
166 media::AudioParameters params
=
167 audio_manager_
->GetInputStreamParameters(info
.device
.id
);
168 input_params
.sample_rate
= params
.sample_rate();
169 input_params
.channel_layout
= params
.channel_layout();
170 input_params
.frames_per_buffer
= params
.frames_per_buffer();
171 input_params
.effects
= params
.effects();
173 // Add preferred output device information if a matching output device
175 out
.device
.matched_output_device_id
=
176 audio_manager_
->GetAssociatedOutputDeviceID(info
.device
.id
);
177 if (!out
.device
.matched_output_device_id
.empty()) {
178 params
= audio_manager_
->GetOutputStreamParameters(
179 out
.device
.matched_output_device_id
);
180 MediaStreamDevice::AudioDeviceParameters
& matched_output_params
=
181 out
.device
.matched_output
;
182 matched_output_params
.sample_rate
= params
.sample_rate();
183 matched_output_params
.channel_layout
= params
.channel_layout();
184 matched_output_params
.frames_per_buffer
= params
.frames_per_buffer();
188 // Return the |session_id| through the listener by posting a task on
189 // IO thread since MediaStreamManager handles the callback asynchronously.
190 BrowserThread::PostTask(BrowserThread::IO
,
192 base::Bind(&AudioInputDeviceManager::OpenedOnIOThread
,
193 this, session_id
, out
));
196 void AudioInputDeviceManager::DevicesEnumeratedOnIOThread(
197 MediaStreamType stream_type
,
198 scoped_ptr
<StreamDeviceInfoArray
> devices
) {
199 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
200 // Ensure that |devices| gets deleted on exit.
202 listener_
->DevicesEnumerated(stream_type
, *devices
);
205 void AudioInputDeviceManager::OpenedOnIOThread(int session_id
,
206 const StreamDeviceInfo
& info
) {
207 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
208 DCHECK_EQ(session_id
, info
.session_id
);
209 DCHECK(GetDevice(session_id
) == devices_
.end());
211 devices_
.push_back(info
);
214 listener_
->Opened(info
.device
.type
, session_id
);
217 void AudioInputDeviceManager::ClosedOnIOThread(MediaStreamType stream_type
,
219 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
221 listener_
->Closed(stream_type
, session_id
);
224 bool AudioInputDeviceManager::IsOnDeviceThread() const {
225 return device_task_runner_
->BelongsToCurrentThread();
228 AudioInputDeviceManager::StreamDeviceList::iterator
229 AudioInputDeviceManager::GetDevice(int session_id
) {
230 for (StreamDeviceList::iterator
i(devices_
.begin()); i
!= devices_
.end();
232 if (i
->session_id
== session_id
)
236 return devices_
.end();
239 void AudioInputDeviceManager::GetFakeDeviceNames(
240 media::AudioDeviceNames
* device_names
) {
241 static const char kFakeDeviceName1
[] = "Fake Audio 1";
242 static const char kFakeDeviceId1
[] = "fake_audio_1";
243 static const char kFakeDeviceName2
[] = "Fake Audio 2";
244 static const char kFakeDeviceId2
[] = "fake_audio_2";
245 DCHECK(device_names
->empty());
246 DCHECK(use_fake_device_
);
247 device_names
->push_back(media::AudioDeviceName(kFakeDeviceName1
,
249 device_names
->push_back(media::AudioDeviceName(kFakeDeviceName2
,
253 } // namespace content