audio_manager_linux.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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. 4 5#include "media/audio/linux/audio_manager_linux.h" 6 7#include "base/command_line.h" 8#include "base/environment.h" 9#include "base/logging.h" 10#include "base/nix/xdg_util.h" 11#include "base/process_util.h" 12#include "base/stl_util.h" 13#include "media/audio/audio_output_dispatcher.h" 14#include "media/audio/audio_util.h" 15#include "media/audio/linux/alsa_input.h" 16#include "media/audio/linux/alsa_output.h" 17#include "media/audio/linux/alsa_wrapper.h" 18#if defined(USE_PULSEAUDIO) 19#include "media/audio/pulse/pulse_output.h" 20#endif 21#if defined(USE_CRAS) 22#include "media/audio/linux/cras_input.h" 23#include "media/audio/linux/cras_output.h" 24#endif 25#include "media/base/limits.h" 26#include "media/base/media_switches.h" 27 28namespace media { 29 30// Maximum number of output streams that can be open simultaneously. 31static const int kMaxOutputStreams = 50; 32 33// Since "default", "pulse" and "dmix" devices are virtual devices mapped to 34// real devices, we remove them from the list to avoiding duplicate counting. 35// In addition, note that we support no more than 2 channels for recording, 36// hence surround devices are not stored in the list. 37static const char* kInvalidAudioInputDevices[] = { 38 "default", 39 "null", 40 "pulse", 41 "dmix", 42 "surround", 43}; 44 45static const char kCrasAutomaticDeviceName[] = "Automatic"; 46static const char kCrasAutomaticDeviceId[] = "automatic"; 47 48// Implementation of AudioManager. 49bool AudioManagerLinux::HasAudioOutputDevices() { 50 if (UseCras()) 51 return true; 52 53 return HasAnyAlsaAudioDevice(kStreamPlayback); 54} 55 56bool AudioManagerLinux::HasAudioInputDevices() { 57 if (UseCras()) 58 return true; 59 60 return HasAnyAlsaAudioDevice(kStreamCapture); 61} 62 63AudioManagerLinux::AudioManagerLinux() 64 : wrapper_(new AlsaWrapper()) { 65 SetMaxOutputStreamsAllowed(kMaxOutputStreams); 66} 67 68AudioManagerLinux::~AudioManagerLinux() { 69 Shutdown(); 70} 71 72bool AudioManagerLinux::CanShowAudioInputSettings() { 73 scoped_ptr<base::Environment> env(base::Environment::Create()); 74 75 switch (base::nix::GetDesktopEnvironment(env.get())) { 76 case base::nix::DESKTOP_ENVIRONMENT_GNOME: 77 case base::nix::DESKTOP_ENVIRONMENT_KDE3: 78 case base::nix::DESKTOP_ENVIRONMENT_KDE4: 79 return true; 80 case base::nix::DESKTOP_ENVIRONMENT_OTHER: 81 case base::nix::DESKTOP_ENVIRONMENT_UNITY: 82 case base::nix::DESKTOP_ENVIRONMENT_XFCE: 83 return false; 84 } 85 // Unless GetDesktopEnvironment() badly misbehaves, this should never happen. 86 NOTREACHED(); 87 return false; 88} 89 90void AudioManagerLinux::ShowAudioInputSettings() { 91 scoped_ptr<base::Environment> env(base::Environment::Create()); 92 base::nix::DesktopEnvironment desktop = base::nix::GetDesktopEnvironment( 93 env.get()); 94 std::string command((desktop == base::nix::DESKTOP_ENVIRONMENT_GNOME) ? 95 "gnome-volume-control" : "kmix"); 96 base::LaunchProcess(CommandLine(FilePath(command)), base::LaunchOptions(), 97 NULL); 98} 99 100void AudioManagerLinux::GetAudioInputDeviceNames( 101 media::AudioDeviceNames* device_names) { 102 DCHECK(device_names->empty()); 103 if (UseCras()) { 104 GetCrasAudioInputDevices(device_names); 105 return; 106 } 107 108 GetAlsaAudioInputDevices(device_names); 109} 110 111bool AudioManagerLinux::UseCras() { 112#if defined(USE_CRAS) 113 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseCras)) { 114 return true; 115 } 116#endif 117 return false; 118} 119 120void AudioManagerLinux::GetCrasAudioInputDevices( 121 media::AudioDeviceNames* device_names) { 122 // Cras will route audio from a proper physical device automatically. 123 device_names->push_back(media::AudioDeviceName( 124 kCrasAutomaticDeviceName, kCrasAutomaticDeviceId)); 125} 126 127void AudioManagerLinux::GetAlsaAudioInputDevices( 128 media::AudioDeviceNames* device_names) { 129 // Constants specified by the ALSA API for device hints. 130 static const char kPcmInterfaceName[] = "pcm"; 131 int card = -1; 132 133 // Loop through the sound cards to get ALSA device hints. 134 while (!wrapper_->CardNext(&card) && card >= 0) { 135 void** hints = NULL; 136 int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); 137 if (!error) { 138 GetAlsaDevicesInfo(hints, device_names); 139 140 // Destroy the hints now that we're done with it. 141 wrapper_->DeviceNameFreeHint(hints); 142 } else { 143 DLOG(WARNING) << "GetAudioInputDevices: unable to get device hints: " 144 << wrapper_->StrError(error); 145 } 146 } 147} 148 149void AudioManagerLinux::GetAlsaDevicesInfo( 150 void** hints, media::AudioDeviceNames* device_names) { 151 static const char kIoHintName[] = "IOID"; 152 static const char kNameHintName[] = "NAME"; 153 static const char kDescriptionHintName[] = "DESC"; 154 static const char kOutputDevice[] = "Output"; 155 156 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { 157 // Only examine devices that are input capable. Valid values are 158 // "Input", "Output", and NULL which means both input and output. 159 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, 160 kIoHintName)); 161 if (io != NULL && strcmp(kOutputDevice, io.get()) == 0) 162 continue; 163 164 // Found an input device, prepend the default device since we always want 165 // it to be on the top of the list for all platforms. And there is no 166 // duplicate counting here since it is only done if the list is still empty. 167 // Note, pulse has exclusively opened the default device, so we must open 168 // the device via the "default" moniker. 169 if (device_names->empty()) { 170 device_names->push_front(media::AudioDeviceName( 171 AudioManagerBase::kDefaultDeviceName, 172 AudioManagerBase::kDefaultDeviceId)); 173 } 174 175 // Get the unique device name for the device. 176 scoped_ptr_malloc<char> unique_device_name( 177 wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName)); 178 179 // Find out if the device is available. 180 if (IsAlsaDeviceAvailable(unique_device_name.get())) { 181 // Get the description for the device. 182 scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint( 183 *hint_iter, kDescriptionHintName)); 184 185 media::AudioDeviceName name; 186 name.unique_id = unique_device_name.get(); 187 if (desc.get()) { 188 // Use the more user friendly description as name. 189 // Replace '\n' with '-'. 190 char* pret = strchr(desc.get(), '\n'); 191 if (pret) 192 *pret = '-'; 193 name.device_name = desc.get(); 194 } else { 195 // Virtual devices don't necessarily have descriptions. 196 // Use their names instead. 197 name.device_name = unique_device_name.get(); 198 } 199 200 // Store the device information. 201 device_names->push_back(name); 202 } 203 } 204} 205 206bool AudioManagerLinux::IsAlsaDeviceAvailable(const char* device_name) { 207 if (!device_name) 208 return false; 209 210 // Check if the device is in the list of invalid devices. 211 for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) { 212 if (strncmp(kInvalidAudioInputDevices[i], device_name, 213 strlen(kInvalidAudioInputDevices[i])) == 0) 214 return false; 215 } 216 // The only way to check if the device is available is to open/close the 217 // device. Return false if it fails either of operations. 218 snd_pcm_t* device_handle = NULL; 219 if (wrapper_->PcmOpen(&device_handle, 220 device_name, 221 SND_PCM_STREAM_CAPTURE, 222 SND_PCM_NONBLOCK)) 223 return false; 224 if (wrapper_->PcmClose(device_handle)) 225 return false; 226 227 return true; 228} 229 230bool AudioManagerLinux::HasAnyAlsaAudioDevice(StreamType stream) { 231 static const char kPcmInterfaceName[] = "pcm"; 232 static const char kIoHintName[] = "IOID"; 233 const char* kNotWantedDevice = 234 (stream == kStreamPlayback ? "Input" : "Output"); 235 void** hints = NULL; 236 bool has_device = false; 237 int card = -1; 238 239 // Loop through the sound cards. 240 // Don't use snd_device_name_hint(-1,..) since there is a access violation 241 // inside this ALSA API with libasound.so.2.0.0. 242 while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) { 243 int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); 244 if (!error) { 245 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { 246 // Only examine devices that are |stream| capable. Valid values are 247 // "Input", "Output", and NULL which means both input and output. 248 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, 249 kIoHintName)); 250 if (io != NULL && strcmp(kNotWantedDevice, io.get()) == 0) 251 continue; // Wrong type, skip the device. 252 253 // Found an input device. 254 has_device = true; 255 break; 256 } 257 258 // Destroy the hints now that we're done with it. 259 wrapper_->DeviceNameFreeHint(hints); 260 hints = NULL; 261 } else { 262 DLOG(WARNING) << "HasAnyAudioDevice: unable to get device hints: " 263 << wrapper_->StrError(error); 264 } 265 } 266 267 return has_device; 268} 269 270AudioOutputStream* AudioManagerLinux::MakeLinearOutputStream( 271 const AudioParameters& params) { 272 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); 273 return MakeOutputStream(params); 274} 275 276AudioOutputStream* AudioManagerLinux::MakeLowLatencyOutputStream( 277 const AudioParameters& params) { 278 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); 279 return MakeOutputStream(params); 280} 281 282AudioInputStream* AudioManagerLinux::MakeLinearInputStream( 283 const AudioParameters& params, const std::string& device_id) { 284 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); 285 return MakeInputStream(params, device_id); 286} 287 288AudioInputStream* AudioManagerLinux::MakeLowLatencyInputStream( 289 const AudioParameters& params, const std::string& device_id) { 290 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); 291 return MakeInputStream(params, device_id); 292} 293 294AudioOutputStream* AudioManagerLinux::MakeOutputStream( 295 const AudioParameters& params) { 296#if defined(USE_CRAS) 297 if (UseCras()) { 298 return new CrasOutputStream(params, this); 299 } 300#endif 301 302#if defined(USE_PULSEAUDIO) 303 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUsePulseAudio)) { 304 return new PulseAudioOutputStream(params, this); 305 } 306#endif 307 308 std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice; 309 if (CommandLine::ForCurrentProcess()->HasSwitch( 310 switches::kAlsaOutputDevice)) { 311 device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 312 switches::kAlsaOutputDevice); 313 } 314 return new AlsaPcmOutputStream(device_name, params, wrapper_.get(), this); 315} 316 317AudioInputStream* AudioManagerLinux::MakeInputStream( 318 const AudioParameters& params, const std::string& device_id) { 319#if defined(USE_CRAS) 320 if (UseCras()) { 321 return new CrasInputStream(params, this); 322 } 323#endif 324 325 std::string device_name = (device_id == AudioManagerBase::kDefaultDeviceId) ? 326 AlsaPcmInputStream::kAutoSelectDevice : device_id; 327 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAlsaInputDevice)) { 328 device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 329 switches::kAlsaInputDevice); 330 } 331 332 return new AlsaPcmInputStream(this, device_name, params, wrapper_.get()); 333} 334 335AudioManager* CreateAudioManager() { 336 return new AudioManagerLinux(); 337} 338 339AudioParameters AudioManagerLinux::GetPreferredLowLatencyOutputStreamParameters( 340 const AudioParameters& input_params) { 341 // Since Linux doesn't actually have a low latency path the hardware buffer 342 // size is quite large in order to prevent glitches with general usage. Some 343 // clients, such as WebRTC, have a more limited use case and work acceptably 344 // with a smaller buffer size. The check below allows clients which want to 345 // try a smaller buffer size on Linux to do so. 346 int buffer_size = GetAudioHardwareBufferSize(); 347 if (input_params.frames_per_buffer() < buffer_size) 348 buffer_size = input_params.frames_per_buffer(); 349 350 int sample_rate = GetAudioHardwareSampleRate(); 351 // CRAS will sample rate convert if needed, so pass through input sample rate. 352 if (UseCras()) 353 sample_rate = input_params.sample_rate(); 354 355 // TODO(dalecurtis): This should include bits per channel and channel layout 356 // eventually. 357 return AudioParameters( 358 AudioParameters::AUDIO_PCM_LOW_LATENCY, input_params.channel_layout(), 359 sample_rate, 16, buffer_size); 360} 361 362} // namespace media 363