PR #25167 opened by whatisyourname0 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25167 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25167.patch
# Summary of changes ALSA capture and playback currently report failures passed to recovery as XRUNs and return `EIO` when recovery fails. Unplugging a device can therefore produce an XRUN warning and hide `ENODEV` or `EBADFD` from the caller. This change emits the XRUN warning for `EPIPE` and preserves the error from the recovery helper in both directions. A failed `snd_pcm_prepare()` keeps its error code and gets an explicit recovery diagnostic. Unsupported suspend recovery returns `ESTRPIPE` with the existing portability guard. I reproduced the error loss by unbinding a virtio-snd device under ARM64 QEMU. Capture returned `EBADFD` and playback returned `ENODEV`; both became `EIO` before this change. With the patch, both errors reach the public API and the false XRUN warning is gone. Three-second capture and playback stalls still recover from real `EPIPE`, and fresh opens after rebinding succeed. The before/after run covers 24 cases. Deterministic tests cover both directions at 44.1, 48, 96 and 192 kHz, including recovery failures. The 160-packet PTS and sample-order traces are identical before and after. All 302 FATE tests enabled in the minimal ALSA build pass, and the official timefilter test matches its reference. This is independent of #25139. Combining the two changes also passes that PR's 24 packet-regression scenarios. The separate short-read PTS fix is not included here. Callers that specifically check for `EIO` will now receive the more specific errno. The CLI still ends the input after an unrecovered error; its default and `-xerror` policies are unchanged. With `-xerror`, the shell exit code reflects the more specific error. The reproducer archive contains the test sources, scripts and logs. Device separation was tested through a real Linux ALSA driver in QEMU. Physical unplug and hardware suspend remain untested; `EINTR`, `ESTRPIPE` and prepare failures were injected in the deterministic tests. Archive SHA-256: `9fac3858d662b545868a10b26a607517c07152ee784b9ded280eb69e381e23bd`. >From f7ae1323c873e8b9de8eae6ffb56e6ee51bc4b80 Mon Sep 17 00:00:00 2001 From: whatisyourname0 <[email protected]> Date: Sun, 18 Oct 2026 07:15:17 +0000 Subject: [PATCH] fix(avdevice/alsa): preserve PCM errors when recovery fails The capture and playback paths report failures passed to recovery as XRUNs and replace unrecovered errors with EIO. This hides errors such as ENODEV and EBADFD from callers. A failed snd_pcm_prepare also loses its error code, while the final diagnostic reports the original EPIPE. Emit the XRUN warning for EPIPE and preserve the error returned by the recovery helper in both directions. Keep the snd_pcm_prepare error and report its failure explicitly. Return ESTRPIPE for unsupported suspend recovery, retaining the existing portability guard. Reproduced by unbinding a virtio-snd device during capture and playback under ARM64 QEMU. Capture EBADFD and playback ENODEV now reach the public API without a false XRUN warning. Real EPIPE recovery and fresh opens after rebinding still succeed. Deterministic error and PTS comparisons pass at 44.1, 48, 96 and 192 kHz. Signed-off-by: Joonho Lee <[email protected]> --- libavdevice/alsa.c | 11 ++++------- libavdevice/alsa.h | 2 +- libavdevice/alsa_dec.c | 9 +++++---- libavdevice/alsa_enc.c | 9 +++++---- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/libavdevice/alsa.c b/libavdevice/alsa.c index a18b532375..8231275b27 100644 --- a/libavdevice/alsa.c +++ b/libavdevice/alsa.c @@ -439,19 +439,16 @@ int ff_alsa_xrun_recover(AVFormatContext *s1, int err) AlsaData *s = s1->priv_data; snd_pcm_t *handle = s->h; - av_log(s1, AV_LOG_WARNING, "ALSA buffer xrun.\n"); if (err == -EPIPE) { + av_log(s1, AV_LOG_WARNING, "ALSA buffer xrun.\n"); err = snd_pcm_prepare(handle); if (err < 0) { - av_log(s1, AV_LOG_ERROR, "cannot recover from underrun (snd_pcm_prepare failed: %s)\n", snd_strerror(err)); - - return AVERROR(EIO); + av_log(s1, AV_LOG_ERROR, "ALSA xrun recovery failed (snd_pcm_prepare: %s)\n", + snd_strerror(err)); } #ifdef ESTRPIPE } else if (err == -ESTRPIPE) { - av_log(s1, AV_LOG_ERROR, "-ESTRPIPE... Unsupported!\n"); - - return -1; + av_log(s1, AV_LOG_ERROR, "ALSA suspend recovery is not supported.\n"); #endif } return err; diff --git a/libavdevice/alsa.h b/libavdevice/alsa.h index a1fa667c04..65c37267d0 100644 --- a/libavdevice/alsa.h +++ b/libavdevice/alsa.h @@ -92,7 +92,7 @@ int ff_alsa_open(AVFormatContext *s, snd_pcm_stream_t mode, int ff_alsa_close(AVFormatContext *s1); /** - * Try to recover from ALSA buffer underrun. + * Try to recover from an ALSA buffer overrun or underrun. * * @param s1 media file handle * @param err error code reported by the previous ALSA call diff --git a/libavdevice/alsa_dec.c b/libavdevice/alsa_dec.c index 7411fcc5fb..6b1bb89684 100644 --- a/libavdevice/alsa_dec.c +++ b/libavdevice/alsa_dec.c @@ -106,7 +106,7 @@ fail: static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt) { AlsaData *s = s1->priv_data; - int res; + int res, ret; int64_t dts; snd_pcm_sframes_t delay = 0; @@ -123,10 +123,11 @@ static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt) return AVERROR(EAGAIN); } s->pkt->size = 0; - if (ff_alsa_xrun_recover(s1, res) < 0) { + ret = ff_alsa_xrun_recover(s1, res); + if (ret < 0) { av_log(s1, AV_LOG_ERROR, "ALSA read error: %s\n", - snd_strerror(res)); - return AVERROR(EIO); + snd_strerror(ret)); + return ret; } ff_timefilter_reset(s->timefilter); } diff --git a/libavdevice/alsa_enc.c b/libavdevice/alsa_enc.c index 971cff688c..2cefd4c61a 100644 --- a/libavdevice/alsa_enc.c +++ b/libavdevice/alsa_enc.c @@ -85,7 +85,7 @@ fail: static int audio_write_packet(AVFormatContext *s1, AVPacket *pkt) { AlsaData *s = s1->priv_data; - int res; + int res, ret; int size = pkt->size; const uint8_t *buf = pkt->data; @@ -107,11 +107,12 @@ static int audio_write_packet(AVFormatContext *s1, AVPacket *pkt) return AVERROR(EAGAIN); } - if (ff_alsa_xrun_recover(s1, res) < 0) { + ret = ff_alsa_xrun_recover(s1, res); + if (ret < 0) { av_log(s1, AV_LOG_ERROR, "ALSA write error: %s\n", - snd_strerror(res)); + snd_strerror(ret)); - return AVERROR(EIO); + return ret; } } -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
