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"
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