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