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]
