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.
5 #include "media/audio/linux/audio_manager_linux.h"
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"
18 #include "media/audio/cras/audio_manager_cras.h"
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"
26 #include "media/base/channel_layout.h"
27 #include "media/base/limits.h"
28 #include "media/base/media_switches.h"
32 // Maximum number of output streams that can be open simultaneously.
33 static const int kMaxOutputStreams = 50;
35 // Default sample rate for input and output streams.
36 static const int kDefaultSampleRate = 48000;
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[] = {
54 kAudioIOMax // Must always be last!
58 void 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"));
65 case base::nix::DESKTOP_ENVIRONMENT_KDE3:
66 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
67 command_line.SetProgram(base::FilePath("kmix"));
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");
75 LOG(ERROR) << "Failed to show audio input settings: we don't know "
76 << "what command to use for your desktop environment.";
79 base::LaunchProcess(command_line, base::LaunchOptions(), NULL);
82 // Implementation of AudioManager.
83 bool AudioManagerLinux::HasAudioOutputDevices() {
84 return HasAnyAlsaAudioDevice(kStreamPlayback);
87 bool AudioManagerLinux::HasAudioInputDevices() {
88 return HasAnyAlsaAudioDevice(kStreamCapture);
91 AudioManagerLinux::AudioManagerLinux()
92 : wrapper_(new AlsaWrapper()) {
93 SetMaxOutputStreamsAllowed(kMaxOutputStreams);
96 AudioManagerLinux::~AudioManagerLinux() {
100 void AudioManagerLinux::ShowAudioInputSettings() {
101 ShowLinuxAudioInputSettings();
104 void AudioManagerLinux::GetAudioInputDeviceNames(
105 AudioDeviceNames* device_names) {
106 DCHECK(device_names->empty());
107 GetAlsaAudioDevices(kStreamCapture, device_names);
110 void AudioManagerLinux::GetAudioOutputDeviceNames(
111 AudioDeviceNames* device_names) {
112 DCHECK(device_names->empty());
113 GetAlsaAudioDevices(kStreamPlayback, device_names);
116 AudioParameters AudioManagerLinux::GetInputStreamParameters(
117 const std::string& device_id) {
118 static const int kDefaultInputBufferSize = 1024;
120 return AudioParameters(
121 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
122 kDefaultSampleRate, 16, kDefaultInputBufferSize);
125 void AudioManagerLinux::GetAlsaAudioDevices(
127 media::AudioDeviceNames* device_names) {
128 // Constants specified by the ALSA API for device hints.
129 static const char kPcmInterfaceName[] = "pcm";
132 // Loop through the sound cards to get ALSA device hints.
133 while (!wrapper_->CardNext(&card) && card >= 0) {
135 int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
137 GetAlsaDevicesInfo(type, hints, device_names);
139 // Destroy the hints now that we're done with it.
140 wrapper_->DeviceNameFreeHint(hints);
142 DLOG(WARNING) << "GetAlsaAudioDevices: unable to get device hints: "
143 << wrapper_->StrError(error);
148 void AudioManagerLinux::GetAlsaDevicesInfo(
149 AudioManagerLinux::StreamType type,
151 media::AudioDeviceNames* device_names) {
152 static const char kIoHintName[] = "IOID";
153 static const char kNameHintName[] = "NAME";
154 static const char kDescriptionHintName[] = "DESC";
156 const char* unwanted_device_type = UnwantedDeviceTypeWhenEnumerating(type);
158 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
159 // Only examine devices of the right type. Valid values are
160 // "Input", "Output", and NULL which means both input and output.
161 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter,
163 if (io != NULL && strcmp(unwanted_device_type, io.get()) == 0)
166 // Found a device, prepend the default device since we always want
167 // it to be on the top of the list for all platforms. And there is
168 // no duplicate counting here since it is only done if the list is
169 // still empty. Note, pulse has exclusively opened the default
170 // device, so we must open the device via the "default" moniker.
171 if (device_names->empty()) {
172 device_names->push_front(media::AudioDeviceName(
173 AudioManagerBase::kDefaultDeviceName,
174 AudioManagerBase::kDefaultDeviceId));
177 // Get the unique device name for the device.
178 scoped_ptr_malloc<char> unique_device_name(
179 wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName));
181 // Find out if the device is available.
182 if (IsAlsaDeviceAvailable(type, unique_device_name.get())) {
183 // Get the description for the device.
184 scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint(
185 *hint_iter, kDescriptionHintName));
187 media::AudioDeviceName name;
188 name.unique_id = unique_device_name.get();
190 // Use the more user friendly description as name.
191 // Replace '\n' with '-'.
192 char* pret = strchr(desc.get(), '\n');
195 name.device_name = desc.get();
197 // Virtual devices don't necessarily have descriptions.
198 // Use their names instead.
199 name.device_name = unique_device_name.get();
202 // Store the device information.
203 device_names->push_back(name);
209 bool AudioManagerLinux::IsAlsaDeviceAvailable(
210 AudioManagerLinux::StreamType type,
211 const char* device_name) {
215 // We do prefix matches on the device name to see whether to include
217 if (type == kStreamCapture) {
218 // Check if the device is in the list of invalid devices.
219 for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) {
220 if (strncmp(kInvalidAudioInputDevices[i], device_name,
221 strlen(kInvalidAudioInputDevices[i])) == 0)
226 DCHECK_EQ(kStreamPlayback, type);
227 // We prefer the device type that maps straight to hardware but
228 // goes through software conversion if needed (e.g. incompatible
230 // TODO(joi): Should we prefer "hw" instead?
231 static const char kDeviceTypeDesired[] = "plughw";
232 return strncmp(kDeviceTypeDesired,
234 arraysize(kDeviceTypeDesired) - 1) == 0;
239 const char* AudioManagerLinux::UnwantedDeviceTypeWhenEnumerating(
240 AudioManagerLinux::StreamType wanted_type) {
241 return wanted_type == kStreamPlayback ? "Input" : "Output";
244 bool AudioManagerLinux::HasAnyAlsaAudioDevice(
245 AudioManagerLinux::StreamType stream) {
246 static const char kPcmInterfaceName[] = "pcm";
247 static const char kIoHintName[] = "IOID";
249 bool has_device = false;
252 // Loop through the sound cards.
253 // Don't use snd_device_name_hint(-1,..) since there is a access violation
254 // inside this ALSA API with libasound.so.2.0.0.
255 while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) {
256 int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
258 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
259 // Only examine devices that are |stream| capable. Valid values are
260 // "Input", "Output", and NULL which means both input and output.
261 scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter,
263 const char* unwanted_type = UnwantedDeviceTypeWhenEnumerating(stream);
264 if (io != NULL && strcmp(unwanted_type, io.get()) == 0)
265 continue; // Wrong type, skip the device.
267 // Found an input device.
272 // Destroy the hints now that we're done with it.
273 wrapper_->DeviceNameFreeHint(hints);
276 DLOG(WARNING) << "HasAnyAudioDevice: unable to get device hints: "
277 << wrapper_->StrError(error);
284 AudioOutputStream* AudioManagerLinux::MakeLinearOutputStream(
285 const AudioParameters& params) {
286 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
287 return MakeOutputStream(params);
290 AudioOutputStream* AudioManagerLinux::MakeLowLatencyOutputStream(
291 const AudioParameters& params,
292 const std::string& device_id,
293 const std::string& input_device_id) {
294 DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
295 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
296 // TODO(xians): Use input_device_id for unified IO.
297 return MakeOutputStream(params);
300 AudioInputStream* AudioManagerLinux::MakeLinearInputStream(
301 const AudioParameters& params, const std::string& device_id) {
302 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
303 return MakeInputStream(params, device_id);
306 AudioInputStream* AudioManagerLinux::MakeLowLatencyInputStream(
307 const AudioParameters& params, const std::string& device_id) {
308 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
309 return MakeInputStream(params, device_id);
312 AudioParameters AudioManagerLinux::GetPreferredOutputStreamParameters(
313 const std::string& output_device_id,
314 const AudioParameters& input_params) {
315 // TODO(tommi): Support |output_device_id|.
316 DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!";
317 static const int kDefaultOutputBufferSize = 2048;
318 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
319 int sample_rate = kDefaultSampleRate;
320 int buffer_size = kDefaultOutputBufferSize;
321 int bits_per_sample = 16;
322 int input_channels = 0;
323 if (input_params.IsValid()) {
324 // Some clients, such as WebRTC, have a more limited use case and work
325 // acceptably with a smaller buffer size. The check below allows clients
326 // which want to try a smaller buffer size on Linux to do so.
327 // TODO(dalecurtis): This should include bits per channel and channel layout
329 sample_rate = input_params.sample_rate();
330 bits_per_sample = input_params.bits_per_sample();
331 channel_layout = input_params.channel_layout();
332 input_channels = input_params.input_channels();
333 buffer_size = std::min(input_params.frames_per_buffer(), buffer_size);
336 int user_buffer_size = GetUserBufferSize();
337 if (user_buffer_size)
338 buffer_size = user_buffer_size;
340 return AudioParameters(
341 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels,
342 sample_rate, bits_per_sample, buffer_size);
345 AudioOutputStream* AudioManagerLinux::MakeOutputStream(
346 const AudioParameters& params) {
347 std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice;
348 if (CommandLine::ForCurrentProcess()->HasSwitch(
349 switches::kAlsaOutputDevice)) {
350 device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
351 switches::kAlsaOutputDevice);
353 return new AlsaPcmOutputStream(device_name, params, wrapper_.get(), this);
356 AudioInputStream* AudioManagerLinux::MakeInputStream(
357 const AudioParameters& params, const std::string& device_id) {
358 std::string device_name = (device_id == AudioManagerBase::kDefaultDeviceId) ?
359 AlsaPcmInputStream::kAutoSelectDevice : device_id;
360 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAlsaInputDevice)) {
361 device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
362 switches::kAlsaInputDevice);
365 return new AlsaPcmInputStream(this, device_name, params, wrapper_.get());
368 AudioManager* CreateAudioManager() {
369 #if defined(USE_CRAS)
370 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseCras)) {
371 UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kCras, kAudioIOMax);
372 return new AudioManagerCras();
376 #if defined(USE_PULSEAUDIO)
377 AudioManager* manager = AudioManagerPulse::Create();
379 UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kPulse, kAudioIOMax);
384 UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kAlsa, kAudioIOMax);
385 return new AudioManagerLinux();