audio_manager_linux.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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 "dmix", 46 "null", 47 "pulse", 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 AudioDeviceNames* device_names) { 107 DCHECK(device_names->empty()); 108 GetAlsaAudioDevices(kStreamCapture, device_names); 109} 110 111void AudioManagerLinux::GetAudioOutputDeviceNames( 112 AudioDeviceNames* device_names) { 113 DCHECK(device_names->empty()); 114 GetAlsaAudioDevices(kStreamPlayback, device_names); 115} 116 117AudioParameters AudioManagerLinux::GetInputStreamParameters( 118 const std::string& device_id) { 119 static const int kDefaultInputBufferSize = 1024; 120 121 return AudioParameters( 122 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, 123 kDefaultSampleRate, 16, kDefaultInputBufferSize); 124} 125 126void AudioManagerLinux::GetAlsaAudioDevices( 127 StreamType type, 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(type, hints, device_names); 139 140 // Destroy the hints now that we're done with it. 141 wrapper_->DeviceNameFreeHint(hints); 142 } else { 143 DLOG(WARNING) << "GetAlsaAudioDevices: unable to get device hints: " 144 << wrapper_->StrError(error); 145 } 146 } 147} 148 149void AudioManagerLinux::GetAlsaDevicesInfo( 150 AudioManagerLinux::StreamType type, 151 void** hints, 152 media::AudioDeviceNames* device_names) { 153 static const char kIoHintName[] = "IOID"; 154 static const char kNameHintName[] = "NAME"; 155 static const char kDescriptionHintName[] = "DESC"; 156 157 const char* unwanted_device_type = UnwantedDeviceTypeWhenEnumerating(type); 158 159 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { 160 // Only examine devices of the right type. Valid values are 161 // "Input", "Output", and NULL which means both input and output. 162 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, 163 kIoHintName)); 164 if (io != NULL && strcmp(unwanted_device_type, io.get()) == 0) 165 continue; 166 167 // Found a device, prepend the default device since we always want 168 // it to be on the top of the list for all platforms. And there is 169 // no duplicate counting here since it is only done if the list is 170 // still empty. Note, pulse has exclusively opened the default 171 // device, so we must open the device via the "default" moniker. 172 if (device_names->empty()) { 173 device_names->push_front(media::AudioDeviceName( 174 AudioManagerBase::kDefaultDeviceName, 175 AudioManagerBase::kDefaultDeviceId)); 176 } 177 178 // Get the unique device name for the device. 179 scoped_ptr_malloc<char> unique_device_name( 180 wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName)); 181 182 // Find out if the device is available. 183 if (IsAlsaDeviceAvailable(type, unique_device_name.get())) { 184 // Get the description for the device. 185 scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint( 186 *hint_iter, kDescriptionHintName)); 187 188 media::AudioDeviceName name; 189 name.unique_id = unique_device_name.get(); 190 if (desc) { 191 // Use the more user friendly description as name. 192 // Replace '\n' with '-'. 193 char* pret = strchr(desc.get(), '\n'); 194 if (pret) 195 *pret = '-'; 196 name.device_name = desc.get(); 197 } else { 198 // Virtual devices don't necessarily have descriptions. 199 // Use their names instead. 200 name.device_name = unique_device_name.get(); 201 } 202 203 // Store the device information. 204 device_names->push_back(name); 205 } 206 } 207} 208 209// static 210bool AudioManagerLinux::IsAlsaDeviceAvailable( 211 AudioManagerLinux::StreamType type, 212 const char* device_name) { 213 if (!device_name) 214 return false; 215 216 // We do prefix matches on the device name to see whether to include 217 // it or not. 218 if (type == kStreamCapture) { 219 // Check if the device is in the list of invalid devices. 220 for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) { 221 if (strncmp(kInvalidAudioInputDevices[i], device_name, 222 strlen(kInvalidAudioInputDevices[i])) == 0) 223 return false; 224 } 225 return true; 226 } else { 227 DCHECK_EQ(kStreamPlayback, type); 228 // We prefer the device type that maps straight to hardware but 229 // goes through software conversion if needed (e.g. incompatible 230 // sample rate). 231 // TODO(joi): Should we prefer "hw" instead? 232 static const char kDeviceTypeDesired[] = "plughw"; 233 return strncmp(kDeviceTypeDesired, 234 device_name, 235 arraysize(kDeviceTypeDesired) - 1) == 0; 236 } 237} 238 239// static 240const char* AudioManagerLinux::UnwantedDeviceTypeWhenEnumerating( 241 AudioManagerLinux::StreamType wanted_type) { 242 return wanted_type == kStreamPlayback ? "Input" : "Output"; 243} 244 245bool AudioManagerLinux::HasAnyAlsaAudioDevice( 246 AudioManagerLinux::StreamType stream) { 247 static const char kPcmInterfaceName[] = "pcm"; 248 static const char kIoHintName[] = "IOID"; 249 void** hints = NULL; 250 bool has_device = false; 251 int card = -1; 252 253 // Loop through the sound cards. 254 // Don't use snd_device_name_hint(-1,..) since there is a access violation 255 // inside this ALSA API with libasound.so.2.0.0. 256 while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) { 257 int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); 258 if (!error) { 259 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { 260 // Only examine devices that are |stream| capable. Valid values are 261 // "Input", "Output", and NULL which means both input and output. 262 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, 263 kIoHintName)); 264 const char* unwanted_type = UnwantedDeviceTypeWhenEnumerating(stream); 265 if (io != NULL && strcmp(unwanted_type, io.get()) == 0) 266 continue; // Wrong type, skip the device. 267 268 // Found an input device. 269 has_device = true; 270 break; 271 } 272 273 // Destroy the hints now that we're done with it. 274 wrapper_->DeviceNameFreeHint(hints); 275 hints = NULL; 276 } else { 277 DLOG(WARNING) << "HasAnyAudioDevice: unable to get device hints: " 278 << wrapper_->StrError(error); 279 } 280 } 281 282 return has_device; 283} 284 285AudioOutputStream* AudioManagerLinux::MakeLinearOutputStream( 286 const AudioParameters& params) { 287 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); 288 return MakeOutputStream(params); 289} 290 291AudioOutputStream* AudioManagerLinux::MakeLowLatencyOutputStream( 292 const AudioParameters& params, 293 const std::string& device_id, 294 const std::string& input_device_id) { 295 DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; 296 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); 297 // TODO(xians): Use input_device_id for unified IO. 298 return MakeOutputStream(params); 299} 300 301AudioInputStream* AudioManagerLinux::MakeLinearInputStream( 302 const AudioParameters& params, const std::string& device_id) { 303 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); 304 return MakeInputStream(params, device_id); 305} 306 307AudioInputStream* AudioManagerLinux::MakeLowLatencyInputStream( 308 const AudioParameters& params, const std::string& device_id) { 309 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); 310 return MakeInputStream(params, device_id); 311} 312 313AudioParameters AudioManagerLinux::GetPreferredOutputStreamParameters( 314 const std::string& output_device_id, 315 const AudioParameters& input_params) { 316 // TODO(tommi): Support |output_device_id|. 317 DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; 318 static const int kDefaultOutputBufferSize = 2048; 319 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; 320 int sample_rate = kDefaultSampleRate; 321 int buffer_size = kDefaultOutputBufferSize; 322 int bits_per_sample = 16; 323 int input_channels = 0; 324 if (input_params.IsValid()) { 325 // Some clients, such as WebRTC, have a more limited use case and work 326 // acceptably with a smaller buffer size. The check below allows clients 327 // which want to try a smaller buffer size on Linux to do so. 328 // TODO(dalecurtis): This should include bits per channel and channel layout 329 // eventually. 330 sample_rate = input_params.sample_rate(); 331 bits_per_sample = input_params.bits_per_sample(); 332 channel_layout = input_params.channel_layout(); 333 input_channels = input_params.input_channels(); 334 buffer_size = std::min(input_params.frames_per_buffer(), buffer_size); 335 } 336 337 int user_buffer_size = GetUserBufferSize(); 338 if (user_buffer_size) 339 buffer_size = user_buffer_size; 340 341 return AudioParameters( 342 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels, 343 sample_rate, bits_per_sample, buffer_size); 344} 345 346AudioOutputStream* AudioManagerLinux::MakeOutputStream( 347 const AudioParameters& params) { 348 std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice; 349 if (CommandLine::ForCurrentProcess()->HasSwitch( 350 switches::kAlsaOutputDevice)) { 351 device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 352 switches::kAlsaOutputDevice); 353 } 354 return new AlsaPcmOutputStream(device_name, params, wrapper_.get(), this); 355} 356 357AudioInputStream* AudioManagerLinux::MakeInputStream( 358 const AudioParameters& params, const std::string& device_id) { 359 std::string device_name = (device_id == AudioManagerBase::kDefaultDeviceId) ? 360 AlsaPcmInputStream::kAutoSelectDevice : device_id; 361 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAlsaInputDevice)) { 362 device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 363 switches::kAlsaInputDevice); 364 } 365 366 return new AlsaPcmInputStream(this, device_name, params, wrapper_.get()); 367} 368 369AudioManager* CreateAudioManager() { 370#if defined(USE_CRAS) 371 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseCras)) { 372 UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kCras, kAudioIOMax); 373 return new AudioManagerCras(); 374 } 375#endif 376 377#if defined(USE_PULSEAUDIO) 378 AudioManager* manager = AudioManagerPulse::Create(); 379 if (manager) { 380 UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kPulse, kAudioIOMax); 381 return manager; 382 } 383#endif 384 385 UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kAlsa, kAudioIOMax); 386 return new AudioManagerLinux(); 387} 388 389} // namespace media 390