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

The libspeex encoder emits empty audio packets since c31a781412a3 (#23692). The 
status returned by `ff_af_queue_remove()` overwrites the byte count from 
`speex_bits_write()`, and the successful status of zero becomes the packet size.

Store the encoded byte count directly in the packet before updating its timing, 
preserving queue error handling. Add generated-input FATE tests for mono 
encoding and stereo encoding with two frames per packet. The tests compare 
decoded frame counts and stream properties, avoiding dependence on libspeex's 
precise encoded bytes or additional sample files.

Addresses #23926.

Validation on Linux AArch64 with GCC 13 and libspeex 1.2.1:

- Built and tested c31a781412a3 and its parent 87bcbbff5722. A one-second, 32 
kHz mono sine encoded at the requested 30 kbit/s produces zero audio payload 
bytes at the culprit, versus 3,700 bytes and 50 decoded frames at the parent.
- Reproduced the same failure on master 6246f7a4cde6; the patch restores 3,700 
payload bytes and 50 decoded frames.
- `make fate-libspeex-encode fate-libspeex-encode-stereo` passes with the 
patch. Both tests fail against the unpatched master executable, reporting no 
decoded frames instead of 50 and 25 respectively.
- Default one-frame-per-packet encoding and decoding succeeds for mono and 
stereo at 8, 16 and 32 kHz.
- `git diff --check` passes.

Reproduction: `ffmpeg -v verbose -f lavfi -i 
sine=frequency=1000:sample_rate=32000:duration=1 -y -vn -c:a libspeex -ab 30k 
-ar 32000 -frames:v 1000 output_spx.ogg`, followed by `ffmpeg -v verbose -i 
output_spx.ogg -f null -`.

The existing queue-closing warning also occurs on the working parent and 
remains outside this packet-size fix. Broader checks found decoding errors for 
some partially filled final packets with nondefault `frames_per_packet=3` or 
`8`; these also occur with output from the working parent. The FATE tests use 
complete packets.

Source inspection found the faulty assignment absent from the available active 
release branches 4.4, 5.1, 6.1, 7.1, 8.0, 8.1 and 9.0.


>From 4aed8a59fe8d2ae15b3ac549e9fc1b5f4f262c11 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Fri, 25 Sep 2026 16:37:36 +0000
Subject: [PATCH] avcodec/libspeexenc: preserve the encoded packet size

ff_af_queue_remove() now returns a status code. Its return value
replaced the byte count from speex_bits_write(), so every successful
encode emitted an empty packet.

Store the encoded byte count in the packet before updating its timing.
Add generated-input FATE coverage for mono and stereo encoding, including
multiple frames per packet. Check decoded frame counts instead of exact
encoded bytes so the tests do not depend on libspeex's implementation.

Fixes: c31a781412a3aa801cc0997ba001177f258f4d80
Fixes: #23926
Assisted-by: Fairy
---
 libavcodec/libspeexenc.c              |  3 +--
 tests/fate/enc_external.mak           | 14 ++++++++++++++
 tests/ref/fate/libspeex-encode        |  6 ++++++
 tests/ref/fate/libspeex-encode-stereo |  6 ++++++
 4 files changed, 27 insertions(+), 2 deletions(-)
 create mode 100644 tests/ref/fate/libspeex-encode
 create mode 100644 tests/ref/fate/libspeex-encode-stereo

diff --git a/libavcodec/libspeexenc.c b/libavcodec/libspeexenc.c
index 32abadcef1..6e1591b397 100644
--- a/libavcodec/libspeexenc.c
+++ b/libavcodec/libspeexenc.c
@@ -292,7 +292,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket 
*avpkt,
         s->pkt_frame_count = 0;
         if ((ret = ff_alloc_packet(avctx, avpkt, speex_bits_nbytes(&s->bits))) 
< 0)
             return ret;
-        ret = speex_bits_write(&s->bits, avpkt->data, avpkt->size);
+        avpkt->size = speex_bits_write(&s->bits, avpkt->data, avpkt->size);
         speex_bits_reset(&s->bits);
 
         /* Get the next frame pts/duration */
@@ -301,7 +301,6 @@ static int encode_frame(AVCodecContext *avctx, AVPacket 
*avpkt,
         if (ret < 0)
             return ret;
 
-        avpkt->size = ret;
         *got_packet_ptr = 1;
         return 0;
     }
diff --git a/tests/fate/enc_external.mak b/tests/fate/enc_external.mak
index 705f733db7..aa017dfcaa 100644
--- a/tests/fate/enc_external.mak
+++ b/tests/fate/enc_external.mak
@@ -14,3 +14,17 @@ fate-libx264-hdr10: CMD = enc_external 
$(TARGET_SAMPLES)/hevc/hdr10_plus_h265_sa
 
 FATE_SAMPLES_FFMPEG_FFPROBE += $(FATE_ENC_EXTERNAL-yes)
 fate-enc-external: $(FATE_ENC_EXTERNAL-yes)
+
+# Check decoded frame counts without depending on libspeex's encoded bytes.
+FATE_LIBSPEEX-$(call ALLYES, LIBSPEEX_ENCODER SPEEX_DECODER OGG_MUXER 
OGG_DEMUXER LAVFI_INDEV SINE_FILTER ARESAMPLE_FILTER PCM_S16LE_DECODER 
FILE_PROTOCOL) += fate-libspeex-encode fate-libspeex-encode-stereo
+fate-libspeex-encode: SPEEX_OPTS = -ac 1
+fate-libspeex-encode-stereo: SPEEX_OPTS = -ac 2 -frames_per_packet 2
+$(FATE_LIBSPEEX-yes): CMD = run_with_temp \
+    "$(FFMPEG) -nostdin -hide_banner -loglevel error \
+    -f lavfi -i sine=frequency=1000:sample_rate=32000:duration=1 \
+    -c:a libspeex -b:a 30k $(SPEEX_OPTS) -f ogg -y" \
+    "ffprobe$(PROGSSUF)$(EXESUF) -v error -bitexact -count_frames \
+    -show_entries stream=codec_name,sample_rate,channels,nb_read_frames" ogg
+
+FATE_FFMPEG_FFPROBE += $(FATE_LIBSPEEX-yes)
+fate-enc-external: $(FATE_LIBSPEEX-yes)
diff --git a/tests/ref/fate/libspeex-encode b/tests/ref/fate/libspeex-encode
new file mode 100644
index 0000000000..179ea07d71
--- /dev/null
+++ b/tests/ref/fate/libspeex-encode
@@ -0,0 +1,6 @@
+[STREAM]
+codec_name=speex
+sample_rate=32000
+channels=1
+nb_read_frames=50
+[/STREAM]
diff --git a/tests/ref/fate/libspeex-encode-stereo 
b/tests/ref/fate/libspeex-encode-stereo
new file mode 100644
index 0000000000..ea01697eb4
--- /dev/null
+++ b/tests/ref/fate/libspeex-encode-stereo
@@ -0,0 +1,6 @@
+[STREAM]
+codec_name=speex
+sample_rate=32000
+channels=2
+nb_read_frames=25
+[/STREAM]
-- 
2.52.0

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

Reply via email to