Commit d85b4d2562 for ffmpeg

commit d85b4d25627db9e97e686685b17eb842994dee99
Author: Rainbaby <r@bgr.jp>
Date:   Fri Aug 28 16:18:08 2026 +0900

    avcodec/cavsdec: remove the annex A pseudo start codes

    Removing them is normative for decoders in every edition since
    GB/T 20090.2-2006 (annex C there, annex A later), so JiZhun payloads are
    de-emulated too: a byte forming 00 00 02 with the two before it, in a
    picture header or slice data, carries two stuffing bits that must be
    dropped. It removes bits rather than whole bytes, so
    ff_h2645_extract_rbsp() does not fit. No existing FATE sample carries a
    pseudo start code; the one added later in this series does.

    Signed-off-by: Rainbaby <r@bgr.jp>

diff --git a/libavcodec/cavs.c b/libavcodec/cavs.c
index 4d24f8846a..3c3b95093b 100644
--- a/libavcodec/cavs.c
+++ b/libavcodec/cavs.c
@@ -852,5 +852,9 @@ av_cold int ff_cavs_end(AVCodecContext *avctx)
     av_freep(&h->col_mv);
     av_freep(&h->col_type_base);
     av_freep(&h->edge_emu_buffer);
+    av_freep(&h->deemulated_buf);
+    h->deemulated_buf_size = 0;
+    av_freep(&h->stc_offset);
+    h->stc_offset_size = 0;
     return 0;
 }
diff --git a/libavcodec/cavs.h b/libavcodec/cavs.h
index 57e619bfd2..213a306ed5 100644
--- a/libavcodec/cavs.h
+++ b/libavcodec/cavs.h
@@ -252,6 +252,20 @@ typedef struct AVSContext {

     uint8_t *edge_emu_buffer;

+    /**
+     * picture payload with the annex A pseudo start codes removed,
+     * allocated only for the pictures that carry one
+     */
+    uint8_t *deemulated_buf;
+    unsigned int deemulated_buf_size;
+    /**
+     * byte offsets in deemulated_buf at which a start code prefix of the
+     * original payload begins
+     */
+    uint32_t *stc_offset;
+    unsigned int stc_offset_size;
+    int nb_stc_offset;
+
     int got_keyframe;
 } AVSContext;

diff --git a/libavcodec/cavsdec.c b/libavcodec/cavsdec.c
index 1177530bed..b0e6c1361a 100644
--- a/libavcodec/cavsdec.c
+++ b/libavcodec/cavsdec.c
@@ -29,6 +29,7 @@

 #include "libavutil/attributes.h"
 #include "libavutil/avassert.h"
+#include "libavutil/intmath.h"
 #include "libavutil/mem.h"
 #include "avcodec.h"
 #include "get_bits.h"
@@ -39,6 +40,7 @@
 #include "decode.h"
 #include "mathops.h"
 #include "mpeg12data.h"
+#include "put_bits.h"
 #include "startcode.h"

 static const uint8_t mv_scan[4] = {
@@ -600,6 +602,30 @@ static int decode_residual_block(AVSContext *h, GetBitContext *gb,
 }


+/**
+ * Tell whether the byte aligned 00 00 01 at @p offset is a start code: in a
+ * de-emulated payload it may be a coincidence (annex A).
+ */
+static int is_start_code_offset(AVSContext *h, int offset)
+{
+    int lo = 0, hi = h->nb_stc_offset;
+
+    if (h->gb.buffer != h->deemulated_buf)
+        return 1;
+    /* stc_offset[] is sorted; a crafted payload can hold one every 4 bytes */
+    while (lo < hi) {
+        int mid = lo + (hi - lo) / 2;
+
+        if (h->stc_offset[mid] == offset)
+            return 1;
+        if (h->stc_offset[mid] < offset)
+            lo = mid + 1;
+        else
+            hi = mid;
+    }
+    return 0;
+}
+
 static inline int decode_residual_chroma(AVSContext *h)
 {
     if (h->cbp & (1 << 4)) {
@@ -996,6 +1022,8 @@ static inline int check_for_slice(AVSContext *h)
         GetBitContext peek;
         int stc;

+        if (!is_start_code_offset(h, (get_bits_count(gb) + align) >> 3))
+            return 0;
         peek = *gb;
         skip_bits_long(&peek, 24 + align);
         stc = get_bits(&peek, 8);
@@ -1336,6 +1364,150 @@ static int decode_seq_header(AVSContext *h)
     return 0;
 }

+/** annex A applies to picture headers and slices, not to other units */
+static int annex_a_applies(int stc)
+{
+    return stc <= (SLICE_MAX_START_CODE & 0xFF) ||
+           stc == (PIC_I_START_CODE  & 0xFF)    ||
+           stc == (PIC_PB_START_CODE & 0xFF);
+}
+
+/**
+ * Copy one region of a picture payload, removing the annex A pseudo start
+ * codes; the clause 5.8.2.4 stuffing is rewritten to keep the next start
+ * code aligned. With pb NULL they are only counted.
+ * @return the number of pseudo start codes found
+ */
+static int deemulate_region(PutBitContext *pb, const uint8_t *buf, int size,
+                            int deemulated)
+{
+    int found = 0;
+    int stuffing = -1;
+    int i;
+
+    if (!deemulated) {
+        for (i = 0; i < size; i++)
+            if (pb)
+                put_bits(pb, 8, buf[i]);
+        return 0;
+    }
+
+    /* the stuffing bit is the last set bit of the region */
+    for (i = size; i > 0 && !buf[i - 1]; i--)
+        ;
+    if (i > 0) {
+        stuffing = i - 1;
+        size     = stuffing;
+    }
+
+    for (i = 0; i < size; i++) {
+        if (i + 2 < size && !buf[i] && !buf[i + 1] && buf[i + 2] == 2) {
+            /* drop the two low bits of the 02 */
+            if (pb) {
+                put_bits(pb, 16, 0);
+                put_bits(pb,  6, 0);
+            }
+            found++;
+            i += 2;
+        } else if (pb) {
+            put_bits(pb, 8, buf[i]);
+        }
+    }
+
+    if (pb) {
+        if (stuffing >= 0) {
+            int n = ff_ctz(buf[stuffing]) + 1;
+            if (n < 8)
+                put_bits(pb, 8 - n, buf[stuffing] >> n);
+            put_bits(pb, 1, 1);     //stuffing_bit
+        }
+        align_put_bits(pb);         /* stuffing_bit until byte aligned */
+    }
+    return found;
+}
+
+/**
+ * Walk the payload region by region, removing the annex A pseudo start
+ * codes; on the writing pass, record every start code prefix offset in
+ * h->stc_offset.
+ * @return the number of pseudo start codes found
+ */
+static int deemulate_picture(AVSContext *h, PutBitContext *pb,
+                             const uint8_t *buf, int size)
+{
+    int deemulated = 1;     /* buf starts inside a picture header */
+    int found      = 0;
+    int pos        = 0;
+
+    while (pos < size) {
+        int end;
+
+        for (end = pos; end + 2 < size; end++) {
+            /* nothing can start at end, end + 1 or end + 2 (annex A) */
+            if (buf[end + 2] > 1) {
+                end += 2;
+                continue;
+            }
+            if (!buf[end] && !buf[end + 1] && buf[end + 2] == 1)
+                break;
+        }
+        if (end + 2 >= size)
+            end = size;
+
+        found += deemulate_region(pb, buf + pos, end - pos, deemulated);
+        if (end == size)
+            break;
+
+        if (pb) {
+            h->stc_offset[h->nb_stc_offset++] = put_bits_count(pb) >> 3;
+            put_bits(pb, 24, 1);    /* start code prefix */
+        }
+        pos = end + 3;
+        if (pos < size) {
+            if (pb)
+                put_bits(pb, 8, buf[pos]);
+            deemulated = annex_a_applies(buf[pos]);
+            pos++;
+        }
+    }
+    return found;
+}
+
+/**
+ * Remove the annex A pseudo start codes from a payload: a byte forming
+ * 00 00 02 with the two before it loses its two least significant bits. buf
+ * runs from just after a picture start code to the packet end.
+ * @return the size of h->deemulated_buf, 0 if buf needs no copy, or < 0
+ */
+static int remove_pseudo_start_codes(AVSContext *h, const uint8_t *buf,
+                                     int size)
+{
+    PutBitContext pb;
+
+    if (size <= 0 || size > INT_MAX / 8 ||
+        !deemulate_picture(h, NULL, buf, size))
+        return 0;
+
+    av_fast_padded_malloc(&h->deemulated_buf, &h->deemulated_buf_size, size);
+    if (!h->deemulated_buf)
+        return AVERROR(ENOMEM);
+
+    av_fast_malloc(&h->stc_offset, &h->stc_offset_size,
+                   (size / 3 + 1) * sizeof(*h->stc_offset));
+    if (!h->stc_offset)
+        return AVERROR(ENOMEM);
+    h->nb_stc_offset = 0;
+
+    init_put_bits(&pb, h->deemulated_buf, size + AV_INPUT_BUFFER_PADDING_SIZE);
+    deemulate_picture(h, &pb, buf, size);
+    flush_put_bits(&pb);
+    av_assert0(put_bytes_output(&pb) <= size);
+    /* de-emulation shrinks, so restore the padding the reader over-reads */
+    memset(h->deemulated_buf + put_bytes_output(&pb), 0,
+           AV_INPUT_BUFFER_PADDING_SIZE);
+    return put_bytes_output(&pb);
+}
+
 static av_cold void cavs_flush(AVCodecContext * avctx)
 {
     AVSContext *h = avctx->priv_data;
@@ -1398,6 +1570,13 @@ static int cavs_decode_frame(AVCodecContext *avctx, AVFrame *rframe,
             *got_frame = 0;
             if (!h->got_keyframe)
                 break;
+            /* annex A, normative for decoders */
+            ret = remove_pseudo_start_codes(h, buf_ptr, input_size);
+            if (ret < 0)
+                return ret;
+            if (ret > 0 &&
+                (ret = init_get_bits8(&h->gb, h->deemulated_buf, ret)) < 0)
+                return ret;
             h->stc = stc;
             ret = decode_pic(h);
             if (ret < 0)