15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/audio/linux/audio_manager_linux.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/environment.h"
9a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "base/files/file_path.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/metrics/histogram.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/nix/xdg_util.h"
13a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "base/process/launch.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/audio/audio_output_dispatcher.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "media/audio/audio_parameters.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/audio/audio_util.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(USE_CRAS)
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "media/audio/cras/audio_manager_cras.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/audio/linux/alsa_input.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/audio/linux/alsa_output.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/audio/linux/alsa_wrapper.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(USE_PULSEAUDIO)
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "media/audio/pulse/audio_manager_pulse.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "media/base/channel_layout.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/base/limits.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/base/media_switches.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace media {
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Maximum number of output streams that can be open simultaneously.
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kMaxOutputStreams = 50;
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Default sample rate for input and output streams.
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static const int kDefaultSampleRate = 48000;
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Since "default", "pulse" and "dmix" devices are virtual devices mapped to
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// real devices, we remove them from the list to avoiding duplicate counting.
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// In addition, note that we support no more than 2 channels for recording,
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// hence surround devices are not stored in the list.
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char* kInvalidAudioInputDevices[] = {
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  "default",
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  "null",
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  "pulse",
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  "dmix",
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  "surround",
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)enum LinuxAudioIO {
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  kPulse,
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  kAlsa,
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  kCras,
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  kAudioIOMax  // Must always be last!
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AudioManagerLinux::ShowLinuxAudioInputSettings() {
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<base::Environment> env(base::Environment::Create());
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CommandLine command_line(CommandLine::NO_PROGRAM);
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  switch (base::nix::GetDesktopEnvironment(env.get())) {
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case base::nix::DESKTOP_ENVIRONMENT_GNOME:
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      command_line.SetProgram(base::FilePath("gnome-volume-control"));
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case base::nix::DESKTOP_ENVIRONMENT_KDE3:
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case base::nix::DESKTOP_ENVIRONMENT_KDE4:
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      command_line.SetProgram(base::FilePath("kmix"));
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case base::nix::DESKTOP_ENVIRONMENT_UNITY:
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      command_line.SetProgram(base::FilePath("gnome-control-center"));
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      command_line.AppendArg("sound");
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      command_line.AppendArg("input");
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    default:
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      LOG(ERROR) << "Failed to show audio input settings: we don't know "
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 << "what command to use for your desktop environment.";
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::LaunchProcess(command_line, base::LaunchOptions(), NULL);
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Implementation of AudioManager.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool AudioManagerLinux::HasAudioOutputDevices() {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return HasAnyAlsaAudioDevice(kStreamPlayback);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool AudioManagerLinux::HasAudioInputDevices() {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return HasAnyAlsaAudioDevice(kStreamCapture);
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioManagerLinux::AudioManagerLinux()
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : wrapper_(new AlsaWrapper()) {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetMaxOutputStreamsAllowed(kMaxOutputStreams);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioManagerLinux::~AudioManagerLinux() {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Shutdown();
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AudioManagerLinux::ShowAudioInputSettings() {
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ShowLinuxAudioInputSettings();
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AudioManagerLinux::GetAudioInputDeviceNames(
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    media::AudioDeviceNames* device_names) {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(device_names->empty());
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetAlsaAudioInputDevices(device_names);
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AudioParameters AudioManagerLinux::GetInputStreamParameters(
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& device_id) {
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static const int kDefaultInputBufferSize = 1024;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return AudioParameters(
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      kDefaultSampleRate, 16, kDefaultInputBufferSize);
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AudioManagerLinux::GetAlsaAudioInputDevices(
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    media::AudioDeviceNames* device_names) {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Constants specified by the ALSA API for device hints.
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const char kPcmInterfaceName[] = "pcm";
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int card = -1;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Loop through the sound cards to get ALSA device hints.
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (!wrapper_->CardNext(&card) && card >= 0) {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    void** hints = NULL;
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!error) {
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetAlsaDevicesInfo(hints, device_names);
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Destroy the hints now that we're done with it.
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      wrapper_->DeviceNameFreeHint(hints);
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DLOG(WARNING) << "GetAudioInputDevices: unable to get device hints: "
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    << wrapper_->StrError(error);
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AudioManagerLinux::GetAlsaDevicesInfo(
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    void** hints, media::AudioDeviceNames* device_names) {
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const char kIoHintName[] = "IOID";
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const char kNameHintName[] = "NAME";
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const char kDescriptionHintName[] = "DESC";
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const char kOutputDevice[] = "Output";
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only examine devices that are input capable.  Valid values are
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // "Input", "Output", and NULL which means both input and output.
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter,
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                           kIoHintName));
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (io != NULL && strcmp(kOutputDevice, io.get()) == 0)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Found an input device, prepend the default device since we always want
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // it to be on the top of the list for all platforms. And there is no
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // duplicate counting here since it is only done if the list is still empty.
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Note, pulse has exclusively opened the default device, so we must open
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the device via the "default" moniker.
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (device_names->empty()) {
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      device_names->push_front(media::AudioDeviceName(
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          AudioManagerBase::kDefaultDeviceName,
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          AudioManagerBase::kDefaultDeviceId));
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Get the unique device name for the device.
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_ptr_malloc<char> unique_device_name(
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName));
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Find out if the device is available.
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (IsAlsaDeviceAvailable(unique_device_name.get())) {
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Get the description for the device.
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint(
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          *hint_iter, kDescriptionHintName));
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      media::AudioDeviceName name;
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name.unique_id = unique_device_name.get();
180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (desc) {
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Use the more user friendly description as name.
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Replace '\n' with '-'.
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        char* pret = strchr(desc.get(), '\n');
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (pret)
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          *pret = '-';
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        name.device_name = desc.get();
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Virtual devices don't necessarily have descriptions.
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Use their names instead.
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        name.device_name = unique_device_name.get();
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Store the device information.
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      device_names->push_back(name);
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool AudioManagerLinux::IsAlsaDeviceAvailable(const char* device_name) {
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!device_name)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check if the device is in the list of invalid devices.
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) {
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (strncmp(kInvalidAudioInputDevices[i], device_name,
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                strlen(kInvalidAudioInputDevices[i])) == 0)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool AudioManagerLinux::HasAnyAlsaAudioDevice(StreamType stream) {
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const char kPcmInterfaceName[] = "pcm";
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const char kIoHintName[] = "IOID";
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char* kNotWantedDevice =
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (stream == kStreamPlayback ? "Input" : "Output");
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void** hints = NULL;
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool has_device = false;
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int card = -1;
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Loop through the sound cards.
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Don't use snd_device_name_hint(-1,..) since there is a access violation
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // inside this ALSA API with libasound.so.2.0.0.
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) {
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!error) {
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Only examine devices that are |stream| capable.  Valid values are
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // "Input", "Output", and NULL which means both input and output.
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter,
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                               kIoHintName));
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (io != NULL && strcmp(kNotWantedDevice, io.get()) == 0)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          continue;  // Wrong type, skip the device.
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Found an input device.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        has_device = true;
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Destroy the hints now that we're done with it.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      wrapper_->DeviceNameFreeHint(hints);
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      hints = NULL;
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DLOG(WARNING) << "HasAnyAudioDevice: unable to get device hints: "
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    << wrapper_->StrError(error);
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return has_device;
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioOutputStream* AudioManagerLinux::MakeLinearOutputStream(
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const AudioParameters& params) {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return MakeOutputStream(params);
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioOutputStream* AudioManagerLinux::MakeLowLatencyOutputStream(
260868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    const AudioParameters& params,
261868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    const std::string& input_device_id) {
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
263868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // TODO(xians): Use input_device_id for unified IO.
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return MakeOutputStream(params);
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioInputStream* AudioManagerLinux::MakeLinearInputStream(
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const AudioParameters& params, const std::string& device_id) {
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return MakeInputStream(params, device_id);
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioInputStream* AudioManagerLinux::MakeLowLatencyInputStream(
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const AudioParameters& params, const std::string& device_id) {
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return MakeInputStream(params, device_id);
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AudioParameters AudioManagerLinux::GetPreferredOutputStreamParameters(
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const AudioParameters& input_params) {
281c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  static const int kDefaultOutputBufferSize = 2048;
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int sample_rate = kDefaultSampleRate;
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int buffer_size = kDefaultOutputBufferSize;
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int bits_per_sample = 16;
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int input_channels = 0;
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (input_params.IsValid()) {
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Some clients, such as WebRTC, have a more limited use case and work
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // acceptably with a smaller buffer size.  The check below allows clients
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // which want to try a smaller buffer size on Linux to do so.
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // TODO(dalecurtis): This should include bits per channel and channel layout
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // eventually.
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sample_rate = input_params.sample_rate();
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bits_per_sample = input_params.bits_per_sample();
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    channel_layout = input_params.channel_layout();
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    input_channels = input_params.input_channels();
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    buffer_size = std::min(input_params.frames_per_buffer(), buffer_size);
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int user_buffer_size = GetUserBufferSize();
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (user_buffer_size)
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    buffer_size = user_buffer_size;
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return AudioParameters(
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels,
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      sample_rate, bits_per_sample, buffer_size);
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AudioOutputStream* AudioManagerLinux::MakeOutputStream(
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const AudioParameters& params) {
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice;
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (CommandLine::ForCurrentProcess()->HasSwitch(
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          switches::kAlsaOutputDevice)) {
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        switches::kAlsaOutputDevice);
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new AlsaPcmOutputStream(device_name, params, wrapper_.get(), this);
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioInputStream* AudioManagerLinux::MakeInputStream(
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const AudioParameters& params, const std::string& device_id) {
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string device_name = (device_id == AudioManagerBase::kDefaultDeviceId) ?
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AlsaPcmInputStream::kAutoSelectDevice : device_id;
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAlsaInputDevice)) {
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        switches::kAlsaInputDevice);
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new AlsaPcmInputStream(this, device_name, params, wrapper_.get());
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AudioManager* CreateAudioManager() {
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(USE_CRAS)
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseCras)) {
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kCras, kAudioIOMax);
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return new AudioManagerCras();
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(USE_PULSEAUDIO)
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AudioManager* manager = AudioManagerPulse::Create();
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (manager) {
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kPulse, kAudioIOMax);
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return manager;
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kAlsa, kAudioIOMax);
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return new AudioManagerLinux();
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace media
353