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