PR #25777 opened by Zhao Zhili (quink)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25777
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25777.patch

# Summary of changes

- skip extension and user data after picture headers
- support PFieldSkip in field-coded P pictures

```fate-samples
cavs/avsplus-user-data.ts
```




>From 18c77e29cba024c9181830138f3c767ef3ec67cf Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Tue, 3 Nov 2026 16:15:03 +0800
Subject: [PATCH 1/3] avcodec/cavsdec: skip extension and user data after
 picture headers

check_for_slice() ended the picture's data at any non-slice start code,
so a picture with extension_and_user_data(1) before its first slice
decoded no macroblock and produced no frame.
---
 libavcodec/cavsdec.c | 58 +++++++++++++++++++++++++++++++-------------
 1 file changed, 41 insertions(+), 17 deletions(-)

diff --git a/libavcodec/cavsdec.c b/libavcodec/cavsdec.c
index efcd156d764..4138a57a670 100644
--- a/libavcodec/cavsdec.c
+++ b/libavcodec/cavsdec.c
@@ -1079,19 +1079,16 @@ static int is_start_code_offset(AVSContext *h, int 
offset)
 }
 
 /**
- * Position the reader on the start code that ends an AEC slice: the first
- * one at or after the slice's own payload. The reader position is not
- * reliable: the arithmetic decoder's look ahead has run past it.
+ * Position the reader on the first start code prefix at or after @p offset,
+ * or near the end of the buffer when there is none.
+ * @return 1 if a start code prefix was found
  */
-static void aec_end_of_slice(AVSContext *h)
+static int seek_to_start_code(AVSContext *h, int offset)
 {
     GetBitContext *gb = &h->gb;
     const uint8_t *buf = gb->buffer;
     int size = gb->size_in_bits >> 3;
-    int i    = h->aec_start;
-
-    /* release gb; check_for_slice() waits for this */
-    h->aec.gb = NULL;
+    int i    = offset;
 
     for (; i + 2 < size; i++)
         if (!buf[i] && !buf[i + 1] && buf[i + 2] == 1 &&
@@ -1099,6 +1096,20 @@ static void aec_end_of_slice(AVSContext *h)
             break;
     if (init_get_bits8(gb, buf, size) >= 0)
         skip_bits_long(gb, i * 8);
+    return i + 2 < size;
+}
+
+/**
+ * Position the reader on the start code that ends an AEC slice: the first
+ * one at or after the slice's own payload. The reader position is not
+ * reliable: the arithmetic decoder's look ahead has run past it.
+ */
+static void aec_end_of_slice(AVSContext *h)
+{
+    /* release gb; check_for_slice() waits for this */
+    h->aec.gb = NULL;
+
+    seek_to_start_code(h, h->aec_start);
 }
 
 /**
@@ -1715,23 +1726,37 @@ static inline int check_for_slice(AVSContext *h)
     /* an AEC slice ends with its stuffing bit, not at a start code (7.2.4) */
     if (h->aec_enable && h->aec.gb)
         return 0;
-    align = (-get_bits_count(gb)) & 7;
-    /* check for stuffing byte */
-    if (!align && (show_bits(gb, 8) == 0x80))
-        align = 8;
-    if ((show_bits_long(gb, 24 + align) & 0xFFFFFF) == 0x000001) {
+    for (;;) {
         GetBitContext peek;
         int stc;
 
+        align = (-get_bits_count(gb)) & 7;
+        /* check for stuffing byte */
+        if (!align && (show_bits(gb, 8) == 0x80))
+            align = 8;
+        if ((show_bits_long(gb, 24 + align) & 0xFFFFFF) != 0x000001)
+            return 0;
         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);
-        /* any other unit ends the picture's data; leave it to the caller */
         if (stc > (SLICE_MAX_START_CODE & 0xFF)) {
-            h->pic_data_end = 1;
-            return 0;
+            /* video_sequence() places extension_and_user_data(1) between
+             * the picture header and picture_data() (table 13)
+             */
+            if (stc != (USER_START_CODE & 0xFF) &&
+                stc != (EXT_START_CODE  & 0xFF)) {
+                /* any other unit ends the picture's data; leave it to the
+                 * caller */
+                h->pic_data_end = 1;
+                return 0;
+            }
+            if (!seek_to_start_code(h, get_bits_count(&peek) >> 3)) {
+                h->pic_data_end = 1;
+                return 0;
+            }
+            continue;
         }
         *gb    = peek;
         h->stc = stc;
@@ -1747,7 +1772,6 @@ static inline int check_for_slice(AVSContext *h)
         }
         return 1;
     }
-    return 0;
 }
 
 /*****************************************************************************
-- 
2.54.0


>From 65211b6e9b37afad65e7cd6e7704cd6a5d68eedd Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Tue, 3 Nov 2026 18:25:21 +0800
Subject: [PATCH 2/3] avcodec/cavsdec: support PFieldSkip in field-coded P
 pictures

Decoding such pictures failed with AVERROR_PATCHWELCOME. Skip
macroblocks now predict from the same-parity field of the previous
frame, and the zero-vector shortcut compares neighbours against the
reference actually in use.
---
 libavcodec/cavs.c    |  7 +++++--
 libavcodec/cavs.h    |  1 +
 libavcodec/cavsdec.c | 15 ++++++++-------
 3 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/libavcodec/cavs.c b/libavcodec/cavs.c
index 05117a098c0..72a3ce70edb 100644
--- a/libavcodec/cavs.c
+++ b/libavcodec/cavs.c
@@ -607,11 +607,14 @@ void ff_cavs_mv(AVSContext *h, enum cavs_mv_loc nP, enum 
cavs_mv_loc nC,
         mvP->ref_idx = 0;
     if (mvC->ref == NOT_AVAIL || (nP == MV_FWD_X3) || (nP == MV_BWD_X3 ))
         mvC = &h->mv[nP - 5];  // set to top-left (mvD)
+    /* the zero-vector cases of 9.9.1 a) 2) and 3), against this block's
+     * own reference
+     */
     if (mode == MV_PRED_PSKIP &&
         (mvA->ref == NOT_AVAIL ||
          mvB->ref == NOT_AVAIL ||
-         (mvA->x | mvA->y | mvA->ref) == 0 ||
-         (mvB->x | mvB->y | mvB->ref) == 0)) {
+         ((mvA->x | mvA->y) == 0 && mvA->ref == ref) ||
+         ((mvB->x | mvB->y) == 0 && mvB->ref == ref))) {
         mvP2 = &un_mv;
     /* if there is only one suitable candidate, take it */
     } else if (mvA->ref >= 0 && mvB->ref < 0  && mvC->ref < 0) {
diff --git a/libavcodec/cavs.h b/libavcodec/cavs.h
index b51a5e6c19e..acc33b07934 100644
--- a/libavcodec/cavs.h
+++ b/libavcodec/cavs.h
@@ -204,6 +204,7 @@ typedef struct AVSContext {
     int pic_structure;
     int top_field_first;  ///< first coded field is the top one, clause 7.2.3
     int b_field_enhanced; ///< BFieldEnhanced of the picture, clause 9.9.1 b)
+    int p_field_skip;     ///< PFieldSkip of the picture, clause 9.9.1 a)
     int field;            ///< field of the current MB, 0 or 1, clause 3.24
     int field_mby;        ///< macroblock row of the current macroblock in it
     int skip_mode_flag;   ///< select between skip_count or one skip_flag per 
MB
diff --git a/libavcodec/cavsdec.c b/libavcodec/cavsdec.c
index 4138a57a670..ab168ba8ca5 100644
--- a/libavcodec/cavsdec.c
+++ b/libavcodec/cavsdec.c
@@ -1397,7 +1397,11 @@ static int decode_mb_p(AVSContext *h, enum cavs_mb 
mb_type)
     set_inter_mb_ctx(h, mb_type, mb_type == P_SKIP);
     switch (mb_type) {
     case P_SKIP:
-        MV(MV_FWD_X0, MV_FWD_C2, MV_PRED_PSKIP,  BLK_16X16, h->ref_base[0]);
+        /* PFieldSkip predicts from the reference marked 1, the same
+         * parity field of the previous frame (9.9.1 a) 1))
+         */
+        MV(MV_FWD_X0, MV_FWD_C2, MV_PRED_PSKIP,  BLK_16X16,
+           h->ref_base[0] + h->p_field_skip);
         break;
     case P_16X16:
         REF(ref[0], 0, MV_FWD_X0);
@@ -1992,14 +1996,11 @@ static int decode_pic(AVSContext *h)
     }
 
     /* in a frame picture the field derivations reduce to the frame ones
-     * (9.9.1 b) 3)); PFieldSkip of 9.9.1 a) 1) is not implemented */
+     * (9.9.1 b) 3)) */
     h->b_field_enhanced = pb_field_enhanced_flag && !h->pic_structure &&
                           h->cur.f->pict_type == AV_PICTURE_TYPE_B;
-    if (pb_field_enhanced_flag && !h->pic_structure &&
-        h->cur.f->pict_type == AV_PICTURE_TYPE_P) {
-        avpriv_request_sample(h->avctx, "Enhanced field coded P picture");
-        return AVERROR_PATCHWELCOME;
-    }
+    h->p_field_skip     = pb_field_enhanced_flag && !h->pic_structure &&
+                          h->cur.f->pict_type == AV_PICTURE_TYPE_P;
 
     if (!h->progressive) {
         h->cur.f->flags |= AV_FRAME_FLAG_INTERLACED;
-- 
2.54.0


>From a1f5e75df44d4a594bb159b84b59cb26f521f140 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Wed, 4 Nov 2026 16:41:41 +0800
Subject: [PATCH 3/3] tests/fate/video: add an AVS+ test for user data and
 PFieldSkip

The sample holds an I picture, an enhanced field-coded P picture and a
B picture from a Broadcasting profile stream. The I picture has user
data after its picture header.
---
 tests/fate/video.mak                  | 3 +++
 tests/ref/fate/cavs-avsplus-user-data | 8 ++++++++
 2 files changed, 11 insertions(+)
 create mode 100644 tests/ref/fate/cavs-avsplus-user-data

diff --git a/tests/fate/video.mak b/tests/fate/video.mak
index caf45d2cfe6..e663b807a84 100644
--- a/tests/fate/video.mak
+++ b/tests/fate/video.mak
@@ -79,6 +79,9 @@ fate-cavs-bunny: CMD = framecrc -f cavsvideo -i 
$(TARGET_SAMPLES)/cavs/bunny.mp4
 FATE_VIDEO-$(call FRAMECRC, MPEGTS, CAVS, CAVSVIDEO_PARSER) += 
fate-cavs-avsplus
 fate-cavs-avsplus: CMD = framecrc -i 
$(TARGET_SAMPLES)/cavs/avsplus-1080i-testcard.ts -an
 
+FATE_VIDEO-$(call FRAMECRC, MPEGTS, CAVS, CAVSVIDEO_PARSER) += 
fate-cavs-avsplus-user-data
+fate-cavs-avsplus-user-data: CMD = framecrc -i 
$(TARGET_SAMPLES)/cavs/avsplus-user-data.ts -an
+
 FATE_VIDEO-$(call FRAMECRC, CDG, CDGRAPHICS, SCALE_FILTER) += fate-cdgraphics
 fate-cdgraphics: CMD = framecrc -i 
$(TARGET_SAMPLES)/cdgraphics/BrotherJohn.cdg -pix_fmt rgba -t 1 -vf scale
 
diff --git a/tests/ref/fate/cavs-avsplus-user-data 
b/tests/ref/fate/cavs-avsplus-user-data
new file mode 100644
index 00000000000..3ae5c7dc315
--- /dev/null
+++ b/tests/ref/fate/cavs-avsplus-user-data
@@ -0,0 +1,8 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 1920x1080
+#sar 0: 0/1
+0,          0,          0,        1,  3110400, 0x75e52781
+0,          1,          1,        1,  3110400, 0x27214479
+0,          2,          2,        1,  3110400, 0xd1df20d2
-- 
2.54.0

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

Reply via email to