This is an automated email from the git hooks/post-receive script.
Git pushed a commit to branch master
in repository ffmpeg.
The following commit(s) were added to refs/heads/master by this push:
new 5ba2a2c841 avcodec/ac3dec_fixed: fix dither at exponent 24
5ba2a2c841 is described below
commit 5ba2a2c841259a04742064da5e79e288f50ef9fa
Author: Ayoub Nabil <[email protected]>
AuthorDate: Wed Aug 5 02:45:10 2026 +0200
Commit: James Almer <[email protected]>
CommitDate: Mon Aug 10 22:01:35 2026 +0000
avcodec/ac3dec_fixed: fix dither at exponent 24
the fixed decoder discarded fractional coefficient data before the imdct,
causing low-level coefficients to be truncated at large exponents. dithered bap
0 coefficients at exponent 24 were consequently quantized with a negative bias.
keep ac-3 coefficients in q2, compensate during windowing, and round the
affected dither symmetrically. keep the forced e-ac-3 path in q0.
add a synthetic fate test covering the corrected output.
---
libavcodec/ac3dec.c | 69 +++++++++++++++++++---
libavcodec/ac3dec_fixed.c | 54 +++++++++++++++++
tests/fate/ac3.mak | 25 +++++++-
.../{filter-atrim-samples => ac3-fixed-dexp24} | 2 +-
4 files changed, 141 insertions(+), 9 deletions(-)
diff --git a/libavcodec/ac3dec.c b/libavcodec/ac3dec.c
index 6f7306ea59..e3a711ebbf 100644
--- a/libavcodec/ac3dec.c
+++ b/libavcodec/ac3dec.c
@@ -390,6 +390,26 @@ typedef struct mant_groups {
int b4;
} mant_groups;
+static av_always_inline int dequantize_coeff(int mantissa, int exponent,
+ int coeff_bits)
+{
+#if USE_FIXED
+ return (mantissa * (1 << coeff_bits)) >> exponent;
+#else
+ return mantissa >> exponent;
+#endif
+}
+
+#if USE_FIXED
+static av_always_inline int dequantize_dexp24_dither(int mantissa)
+{
+ int scaled = mantissa * (1 << AC3_FIXED_COEFF_BITS);
+ int round = 1 << (AC3_FIXED_EXPONENT_MAX - 1);
+
+ return (scaled + round - (scaled < 0)) >> AC3_FIXED_EXPONENT_MAX;
+}
+#endif
+
/**
* Decode the transform coefficients for a particular channel
* reference: Section 7.3 Quantization and Decoding of Mantissas
@@ -402,6 +422,11 @@ static void
ac3_decode_transform_coeffs_ch(AC3DecodeContext *s, int ch_index, ma
int8_t *exps = s->dexps[ch_index];
int32_t *coeffs = s->fixed_coeffs[ch_index];
int dither = (ch_index == CPL_CH) || s->dither_flag[ch_index];
+#if USE_FIXED
+ int coeff_bits = fixed_coeff_bits(s);
+#else
+ int coeff_bits = 0;
+#endif
GetBitContext *gbc = &s->gbc;
int freq;
@@ -411,10 +436,19 @@ static void
ac3_decode_transform_coeffs_ch(AC3DecodeContext *s, int ch_index, ma
switch (bap) {
case 0:
/* random noise with approximate range of -0.707 to 0.707 */
- if (dither)
+ if (dither) {
mantissa = (((av_lfg_get(&s->dith_state)>>8)*181)>>8) -
5931008;
- else
+#if USE_FIXED
+ /* At dexp 24 the dither is below half a Q0 step. Keep two
+ * fractional bits so it is not truncated to -1 or 0. */
+ if (coeff_bits && exps[freq] == AC3_FIXED_EXPONENT_MAX) {
+ coeffs[freq] = dequantize_dexp24_dither(mantissa);
+ continue;
+ }
+#endif
+ } else {
mantissa = 0;
+ }
break;
case 1:
if (m->b1) {
@@ -466,7 +500,7 @@ static void ac3_decode_transform_coeffs_ch(AC3DecodeContext
*s, int ch_index, ma
mantissa = (unsigned)get_sbits(gbc, ff_ac3_quantization_tab[bap])
<< (24 - ff_ac3_quantization_tab[bap]);
break;
}
- coeffs[freq] = mantissa >> exps[freq];
+ coeffs[freq] = dequantize_coeff(mantissa, exps[freq], coeff_bits);
}
}
@@ -500,7 +534,8 @@ static inline void
decode_transform_coeffs_ch(AC3DecodeContext *s, int blk,
if (CONFIG_EAC3_DECODER && !blk)
ff_eac3_decode_transform_coeffs_aht_ch(s, ch);
for (bin = s->start_freq[ch]; bin < s->end_freq[ch]; bin++) {
- s->fixed_coeffs[ch][bin] = s->pre_mantissa[ch][bin][blk] >>
s->dexps[ch][bin];
+ s->fixed_coeffs[ch][bin] = dequantize_coeff(
+ s->pre_mantissa[ch][bin][blk], s->dexps[ch][bin], 0);
}
}
}
@@ -571,6 +606,9 @@ static void do_rematrixing(AC3DecodeContext *s)
static inline void do_imdct(AC3DecodeContext *s, int channels, int offset)
{
int ch;
+#if USE_FIXED
+ int window_bits = 8 + fixed_coeff_bits(s);
+#endif
for (ch = 1; ch <= channels; ch++) {
if (s->block_switch[ch]) {
@@ -581,7 +619,7 @@ static inline void do_imdct(AC3DecodeContext *s, int
channels, int offset)
s->tx_fn_128(s->tx_128, s->tmp_output, x, sizeof(INTFLOAT));
#if USE_FIXED
s->fdsp->vector_fmul_window_scaled(s->outptr[ch - 1], s->delay[ch
- 1 + offset],
- s->tmp_output, s->window, 128, 8);
+ s->tmp_output, s->window, 128,
window_bits);
#else
s->fdsp->vector_fmul_window(s->outptr[ch - 1], s->delay[ch - 1 +
offset],
s->tmp_output, s->window, 128);
@@ -593,7 +631,7 @@ static inline void do_imdct(AC3DecodeContext *s, int
channels, int offset)
s->tx_fn_256(s->tx_256, s->tmp_output, s->transform_coeffs[ch],
sizeof(INTFLOAT));
#if USE_FIXED
s->fdsp->vector_fmul_window_scaled(s->outptr[ch - 1], s->delay[ch
- 1 + offset],
- s->tmp_output, s->window, 128, 8);
+ s->tmp_output, s->window, 128,
window_bits);
#else
s->fdsp->vector_fmul_window(s->outptr[ch - 1], s->delay[ch - 1 +
offset],
s->tmp_output, s->window, 128);
@@ -1287,7 +1325,11 @@ static int decode_audio_block(AC3DecodeContext *s, int
blk, int offset)
gain = s->dynamic_range[audio_channel];
#if USE_FIXED
- scale_coefs(s->transform_coeffs[ch], s->fixed_coeffs[ch], gain, 256);
+ if (fixed_coeff_bits(s))
+ scale_coefs_q2(s->transform_coeffs[ch], s->fixed_coeffs[ch], gain,
+ 256);
+ else
+ scale_coefs(s->transform_coeffs[ch], s->fixed_coeffs[ch], gain,
256);
#else
if (s->target_level != 0)
gain = gain * s->level_gain[audio_channel];
@@ -1356,6 +1398,9 @@ static int ac3_decode_frame(AVCodecContext *avctx,
AVFrame *frame,
AC3DecodeContext *s = avctx->priv_data;
int blk, ch, err, offset, ret;
int i;
+#if USE_FIXED
+ int previous_coeff_bits;
+#endif
int skip = 0, got_independent_frame = 0;
const uint8_t *channel_map;
uint8_t extended_channel_map[EAC3_MAX_CHANNELS];
@@ -1390,6 +1435,9 @@ static int ac3_decode_frame(AVCodecContext *avctx,
AVFrame *frame,
buf = s->input_buffer;
dependent_frame:
+#if USE_FIXED
+ previous_coeff_bits = fixed_coeff_bits(s);
+#endif
/* initialize the GetBitContext with the start of valid AC-3 Frame */
if ((ret = init_get_bits8(&s->gbc, buf, buf_size)) < 0)
return ret;
@@ -1397,6 +1445,13 @@ dependent_frame:
/* parse the syncinfo */
err = parse_frame_header(s);
+#if USE_FIXED
+ /* Do not mix Q0 and Q2 overlap samples if a malformed or explicitly
+ * forced stream switches between E-AC-3 and AC-3. */
+ if (!err && previous_coeff_bits != fixed_coeff_bits(s))
+ memset(s->delay, 0, sizeof(s->delay));
+#endif
+
if (err) {
switch (err) {
case AC3_PARSE_ERROR_SYNC:
diff --git a/libavcodec/ac3dec_fixed.c b/libavcodec/ac3dec_fixed.c
index 285553c22e..5fb07dc58f 100644
--- a/libavcodec/ac3dec_fixed.c
+++ b/libavcodec/ac3dec_fixed.c
@@ -55,6 +55,17 @@
#include "ac3dec.h"
+/* Keep two fractional bits in fixed-point transform coefficients. */
+#define AC3_FIXED_COEFF_BITS 2
+#define AC3_FIXED_EXPONENT_MAX 24
+
+static av_always_inline int fixed_coeff_bits(const AC3DecodeContext *s)
+{
+ /* ac3_fixed normally decodes AC-3. Keep Q0 when it is explicitly forced
+ * to decode E-AC-3, whose AHT coefficient bounds are different. */
+ return s->eac3 ? 0 : AC3_FIXED_COEFF_BITS;
+}
+
static const int end_freq_inv_tab[8] =
{
50529027, 44278013, 39403370, 32292987, 27356480, 23729101, 20951060,
18755316
@@ -124,6 +135,49 @@ static void scale_coefs (
}
}
+static void scale_coefs_q2(int32_t *dst, const int32_t *src, int dynrng,
+ int len)
+{
+ int i, shift;
+ int mul;
+
+ mul = (dynrng & 0x1f) + 0x20;
+ shift = 4 - (sign_extend(dynrng, 9) >> 5);
+
+ /* AC-3 mantissas have magnitude at most 2^23, hence Q2 coefficients
+ * have magnitude at most 2^25. Coupling uses MULH(coeff * 2^4, coord)
+ * with coord < 2^31, so coupled coefficients are below 2^28.
+ * Rematrixing can at most double them, keeping src below 2^29. */
+ if (dynrng == 32) {
+ for (i = 0; i < len; i++)
+ dst[i] = src[i] * 4;
+ return;
+ }
+
+ if (shift >= 4) {
+ const int round = 1 << (shift - 1);
+ const int unit = 1 << shift;
+
+ /* With shift >= 4, quotient * mul is below 2^25 * 63. Splitting
+ * quotient and remainder therefore keeps both products in int32_t. */
+ for (i = 0; i < len; i++) {
+ int quotient = src[i] >> shift;
+ int remainder = src[i] - quotient * unit;
+
+ dst[i] = quotient * mul + ((remainder * mul + round) >> shift);
+ }
+ } else if (shift > 0) {
+ const int round = 1 << (shift - 1);
+
+ for (i = 0; i < len; i++)
+ dst[i] = av_clipl_int32(((int64_t)src[i] * mul + round) >> shift);
+ } else {
+ mul <<= -shift;
+ for (i = 0; i < len; i++)
+ dst[i] = av_clipl_int32((int64_t)src[i] * mul);
+ }
+}
+
/**
* Downmix samples from original signal to stereo or mono (this is for 16-bit
samples
* and fixed point decoder - original (for 32-bit samples) is in ac3dsp.c).
diff --git a/tests/fate/ac3.mak b/tests/fate/ac3.mak
index 875fa12418..90977a80c0 100644
--- a/tests/fate/ac3.mak
+++ b/tests/fate/ac3.mak
@@ -102,11 +102,34 @@ fate-ac3-fixed-encode-3: tests/data/asynth-44100-6.wav
fate-ac3-fixed-encode-3: SRC = $(TARGET_PATH)/tests/data/asynth-44100-6.wav
fate-ac3-fixed-encode-3: CMD = framecrc -i $(SRC) -c:a ac3_fixed -flags2
+fixed_frame_size -ab 256k -af aresample,atrim=start_sample=0:end_sample=12096
+# With coupling and rematrixing disabled, this produces bap=0, dexp=24 bins
+# whose dither affects the decoded output.
+tests/data/fate/ac3-fixed-dexp24.ac3: TAG = GEN
+tests/data/fate/ac3-fixed-dexp24.ac3: tests/data/asynth-44100-2.wav
+tests/data/fate/ac3-fixed-dexp24.ac3: ffmpeg$(PROGSSUF)$(EXESUF) |
tests/data/fate
+ $(M)$(TARGET_EXEC) $(TARGET_PATH)/$< \
+ -hide_banner -loglevel error -nostdin \
+ -i $(TARGET_PATH)/tests/data/asynth-44100-2.wav \
+ -c:a ac3_fixed -b:a 192k \
+ -channel_coupling 0 -stereo_rematrixing 0 -flags +bitexact \
+ -frames:a 173 -f ac3 -y $(TARGET_PATH)/$@
+
+FATE_AC3_FIXED_DEXP24-$(call ALLYES, FFMPEG WAV_DEMUXER ARESAMPLE_FILTER
ATRIM_FILTER \
+ AC3_FIXED_ENCODER FRAMECRC_MUXER \
+ AC3_MUXER AC3_DEMUXER AC3_FIXED_DECODER \
+ PCM_S16LE_DECODER PCM_S16LE_ENCODER \
+ FILE_PROTOCOL) += fate-ac3-fixed-dexp24
+fate-ac3-fixed-dexp24: tests/data/fate/ac3-fixed-dexp24.ac3
+fate-ac3-fixed-dexp24: CMD = framecrc -auto_conversion_filters -c ac3_fixed \
+ -i
$(TARGET_PATH)/tests/data/fate/ac3-fixed-dexp24.ac3 \
+ -af atrim=start_sample=264192
+
FATE_EAC3-$(call ALLYES, EAC3_DEMUXER EAC3_MUXER EAC3_CORE_BSF) +=
fate-eac3-core-bsf
fate-eac3-core-bsf: CMD = md5pipe -i
$(TARGET_SAMPLES)/eac3/the_great_wall_7.1.eac3 -c:a copy -bsf:a eac3_core
-fflags +bitexact -f eac3
fate-eac3-core-bsf: CMP = oneline
fate-eac3-core-bsf: REF = b704bf851e99b7442e9bed368b60e6ca
FATE_SAMPLES_AVCONV += $(FATE_AC3-yes) $(FATE_EAC3-yes)
+FATE_FFMPEG += $(FATE_AC3_FIXED_DEXP24-yes)
-fate-ac3: $(FATE_AC3-yes) $(FATE_EAC3-yes)
+fate-ac3: $(FATE_AC3-yes) $(FATE_EAC3-yes) $(FATE_AC3_FIXED_DEXP24-yes)
diff --git a/tests/ref/fate/filter-atrim-samples
b/tests/ref/fate/ac3-fixed-dexp24
similarity index 65%
copy from tests/ref/fate/filter-atrim-samples
copy to tests/ref/fate/ac3-fixed-dexp24
index 74dc5d3b26..857d6c20ec 100644
--- a/tests/ref/fate/filter-atrim-samples
+++ b/tests/ref/fate/ac3-fixed-dexp24
@@ -3,4 +3,4 @@
#codec_id 0: pcm_s16le
#sample_rate 0: 44100
#channel_layout_name 0: stereo
-0, 26, 26, 54, 216, 0x6b376c6c
+0, 264134, 264134, 1536, 6144, 0xff5a9359
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]