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_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"
20 const int AudioInputDeviceManager::kFakeOpenSessionId
= 1;
23 // Starting id for the first capture session.
24 const int kFirstSessionId
= AudioInputDeviceManager::kFakeOpenSessionId
+ 1;
27 AudioInputDeviceManager::AudioInputDeviceManager(
28 media::AudioManager
* audio_manager
)
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
);
42 AudioInputDeviceManager::~AudioInputDeviceManager() {
45 const StreamDeviceInfo
* AudioInputDeviceManager::GetOpenedDeviceInfoById(
47 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
48 StreamDeviceList::iterator device
= GetDevice(session_id
);
49 if (device
== devices_
.end())
55 void AudioInputDeviceManager::Register(
56 MediaStreamProviderListener
* listener
,
57 base::MessageLoopProxy
* device_thread_loop
) {
58 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
60 DCHECK(!device_loop_
.get());
62 device_loop_
= device_thread_loop
;
65 void AudioInputDeviceManager::Unregister() {
70 void AudioInputDeviceManager::EnumerateDevices(MediaStreamType stream_type
) {
71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
74 device_loop_
->PostTask(
76 base::Bind(&AudioInputDeviceManager::EnumerateOnDeviceThread
,
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(
86 base::Bind(&AudioInputDeviceManager::OpenOnDeviceThread
,
87 this, session_id
, device
));
92 void AudioInputDeviceManager::Close(int session_id
) {
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
95 StreamDeviceList::iterator device
= GetDevice(session_id
);
96 if (device
== devices_
.end())
98 const MediaStreamType stream_type
= device
->device
.type
;
99 if (session_id
!= kFakeOpenSessionId
)
100 devices_
.erase(device
);
102 // Post a callback through the listener on IO thread since
103 // MediaStreamManager is expecting the callback asynchronously.
104 BrowserThread::PostTask(BrowserThread::IO
,
106 base::Bind(&AudioInputDeviceManager::ClosedOnIOThread
,
107 this, stream_type
, session_id
));
110 void AudioInputDeviceManager::UseFakeDevice() {
111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
112 use_fake_device_
= true;
115 bool AudioInputDeviceManager::ShouldUseFakeDevice() const {
116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
117 return use_fake_device_
;
120 void AudioInputDeviceManager::EnumerateOnDeviceThread(
121 MediaStreamType stream_type
) {
122 SCOPED_UMA_HISTOGRAM_TIMER(
123 "Media.AudioInputDeviceManager.EnumerateOnDeviceThreadTime");
124 DCHECK(IsOnDeviceThread());
126 media::AudioDeviceNames device_names
;
128 switch (stream_type
) {
129 case MEDIA_DEVICE_AUDIO_CAPTURE
:
130 // AudioManager is guaranteed to outlive MediaStreamManager in
132 audio_manager_
->GetAudioInputDeviceNames(&device_names
);
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));
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));
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(
161 base::Bind(&AudioInputDeviceManager::DevicesEnumeratedOnIOThread
,
162 this, stream_type
, base::Passed(&devices
)));
165 void AudioInputDeviceManager::OpenOnDeviceThread(
166 int session_id
, const StreamDeviceInfo
& info
) {
167 SCOPED_UMA_HISTOGRAM_TIMER(
168 "Media.AudioInputDeviceManager.OpenOnDeviceThreadTime");
169 DCHECK(IsOnDeviceThread());
171 StreamDeviceInfo
out(info
.device
.type
, info
.device
.name
, info
.device
.id
,
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
;
179 // Get the preferred sample rate and channel configuration for the
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();
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
,
191 base::Bind(&AudioInputDeviceManager::OpenedOnIOThread
,
192 this, session_id
, out
));
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.
201 listener_
->DevicesEnumerated(stream_type
, *devices
);
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
);
212 listener_
->Opened(info
.device
.type
, session_id
);
215 void AudioInputDeviceManager::ClosedOnIOThread(MediaStreamType stream_type
,
217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
219 listener_
->Closed(stream_type
, session_id
);
222 bool AudioInputDeviceManager::IsOnDeviceThread() const {
223 return device_loop_
->BelongsToCurrentThread();
226 AudioInputDeviceManager::StreamDeviceList::iterator
227 AudioInputDeviceManager::GetDevice(int session_id
) {
228 for (StreamDeviceList::iterator
i(devices_
.begin()); i
!= devices_
.end();
230 if (i
->session_id
== session_id
)
234 return devices_
.end();
237 } // namespace content