OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/audio/linux/audio_manager_linux.h" | 5 #include "media/audio/linux/audio_manager_linux.h" |
6 | 6 |
7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
8 #include "base/environment.h" | 8 #include "base/environment.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/nix/xdg_util.h" | 10 #include "base/nix/xdg_util.h" |
(...skipping 13 matching lines...) Expand all Loading... | |
24 | 24 |
25 // Maximum number of output streams that can be open simultaneously. | 25 // Maximum number of output streams that can be open simultaneously. |
26 static const size_t kMaxOutputStreams = 50; | 26 static const size_t kMaxOutputStreams = 50; |
27 | 27 |
28 static const int kMaxInputChannels = 2; | 28 static const int kMaxInputChannels = 2; |
29 | 29 |
30 // Since "default", "pulse" and "dmix" devices are virtual devices mapped to | 30 // Since "default", "pulse" and "dmix" devices are virtual devices mapped to |
31 // real devices, we remove them from the list to avoiding duplicate counting. | 31 // real devices, we remove them from the list to avoiding duplicate counting. |
32 // In addition, note that we support no more than 2 channels for recording, | 32 // In addition, note that we support no more than 2 channels for recording, |
33 // hence surround devices are not stored in the list. | 33 // hence surround devices are not stored in the list. |
34 static const char* kInvalidAudioInputDevices[] = { | 34 static const char* kInvalidAudioInputDevices[] = { |
tommi (sloooow) - chröme
2011/10/24 07:05:01
nit: I'm wondering if this could be [][] instead o
no longer working on chromium
2011/10/25 20:35:53
Sorry, I don't fully understand here.
I need the s
| |
35 "default", | 35 "default", |
36 "null", | 36 "null", |
37 "pulse", | 37 "pulse", |
38 "dmix", | 38 "dmix", |
39 "surround", | 39 "surround", |
40 }; | 40 }; |
41 | 41 |
42 static bool IsValidAudioInputDevice(const char* device_name) { | |
43 if (!device_name) | |
44 return false; | |
45 | |
46 for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) { | |
47 if (strcmp(kInvalidAudioInputDevices[i], device_name) == 0) | |
48 return false; | |
49 } | |
50 | |
51 return true; | |
52 } | |
53 | |
54 // Implementation of AudioManager. | 42 // Implementation of AudioManager. |
55 bool AudioManagerLinux::HasAudioOutputDevices() { | 43 bool AudioManagerLinux::HasAudioOutputDevices() { |
56 // TODO(ajwong): Make this actually query audio devices. | 44 return HasAnyAlsaAudioDevice(kStreamPlayback); |
57 return true; | |
58 } | 45 } |
59 | 46 |
60 bool AudioManagerLinux::HasAudioInputDevices() { | 47 bool AudioManagerLinux::HasAudioInputDevices() { |
61 if (!initialized()) { | 48 return HasAnyAlsaAudioDevice(kStreamCapture); |
62 return false; | |
63 } | |
64 | |
65 // Constants specified by the ALSA API for device hints. | |
66 static const char kPcmInterfaceName[] = "pcm"; | |
67 bool has_device = false; | |
68 void** hints = NULL; | |
69 int card = -1; | |
70 | |
71 // Loop through the sound cards to get Alsa device hints. | |
72 // Don't use snd_device_name_hint(-1,..) since there is a access violation | |
73 // inside this ALSA API with libasound.so.2.0.0. | |
74 while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) { | |
75 int error = wrapper_->DeviceNameHint(card, | |
76 kPcmInterfaceName, | |
77 &hints); | |
78 if (error == 0) { | |
79 has_device = HasAnyValidAudioInputDevice(hints); | |
80 | |
81 // Destroy the hints now that we're done with it. | |
82 wrapper_->DeviceNameFreeHint(hints); | |
83 hints = NULL; | |
84 } else { | |
85 LOG(ERROR) << "Unable to get device hints: " << wrapper_->StrError(error); | |
86 } | |
87 } | |
88 | |
89 return has_device; | |
90 } | 49 } |
91 | 50 |
92 AudioOutputStream* AudioManagerLinux::MakeAudioOutputStream( | 51 AudioOutputStream* AudioManagerLinux::MakeAudioOutputStream( |
93 const AudioParameters& params) { | 52 const AudioParameters& params) { |
94 // Early return for testing hook. Do this before checking for | 53 // Early return for testing hook. Do this before checking for |
95 // |initialized_|. | 54 // |initialized_|. |
96 if (params.format == AudioParameters::AUDIO_MOCK) { | 55 if (params.format == AudioParameters::AUDIO_MOCK) { |
97 return FakeAudioOutputStream::MakeFakeStream(params); | 56 return FakeAudioOutputStream::MakeFakeStream(params); |
98 } | 57 } |
99 | 58 |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
134 | 93 |
135 if (params.format == AudioParameters::AUDIO_MOCK) { | 94 if (params.format == AudioParameters::AUDIO_MOCK) { |
136 return FakeAudioInputStream::MakeFakeStream(params); | 95 return FakeAudioInputStream::MakeFakeStream(params); |
137 } else if (params.format != AudioParameters::AUDIO_PCM_LINEAR) { | 96 } else if (params.format != AudioParameters::AUDIO_PCM_LINEAR) { |
138 return NULL; | 97 return NULL; |
139 } | 98 } |
140 | 99 |
141 if (!initialized()) | 100 if (!initialized()) |
142 return NULL; | 101 return NULL; |
143 | 102 |
103 // TODO(xians): Pass the device name From AudioInputController instead. | |
144 std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice; | 104 std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice; |
145 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAlsaInputDevice)) { | 105 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAlsaInputDevice)) { |
146 device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | 106 device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
147 switches::kAlsaInputDevice); | 107 switches::kAlsaInputDevice); |
148 } | 108 } |
149 | 109 |
150 AlsaPcmInputStream* stream = new AlsaPcmInputStream( | 110 AlsaPcmInputStream* stream = new AlsaPcmInputStream( |
151 device_name, params, wrapper_.get()); | 111 device_name, params, wrapper_.get()); |
152 | 112 |
153 return stream; | 113 return stream; |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
206 base::nix::DesktopEnvironment desktop = base::nix::GetDesktopEnvironment( | 166 base::nix::DesktopEnvironment desktop = base::nix::GetDesktopEnvironment( |
207 env.get()); | 167 env.get()); |
208 std::string command((desktop == base::nix::DESKTOP_ENVIRONMENT_GNOME) ? | 168 std::string command((desktop == base::nix::DESKTOP_ENVIRONMENT_GNOME) ? |
209 "gnome-volume-control" : "kmix"); | 169 "gnome-volume-control" : "kmix"); |
210 base::LaunchProcess(CommandLine(FilePath(command)), base::LaunchOptions(), | 170 base::LaunchProcess(CommandLine(FilePath(command)), base::LaunchOptions(), |
211 NULL); | 171 NULL); |
212 } | 172 } |
213 | 173 |
214 void AudioManagerLinux::GetAudioInputDeviceNames( | 174 void AudioManagerLinux::GetAudioInputDeviceNames( |
215 media::AudioDeviceNames* device_names) { | 175 media::AudioDeviceNames* device_names) { |
216 // TODO(xians): query a full list of valid devices. | 176 device_names->clear(); |
tommi (sloooow) - chröme
2011/10/24 07:05:01
nit: you could also make it a requirement that the
no longer working on chromium
2011/10/25 20:35:53
Done.
| |
217 if (HasAudioInputDevices()) { | 177 |
218 // Add the default device to the list. | 178 GetAlsaAudioInputDevices(device_names); |
219 // We use index 0 to make up the unique_id to identify the | 179 |
220 // default devices. | 180 if (!device_names->empty()) { |
221 media::AudioDeviceName name; | 181 // Prepend the default device to the list since we always want it to be |
222 name.device_name = AudioManagerBase::kDefaultDeviceName; | 182 // on the top of the list for all platforms. There is no duplicate |
223 name.unique_id = "0"; | 183 // counting here since the default device has been abstracted out before. |
224 device_names->push_back(name); | 184 // We use index 0 to make up the unique_id to identify the default device. |
185 device_names->push_front(media::AudioDeviceName( | |
186 AudioManagerBase::kDefaultDeviceName, "0")); | |
225 } | 187 } |
226 } | 188 } |
227 | 189 |
228 bool AudioManagerLinux::HasAnyValidAudioInputDevice(void** hints) { | 190 void AudioManagerLinux::GetAlsaAudioInputDevices( |
191 media::AudioDeviceNames* device_names) { | |
192 // Constants specified by the ALSA API for device hints. | |
193 static const char kPcmInterfaceName[] = "pcm"; | |
194 int card = -1; | |
195 | |
196 // Loop through the sound cards to get ALSA device hints. | |
197 while (!wrapper_->CardNext(&card) && (card >= 0)) { | |
tommi (sloooow) - chröme
2011/10/24 07:05:01
nit: no need for () around the second expression
no longer working on chromium
2011/10/25 20:35:53
Done.
| |
198 void** hints = NULL; | |
199 int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); | |
200 if (!error) { | |
201 GetAlsaDevicesInfo(hints, device_names); | |
202 | |
203 // Destroy the hints now that we're done with it. | |
204 wrapper_->DeviceNameFreeHint(hints); | |
205 } else { | |
206 DLOG(WARNING) << "GetAudioInputDevices: unable to get device hints: " | |
207 << wrapper_->StrError(error); | |
208 } | |
209 } | |
210 } | |
211 | |
212 void AudioManagerLinux::GetAlsaDevicesInfo( | |
213 void** hints, media::AudioDeviceNames* device_names) { | |
229 static const char kIoHintName[] = "IOID"; | 214 static const char kIoHintName[] = "IOID"; |
230 static const char kNameHintName[] = "NAME"; | 215 static const char kNameHintName[] = "NAME"; |
216 static const char kDescriptionHintName[] = "DESC"; | |
231 static const char kOutputDevice[] = "Output"; | 217 static const char kOutputDevice[] = "Output"; |
232 | 218 |
233 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { | 219 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { |
234 // Only examine devices that are input capable. Valid values are | 220 // Only examine devices that are input capable. Valid values are |
235 // "Input", "Output", and NULL which means both input and output. | 221 // "Input", "Output", and NULL which means both input and output. |
236 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, | 222 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, |
237 kIoHintName)); | 223 kIoHintName)); |
238 if (io != NULL && strcmp(kOutputDevice, io.get()) == 0) | 224 if (io != NULL && strcmp(kOutputDevice, io.get()) == 0) |
239 continue; | 225 continue; |
240 | 226 |
241 scoped_ptr_malloc<char> hint_device_name( | 227 // Get the unique device name for the device. |
228 scoped_ptr_malloc<char> unique_device_name( | |
242 wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName)); | 229 wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName)); |
243 if (IsValidAudioInputDevice(hint_device_name.get())) | 230 |
244 return true; | 231 // Find out if the device is available. |
232 if (IsAlsaDeviceAvailable(unique_device_name.get())) { | |
233 // Get the description for the device. | |
234 scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint( | |
235 *hint_iter, kDescriptionHintName)); | |
236 | |
237 media::AudioDeviceName name; | |
238 name.unique_id = unique_device_name.get(); | |
239 if (desc.get()) { | |
240 // Use the more user friendly description as name. | |
241 // Replace '\n' with '-'. | |
242 char* pret = strchr(desc.get(), '\n'); | |
tommi (sloooow) - chröme
2011/10/24 07:05:01
nit: This won't replace all occurrences of \n but
no longer working on chromium
2011/10/25 20:35:53
It is not a problem here since the string only con
| |
243 if (pret) | |
244 *pret = '-'; | |
245 name.device_name = desc.get(); | |
246 } else { | |
247 // Virtual devices don't necessarily have descriptions. | |
248 // Use their names instead. | |
249 name.device_name = unique_device_name.get(); | |
250 } | |
251 | |
252 // Store the device information. | |
253 device_names->push_back(name); | |
254 } | |
255 } | |
256 } | |
257 | |
258 bool AudioManagerLinux::IsAlsaDeviceAvailable(const char* device_name) { | |
259 if (!device_name) | |
260 return false; | |
261 | |
262 // Check if the device is in the list of invalid devices. | |
263 for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) { | |
264 if (!strncmp(kInvalidAudioInputDevices[i], device_name, | |
265 strlen(kInvalidAudioInputDevices[i]))) | |
tommi (sloooow) - chröme
2011/10/24 07:05:01
nit: see comment at the top for the strlen call
| |
266 return false; | |
245 } | 267 } |
246 | 268 |
247 return false; | 269 // The only way to check if the device is available is to open/close the |
270 // device. Return false if it fails either of operations. | |
271 snd_pcm_t* device_handle = NULL; | |
272 if (wrapper_->PcmOpen( | |
273 &device_handle, device_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) | |
tommi (sloooow) - chröme
2011/10/24 07:05:01
I think this should be indented 4 more spaces so t
| |
274 return false; | |
275 if (wrapper_->PcmClose(device_handle)) | |
276 return false; | |
277 | |
278 return true; | |
279 } | |
280 | |
281 bool AudioManagerLinux::HasAnyAlsaAudioDevice(StreamType stream) { | |
282 static const char kPcmInterfaceName[] = "pcm"; | |
283 static const char kIoHintName[] = "IOID"; | |
284 const char* kNotWantedDevice = | |
285 (stream == kStreamPlayback ? "Input" : "Output"); | |
286 void** hints = NULL; | |
287 bool has_device = false; | |
288 int card = -1; | |
289 | |
290 // Loop through the sound cards. | |
291 // Don't use snd_device_name_hint(-1,..) since there is a access violation | |
292 // inside this ALSA API with libasound.so.2.0.0. | |
293 while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) { | |
294 int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); | |
295 if (!error) { | |
296 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { | |
297 // Only examine devices that are |stream| capable. Valid values are | |
298 // "Input", "Output", and NULL which means both input and output. | |
299 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, | |
300 kIoHintName)); | |
301 if (io != NULL && strcmp(kNotWantedDevice, io.get()) == 0) | |
302 continue; // Wrong type, skip the device. | |
303 | |
304 // Found an input device. | |
305 has_device = true; | |
306 break; | |
307 } | |
308 | |
309 // Destroy the hints now that we're done with it. | |
310 wrapper_->DeviceNameFreeHint(hints); | |
311 hints = NULL; | |
312 } else { | |
313 DLOG(WARNING) << "HasAnyAudioDevice: unable to get device hints: " | |
314 << wrapper_->StrError(error); | |
315 } | |
316 } | |
317 | |
318 return has_device; | |
248 } | 319 } |
249 | 320 |
250 // static | 321 // static |
251 AudioManager* AudioManager::CreateAudioManager() { | 322 AudioManager* AudioManager::CreateAudioManager() { |
252 return new AudioManagerLinux(); | 323 return new AudioManagerLinux(); |
253 } | 324 } |
OLD | NEW |