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