Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(836)

Side by Side Diff: media/audio/linux/audio_manager_linux.cc

Issue 23480030: Output device enumeration for Alsa, plus unit test. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@mediaPulseaudio
Patch Set: Merge parent, and add default device. Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 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/files/file_path.h" 9 #include "base/files/file_path.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
(...skipping 24 matching lines...) Expand all
35 35
36 // Default sample rate for input and output streams. 36 // Default sample rate for input and output streams.
37 static const int kDefaultSampleRate = 48000; 37 static const int kDefaultSampleRate = 48000;
38 38
39 // Since "default", "pulse" and "dmix" devices are virtual devices mapped to 39 // Since "default", "pulse" and "dmix" devices are virtual devices mapped to
40 // real devices, we remove them from the list to avoiding duplicate counting. 40 // real devices, we remove them from the list to avoiding duplicate counting.
41 // In addition, note that we support no more than 2 channels for recording, 41 // In addition, note that we support no more than 2 channels for recording,
42 // hence surround devices are not stored in the list. 42 // hence surround devices are not stored in the list.
43 static const char* kInvalidAudioInputDevices[] = { 43 static const char* kInvalidAudioInputDevices[] = {
44 "default", 44 "default",
45 "dmix",
45 "null", 46 "null",
46 "pulse", 47 "pulse",
47 "dmix",
48 "surround", 48 "surround",
49 }; 49 };
50 50
51 enum LinuxAudioIO { 51 enum LinuxAudioIO {
52 kPulse, 52 kPulse,
53 kAlsa, 53 kAlsa,
54 kCras, 54 kCras,
55 kAudioIOMax // Must always be last! 55 kAudioIOMax // Must always be last!
56 }; 56 };
57 57
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
98 Shutdown(); 98 Shutdown();
99 } 99 }
100 100
101 void AudioManagerLinux::ShowAudioInputSettings() { 101 void AudioManagerLinux::ShowAudioInputSettings() {
102 ShowLinuxAudioInputSettings(); 102 ShowLinuxAudioInputSettings();
103 } 103 }
104 104
105 void AudioManagerLinux::GetAudioInputDeviceNames( 105 void AudioManagerLinux::GetAudioInputDeviceNames(
106 media::AudioDeviceNames* device_names) { 106 media::AudioDeviceNames* device_names) {
107 DCHECK(device_names->empty()); 107 DCHECK(device_names->empty());
108 GetAlsaAudioInputDevices(device_names); 108 GetAlsaAudioDevices(kStreamCapture, device_names);
109 }
110
111 void AudioManagerLinux::GetAudioOutputDeviceNames(
112 media::AudioDeviceNames* device_names) {
113 DCHECK(device_names->empty());
114 GetAlsaAudioDevices(kStreamPlayback, device_names);
109 } 115 }
110 116
111 AudioParameters AudioManagerLinux::GetInputStreamParameters( 117 AudioParameters AudioManagerLinux::GetInputStreamParameters(
112 const std::string& device_id) { 118 const std::string& device_id) {
113 static const int kDefaultInputBufferSize = 1024; 119 static const int kDefaultInputBufferSize = 1024;
114 120
115 return AudioParameters( 121 return AudioParameters(
116 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, 122 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
117 kDefaultSampleRate, 16, kDefaultInputBufferSize); 123 kDefaultSampleRate, 16, kDefaultInputBufferSize);
118 } 124 }
119 125
120 void AudioManagerLinux::GetAlsaAudioInputDevices( 126 void AudioManagerLinux::GetAlsaAudioDevices(
127 StreamType type,
121 media::AudioDeviceNames* device_names) { 128 media::AudioDeviceNames* device_names) {
122 // Constants specified by the ALSA API for device hints. 129 // Constants specified by the ALSA API for device hints.
123 static const char kPcmInterfaceName[] = "pcm"; 130 static const char kPcmInterfaceName[] = "pcm";
124 int card = -1; 131 int card = -1;
125 132
126 // Loop through the sound cards to get ALSA device hints. 133 // Loop through the sound cards to get ALSA device hints.
127 while (!wrapper_->CardNext(&card) && card >= 0) { 134 while (!wrapper_->CardNext(&card) && card >= 0) {
128 void** hints = NULL; 135 void** hints = NULL;
129 int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); 136 int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
130 if (!error) { 137 if (!error) {
131 GetAlsaDevicesInfo(hints, device_names); 138 GetAlsaDevicesInfo(type, hints, device_names);
132 139
133 // Destroy the hints now that we're done with it. 140 // Destroy the hints now that we're done with it.
134 wrapper_->DeviceNameFreeHint(hints); 141 wrapper_->DeviceNameFreeHint(hints);
135 } else { 142 } else {
136 DLOG(WARNING) << "GetAudioInputDevices: unable to get device hints: " 143 DLOG(WARNING) << "GetAlsaAudioDevices: unable to get device hints: "
137 << wrapper_->StrError(error); 144 << wrapper_->StrError(error);
138 } 145 }
139 } 146 }
140 } 147 }
141 148
142 void AudioManagerLinux::GetAlsaDevicesInfo( 149 void AudioManagerLinux::GetAlsaDevicesInfo(
143 void** hints, media::AudioDeviceNames* device_names) { 150 AudioManagerLinux::StreamType type,
151 void** hints,
152 media::AudioDeviceNames* device_names) {
144 static const char kIoHintName[] = "IOID"; 153 static const char kIoHintName[] = "IOID";
145 static const char kNameHintName[] = "NAME"; 154 static const char kNameHintName[] = "NAME";
146 static const char kDescriptionHintName[] = "DESC"; 155 static const char kDescriptionHintName[] = "DESC";
147 static const char kOutputDevice[] = "Output"; 156
157 const char* unwanted_device_type = UnwantedDeviceTypeWhenEnumerating(type);
148 158
149 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { 159 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
150 // Only examine devices that are input capable. Valid values are 160 // Only examine devices of the right type. Valid values are
151 // "Input", "Output", and NULL which means both input and output. 161 // "Input", "Output", and NULL which means both input and output.
152 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, 162 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter,
153 kIoHintName)); 163 kIoHintName));
154 if (io != NULL && strcmp(kOutputDevice, io.get()) == 0) 164 if (io != NULL && strcmp(unwanted_device_type, io.get()) == 0)
155 continue; 165 continue;
156 166
157 // Found an input device, prepend the default device since we always want 167 // Found a device, prepend the default device since we always want
158 // it to be on the top of the list for all platforms. And there is no 168 // it to be on the top of the list for all platforms. And there is
159 // duplicate counting here since it is only done if the list is still empty. 169 // no duplicate counting here since it is only done if the list is
160 // Note, pulse has exclusively opened the default device, so we must open 170 // still empty. Note, pulse has exclusively opened the default
161 // the device via the "default" moniker. 171 // device, so we must open the device via the "default" moniker.
162 if (device_names->empty()) { 172 if (device_names->empty()) {
163 device_names->push_front(media::AudioDeviceName( 173 device_names->push_front(media::AudioDeviceName(
164 AudioManagerBase::kDefaultDeviceName, 174 AudioManagerBase::kDefaultDeviceName,
165 AudioManagerBase::kDefaultDeviceId)); 175 AudioManagerBase::kDefaultDeviceId));
166 } 176 }
167 177
168 // Get the unique device name for the device. 178 // Get the unique device name for the device.
169 scoped_ptr_malloc<char> unique_device_name( 179 scoped_ptr_malloc<char> unique_device_name(
170 wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName)); 180 wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName));
171 181
172 // Find out if the device is available. 182 // Find out if the device is available.
173 if (IsAlsaDeviceAvailable(unique_device_name.get())) { 183 if (IsAlsaDeviceAvailable(type, unique_device_name.get())) {
174 // Get the description for the device. 184 // Get the description for the device.
175 scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint( 185 scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint(
176 *hint_iter, kDescriptionHintName)); 186 *hint_iter, kDescriptionHintName));
177 187
178 media::AudioDeviceName name; 188 media::AudioDeviceName name;
179 name.unique_id = unique_device_name.get(); 189 name.unique_id = unique_device_name.get();
180 if (desc) { 190 if (desc) {
181 // Use the more user friendly description as name. 191 // Use the more user friendly description as name.
182 // Replace '\n' with '-'. 192 // Replace '\n' with '-'.
183 char* pret = strchr(desc.get(), '\n'); 193 char* pret = strchr(desc.get(), '\n');
184 if (pret) 194 if (pret)
185 *pret = '-'; 195 *pret = '-';
186 name.device_name = desc.get(); 196 name.device_name = desc.get();
187 } else { 197 } else {
188 // Virtual devices don't necessarily have descriptions. 198 // Virtual devices don't necessarily have descriptions.
189 // Use their names instead. 199 // Use their names instead.
190 name.device_name = unique_device_name.get(); 200 name.device_name = unique_device_name.get();
191 } 201 }
192 202
193 // Store the device information. 203 // Store the device information.
194 device_names->push_back(name); 204 device_names->push_back(name);
195 } 205 }
196 } 206 }
197 } 207 }
198 208
199 bool AudioManagerLinux::IsAlsaDeviceAvailable(const char* device_name) { 209 bool AudioManagerLinux::IsAlsaDeviceAvailable(
tommi (sloooow) - chröme 2013/09/04 14:32:51 nit: looks like this could also be a static utilit
Jói 2013/09/04 15:32:22 Becuase of the use of StreamType this has the same
210 AudioManagerLinux::StreamType type,
211 const char* device_name) {
200 if (!device_name) 212 if (!device_name)
201 return false; 213 return false;
202 214
203 // Check if the device is in the list of invalid devices. 215 if (type == kStreamCapture) {
204 for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) { 216 // Check if the device is in the list of invalid devices.
205 if (strncmp(kInvalidAudioInputDevices[i], device_name, 217 for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) {
206 strlen(kInvalidAudioInputDevices[i])) == 0) 218 if (strncmp(kInvalidAudioInputDevices[i], device_name,
tommi (sloooow) - chröme 2013/09/04 14:32:51 Don't we also have to check that the length of the
Jói 2013/09/04 15:32:22 Prefix matching is the intent. I added a comment.
tommi (sloooow) - chröme 2013/09/04 16:43:52 Great. thanks.
207 return false; 219 strlen(kInvalidAudioInputDevices[i])) == 0)
220 return false;
221 }
222
223 return true;
224 } else {
225 DCHECK_EQ(kStreamPlayback, type);
226 // We prefer the device type that maps straight to hardware but
227 // goes through software conversion if needed (e.g. incompatible
228 // sample rate).
229 // TODO(joi): Should we prefer "hw" instead?
230 static const char kDeviceTypeDesired[] = "plughw";
231 return strncmp(kDeviceTypeDesired,
232 device_name,
233 arraysize(kDeviceTypeDesired) - 1) == 0;
208 } 234 }
209
210 return true;
211 } 235 }
212 236
213 bool AudioManagerLinux::HasAnyAlsaAudioDevice(StreamType stream) { 237 const char* AudioManagerLinux::UnwantedDeviceTypeWhenEnumerating(
238 AudioManagerLinux::StreamType wanted_type) {
239 return wanted_type == kStreamPlayback ? "Input" : "Output";
240 }
241
242 bool AudioManagerLinux::HasAnyAlsaAudioDevice(
243 AudioManagerLinux::StreamType stream) {
214 static const char kPcmInterfaceName[] = "pcm"; 244 static const char kPcmInterfaceName[] = "pcm";
215 static const char kIoHintName[] = "IOID"; 245 static const char kIoHintName[] = "IOID";
216 const char* kNotWantedDevice =
217 (stream == kStreamPlayback ? "Input" : "Output");
218 void** hints = NULL; 246 void** hints = NULL;
219 bool has_device = false; 247 bool has_device = false;
220 int card = -1; 248 int card = -1;
221 249
222 // Loop through the sound cards. 250 // Loop through the sound cards.
223 // Don't use snd_device_name_hint(-1,..) since there is a access violation 251 // Don't use snd_device_name_hint(-1,..) since there is a access violation
224 // inside this ALSA API with libasound.so.2.0.0. 252 // inside this ALSA API with libasound.so.2.0.0.
225 while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) { 253 while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) {
226 int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); 254 int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
227 if (!error) { 255 if (!error) {
228 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { 256 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
229 // Only examine devices that are |stream| capable. Valid values are 257 // Only examine devices that are |stream| capable. Valid values are
230 // "Input", "Output", and NULL which means both input and output. 258 // "Input", "Output", and NULL which means both input and output.
231 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, 259 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter,
232 kIoHintName)); 260 kIoHintName));
233 if (io != NULL && strcmp(kNotWantedDevice, io.get()) == 0) 261 const char* unwanted_type = UnwantedDeviceTypeWhenEnumerating(stream);
262 if (io != NULL && strcmp(unwanted_type, io.get()) == 0)
234 continue; // Wrong type, skip the device. 263 continue; // Wrong type, skip the device.
235 264
236 // Found an input device. 265 // Found an input device.
237 has_device = true; 266 has_device = true;
238 break; 267 break;
239 } 268 }
240 269
241 // Destroy the hints now that we're done with it. 270 // Destroy the hints now that we're done with it.
242 wrapper_->DeviceNameFreeHint(hints); 271 wrapper_->DeviceNameFreeHint(hints);
243 hints = NULL; 272 hints = NULL;
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
343 UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kPulse, kAudioIOMax); 372 UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kPulse, kAudioIOMax);
344 return manager; 373 return manager;
345 } 374 }
346 #endif 375 #endif
347 376
348 UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kAlsa, kAudioIOMax); 377 UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kAlsa, kAudioIOMax);
349 return new AudioManagerLinux(); 378 return new AudioManagerLinux();
350 } 379 }
351 380
352 } // namespace media 381 } // namespace media
OLDNEW
« media/audio/linux/audio_manager_linux.h ('K') | « media/audio/linux/audio_manager_linux.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698