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