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 21 matching lines...) Expand all Loading... |
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[] = { |
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 DCHECK(device_names->empty()); |
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) { |
| 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'); |
| 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]))) |
| 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(&device_handle, |
| 273 device_name, |
| 274 SND_PCM_STREAM_CAPTURE, |
| 275 SND_PCM_NONBLOCK)) |
| 276 return false; |
| 277 if (wrapper_->PcmClose(device_handle)) |
| 278 return false; |
| 279 |
| 280 return true; |
| 281 } |
| 282 |
| 283 bool AudioManagerLinux::HasAnyAlsaAudioDevice(StreamType stream) { |
| 284 static const char kPcmInterfaceName[] = "pcm"; |
| 285 static const char kIoHintName[] = "IOID"; |
| 286 const char* kNotWantedDevice = |
| 287 (stream == kStreamPlayback ? "Input" : "Output"); |
| 288 void** hints = NULL; |
| 289 bool has_device = false; |
| 290 int card = -1; |
| 291 |
| 292 // Loop through the sound cards. |
| 293 // Don't use snd_device_name_hint(-1,..) since there is a access violation |
| 294 // inside this ALSA API with libasound.so.2.0.0. |
| 295 while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) { |
| 296 int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); |
| 297 if (!error) { |
| 298 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { |
| 299 // Only examine devices that are |stream| capable. Valid values are |
| 300 // "Input", "Output", and NULL which means both input and output. |
| 301 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, |
| 302 kIoHintName)); |
| 303 if (io != NULL && strcmp(kNotWantedDevice, io.get()) == 0) |
| 304 continue; // Wrong type, skip the device. |
| 305 |
| 306 // Found an input device. |
| 307 has_device = true; |
| 308 break; |
| 309 } |
| 310 |
| 311 // Destroy the hints now that we're done with it. |
| 312 wrapper_->DeviceNameFreeHint(hints); |
| 313 hints = NULL; |
| 314 } else { |
| 315 DLOG(WARNING) << "HasAnyAudioDevice: unable to get device hints: " |
| 316 << wrapper_->StrError(error); |
| 317 } |
| 318 } |
| 319 |
| 320 return has_device; |
248 } | 321 } |
249 | 322 |
250 // static | 323 // static |
251 AudioManager* AudioManager::CreateAudioManager() { | 324 AudioManager* AudioManager::CreateAudioManager() { |
252 return new AudioManagerLinux(); | 325 return new AudioManagerLinux(); |
253 } | 326 } |
OLD | NEW |