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