input/plugins/Alsa: limit ALSA buffer time to 2 seconds maximum

Some ALSA capture devices can have very large buffers, holding 10
seconds or more audio. Using the maximum buffer size with such
devices leads to unacceptably large, and unnecessary, latency.
Also, some ALSA drivers (e.g. HDA Intel PCH) report an invalid
maximum period size, and the period size that mpd calculates from
the maximum buffer size results in "Invalid argument" error when
applying the hw_params. Note that the "default" capture device on
many cards includes the "dsnoop" plugin which imposes a buffer
size of 16384 frames, so that "alsa://" works OK but
"alsa://plughw" or "alsa://hw" both fail.

Limit the maximum buffer time for ALSA input devices to a more useable
2 seconds, thereby avoiding both the above problems.
This commit is contained in:
borine 2023-05-06 08:40:07 +01:00 committed by Max Kellermann
parent 0f82f18652
commit 17d944f6ce
2 changed files with 11 additions and 4 deletions

2
NEWS
View File

@ -1,6 +1,8 @@
ver 0.23.15 (not yet released) ver 0.23.15 (not yet released)
* decoder * decoder
- ffmpeg: fix build failure with FFmpeg 6.1 - ffmpeg: fix build failure with FFmpeg 6.1
* output
- alsa: limit buffer time to 2 seconds
ver 0.23.14 (2023/10/08) ver 0.23.14 (2023/10/08)
* decoder * decoder

View File

@ -369,9 +369,14 @@ AlsaInputStream::ConfigureCapture(AudioFormat audio_format)
period_size_min, period_size_max, period_size_min, period_size_max,
period_time_min, period_time_max); period_time_min, period_time_max);
/* choose the maximum possible buffer_size ... */ /* choose the maximum buffer_time up to limit of 2 seconds ... */
snd_pcm_hw_params_set_buffer_size(capture_handle, hw_params, unsigned buffer_time = buffer_time_max;
buffer_size_max); if (buffer_time > 2000000U)
buffer_time = 2000000U;
int direction = -1;
if ((err = snd_pcm_hw_params_set_buffer_time_near(capture_handle,
hw_params, &buffer_time, &direction)) < 0)
throw Alsa::MakeError(err, "Cannot set buffer time");
/* ... and calculate the period_size to have four periods in /* ... and calculate the period_size to have four periods in
one buffer; this way, we get woken up often enough to avoid one buffer; this way, we get woken up often enough to avoid
@ -379,7 +384,7 @@ AlsaInputStream::ConfigureCapture(AudioFormat audio_format)
snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t buffer_size;
if (snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size) == 0) { if (snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size) == 0) {
snd_pcm_uframes_t period_size = buffer_size / 4; snd_pcm_uframes_t period_size = buffer_size / 4;
int direction = -1; direction = -1;
if ((err = snd_pcm_hw_params_set_period_size_near(capture_handle, if ((err = snd_pcm_hw_params_set_period_size_near(capture_handle,
hw_params, &period_size, &direction)) < 0) hw_params, &period_size, &direction)) < 0)
throw Alsa::MakeError(err, "Cannot set period size"); throw Alsa::MakeError(err, "Cannot set period size");