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