AlsaInputPlugin.cxx 11.6 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2017 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 * http://www.musicpd.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/*
 * ALSA code based on an example by Paul Davis released under GPL here:
 * http://equalarea.com/paul/alsa-audio.html
 * and one by Matthias Nagorni, also GPL, here:
 * http://alsamodular.sourceforge.net/alsa_programming_howto.html
 */

#include "config.h"
#include "AlsaInputPlugin.hxx"
29
#include "lib/alsa/NonBlock.hxx"
Max Kellermann's avatar
Max Kellermann committed
30
#include "../InputPlugin.hxx"
31
#include "../AsyncInputStream.hxx"
32
#include "event/Call.hxx"
33
#include "thread/Cond.hxx"
34
#include "util/Domain.hxx"
35
#include "util/RuntimeError.hxx"
36
#include "util/StringCompare.hxx"
37
#include "util/ReusableArray.hxx"
38

39
#include "Log.hxx"
40
#include "event/MultiSocketMonitor.hxx"
41
#include "event/DeferEvent.hxx"
42 43 44

#include <alsa/asoundlib.h>

45 46 47
#include <assert.h>
#include <string.h>

48 49 50 51 52 53 54 55 56
static constexpr Domain alsa_input_domain("alsa");

static constexpr const char *default_device = "hw:0,0";

// the following defaults are because the PcmDecoderPlugin forces CD format
static constexpr snd_pcm_format_t default_format = SND_PCM_FORMAT_S16;
static constexpr int default_channels = 2; // stereo
static constexpr unsigned int default_rate = 44100; // cd quality

57 58 59
static constexpr size_t ALSA_MAX_BUFFERED = default_rate * default_channels * 2;
static constexpr size_t ALSA_RESUME_AT = ALSA_MAX_BUFFERED / 2;

60
class AlsaInputStream final
61
	: public AsyncInputStream,
62
	  MultiSocketMonitor {
63 64 65 66 67 68

	/**
	 * The configured name of the ALSA device.
	 */
	const std::string device;

69 70
	snd_pcm_t *const capture_handle;
	const size_t frame_size;
71 72 73

	ReusableArray<pollfd> pfd_buffer;

74 75
	DeferEvent defer_invalidate_sockets;

76
public:
77
	AlsaInputStream(EventLoop &_loop,
78
			const char *_uri, Mutex &_mutex, Cond &_cond,
79
			const char *_device,
80
			snd_pcm_t *_handle, int _frame_size)
81
		:AsyncInputStream(_loop, _uri, _mutex, _cond,
82
				  ALSA_MAX_BUFFERED, ALSA_RESUME_AT),
83
		 MultiSocketMonitor(_loop),
84
		 device(_device),
85
		 capture_handle(_handle),
86 87 88
		 frame_size(_frame_size),
		 defer_invalidate_sockets(_loop,
					  BIND_THIS_METHOD(InvalidateSockets))
89
	{
90
		assert(_uri != nullptr);
91 92 93 94 95
		assert(_handle != nullptr);

		/* this mime type forces use of the PcmDecoderPlugin.
		   Needs to be generalised when/if that decoder is
		   updated to support other audio formats */
96 97
		SetMimeType("audio/x-mpd-cdda-pcm");
		InputStream::SetReady();
98

99 100
		snd_pcm_start(capture_handle);

101
		defer_invalidate_sockets.Schedule();
102 103 104
	}

	~AlsaInputStream() {
105
		BlockingCall(MultiSocketMonitor::GetEventLoop(), [this](){
106
				MultiSocketMonitor::Reset();
107
				defer_invalidate_sockets.Cancel();
108 109
			});

110 111
		snd_pcm_close(capture_handle);
	}
112

113 114
	static InputStream *Create(EventLoop &event_loop, const char *uri,
				   Mutex &mutex, Cond &cond);
115

116 117 118 119
protected:
	/* virtual methods from AsyncInputStream */
	virtual void DoResume() override {
		snd_pcm_resume(capture_handle);
120

121
		InvalidateSockets();
122 123
	}

124 125 126
	virtual void DoSeek(gcc_unused offset_type new_offset) override {
		/* unreachable because seekable==false */
		SeekDone();
127 128 129 130
	}

private:
	static snd_pcm_t *OpenDevice(const char *device, int rate,
131
				     snd_pcm_format_t format, int channels);
132

133 134 135 136 137
	void Pause() {
		AsyncInputStream::Pause();
		InvalidateSockets();
	}

138 139 140
	int Recover(int err);

	void SafeInvalidateSockets() {
141
		defer_invalidate_sockets.Schedule();
142 143
	}

144 145 146
	/* virtual methods from class MultiSocketMonitor */
	std::chrono::steady_clock::duration PrepareSockets() noexcept override;
	void DispatchSockets() noexcept override;
147 148
};

149
inline InputStream *
150 151
AlsaInputStream::Create(EventLoop &event_loop, const char *uri,
			Mutex &mutex, Cond &cond)
152
{
153 154
	const char *device = StringAfterPrefix(uri, "alsa://");
	if (device == nullptr)
155 156
		return nullptr;

157
	if (*device == 0)
158 159
		device = default_device;

160 161 162 163 164 165 166
	/* placeholders - eventually user-requested audio format will
	   be passed via the URI. For now we just force the
	   defaults */
	int rate = default_rate;
	snd_pcm_format_t format = default_format;
	int channels = default_channels;

167
	snd_pcm_t *handle = OpenDevice(device, rate, format, channels);
168 169

	int frame_size = snd_pcm_format_width(format) / 8 * channels;
170
	return new AlsaInputStream(event_loop,
171
				   uri, mutex, cond,
172
				   device, handle, frame_size);
173 174
}

175
std::chrono::steady_clock::duration
176
AlsaInputStream::PrepareSockets() noexcept
177
{
178
	if (IsPaused()) {
179
		ClearSocketList();
180
		return std::chrono::steady_clock::duration(-1);
181 182
	}

183
	return PrepareAlsaPcmSockets(*this, capture_handle, pfd_buffer);
184 185 186
}

void
187
AlsaInputStream::DispatchSockets() noexcept
188
{
189
	const std::lock_guard<Mutex> protect(mutex);
190 191 192 193 194 195 196 197 198 199 200 201

	auto w = PrepareWriteBuffer();
	const snd_pcm_uframes_t w_frames = w.size / frame_size;
	if (w_frames == 0) {
		/* buffer is full */
		Pause();
		return;
	}

	snd_pcm_sframes_t n_frames;
	while ((n_frames = snd_pcm_readi(capture_handle,
					 w.data, w_frames)) < 0) {
202 203 204
		if (n_frames == -EAGAIN)
			return;

205
		if (Recover(n_frames) < 0) {
206 207
			postponed_exception = std::make_exception_ptr(std::runtime_error("PCM error - stream aborted"));
			cond.broadcast();
208 209 210 211 212 213
			return;
		}
	}

	size_t nbytes = n_frames * frame_size;
	CommitWriteBuffer(nbytes);
214 215 216 217 218 219 220
}

inline int
AlsaInputStream::Recover(int err)
{
	switch(err) {
	case -EPIPE:
221 222 223 224 225 226 227 228 229
		FormatDebug(alsa_input_domain,
			    "Overrun on ALSA capture device \"%s\"",
			    device.c_str());
		break;

	case -ESTRPIPE:
		FormatDebug(alsa_input_domain,
			    "ALSA capture device \"%s\" was suspended",
			    device.c_str());
230 231 232 233 234 235 236 237 238 239 240 241 242
		break;
	}

	switch (snd_pcm_state(capture_handle)) {
	case SND_PCM_STATE_PAUSED:
		err = snd_pcm_pause(capture_handle, /* disable */ 0);
		break;

	case SND_PCM_STATE_SUSPENDED:
		err = snd_pcm_resume(capture_handle);
		if (err == -EAGAIN)
			return 0;
		/* fall-through to snd_pcm_prepare: */
243 244 245
#if GCC_CHECK_VERSION(7,0)
		[[fallthrough]];
#endif
246 247 248 249
	case SND_PCM_STATE_OPEN:
	case SND_PCM_STATE_SETUP:
	case SND_PCM_STATE_XRUN:
		err = snd_pcm_prepare(capture_handle);
250 251
		if (err == 0)
			err = snd_pcm_start(capture_handle);
252 253 254 255
		break;

	case SND_PCM_STATE_DISCONNECTED:
		break;
256

257 258 259 260 261
	case SND_PCM_STATE_PREPARED:
	case SND_PCM_STATE_RUNNING:
	case SND_PCM_STATE_DRAINING:
		/* this is no error, so just keep running */
		err = 0;
262 263
		break;
	}
264 265


266 267 268
	return err;
}

269
static void
270
ConfigureCapture(snd_pcm_t *capture_handle,
271
		 int rate, snd_pcm_format_t format, int channels)
272 273
{
	int err;
274 275

	snd_pcm_hw_params_t *hw_params;
276
	snd_pcm_hw_params_alloca(&hw_params);
277

278 279 280
	if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0)
		throw FormatRuntimeError("Cannot initialize hardware parameter structure (%s)",
					 snd_strerror(err));
281

282 283 284
	if ((err = snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
		throw FormatRuntimeError("Cannot set access type (%s)",
					 snd_strerror(err));
285

286 287 288
	if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, format)) < 0)
		throw FormatRuntimeError("Cannot set sample format (%s)",
					 snd_strerror(err));
289

290 291 292
	if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, channels)) < 0)
		throw FormatRuntimeError("Cannot set channels (%s)",
					 snd_strerror(err));
293

294 295 296
	if ((err = snd_pcm_hw_params_set_rate(capture_handle, hw_params, rate, 0)) < 0)
		throw FormatRuntimeError("Cannot set sample rate (%s)",
					 snd_strerror(err));
297

298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
	snd_pcm_uframes_t buffer_size_min, buffer_size_max;
	snd_pcm_hw_params_get_buffer_size_min(hw_params, &buffer_size_min);
	snd_pcm_hw_params_get_buffer_size_max(hw_params, &buffer_size_max);
	unsigned buffer_time_min, buffer_time_max;
	snd_pcm_hw_params_get_buffer_time_min(hw_params, &buffer_time_min, 0);
	snd_pcm_hw_params_get_buffer_time_max(hw_params, &buffer_time_max, 0);
	FormatDebug(alsa_input_domain, "buffer: size=%u..%u time=%u..%u",
		    (unsigned)buffer_size_min, (unsigned)buffer_size_max,
		    buffer_time_min, buffer_time_max);

	snd_pcm_uframes_t period_size_min, period_size_max;
	snd_pcm_hw_params_get_period_size_min(hw_params, &period_size_min, 0);
	snd_pcm_hw_params_get_period_size_max(hw_params, &period_size_max, 0);
	unsigned period_time_min, period_time_max;
	snd_pcm_hw_params_get_period_time_min(hw_params, &period_time_min, 0);
	snd_pcm_hw_params_get_period_time_max(hw_params, &period_time_max, 0);
	FormatDebug(alsa_input_domain, "period: size=%u..%u time=%u..%u",
		    (unsigned)period_size_min, (unsigned)period_size_max,
		    period_time_min, period_time_max);

318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
	/* choose the maximum possible buffer_size ... */
	snd_pcm_hw_params_set_buffer_size(capture_handle, hw_params,
					  buffer_size_max);

	/* ... and calculate the period_size to have four periods in
	   one buffer; this way, we get woken up often enough to avoid
	   buffer overruns, but not too often */
	snd_pcm_uframes_t buffer_size;
	if (snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size) == 0) {
		snd_pcm_uframes_t period_size = buffer_size / 4;
		int direction = -1;
		if ((err = snd_pcm_hw_params_set_period_size_near(capture_handle, hw_params,
								  &period_size, &direction)) < 0)
			throw FormatRuntimeError("Cannot set period size (%s)",
						 snd_strerror(err));
	}
334

335 336 337
	if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0)
		throw FormatRuntimeError("Cannot set parameters (%s)",
					 snd_strerror(err));
338

339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
	snd_pcm_uframes_t alsa_buffer_size;
	err = snd_pcm_hw_params_get_buffer_size(hw_params, &alsa_buffer_size);
	if (err < 0)
		throw FormatRuntimeError("snd_pcm_hw_params_get_buffer_size() failed: %s",
					 snd_strerror(-err));

	snd_pcm_uframes_t alsa_period_size;
	err = snd_pcm_hw_params_get_period_size(hw_params, &alsa_period_size,
						nullptr);
	if (err < 0)
		throw FormatRuntimeError("snd_pcm_hw_params_get_period_size() failed: %s",
					 snd_strerror(-err));

	FormatDebug(alsa_input_domain, "buffer_size=%u period_size=%u",
		    (unsigned)alsa_buffer_size, (unsigned)alsa_period_size);

355
	snd_pcm_sw_params_t *sw_params;
356
	snd_pcm_sw_params_alloca(&sw_params);
357 358 359

	snd_pcm_sw_params_current(capture_handle, sw_params);

360 361 362
	if ((err = snd_pcm_sw_params(capture_handle, sw_params)) < 0)
		throw FormatRuntimeError("unable to install sw params (%s)",
					 snd_strerror(err));
363 364 365 366
}

inline snd_pcm_t *
AlsaInputStream::OpenDevice(const char *device,
367
			    int rate, snd_pcm_format_t format, int channels)
368 369 370 371
{
	snd_pcm_t *capture_handle;
	int err;
	if ((err = snd_pcm_open(&capture_handle, device,
372 373
				SND_PCM_STREAM_CAPTURE,
				SND_PCM_NONBLOCK)) < 0)
374 375
		throw FormatRuntimeError("Failed to open device: %s (%s)",
					 device, snd_strerror(err));
376

377 378 379
	try {
		ConfigureCapture(capture_handle, rate, format, channels);
	} catch (...) {
380
		snd_pcm_close(capture_handle);
381
		throw;
382 383
	}

384 385 386 387 388 389 390
	snd_pcm_prepare(capture_handle);

	return capture_handle;
}

/*#########################  Plugin Functions  ##############################*/

391 392 393 394 395 396 397 398
static EventLoop *alsa_input_event_loop;

static void
alsa_input_init(EventLoop &event_loop, const ConfigBlock &)
{
	alsa_input_event_loop = &event_loop;
}

399
static InputStream *
400
alsa_input_open(const char *uri, Mutex &mutex, Cond &cond)
401
{
402 403
	return AlsaInputStream::Create(*alsa_input_event_loop, uri,
				       mutex, cond);
404 405 406
}

const struct InputPlugin input_plugin_alsa = {
407
	"alsa",
408
	alsa_input_init,
409 410
	nullptr,
	alsa_input_open,
411
};