output/alsa: work around dmix non-blocking snd_pcm_drain() bug

See code comment.  Bug was reported against MPD, but it's really an
alsa-lib bug.

 https://bugs.musicpd.org/view.php?id=4662
This commit is contained in:
Max Kellermann 2017-03-29 20:12:14 +02:00
parent b4e4bdcda9
commit f85d4d28d1
1 changed files with 36 additions and 0 deletions

View File

@ -20,6 +20,7 @@
#include "config.h" #include "config.h"
#include "AlsaOutputPlugin.hxx" #include "AlsaOutputPlugin.hxx"
#include "lib/alsa/NonBlock.hxx" #include "lib/alsa/NonBlock.hxx"
#include "lib/alsa/Version.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx" #include "../Wrapper.hxx"
#include "mixer/MixerList.hxx" #include "mixer/MixerList.hxx"
@ -107,6 +108,17 @@ class AlsaOutput final
*/ */
snd_pcm_uframes_t period_frames; 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() * After Open(), has this output been activated by a Play()
* command? * command?
@ -988,6 +1000,19 @@ AlsaOutput::SetupOrDop(AudioFormat &audio_format, PcmExport::Params &params)
#endif #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 inline void
AlsaOutput::Open(AudioFormat &audio_format) AlsaOutput::Open(AudioFormat &audio_format)
{ {
@ -1012,6 +1037,9 @@ AlsaOutput::Open(AudioFormat &audio_format)
GetDevice())); GetDevice()));
} }
work_around_drain_bug = MaybeDmix(pcm) &&
GetRuntimeAlsaVersion() < MakeAlsaVersion(1, 1, 4);
snd_pcm_nonblock(pcm, 1); snd_pcm_nonblock(pcm, 1);
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
@ -1117,6 +1145,14 @@ AlsaOutput::DrainInternal()
} }
/* .. and finally drain the ALSA hardware buffer */ /* .. 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; return snd_pcm_drain(pcm) != -EAGAIN;
} }