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 // Empty the name list. |
henrika (OOO until Aug 14)
2011/10/07 12:07:51
remove
no longer working on chromium
2011/10/10 13:49:24
Done.
| |
217 if (HasAudioInputDevices()) { | 177 device_names->clear(); |
218 // Add the default device to the list. | 178 |
219 // We use index 0 to make up the unique_id to identify the | 179 GetAlsaAudioInputDevices(device_names); |
220 // default devices. | 180 |
221 media::AudioDeviceName name; | 181 if (!device_names->empty()) { |
222 name.device_name = AudioManagerBase::kDefaultDeviceName; | 182 // Pre-pend the default device to the list. |
henrika (OOO until Aug 14)
2011/10/07 12:07:51
Perhaps extend the comments why this is needed.
scherkus (not reviewing)
2011/10/07 16:54:45
nit: Pre-pend -> Prepend
also agree w/ henrik on
no longer working on chromium
2011/10/10 13:49:24
Done.
no longer working on chromium
2011/10/10 13:49:24
Done.
| |
223 name.unique_id = "0"; | 183 // We use index 0 to make up the unique_id to identify the default devices. |
224 device_names->push_back(name); | 184 device_names->push_front(media::AudioDeviceName( |
185 AudioManagerBase::kDefaultDeviceName, "0")); | |
henrika (OOO until Aug 14)
2011/10/07 12:07:51
indent by 4
no longer working on chromium
2011/10/10 13:49:24
Done.
| |
225 } | 186 } |
226 } | 187 } |
227 | 188 |
228 bool AudioManagerLinux::HasAnyValidAudioInputDevice(void** hints) { | 189 void AudioManagerLinux::GetAlsaAudioInputDevices( |
190 media::AudioDeviceNames* device_names) { | |
191 | |
scherkus (not reviewing)
2011/10/07 16:54:45
nit: remove blank line
no longer working on chromium
2011/10/10 13:49:24
Done.
| |
192 // Constants specified by the ALSA API for device hints. | |
193 static const char kPcmInterfaceName[] = "pcm"; | |
194 void** hints = NULL; | |
195 int card = -1; | |
196 | |
197 // Loop through the sound cards to get Alsa device hints. | |
henrika (OOO until Aug 14)
2011/10/07 12:07:51
ALSA?
no longer working on chromium
2011/10/10 13:49:24
Done.
| |
198 while (!wrapper_->CardNext(&card) && (card >= 0)) { | |
199 int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); | |
henrika (OOO until Aug 14)
2011/10/07 12:07:51
Do you allocate memory here?
I think you should m
no longer working on chromium
2011/10/10 13:49:24
Done.
| |
200 if (!error) { | |
201 // Get devices | |
henrika (OOO until Aug 14)
2011/10/07 12:07:51
remove
no longer working on chromium
2011/10/10 13:49:24
Done.
| |
202 GetAlsaDevicesInfo(hints, device_names); | |
203 | |
204 // Destroy the hints now that we're done with it. | |
205 wrapper_->DeviceNameFreeHint(hints); | |
206 hints = NULL; | |
henrika (OOO until Aug 14)
2011/10/07 12:07:51
remove
no longer working on chromium
2011/10/10 13:49:24
Done.
| |
207 } else { | |
208 LOG(WARNING) << "GetAudioInputDevices error: unable to get device hints: " | |
henrika (OOO until Aug 14)
2011/10/07 12:07:51
DLOG
no longer working on chromium
2011/10/10 13:49:24
Done.
| |
209 << wrapper_->StrError(error); | |
210 } | |
211 } | |
212 } | |
213 | |
214 void AudioManagerLinux::GetAlsaDevicesInfo( | |
215 void** hints, media::AudioDeviceNames* device_names) { | |
229 static const char kIoHintName[] = "IOID"; | 216 static const char kIoHintName[] = "IOID"; |
230 static const char kNameHintName[] = "NAME"; | 217 static const char kNameHintName[] = "NAME"; |
218 static const char kDescriptionHintName[] = "DESC"; | |
231 static const char kOutputDevice[] = "Output"; | 219 static const char kOutputDevice[] = "Output"; |
232 | 220 |
233 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { | 221 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { |
234 // Only examine devices that are input capable. Valid values are | 222 // Only examine devices that are input capable. Valid values are |
235 // "Input", "Output", and NULL which means both input and output. | 223 // "Input", "Output", and NULL which means both input and output. |
236 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, | 224 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, |
237 kIoHintName)); | 225 kIoHintName)); |
238 if (io != NULL && strcmp(kOutputDevice, io.get()) == 0) | 226 if (io != NULL && strcmp(kOutputDevice, io.get()) == 0) |
239 continue; | 227 continue; |
240 | 228 |
241 scoped_ptr_malloc<char> hint_device_name( | 229 // Get the unique device name for the device. |
230 scoped_ptr_malloc<char> unique_device_name( | |
242 wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName)); | 231 wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName)); |
243 if (IsValidAudioInputDevice(hint_device_name.get())) | 232 // Find out if the device is available. |
henrika (OOO until Aug 14)
2011/10/07 12:07:51
empty line before
no longer working on chromium
2011/10/10 13:49:24
Done.
| |
244 return true; | 233 if (IsAlsaDeviceAvailable(unique_device_name.get())) { |
234 // Get the description for the device. | |
235 scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint( | |
236 *hint_iter, kDescriptionHintName)); | |
237 | |
238 media::AudioDeviceName name; | |
239 name.unique_id = unique_device_name.get(); | |
240 if (desc.get()) { | |
241 // Use the more user friendly description as name. | |
242 // Replace '\n' with '-'. | |
243 char* pret = strchr(desc.get(), '\n'); | |
244 if (pret) | |
245 *pret = '-'; | |
246 name.device_name = desc.get(); | |
247 } else { | |
248 // Virtual devices don't necessarily have descriptions. | |
249 // Use their names instead. | |
250 name.device_name = unique_device_name.get(); | |
251 } | |
252 | |
253 // Store the device information. | |
254 device_names->push_back(name); | |
255 } | |
256 } | |
257 } | |
258 | |
259 bool AudioManagerLinux::IsAlsaDeviceAvailable(const char* device_name) { | |
260 if (!device_name) | |
261 return false; | |
262 | |
263 // Check if the device is in the list of invalid devices. | |
264 for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) { | |
265 if (!strncmp(kInvalidAudioInputDevices[i], device_name, | |
266 strlen(kInvalidAudioInputDevices[i]))) | |
scherkus (not reviewing)
2011/10/07 16:54:45
indent by 1 more space
no longer working on chromium
2011/10/10 13:49:24
Done.
| |
267 return false; | |
245 } | 268 } |
246 | 269 |
247 return false; | 270 // The only way to check if the device is available is to open/close the |
271 // device, return false if it fails either of operations. | |
272 snd_pcm_t* device_handle = NULL; | |
273 if (wrapper_->PcmOpen(&device_handle, device_name, | |
scherkus (not reviewing)
2011/10/07 16:54:45
how long does this take to run?
The call stack is
no longer working on chromium
2011/10/10 13:49:24
I have totally 3 physical devices attached to PC,
| |
274 SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) < 0 || | |
275 wrapper_->PcmClose(device_handle) < 0) { | |
scherkus (not reviewing)
2011/10/07 16:54:45
could you re-write this to be more explicit? usin
no longer working on chromium
2011/10/10 13:49:24
Done.
| |
276 return false; | |
277 } | |
278 | |
279 return true; | |
280 } | |
281 | |
282 bool AudioManagerLinux::HasAnyAlsaAudioDevice(StreamType stream) { | |
283 static const char kPcmInterfaceName[] = "pcm"; | |
284 static const char kIoHintName[] = "IOID"; | |
285 static const char* kNotWantedDevice = | |
286 (stream == kStreamPlayback ? "Input" : "Output"); | |
287 void** hints = NULL; | |
288 bool has_device = false; | |
289 int card = -1; | |
290 | |
291 // Loop through the sound cards. | |
292 // Don't use snd_device_name_hint(-1,..) since there is a access violation | |
293 // inside this ALSA API with libasound.so.2.0.0. | |
scherkus (not reviewing)
2011/10/07 16:54:45
do you know whether we can do a compile time/run t
no longer working on chromium
2011/10/10 13:49:24
I am not sure, probably yes.
But I am not sure if
| |
294 while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) { | |
295 int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); | |
296 if(!error) { | |
scherkus (not reviewing)
2011/10/07 16:54:45
space between if and (
no longer working on chromium
2011/10/10 13:49:24
Done.
| |
297 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { | |
298 // Only examine devices that are |stream| capable. Valid values are | |
299 // "Input", "Output", and NULL which means both input and output. | |
300 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, | |
301 kIoHintName)); | |
302 if (io != NULL && strcmp(kNotWantedDevice, io.get()) == 0) | |
303 continue; // Wrong type, skip the device. | |
304 | |
305 // Found an input device. | |
306 has_device = true; | |
307 break; | |
308 } | |
309 // Destroy the hints now that we're done with it. | |
scherkus (not reviewing)
2011/10/07 16:54:45
nit: blank line before this one
no longer working on chromium
2011/10/10 13:49:24
Done.
| |
310 wrapper_->DeviceNameFreeHint(hints); | |
311 hints = NULL; | |
312 } else { | |
313 LOG(WARNING) << "HasAnyAudioDevice warning: unable to get device hints: " | |
henrika (OOO until Aug 14)
2011/10/07 12:07:51
DLOG
henrika (OOO until Aug 14)
2011/10/07 12:07:51
remove warning
| |
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 |