PR #25139 opened by whatisyourname0
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25139
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25139.patch

# Summary of changes

ALSA capture can assemble a packet from several successful reads, including 
reads separated by EAGAIN. `audio_read_packet()` currently uses the final 
read's frame count when calculating PTS and updating `last_period`. Earlier 
reads in the same packet are omitted from both calculations.

Use the completed packet's frame count for both. At 48 kHz, a 7680-frame packet 
assembled from two 3840-frame reads otherwise receives its first PTS about 80 
ms late. Single-read packets keep the same calculation.

## Reproduction and validation

The attached reproducer contains a hardware-free regression test and an 
optional real ALSA experiment, with build and execution instructions in 
`README.md`.

- The regression test calls the FFmpeg ALSA demuxer and time filter, using GNU 
ld wrappers for ALSA reads and the clock. All 24 cases pass across 44.1, 48, 96 
and 192 kHz, covering sample order, partial reads, EAGAIN, PTS and XRUN 
recovery. Short-read timestamp assertions fail on the parent revision.
- Under ARM64 QEMU, half-period priming followed by nonblocking reads or 
SIGALRM produces actual ALSA short returns. These tests leave read return 
values unchanged. Eighteen before/after runs observed 48 short returns. Split 
packets had a first-PTS error of 79.976–79.996 ms before the fix and 5–15 us of 
observation overhead afterwards.
- The ALSA-enabled minimal build passes FATE (302 test entries). GNU patcheck 
was reviewed: its `res` warnings are diff-only false positives; `res` remains 
assigned and used in the read loop. The minor-fix Changelog hint was reviewed.
- Consumer-stall XRUN recovery and reopen tests passed. QEMU KASAN/lockdep logs 
showed no diagnostics in those experiments.

Runtime validation used base `3e6cd5e7603cb81b028b9c17530697b8986bf69a` plus 
signed commit `2d1cf34a9d11a83c9063bc1ac36a45b638e1d7bc`. This PR applies the 
same diff to master `b7e835e069`; the ALSA source is byte-identical to the 
tested version, and the stable patch IDs match. ALSA/timefilter sources have 
not changed since the tested base.

The device experiment used silent virtual input. Physical-device behavior and 
how often short reads occur in field recordings remain unmeasured. This fix 
does not reconstruct samples lost to XRUN or add timestamps to raw PCM output. 
The standalone regression test is supplied for review and is not integrated 
into FATE.

## Previous email submission

The patch and reproducer were initially sent before list subscription and held 
for moderation. This PR replaces that submission. The two original Message-IDs 
are:

- `<[email protected]>`
- `<[email protected]>`

I have asked `[email protected]` to discard both held messages. The 
withdrawal request was accepted by Gmail SMTP; moderator confirmation is 
pending. Please use this PR for review.



From d06b18f53f59506d8a5a5247115c84dd752e5dff Mon Sep 17 00:00:00 2001
From: whatisyourname0 <[email protected]>
Date: Sat, 17 Oct 2026 04:17:44 +0000
Subject: [PATCH] fix(avdevice/alsa): timestamp the complete capture packet
 after short reads

ALSA capture can assemble a packet from multiple successful reads, possibly
across EAGAIN returns. The timestamp calculation still uses the frame count
from the final read, so it subtracts too little audio time. The same count
also supplies the wrong interval to the next time-filter update.

Use the completed packet's frame count for both calculations. A 7680-frame
packet assembled from two 3840-frame reads at 48 kHz otherwise receives a
first PTS 80 ms too late. Single-read packets retain the same calculation.

Reproduced with real ALSA short returns under ARM64 QEMU: a capture PCM
was primed by half a period, then read in nonblocking mode or interrupted
by SIGALRM while waiting for the remainder. No read return values were
changed in those tests. The fix also passes deterministic sample-order,
PTS, EAGAIN and XRUN recovery checks at 44.1, 48, 96 and 192 kHz.

Signed-off-by: Joonho Lee <[email protected]>
---
 libavdevice/alsa_dec.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/libavdevice/alsa_dec.c b/libavdevice/alsa_dec.c
index 7411fcc5fb..78ced66de9 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, frames;
     int64_t dts;
     snd_pcm_sframes_t delay = 0;
 
@@ -134,11 +134,13 @@ static int audio_read_packet(AVFormatContext *s1, 
AVPacket *pkt)
     } while (s->pkt->size < s->period_size * s->frame_size);
 
     av_packet_move_ref(pkt, s->pkt);
+    /* A packet may have been assembled from multiple short reads. */
+    frames = pkt->size / s->frame_size;
     dts = av_gettime();
     snd_pcm_delay(s->h, &delay);
-    dts -= av_rescale(delay + res, 1000000, s->sample_rate);
+    dts -= av_rescale(delay + frames, 1000000, s->sample_rate);
     pkt->pts = ff_timefilter_update(s->timefilter, dts, s->last_period);
-    s->last_period = res;
+    s->last_period = frames;
 
     return 0;
 }
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to