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]

Reply via email to