PR #25126 opened by Baptiste Coudurier (bcoudurier)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25126
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25126.patch

With complete frames, the parser stopped at the first slice and reported a 
field picture as a single field even when the packet holds both fields of the 
frame, as in MXF. This halved the avg_frame_rate estimated by 
avformat_find_stream_info() (25/2 for 1080i25 XAVC Long GOP), so stream copy to 
MXF failed with "Unsupported frame rate 25/2".

The parser now looks for the first slice of the opposite parity field and, if 
found, adds it to repeat_pict, sets field_order from the first field and 
picture_structure to frame, as the MPEG-2 parser does for field pairs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


From 96211bc708a4ec5cbf786aafb5f7d91c2ecfda67 Mon Sep 17 00:00:00 2001
From: Baptiste Coudurier <[email protected]>
Date: Fri, 16 Oct 2026 12:08:09 -0700
Subject: [PATCH] avcodec/h264_parser: count both fields of a field pair in
 complete frames

With complete frames, the parser stopped at the first slice and
reported a field picture as a single field even when the packet holds
both fields of the frame, as in MXF. This halved the avg_frame_rate
estimated by avformat_find_stream_info().

Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 libavcodec/h264_parser.c | 52 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 51 insertions(+), 1 deletion(-)

diff --git a/libavcodec/h264_parser.c b/libavcodec/h264_parser.c
index 1bd0be38a5..751892692d 100644
--- a/libavcodec/h264_parser.c
+++ b/libavcodec/h264_parser.c
@@ -251,6 +251,30 @@ static int scan_mmco_reset(AVCodecParserContext *s, 
GetBitContext *gb,
     return 0;
 }
 
+/**
+ * Check whether a slice is the first slice of the field of opposite
+ * parity to the field picture already parsed.
+ */
+static int is_second_field(const H264ParseContext *p, GetBitContext *gb)
+{
+    const SPS *sps;
+    unsigned int pps_id;
+
+    if (get_ue_golomb_long(gb)) // first_mb_in_slice
+        return 0;
+    get_ue_golomb_31(gb);       // slice_type
+    pps_id = get_ue_golomb(gb);
+    if (pps_id >= MAX_PPS_COUNT || !p->ps.pps_list[pps_id])
+        return 0;
+    sps = p->ps.pps_list[pps_id]->sps;
+    if (sps->residual_color_transform_flag)
+        skip_bits(gb, 2);       // colour_plane_id
+    skip_bits(gb, sps->log2_max_frame_num); // frame_num
+    if (sps->frame_mbs_only_flag || !get_bits1(gb)) // field_pic_flag
+        return 0;
+    return PICT_TOP_FIELD + get_bits1(gb) != p->picture_structure; // 
bottom_field_flag
+}
+
 /**
  * Parse NAL units of found picture and decode some basic information.
  *
@@ -270,6 +294,7 @@ static inline int parse_nal_units(AVCodecParserContext *s,
     unsigned int pps_id;
     unsigned int slice_type;
     int state = -1, got_reset = 0;
+    int first_field = 0;
     int q264 = buf_size >=4 && !memcmp("Q264", buf, 4);
     int field_poc[2];
     int ret;
@@ -341,6 +366,23 @@ static inline int parse_nal_units(AVCodecParserContext *s,
         nal.ref_idc = get_bits(&nal.gb, 2);
         nal.type    = get_bits(&nal.gb, 5);
 
+        if (first_field) {
+            /* The packet also holds the second field of the pair. */
+            if ((nal.type == H264_NAL_SLICE     ||
+                 nal.type == H264_NAL_IDR_SLICE ||
+                 nal.type == H264_NAL_DPA) &&
+                is_second_field(p, &nal.gb)) {
+                s->repeat_pict++;
+                s->field_order = s->picture_structure == 
AV_PICTURE_STRUCTURE_TOP_FIELD ?
+                                 AV_FIELD_TT : AV_FIELD_BB;
+                s->picture_structure = AV_PICTURE_STRUCTURE_FRAME;
+                break;
+            }
+            /* Keep the parameter sets the second field may refer to. */
+            if (nal.type != H264_NAL_SPS && nal.type != H264_NAL_PPS)
+                continue;
+        }
+
         switch (nal.type) {
         case H264_NAL_SPS:
             ff_h264_decode_seq_parameter_set(&nal.gb, avctx, &p->ps, 0);
@@ -577,11 +619,19 @@ static inline int parse_nal_units(AVCodecParserContext *s,
                           sps->num_units_in_tick * 2, den, 1 << 30);
             }
 
+            /* A complete frame starting with a field picture may hold
+             * both fields of the pair, look for the second one. */
+            if ((s->flags & PARSER_FLAG_COMPLETE_FRAMES) &&
+                p->picture_structure != PICT_FRAME) {
+                first_field = 1;
+                break;
+            }
+
             av_freep(&rbsp.rbsp_buffer);
             return 0; /* no need to evaluate the rest */
         }
     }
-    if (q264) {
+    if (first_field || q264) {
         av_freep(&rbsp.rbsp_buffer);
         return 0;
     }
-- 
2.54.0

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

Reply via email to