PR #25219 opened by Seungmin Kim (ehf)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25219
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25219.patch

After a frame_num gap that covers frame_num 0, the next picture's POC (types 1 
and 2) comes out 2 * MaxFrameNum too small, and the pictures after the gap are 
dropped until it catches up. The commit message has the details.

Tested: `make fate-h264` passes (232 tests, the new one included). Without the 
h264_slice.c change, the new test fails: pictures 15 onwards are missing from 
its output.

The new test needs one sample in fate-suite: `h264/frame_num_gap_wrap.h264` 
(1920 bytes), attached here.



>From d740a6e02a2f3d15fcec44f26416021cbd78bdeb Mon Sep 17 00:00:00 2001
From: Seungmin Kim <[email protected]>
Date: Thu, 1 Oct 2026 00:30:23 +0900
Subject: [PATCH] avcodec/h264_slice: carry FrameNumOffset across a frame_num
 gap that wraps

A frame_num gap is filled with inferred frames, and prev_frame_num steps
through them, wrapping at MaxFrameNum, while prev_frame_num_offset keeps
the offset of the last decoded picture. ff_h264_init_poc() then compares
the next picture's frame_num with the inferred prev_frame_num, which lies
below it, so its FrameNumOffset misses the wrap and its POC (types 1 and
2) comes out 2 * MaxFrameNum too small. The pictures after the gap are
taken as older than the ones already output and are dropped until the
POC catches up.

An encoder that predicts past a lost frame (x264's
x264_encoder_invalidate_reference(), as used for reference picture
invalidation over RTP) produces such a gap whenever the loss covers the
frame with frame_num 0. With x264's MaxFrameNum of 16, about a wrap's
worth of pictures is dropped after every such loss; this decoder behind
the browsers' WebCodecs and WebRTC on Linux drops the same ones.

Advance prev_frame_num_offset where the inferred frames, or the frames
the gap shortening skips, cross the wrap, as decoding the missing
pictures would have.

The new test predicts past the loss of frames 15 and 16 (frame_num 15
and 0) of a 176x144 x264 stream: 38 pictures out rather than 25.
Sample: h264/frame_num_gap_wrap.h264 (1920 bytes).

Signed-off-by: Seungmin Kim <[email protected]>
---
 libavcodec/h264_slice.c                |  9 ++++++
 tests/fate/h264.mak                    |  2 ++
 tests/ref/fate/h264-frame-num-gap-wrap | 43 ++++++++++++++++++++++++++
 3 files changed, 54 insertions(+)
 create mode 100644 tests/ref/fate/h264-frame-num-gap-wrap

diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c
index 2bcb64378d..78c42eced3 100644
--- a/libavcodec/h264_slice.c
+++ b/libavcodec/h264_slice.c
@@ -1457,6 +1457,11 @@ static int h264_field_start(H264Context *h, const 
H264SliceContext *sl,
             unwrap_prev_frame_num -= max_frame_num;
 
         if ((h->poc.frame_num - unwrap_prev_frame_num) > sps->ref_frame_count) 
{
+            /* The frames skipped here cross frame_num's wrap when the gap
+             * does and the kept part starts at or past 0. */
+            if (unwrap_prev_frame_num < 0 &&
+                h->poc.frame_num - sps->ref_frame_count - 1 >= 0)
+                h->poc.prev_frame_num_offset += max_frame_num;
             unwrap_prev_frame_num = (h->poc.frame_num - sps->ref_frame_count) 
- 1;
             if (unwrap_prev_frame_num < 0)
                 unwrap_prev_frame_num += max_frame_num;
@@ -1538,6 +1543,10 @@ static int h264_field_start(H264Context *h, const 
H264SliceContext *sl,
 
         h->poc.prev_frame_num++;
         h->poc.prev_frame_num        %= 1 << sps->log2_max_frame_num;
+        /* A missing frame with frame_num 0 starts a new FrameNumOffset for the
+         * pictures after it, as a received one would (8.2.1). */
+        if (!h->poc.prev_frame_num)
+            h->poc.prev_frame_num_offset += 1 << sps->log2_max_frame_num;
         h->cur_pic_ptr->frame_num = h->poc.prev_frame_num;
         h->cur_pic_ptr->invalid_gap = !sps->gaps_in_frame_num_allowed_flag;
         ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, 0);
diff --git a/tests/fate/h264.mak b/tests/fate/h264.mak
index 1ee70f216d..22770e3c8a 100644
--- a/tests/fate/h264.mak
+++ b/tests/fate/h264.mak
@@ -197,6 +197,7 @@ FATE_H264  := $(FATE_H264:%=fate-h264-conformance-%)        
            \
               fate-h264-data-partitioning-ab                            \
               fate-h264-data-partitioning-cip                           \
               fate-h264-data-partitioning-cip-strict                    \
+              fate-h264-frame-num-gap-wrap                              \
               fate-h264-intra-refresh-recovery                          \
               fate-h264-lossless                                        \
               fate-h264-3386                                            \
@@ -490,6 +491,7 @@ fate-h264-ref-pic-mod-overflow:                   CMD = 
framecrc -i $(TARGET_SAM
 fate-h264-twofields-packet:                       CMD = framecrc -i 
$(TARGET_SAMPLES)/h264/twofields_packet.mp4 -an -frames 30
 fate-h264-unescaped-extradata:                    CMD = framecrc -i 
$(TARGET_SAMPLES)/h264/unescaped_extradata.mp4 -an -frames 10
 fate-h264-3386:                                   CMD = framecrc -i 
$(TARGET_SAMPLES)/h264/bbc2.sample.h264
+fate-h264-frame-num-gap-wrap:                     CMD = framecrc -i 
$(TARGET_SAMPLES)/h264/frame_num_gap_wrap.h264
 fate-h264-missing-frame:                          CMD = framecrc -i 
$(TARGET_SAMPLES)/h264/nondeterministic_cut.h264
 fate-h264-timecode:                               CMD = framecrc -i 
$(TARGET_SAMPLES)/h264/crew_cif_timecode-2.h264
 
diff --git a/tests/ref/fate/h264-frame-num-gap-wrap 
b/tests/ref/fate/h264-frame-num-gap-wrap
new file mode 100644
index 0000000000..673b3c4915
--- /dev/null
+++ b/tests/ref/fate/h264-frame-num-gap-wrap
@@ -0,0 +1,43 @@
+#tb 0: 1/30
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 176x144
+#sar 0: 0/1
+0,          0,          0,        1,    38016, 0x07bdf766
+0,          1,          1,        1,    38016, 0x361bf636
+0,          2,          2,        1,    38016, 0xfe10f6c6
+0,          3,          3,        1,    38016, 0xc835f7f6
+0,          4,          4,        1,    38016, 0xae1ff866
+0,          5,          5,        1,    38016, 0x017ff836
+0,          6,          6,        1,    38016, 0xf06ff9c6
+0,          7,          7,        1,    38016, 0x85b1f956
+0,          8,          8,        1,    38016, 0x7030fae6
+0,          9,          9,        1,    38016, 0xaca9fb76
+0,         10,         10,        1,    38016, 0xe6e2fc06
+0,         11,         11,        1,    38016, 0x1eeafc96
+0,         12,         12,        1,    38016, 0xec2ffed6
+0,         13,         13,        1,    38016, 0x71ff0029
+0,         14,         14,        1,    38016, 0x78bbffde
+0,         15,         15,        1,    38016, 0xd6e202c9
+0,         16,         16,        1,    38016, 0x93e0034d
+0,         17,         17,        1,    38016, 0x4ece03d1
+0,         18,         18,        1,    38016, 0x527504b5
+0,         19,         19,        1,    38016, 0xdafa0551
+0,         20,         20,        1,    38016, 0x611e05ed
+0,         21,         21,        1,    38016, 0xe4c30689
+0,         22,         22,        1,    38016, 0x66070725
+0,         23,         23,        1,    38016, 0xe4cc07c1
+0,         24,         24,        1,    38016, 0x6130085d
+0,         25,         25,        1,    38016, 0xdb1508f9
+0,         26,         26,        1,    38016, 0x52990995
+0,         27,         27,        1,    38016, 0xc79e0a31
+0,         28,         28,        1,    38016, 0x3a420acd
+0,         29,         29,        1,    38016, 0xaa670b69
+0,         30,         30,        1,    38016, 0x182b0c05
+0,         31,         31,        1,    38016, 0x83700ca1
+0,         32,         32,        1,    38016, 0x9a760d1d
+0,         33,         33,        1,    38016, 0xaf8c0d99
+0,         34,         34,        1,    38016, 0xc2b20e15
+0,         35,         35,        1,    38016, 0xd3e80e91
+0,         36,         36,        1,    38016, 0xe32e0f0d
+0,         37,         37,        1,    38016, 0xf0840f89
-- 
2.54.0

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

Reply via email to