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

Add the opt-in `seq_end` MPEG-2 encoder option to append a sequence-end code to 
the final picture packet. Use both existing picture queues to detect the final 
picture during draining so B-frame reordering does not cause missing or 
repeated end codes.

Keep the option in the encoder context, document it, and reject `seq_end=1` 
with `+low_delay`, which would require a separate trailer packet. Add FATE 
regressions for a partial B-frame chain and queued B-frames.

Validation on current upstream master:
- Both new FATE regressions pass.
- 240 combinations of frame counts, B-frame counts/strategies, and VBR/CBR: 
exactly one trailing end code and the expected decoded frame count.
- With VBR, enabled output is byte-identical to disabled output plus the 
four-byte sequence-end code.
- Low-delay option validation passes.



>From f5927391c502d4670bd197e59eddadee8110ebcd Mon Sep 17 00:00:00 2001
From: Baptiste Coudurier <[email protected]>
Date: Sat, 17 Oct 2026 11:18:11 -0700
Subject: [PATCH] avcodec/mpeg12enc: add optional sequence end code

Add the seq_end option to append a sequence end code to the final
MPEG-2 picture packet. Detect the final picture from both existing
picture queues so B-frame draining emits exactly one end code.

Keep the option in the encoder context and reject low-delay mode,
which would require a separate trailer packet. Document the option
and add FATE coverage for partial and queued B-frame chains.

Signed-off-by: Baptiste Coudurier <[email protected]>
---
 doc/encoders.texi                    |  4 ++++
 libavcodec/mpeg12enc.c               |  6 ++++++
 libavcodec/mpegvideo_enc.c           | 11 +++++++++++
 libavcodec/mpegvideoenc.h            |  2 ++
 tests/fate/ffmpeg.mak                |  5 +++++
 tests/ref/fate/mpeg2-seq-end-bframes |  1 +
 tests/ref/fate/mpeg2-seq-end-short   |  1 +
 7 files changed, 30 insertions(+)
 create mode 100644 tests/ref/fate/mpeg2-seq-end-bframes
 create mode 100644 tests/ref/fate/mpeg2-seq-end-short

diff --git a/doc/encoders.texi b/doc/encoders.texi
index d3487b40a5c..4ec09ac38d5 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -3781,6 +3781,10 @@ Select the mpeg2 level to encode:
 @item low
 @end table
 
+@item seq_end @var{boolean}
+Write a sequence end code after the last picture. Disabled by default.
+Cannot be combined with @option{flags +low_delay}.
+
 @item seq_disp_ext @var{integer}
 Specifies if the encoder should write a sequence_display_extension to the
 output.
diff --git a/libavcodec/mpeg12enc.c b/libavcodec/mpeg12enc.c
index ab2df9d1f9b..b30a348112b 100644
--- a/libavcodec/mpeg12enc.c
+++ b/libavcodec/mpeg12enc.c
@@ -1056,6 +1056,11 @@ static av_cold int encode_init(AVCodecContext *avctx)
     int ret;
     int max_size = avctx->codec_id == AV_CODEC_ID_MPEG2VIDEO ? 16383 : 4095;
 
+    if (m->seq_end && (avctx->flags & AV_CODEC_FLAG_LOW_DELAY)) {
+        av_log(avctx, AV_LOG_ERROR, "Sequence end code is not supported with 
low delay\n");
+        return AVERROR(EINVAL);
+    }
+
     if (avctx->width > max_size || avctx->height > max_size) {
         av_log(avctx, AV_LOG_ERROR, "%s does not support resolutions above 
%dx%d\n",
                CONFIG_SMALL ? avctx->codec->name : avctx->codec->long_name,
@@ -1268,6 +1273,7 @@ static const AVOption mpeg2_options[] = {
       FF_MPV_OFFSET(c.intra_vlc_format),    AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 
1, VE },
     { "non_linear_quant", "Use nonlinear quantizer.",    
FF_MPV_OFFSET(c.q_scale_type),   AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
     { "alternate_scan",   "Enable alternate scantable.", 
FF_MPV_OFFSET(c.alternate_scan), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
+    { "seq_end",          "Write sequence end code.",    
FF_MPV_MAIN_OFFSET(seq_end),     AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
     { "a53cc", "Use A53 Closed Captions (if available)", OFFSET(a53_cc),       
  AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE },
     { "seq_disp_ext",     "Write sequence_display_extension blocks.", 
OFFSET(seq_disp_ext), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE, .unit = 
"seq_disp_ext" },
     {     "auto",   NULL, 0, AV_OPT_TYPE_CONST,  {.i64 = -1},  0, 0, VE, .unit 
= "seq_disp_ext" },
diff --git a/libavcodec/mpegvideo_enc.c b/libavcodec/mpegvideo_enc.c
index c9da0326164..aa4caec3233 100644
--- a/libavcodec/mpegvideo_enc.c
+++ b/libavcodec/mpegvideo_enc.c
@@ -1918,6 +1918,7 @@ int ff_mpv_encode_picture(AVCodecContext *avctx, AVPacket 
*pkt,
     MPVMainEncContext *const m = avctx->priv_data;
     MPVEncContext    *const s = &m->s;
     int stuffing_count, ret;
+    int seq_end = !pic_arg && m->seq_end;
     int context_count = s->c.slice_context_count;
 
     ff_mpv_unref_picture(&s->c.cur_pic);
@@ -1934,6 +1935,10 @@ int ff_mpv_encode_picture(AVCodecContext *avctx, 
AVPacket *pkt,
     if (ret < 0)
         return ret;
 
+    /* Both queues must be empty after selecting the last picture. */
+    for (int i = 1; seq_end && i <= MPVENC_MAX_B_FRAMES; i++)
+        seq_end = !m->input_picture[i] && !m->reordered_input_picture[i];
+
     /* output? */
     if (s->new_pic->data[0]) {
         int growing_buffer = context_count == 1 && !s->data_partitioning;
@@ -2058,6 +2063,12 @@ vbv_retry:
             m->frame_bits  = put_bits_count(&s->pb);
         }
 
+        if (seq_end) {
+            put_bits32(&s->pb, 0x1B7); // sequence_end_code
+            flush_put_bits(&s->pb);
+            m->frame_bits = put_bits_count(&s->pb);
+        }
+
         /* update MPEG-1/2 vbv_delay for CBR */
         if (avctx->rc_max_rate                          &&
             avctx->rc_min_rate == avctx->rc_max_rate &&
diff --git a/libavcodec/mpegvideoenc.h b/libavcodec/mpegvideoenc.h
index 3943a284e8c..c4275d2b016 100644
--- a/libavcodec/mpegvideoenc.h
+++ b/libavcodec/mpegvideoenc.h
@@ -209,6 +209,8 @@ typedef struct MPVMainEncContext {
     int input_picture_number;      ///< used to set pic->display_picture_number
     int coded_picture_number;      ///< used to set pic->coded_picture_number
 
+    int seq_end;                   ///< write sequence end code after the last 
picture
+
     MPVPicture *input_picture[MPVENC_MAX_B_FRAMES + 1]; ///< next pictures in 
display order
     MPVPicture *reordered_input_picture[MPVENC_MAX_B_FRAMES + 1]; ///< next 
pictures in coded order
 
diff --git a/tests/fate/ffmpeg.mak b/tests/fate/ffmpeg.mak
index c2bb00b67c6..25c2e5909f4 100644
--- a/tests/fate/ffmpeg.mak
+++ b/tests/fate/ffmpeg.mak
@@ -469,6 +469,11 @@ fate-ffmpeg-loopback-decoding: CMD = transcode \
     "-map 0:v:0 -c:v mpeg2video -f null - -flags +bitexact -idct simple 
-threads $$threads -dec 0:0 -filter_complex '[0:v][dec:0]hstack[stack]' -map 
'[stack]' -c:v ffv1" ""
 FATE_FFMPEG-$(call ENCDEC2, MPEG2VIDEO, FFV1, NUT, HSTACK_FILTER PIPE_PROTOCOL 
FRAMECRC_MUXER) += fate-ffmpeg-loopback-decoding
 
+# Test sequence end with a partial B-frame chain and with queued B-frames.
+FATE_FFMPEG-$(call ENCMUX, MPEG2VIDEO, MPEG2VIDEO, TESTSRC2_FILTER 
FILE_PROTOCOL) += fate-mpeg2-seq-end-short fate-mpeg2-seq-end-bframes
+fate-mpeg2-seq-end-short: CMD = md5 -filter_complex 
testsrc2=s=64x64:r=25:d=0.08 -c:v mpeg2video -flags +bitexact -dct fastint -q:v 
2 -bf 2 -seq_end 1 -f mpeg2video
+fate-mpeg2-seq-end-bframes: CMD = md5 -filter_complex 
testsrc2=s=64x64:r=25:d=0.12 -c:v mpeg2video -flags +bitexact -dct fastint -q:v 
2 -bf 1 -seq_end 1 -f mpeg2video
+
 # test matching by stream disposition
 fate-ffmpeg-spec-disposition: CMD = framecrc -i 
$(TARGET_SAMPLES)/mpegts/pmtchange.ts -map 
'0:disp:visual_impaired+descriptions:1' -c copy
 FATE_SAMPLES_FFMPEG-$(call FRAMECRC, MPEGTS,,) += fate-ffmpeg-spec-disposition
diff --git a/tests/ref/fate/mpeg2-seq-end-bframes 
b/tests/ref/fate/mpeg2-seq-end-bframes
new file mode 100644
index 00000000000..76323f5145a
--- /dev/null
+++ b/tests/ref/fate/mpeg2-seq-end-bframes
@@ -0,0 +1 @@
+79f0eefaeaf99d10de679f2b7d640398
diff --git a/tests/ref/fate/mpeg2-seq-end-short 
b/tests/ref/fate/mpeg2-seq-end-short
new file mode 100644
index 00000000000..c88f08f5648
--- /dev/null
+++ b/tests/ref/fate/mpeg2-seq-end-short
@@ -0,0 +1 @@
+0df4fa71ba94fa23a72e853133febfd7
-- 
2.54.0

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

Reply via email to