output/alsa: handle recoverable errors in DrainInternal()

It is possible that an underrun may occur in the ALSA output
device while MPD is draining its own internal buffer. If this
happens then MPD stops playback, reporting the error EPIPE.

This commit attempts to recover the ALSA device instead of
stopping playback, so that the drain can complete and the next
song in the play queue is played.
This commit is contained in:
borine 2023-11-30 11:25:43 +00:00
parent e9c40dead8
commit 50e6950fa8

View File

@ -1002,14 +1002,19 @@ AlsaOutput::DrainInternal()
period_buffer.FillWithSilence(silence, out_frame_size); period_buffer.FillWithSilence(silence, out_frame_size);
/* drain period_buffer */ /* drain period_buffer */
if (!period_buffer.IsDrained()) { unsigned int retry_count = 0;
while (!period_buffer.IsDrained() && retry_count <= 1) {
auto frames_written = WriteFromPeriodBuffer(); auto frames_written = WriteFromPeriodBuffer();
if (frames_written < 0) { if (frames_written < 0) {
if (frames_written == -EAGAIN) if (frames_written == -EAGAIN || frames_written == -EINTR)
return false; return false;
throw Alsa::MakeError(frames_written, if (Recover(frames_written) < 0)
"snd_pcm_writei() failed"); throw Alsa::MakeError(frames_written,
"snd_pcm_writei() failed");
retry_count++;
continue;
} }
/* need to call CopyRingToPeriodBuffer() and /* need to call CopyRingToPeriodBuffer() and