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