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