Commit 8aff7f9f87 for ffmpeg
commit 8aff7f9f875a247ef7f10bcf103c60807b5fdc96
Author: Rainbaby <r@bgr.jp>
Date: Fri Aug 28 16:18:09 2026 +0900
avcodec/cavs_aec: add the AVS1-P16 arithmetic decoder
The Broadcasting profile codes its macroblock layer with the arithmetic
coder of clause 8.4. This is the decoder only; its renormalization loops
end with the buffer, so truncated input terminates.
Signed-off-by: Rainbaby <r@bgr.jp>
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 2ea4f9417e..b30c263fae 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -306,7 +306,7 @@ OBJS-$(CONFIG_BONK_DECODER) += bonk.o
OBJS-$(CONFIG_BRENDER_PIX_DECODER) += brenderpix.o
OBJS-$(CONFIG_C93_DECODER) += c93.o
OBJS-$(CONFIG_CAVS_DECODER) += cavs.o cavsdec.o cavsdsp.o \
- cavsdata.o
+ cavsdata.o cavs_aec.o
OBJS-$(CONFIG_CBD2_DPCM_DECODER) += dpcm.o
OBJS-$(CONFIG_CCAPTION_DECODER) += ccaption_dec.o ass.o
OBJS-$(CONFIG_CDGRAPHICS_DECODER) += cdgraphics.o
diff --git a/libavcodec/cavs_aec.c b/libavcodec/cavs_aec.c
new file mode 100644
index 0000000000..bada516f16
--- /dev/null
+++ b/libavcodec/cavs_aec.c
@@ -0,0 +1,54 @@
+/*
+ * Chinese AVS video (AVS1-P16, Broadcasting profile) AEC decoder.
+ * Copyright (c) 2026 Rainbaby <r@bgr.jp>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * Chinese AVS video (AVS1-P16, Broadcasting profile) AEC decoder
+ *
+ * Clause numbers refer to GY/T 257.1-2012. The renormalization loops end
+ * with the buffer, so truncated input terminates. The caller must have
+ * removed the annex A pseudo start codes from the payload.
+ */
+
+#include <stdint.h>
+
+#include "cavs_aec.h"
+#include "get_bits.h"
+
+void ff_cavs_aec_init(CAVSAECContext *aec, GetBitContext *gb)
+{
+ for (int i = 0; i < CAVS_AEC_NUM_CTX; i++) {
+ aec->ctx[i].mps = 0;
+ aec->ctx[i].cycno = 0;
+ aec->ctx[i].lg_pmps = CAVS_AEC_LGPMPS_INIT;
+ }
+
+ aec->gb = gb;
+ aec->rs1 = 0;
+ aec->rt1 = CAVS_AEC_RT1_INIT;
+ aec->vs = 0;
+ aec->vt = get_bits(gb, 9);
+ while (!((aec->vt >> 8) & 0x01) && get_bits_left(gb) > 0) {
+ aec->vt = (aec->vt << 1) | get_bits1(gb);
+ aec->vs++;
+ }
+ aec->vt &= 0xFF;
+}
diff --git a/libavcodec/cavs_aec.h b/libavcodec/cavs_aec.h
new file mode 100644
index 0000000000..251cef9713
--- /dev/null
+++ b/libavcodec/cavs_aec.h
@@ -0,0 +1,214 @@
+/*
+ * Chinese AVS video (AVS1-P16, Broadcasting profile) AEC decoder.
+ * Copyright (c) 2026 Rainbaby <r@bgr.jp>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * Chinese AVS video (AVS1-P16, Broadcasting profile) AEC decoder
+ */
+
+#ifndef AVCODEC_CAVS_AEC_H
+#define AVCODEC_CAVS_AEC_H
+
+#include <stdint.h>
+
+#include "libavutil/attributes.h"
+#include "libavutil/avassert.h"
+#include "libavutil/error.h"
+#include "libavutil/macros.h"
+
+#include "get_bits.h"
+
+/* Table 51's largest start index is 322, weighting_prediction, one model. */
+#define CAVS_AEC_NUM_CTX 323
+
+/** Table 51 start index + ctxIdxInc, or AVERROR_INVALIDDATA/AVERROR_BUG. */
+static inline int ff_cavs_aec_ctx_idx(int start, int span, int inc)
+{
+ if (span <= 0 || start < 0 || start > CAVS_AEC_NUM_CTX - span)
+ return AVERROR_BUG;
+ if ((unsigned)inc >= (unsigned)span)
+ return AVERROR_INVALIDDATA;
+
+ return start + inc;
+}
+
+typedef struct CAVSAECModel {
+ uint8_t mps; ///< mps: more probable symbol, 1 bit, init 0
+ uint8_t cycno; ///< cycno: adaptation counter, 2 bits, init 0
+ uint16_t lg_pmps; ///< lgPmps: log domain P(MPS), 11 bits, init 1023
+} CAVSAECModel;
+
+typedef struct CAVSAECContext {
+ GetBitContext *gb; ///< bitstream reader, shared with the slice parser
+ int rs1; ///< range exponent, init 0 (rS1)
+ int rt1; ///< range mantissa, init 0xFF (rT1)
+ int vs; ///< offset exponent (valueS)
+ int vt; ///< offset mantissa, 9 bits inside a call (valueT)
+ CAVSAECModel ctx[CAVS_AEC_NUM_CTX]; ///< context models, 8.4.2.1
+} CAVSAECContext;
+
+/* Constants of clauses 8.4.2 and 8.4.4.3. */
+#define CAVS_AEC_LGPMPS_INIT 1023
+#define CAVS_AEC_LGPMPS_MAX 1023
+#define CAVS_AEC_LGPMPS_REFLECT 2047
+#define CAVS_AEC_LGPMPS_BYPASS 1023
+#define CAVS_AEC_LGPMPS_STUFF 4
+#define CAVS_AEC_RT1_INIT 0xFF
+
+/** Adapt one context model after a bin has been decoded, 8.4.4.3.5. */
+static inline void cavs_aec_update_ctx(CAVSAECModel *m, int bin)
+{
+ static const uint8_t cwr_tab[4] = { 3, 3, 4, 5 };
+ /* cwr is 3..5, the leading entries only pad the table so it indexes */
+ static const uint16_t lps_inc_tab[6] = { 0, 0, 0, 197, 95, 46 };
+ const int cwr = cwr_tab[m->cycno];
+
+ if (bin != m->mps)
+ m->cycno = FFMIN(m->cycno + 1, 3);
+ else if (m->cycno == 0)
+ m->cycno = 1;
+
+ if (bin == m->mps) {
+ m->lg_pmps -= (m->lg_pmps >> cwr) + (m->lg_pmps >> (cwr + 2));
+ } else {
+ m->lg_pmps += lps_inc_tab[cwr];
+ if (m->lg_pmps > CAVS_AEC_LGPMPS_MAX) {
+ m->lg_pmps = CAVS_AEC_LGPMPS_REFLECT - m->lg_pmps;
+ m->mps ^= 1;
+ }
+ }
+}
+
+/** Decode one bin (8.4.4.3.2); the caller supplies predMps and lgPmps. */
+static av_always_inline int cavs_aec_decode_bin(CAVSAECContext *aec,
+ int pred_mps, int lg_pmps)
+{
+ GetBitContext *gb = aec->gb;
+ const int lps_range = lg_pmps >> 2;
+ int rs2, rt2, s_flag, bin;
+
+ av_assert2(lps_range > 0);
+
+ if (aec->rt1 >= lps_range) {
+ rs2 = aec->rs1;
+ rt2 = aec->rt1 - lps_range;
+ s_flag = 0;
+ } else {
+ rs2 = aec->rs1 + 1;
+ rt2 = 256 + aec->rt1 - lps_range;
+ s_flag = 1;
+ }
+
+ if (rs2 > aec->vs || (rs2 == aec->vs && aec->vt >= rt2)) {
+ int t_rlps = s_flag ? aec->rt1 + lps_range : lps_range;
+
+ bin = !pred_mps;
+ if (rs2 == aec->vs)
+ aec->vt -= rt2;
+ else
+ aec->vt = 256 + ((aec->vt << 1) | get_bits1(gb)) - rt2;
+ while (t_rlps < 0x100) {
+ t_rlps = t_rlps << 1;
+ aec->vt = (aec->vt << 1) | get_bits1(gb);
+ }
+ aec->rt1 = t_rlps & 0xFF;
+ } else {
+ bin = pred_mps;
+ aec->rs1 = rs2;
+ aec->rt1 = rt2;
+ }
+
+ if (bin != pred_mps) {
+ aec->rs1 = 0;
+ aec->vs = 0;
+ /* the loop ends with the buffer, so truncated input terminates */
+ while (aec->vt < 0x100 && get_bits_left(gb) > 0) {
+ aec->vs++;
+ aec->vt = (aec->vt << 1) | get_bits1(gb);
+ }
+ aec->vt &= 0xFF;
+ }
+
+ return bin;
+}
+
+/** Reset all contexts and start the decoder on byte aligned @p gb (8.4.1). */
+void ff_cavs_aec_init(CAVSAECContext *aec, GetBitContext *gb);
+
+/** Decode a bin with one context model (8.4.4.3.2). */
+static inline int ff_cavs_aec_decode_decision(CAVSAECContext *aec, int ctx_idx)
+{
+ CAVSAECModel *m;
+ int bin;
+
+ av_assert2(ctx_idx >= 0 && ctx_idx < CAVS_AEC_NUM_CTX);
+
+ m = &aec->ctx[ctx_idx];
+ bin = cavs_aec_decode_bin(aec, m->mps, m->lg_pmps);
+ cavs_aec_update_ctx(m, bin);
+
+ return bin;
+}
+
+/** Decode a bin with two weighted context models (8.4.4.3.2). */
+static inline int ff_cavs_aec_decode_decision_w(CAVSAECContext *aec,
+ int ctx_idx, int ctx_idx_w)
+{
+ CAVSAECModel *m1, *m2;
+ int pred_mps, lg_pmps, bin;
+
+ av_assert2(ctx_idx >= 0 && ctx_idx < CAVS_AEC_NUM_CTX);
+ av_assert2(ctx_idx_w >= 0 && ctx_idx_w < CAVS_AEC_NUM_CTX);
+
+ m1 = &aec->ctx[ctx_idx];
+ m2 = &aec->ctx[ctx_idx_w];
+
+ if (m1->mps == m2->mps) {
+ pred_mps = m1->mps;
+ lg_pmps = (m1->lg_pmps + m2->lg_pmps) / 2;
+ } else if (m1->lg_pmps < m2->lg_pmps) {
+ pred_mps = m1->mps;
+ lg_pmps = CAVS_AEC_LGPMPS_MAX - ((m2->lg_pmps - m1->lg_pmps) >> 1);
+ } else {
+ pred_mps = m2->mps;
+ lg_pmps = CAVS_AEC_LGPMPS_MAX - ((m1->lg_pmps - m2->lg_pmps) >> 1);
+ }
+
+ bin = cavs_aec_decode_bin(aec, pred_mps, lg_pmps);
+ cavs_aec_update_ctx(m1, bin);
+ cavs_aec_update_ctx(m2, bin);
+
+ return bin;
+}
+
+/** Decode a bypass bin, fixed lgPmps (8.4.4.3.3). */
+static inline int ff_cavs_aec_decode_bypass(CAVSAECContext *aec)
+{
+ return cavs_aec_decode_bin(aec, 0, CAVS_AEC_LGPMPS_BYPASS);
+}
+
+/** Decode aec_mb_stuffing_bit, fixed lgPmps (8.4.4.3.4). */
+static inline int ff_cavs_aec_decode_stuffing_bit(CAVSAECContext *aec)
+{
+ return cavs_aec_decode_bin(aec, 0, CAVS_AEC_LGPMPS_STUFF);
+}
+
+#endif /* AVCODEC_CAVS_AEC_H */