diff --git a/src/output/plugins/AlsaOutputPlugin.cxx b/src/output/plugins/AlsaOutputPlugin.cxx index b93a5362b..6b7f9a7f3 100644 --- a/src/output/plugins/AlsaOutputPlugin.cxx +++ b/src/output/plugins/AlsaOutputPlugin.cxx @@ -20,6 +20,7 @@ #include "config.h" #include "AlsaOutputPlugin.hxx" #include "lib/alsa/NonBlock.hxx" +#include "lib/alsa/Version.hxx" #include "../OutputAPI.hxx" #include "../Wrapper.hxx" #include "mixer/MixerList.hxx" @@ -107,6 +108,17 @@ class AlsaOutput final */ snd_pcm_uframes_t period_frames; + /** + * Is this a buggy alsa-lib version, which needs a workaround + * for the snd_pcm_drain() bug always returning -EAGAIN? See + * alsa-lib commits fdc898d41135 and e4377b16454f for details. + * This bug was fixed in alsa-lib version 1.1.4. + * + * The workaround is to re-enable blocking mode for the + * snd_pcm_drain() call. + */ + bool work_around_drain_bug; + /** * After Open(), has this output been activated by a Play() * command? @@ -988,6 +1000,19 @@ AlsaOutput::SetupOrDop(AudioFormat &audio_format, PcmExport::Params ¶ms) #endif } +static constexpr bool +MaybeDmix(snd_pcm_type_t type) +{ + return type == SND_PCM_TYPE_DMIX || type == SND_PCM_TYPE_PLUG; +} + +gcc_pure +static bool +MaybeDmix(snd_pcm_t *pcm) +{ + return MaybeDmix(snd_pcm_type(pcm)); +} + inline void AlsaOutput::Open(AudioFormat &audio_format) { @@ -1012,6 +1037,9 @@ AlsaOutput::Open(AudioFormat &audio_format) GetDevice())); } + work_around_drain_bug = MaybeDmix(pcm) && + GetRuntimeAlsaVersion() < MakeAlsaVersion(1, 1, 4); + snd_pcm_nonblock(pcm, 1); #ifdef ENABLE_DSD @@ -1117,6 +1145,14 @@ AlsaOutput::DrainInternal() } /* .. and finally drain the ALSA hardware buffer */ + + if (work_around_drain_bug) { + snd_pcm_nonblock(pcm, 0); + bool result = snd_pcm_drain(pcm) != -EAGAIN; + snd_pcm_nonblock(pcm, 1); + return result; + } + return snd_pcm_drain(pcm) != -EAGAIN; }