PR #24376 opened by AYOUB NABIL BOUBAGRAT (ayoubnabil)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24376
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24376.patch

peed up AC-3 and E-AC-3 encoding by skipping unnecessary band loops, reusing 
bap counts across blocks and simplifying mantissa writing.    
add bit allocation coverage and fix the incomplete checkasm comparison.    
encoded output stays byte-identical in the tested cases.   


>From e1848bd609ac28483478f966303ee6dbf862fb45 Mon Sep 17 00:00:00 2001
From: Ayoub Nabil Boubagrat <[email protected]>
Date: Sat, 5 Sep 2026 12:56:11 +0200
Subject: [PATCH 1/6] checkasm/ac3dsp: compare the full fixed-point output

BUF_SIZE counts coefficients, not bytes. the old comparison
only checked a quarter of the output.

Signed-off-by: Ayoub Nabil Boubagrat 
<[email protected]>
---
 tests/checkasm/ac3dsp.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/checkasm/ac3dsp.c b/tests/checkasm/ac3dsp.c
index 75a2b1d7ac..7081918d75 100644
--- a/tests/checkasm/ac3dsp.c
+++ b/tests/checkasm/ac3dsp.c
@@ -129,7 +129,7 @@ static void check_float_to_fixed24(AC3DSPContext *c) {
         call_ref(dst, src, BUF_SIZE);
         call_new(dst2, src, BUF_SIZE);
 
-        if (memcmp(dst, dst2, BUF_SIZE) != 0)
+        if (memcmp(dst, dst2, BUF_SIZE * sizeof(*dst)) != 0)
             fail();
 
         bench_new(dst, src, BUF_SIZE);
-- 
2.52.0


>From 828a5b8b7b6ed545aa237b7f4627ab0fa67a7802 Mon Sep 17 00:00:00 2001
From: Ayoub Nabil Boubagrat <[email protected]>
Date: Sat, 5 Sep 2026 12:56:11 +0200
Subject: [PATCH 2/6] tests/ac3: cover bit allocation

cover band boundaries, partial ranges and exponent limits
before changing the bit allocation code.

Signed-off-by: Ayoub Nabil Boubagrat 
<[email protected]>
---
 libavcodec/Makefile               |  1 +
 libavcodec/tests/.gitignore       |  1 +
 libavcodec/tests/ac3.c            | 92 +++++++++++++++++++++++++++++++
 tests/fate/libavcodec.mak         |  4 ++
 tests/ref/fate/ac3-bit-allocation |  4 ++
 5 files changed, 102 insertions(+)
 create mode 100644 libavcodec/tests/ac3.c
 create mode 100644 tests/ref/fate/ac3-bit-allocation

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 3cdfa4f383..b7f4e4dcec 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1381,6 +1381,7 @@ TESTPROGS = avcodec                                       
              \
             jpeg2000dwt                                                 \
             mathops                                                    \
 
+TESTPROGS-$(CONFIG_AC3DSP)                += ac3
 TESTPROGS-$(CONFIG_APV_DECODER)           += apv
 TESTPROGS-$(CONFIG_AV1_VAAPI_ENCODER)     += av1_levels
 TESTPROGS-$(CONFIG_CABAC)                 += cabac
diff --git a/libavcodec/tests/.gitignore b/libavcodec/tests/.gitignore
index 04e1e2518b..4977c63e00 100644
--- a/libavcodec/tests/.gitignore
+++ b/libavcodec/tests/.gitignore
@@ -1,3 +1,4 @@
+/ac3
 /apv
 /av1_levels
 /avcodec
diff --git a/libavcodec/tests/ac3.c b/libavcodec/tests/ac3.c
new file mode 100644
index 0000000000..c343e265f7
--- /dev/null
+++ b/libavcodec/tests/ac3.c
@@ -0,0 +1,92 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libavutil/crc.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/lfg.h"
+#include "libavutil/macros.h"
+#include "libavcodec/ac3.h"
+#include "libavcodec/ac3dsp.h"
+#include "libavcodec/ac3tab.h"
+
+static uint32_t hash_int16(const AVCRC *crc, uint32_t hash,
+                          const int16_t *data, int count)
+{
+    uint8_t bytes[2 * AC3_MAX_COEFS];
+
+    for (int i = 0; i < count; i++)
+        AV_WL16(bytes + 2 * i, data[i]);
+    return av_crc(crc, hash, bytes, 2 * count);
+}
+
+int main(void)
+{
+    static const int ends[] = { 7, 27, 28, 29, 37, 73, 133, 229, 253 };
+    static const int offsets[] = { -960, -959, 0, 1024, 3132 };
+    const AVCRC *crc = av_crc_get_table(AV_CRC_32_IEEE);
+    int8_t exp[AC3_MAX_COEFS];
+    int16_t psd[AC3_MAX_COEFS], band_psd[AC3_CRITICAL_BANDS];
+    int16_t mask[AC3_CRITICAL_BANDS];
+    uint8_t bap[AC3_MAX_COEFS];
+    AC3DSPContext dsp;
+    AVLFG lfg;
+
+    ff_ac3dsp_init(&dsp);
+    av_lfg_init(&lfg, 1);
+    for (int i = 0; i < AC3_CRITICAL_BANDS; i++)
+        mask[i] = av_lfg_get(&lfg) % 3073;
+
+    for (int pattern = 0; pattern < 4; pattern++) {
+        uint32_t psd_hash = 0, bap_hash = 0;
+
+        for (int i = 0; i < AC3_MAX_COEFS; i++)
+            exp[i] = pattern == 0 ? 0 : pattern == 1 ? 24 :
+                     pattern == 2 ? (i & 1) * 24 : av_lfg_get(&lfg) % 25;
+
+        for (int start = 0; start < 253; start++) {
+            for (int j = 0; j <= FF_ARRAY_ELEMS(ends); j++) {
+                int end = j ? ends[j - 1] : start + 1;
+
+                if (start >= end)
+                    continue;
+                memset(psd,      0x5a, sizeof(psd));
+                memset(band_psd, 0x5a, sizeof(band_psd));
+                ff_ac3_bit_alloc_calc_psd(exp, start, end, psd, band_psd);
+                psd_hash = hash_int16(crc, psd_hash, psd, AC3_MAX_COEFS);
+                psd_hash = hash_int16(crc, psd_hash, band_psd, 
AC3_CRITICAL_BANDS);
+
+                for (int floor = 0; floor < 8; floor++) {
+                    for (int k = 0; k < FF_ARRAY_ELEMS(offsets); k++) {
+                        memset(bap, 0x5a, sizeof(bap));
+                        dsp.bit_alloc_calc_bap(mask, psd, start, end,
+                                               offsets[k], 
ff_ac3_floor_tab[floor],
+                                               ff_ac3_bap_tab, bap);
+                        bap_hash = av_crc(crc, bap_hash, bap, sizeof(bap));
+                    }
+                }
+            }
+        }
+        printf("pattern %d: psd %08"PRIx32" bap %08"PRIx32"\n",
+               pattern, psd_hash, bap_hash);
+    }
+    return 0;
+}
diff --git a/tests/fate/libavcodec.mak b/tests/fate/libavcodec.mak
index e2d616e307..f6858c4921 100644
--- a/tests/fate/libavcodec.mak
+++ b/tests/fate/libavcodec.mak
@@ -1,3 +1,7 @@
+FATE_LIBAVCODEC-$(CONFIG_AC3DSP) += fate-ac3-bit-allocation
+fate-ac3-bit-allocation: libavcodec/tests/ac3$(EXESUF)
+fate-ac3-bit-allocation: CMD = run libavcodec/tests/ac3$(EXESUF)
+
 FATE_LIBAVCODEC-$(CONFIG_AV1_VAAPI_ENCODER) += fate-av1-levels
 fate-av1-levels: libavcodec/tests/av1_levels$(EXESUF)
 fate-av1-levels: CMD = run libavcodec/tests/av1_levels$(EXESUF)
diff --git a/tests/ref/fate/ac3-bit-allocation 
b/tests/ref/fate/ac3-bit-allocation
new file mode 100644
index 0000000000..faa4d1bb39
--- /dev/null
+++ b/tests/ref/fate/ac3-bit-allocation
@@ -0,0 +1,4 @@
+pattern 0: psd 849e1ac4 bap e81b62f5
+pattern 1: psd 8443966b bap 9175e8d9
+pattern 2: psd 6713433c bap 9aed758b
+pattern 3: psd 129b9438 bap 0f3be9c7
-- 
2.52.0


>From 02d00195716a9b198c36c8a490716bc776fe2ce2 Mon Sep 17 00:00:00 2001
From: Ayoub Nabil Boubagrat <[email protected]>
Date: Sat, 5 Sep 2026 12:56:25 +0200
Subject: [PATCH 3/6] avcodec/ac3: skip psd integration for single-bin bands

the first 28 bands have one coefficient each. copy its psd
instead of running the integration loop.

Signed-off-by: Ayoub Nabil Boubagrat 
<[email protected]>
---
 libavcodec/ac3.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/libavcodec/ac3.c b/libavcodec/ac3.c
index 60491d1a7a..dbb4a62b74 100644
--- a/libavcodec/ac3.c
+++ b/libavcodec/ac3.c
@@ -183,8 +183,13 @@ void ff_ac3_bit_alloc_calc_psd(int8_t *exp, int start, int 
end, int16_t *psd,
     }
 
     /* PSD integration */
-    bin  = start;
-    band = ff_ac3_bin_to_band_tab[start];
+    /* the first 28 bands have one coefficient each */
+    for (bin = start; bin < FFMIN(end, 28); bin++)
+        band_psd[bin] = psd[bin];
+    if (bin >= end)
+        return;
+
+    band = ff_ac3_bin_to_band_tab[bin];
     do {
         int v = psd[bin++];
         int band_end = FFMIN(ff_ac3_band_start_tab[band+1], end);
-- 
2.52.0


>From 6d27a6be93a6fcb688ccff5a9a0fc03ce1cd21d8 Mon Sep 17 00:00:00 2001
From: Ayoub Nabil Boubagrat <[email protected]>
Date: Sat, 5 Sep 2026 12:56:25 +0200
Subject: [PATCH 4/6] avcodec/ac3dsp: skip the inner loop for single-bin bands

calculate bap directly for the first 28 bands. each contains
only one coefficient.

Signed-off-by: Ayoub Nabil Boubagrat 
<[email protected]>
---
 libavcodec/ac3dsp.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/libavcodec/ac3dsp.c b/libavcodec/ac3dsp.c
index 090287e42d..385a642af1 100644
--- a/libavcodec/ac3dsp.c
+++ b/libavcodec/ac3dsp.c
@@ -83,8 +83,16 @@ static void ac3_bit_alloc_calc_bap_c(int16_t *mask, int16_t 
*psd,
         return;
     }
 
-    bin  = start;
-    band = ff_ac3_bin_to_band_tab[start];
+    /* the first 28 bands have one coefficient each */
+    for (bin = start; bin < FFMIN(end, 28); bin++) {
+        int m = (FFMAX(mask[bin] - snr_offset - floor, 0) & 0x1FE0) + floor;
+        int address = av_clip_uintp2((psd[bin] - m) >> 5, 6);
+        bap[bin] = bap_tab[address];
+    }
+    if (bin >= end)
+        return;
+
+    band = ff_ac3_bin_to_band_tab[bin];
     do {
         int m = (FFMAX(mask[band] - snr_offset - floor, 0) & 0x1FE0) + floor;
         band_end = ff_ac3_band_start_tab[++band];
-- 
2.52.0


>From 634457be4d686a50cfac491a0a4a646afc2de794 Mon Sep 17 00:00:00 2001
From: Ayoub Nabil Boubagrat <[email protected]>
Date: Sat, 5 Sep 2026 12:56:25 +0200
Subject: [PATCH 5/6] avcodec/ac3enc: reuse bap counts across blocks

reuse counts when the bap pointer and length match.
keep direct counting when the next block differs.

Signed-off-by: Ayoub Nabil Boubagrat 
<[email protected]>
---
 libavcodec/ac3enc.c | 28 +++++++++++++++++++++++++---
 1 file changed, 25 insertions(+), 3 deletions(-)

diff --git a/libavcodec/ac3enc.c b/libavcodec/ac3enc.c
index 903ba66ee0..8760166b8b 100644
--- a/libavcodec/ac3enc.c
+++ b/libavcodec/ac3enc.c
@@ -1326,15 +1326,37 @@ static void 
count_mantissa_bits_update_ch(AC3EncodeContext *s, int ch,
                                           uint16_t 
mant_cnt[AC3_MAX_BLOCKS][16],
                                           int start, int end)
 {
+    uint16_t counts[16];
+    const uint8_t *prev_bap = NULL;
+    int prev_len = -1;
     int blk;
 
     for (blk = 0; blk < s->num_blocks; blk++) {
         AC3Block *block = &s->blocks[blk];
+        const uint8_t *bap;
+        int len;
+
         if (ch == CPL_CH && !block->cpl_in_use)
             continue;
-        s->ac3dsp.update_bap_counts(mant_cnt[blk],
-                                    s->ref_bap[ch][blk] + start,
-                                    FFMIN(end, block->end_freq[ch]) - start);
+
+        bap = s->ref_bap[ch][blk] + start;
+        len = FFMIN(end, block->end_freq[ch]) - start;
+        /* the same bap buffer can be counted over different lengths */
+        if (bap != prev_bap || len != prev_len) {
+            if (blk + 1 == s->num_blocks ||
+                (ch == CPL_CH && !s->blocks[blk + 1].cpl_in_use) ||
+                bap != s->ref_bap[ch][blk + 1] + start ||
+                len != FFMIN(end, s->blocks[blk + 1].end_freq[ch]) - start) {
+                s->ac3dsp.update_bap_counts(mant_cnt[blk], bap, len);
+                continue;
+            }
+            memset(counts, 0, sizeof(counts));
+            s->ac3dsp.update_bap_counts(counts, bap, len);
+            prev_bap = bap;
+            prev_len = len;
+        }
+        for (int i = 0; i < 16; i++)
+            mant_cnt[blk][i] += counts[i];
     }
 }
 
-- 
2.52.0


>From e73e011f6116a4395eba37c0c23c953982ba4be8 Mon Sep 17 00:00:00 2001
From: Ayoub Nabil Boubagrat <[email protected]>
Date: Sat, 5 Sep 2026 12:56:25 +0200
Subject: [PATCH 6/6] avcodec/ac3enc: use the bit-width table for mantissas

replace the ungrouped mantissa cases with the existing table.
grouped mantissas stay unchanged.

Signed-off-by: Ayoub Nabil Boubagrat 
<[email protected]>
---
 libavcodec/ac3enc.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/libavcodec/ac3enc.c b/libavcodec/ac3enc.c
index 8760166b8b..47dbe94cd3 100644
--- a/libavcodec/ac3enc.c
+++ b/libavcodec/ac3enc.c
@@ -1898,11 +1898,8 @@ static void output_audio_block(AC3EncodeContext *s, 
PutBitContext *pb, int blk)
             case 0:                                          break;
             case 1: if (q != 128) put_bits (pb,   5, q); break;
             case 2: if (q != 128) put_bits (pb,   7, q); break;
-            case 3:               put_sbits(pb,   3, q); break;
             case 4: if (q != 128) put_bits (pb,   7, q); break;
-            case 14:              put_sbits(pb,  14, q); break;
-            case 15:              put_sbits(pb,  16, q); break;
-            default:              put_sbits(pb, b-1, q); break;
+            default:              put_sbits(pb, ff_ac3_bap_bits[b], q); break;
             }
         }
         if (ch == CPL_CH)
-- 
2.52.0

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

Reply via email to