PR #24583 opened by Forgejo_Fairy
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24583
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24583.patch

MXF output currently reserves only alignment padding after header metadata. 
Downstream systems that insert additional metadata may need more space to avoid 
relocating essence. Add `mxf_header_reserve` to the MXF, D-10, and OP-Atom 
muxers to reserve additional header KLV Fill. Addresses #23853.

The option defaults to zero and rounds positive values up to a multiple of the 
512-byte KAG. For example, `-mxf_header_reserve 65536` adds 65,536 bytes beyond 
the existing padding; it does not specify a total header partition size. 
Combine the reserve with the trailing Fill item and include it in 
`HeaderByteCount`. Retain four-byte BER lengths where sufficient and use 
nine-byte lengths for larger Fill items. Backpatch the header count before 
writing the reserve so that large reserves also work with non-seekable output.

Documentation and four FATE tests cover ordinary MXF, D-10, OP-Atom, rounding, 
and a reserve above 16 MiB written to a pipe. All samples already exist in the 
FATE suite.

Validation on Linux/AArch64:

- Built with `--disable-autodetect --disable-doc --disable-debug`.
- Passed `make -j8 fate-mxf SAMPLES=/opt/fate-suite` and the eight existing 
lavf MXF muxing tests: `mxf`, `mxf_d10`, `mxf_opatom`, `mxf_opatom_audio`, 
`mxf_dv25`, `mxf_dvcpro50`, `mxf_dvcpro100`, and `mxf_ffv1`.
- Checked 64 reserve cases across variable-rate/constant-rate MXF, D-10, and 
video/audio OP-Atom, including MXF/D-10 pipe output. Values were 1, 511, 512, 
513, 65536, 65537, 16777216, and 16777217. Verified header counts, Fill 
boundaries, partition/RIP offsets, and unchanged packet checksums.
- Compared omitted/zero-option output byte-for-byte against unmodified master.
- Verified rejection of -1 and 2147483648, and successful streaming of 
2147483647 rounded to 2 GiB of additional Fill.

Harmonic Omneon/Crispin interoperability remains for the reporter to validate. 
This change implements the configurable reserve; a separate broadcast-profile 
muxer is outside its scope.


>From d884b92b8433e16e382a80e745ee3026e1bebf22 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sun, 20 Sep 2026 12:51:34 +0000
Subject: [PATCH] avformat/mxfenc: add configurable header metadata reserve

Add mxf_header_reserve to reserve additional KLV Fill after the header
metadata for downstream metadata insertion. Round the requested space up
to the KAG size and include it in HeaderByteCount. A zero default preserves
the existing output.

Combine the reserve with the existing trailing fill and use a longer BER
length when it exceeds the four-byte encoding's capacity. Backpatch the
header byte count before writing the potentially large reserve so that
non-seekable output still works.

Document the option and add FATE coverage for MXF, D-10, OP-Atom, and a
large reserve written to a pipe. Existing FATE samples suffice.

Addresses #23853.

Assisted-by: Fairy
---
 doc/muxers.texi                          | 10 ++++++++++
 libavformat/mxfenc.c                     | 24 ++++++++++++++++++++++--
 tests/fate/mxf.mak                       | 14 ++++++++++++++
 tests/ref/fate/mxf-d10-header-reserve    | 11 +++++++++++
 tests/ref/fate/mxf-header-reserve        | 19 +++++++++++++++++++
 tests/ref/fate/mxf-header-reserve-pipe   |  1 +
 tests/ref/fate/mxf-opatom-header-reserve | 11 +++++++++++
 7 files changed, 88 insertions(+), 2 deletions(-)
 create mode 100644 tests/ref/fate/mxf-d10-header-reserve
 create mode 100644 tests/ref/fate/mxf-header-reserve
 create mode 100644 tests/ref/fate/mxf-header-reserve-pipe
 create mode 100644 tests/ref/fate/mxf-opatom-header-reserve

diff --git a/doc/muxers.texi b/doc/muxers.texi
index 95f7ea33a7..181715bff1 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -3348,6 +3348,16 @@ MXF muxer.
 The muxer options are:
 
 @table @option
+@item mxf_header_reserve @var{bytes}
+Reserve additional space after the header metadata for downstream metadata
+insertion. The space is written as KLV Fill in the header partition and
+included in its HeaderByteCount. The value specifies additional bytes of
+KLV Fill, including any key and length overhead, and is rounded up to a
+multiple of 512 bytes. It does not specify the total header partition size.
+The default is 0, which preserves the existing alignment-only padding.
+The range is 0 to 2147483647. This option is available for @code{mxf},
+@code{mxf_d10}, and @code{mxf_opatom}.
+
 @item store_user_comments @var{bool}
 Set if user comments should be stored if available or never.
 IRT D-10 does not allow user comments. The default is thus to write them for
diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c
index 48043a7d47..03cbcecbc0 100644
--- a/libavformat/mxfenc.c
+++ b/libavformat/mxfenc.c
@@ -466,6 +466,7 @@ typedef struct MXFContext {
     int essence_container_count;
     AVRational time_base;
     int header_written;
+    int header_reserve;
     MXFIndexEntry *index_entries;
     unsigned edit_units_count;
     uint64_t timestamp;   ///< timestamp, as 
year(16),month(8),day(8),hour(8),minutes(8),msec/4(8)
@@ -2273,7 +2274,9 @@ static int mxf_write_partition(AVFormatContext *s, int 
bodysid,
     if (write_metadata) {
         // mark the start of the headermetadata and calculate metadata size
         int64_t pos, start;
-        unsigned header_byte_count;
+        uint64_t header_byte_count;
+        int64_t reserve = partition_offset ? 0 :
+                          FFALIGN((int64_t)mxf->header_reserve, KAG_SIZE);
 
         mxf_write_klv_fill(s);
         start = avio_tell(s->pb);
@@ -2281,12 +2284,27 @@ static int mxf_write_partition(AVFormatContext *s, int 
bodysid,
         mxf_write_klv_fill(s);
         mxf_write_header_metadata_sets(s);
         pos = avio_tell(s->pb);
-        header_byte_count = pos - start + klv_fill_size(pos);
+        header_byte_count = pos - start + klv_fill_size(pos) + reserve;
 
         // update header_byte_count
         avio_seek(pb, header_byte_count_offset, SEEK_SET);
         avio_wb64(pb, header_byte_count);
         avio_seek(pb, pos, SEEK_SET);
+
+        if (reserve) {
+            /* Backpatch before writing the reserve, which may flush the
+             * buffer even on non-seekable outputs. */
+            reserve += klv_fill_size(pos);
+            avio_write(pb, klv_fill_key, 16);
+            if (reserve - 20 <= 0xFFFFFF) {
+                reserve -= 20;
+                klv_encode_ber4_length(pb, reserve);
+            } else {
+                reserve -= 25;
+                klv_encode_ber9_length(pb, reserve);
+            }
+            ffio_fill(pb, 0, reserve);
+        }
     }
 
     if(key)
@@ -3639,6 +3657,8 @@ static int mxf_check_bitstream(AVFormatContext *s, 
AVStream *st, const AVPacket
 }
 
 #define MXF_COMMON_OPTIONS \
+    { "mxf_header_reserve", "Additional header KLV Fill size in bytes, rounded 
up to a multiple of 512",\
+      offsetof(MXFContext, header_reserve), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 
INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},\
     { "signal_standard", "Force/set Signal Standard",\
       offsetof(MXFContext, signal_standard), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 
7, AV_OPT_FLAG_ENCODING_PARAM, .unit = "signal_standard"},\
     { "bt601", "ITU-R BT.601 and BT.656, also SMPTE 125M (525 and 625 line 
interlaced)",\
diff --git a/tests/fate/mxf.mak b/tests/fate/mxf.mak
index 10c310dd34..2d9eace3f5 100644
--- a/tests/fate/mxf.mak
+++ b/tests/fate/mxf.mak
@@ -58,6 +58,20 @@ fate-mxf-d10-user-comments: CMD = transcode mxf 
$(TARGET_SAMPLES)/mxf/Avid-00005
 FATE_MXF-$(call DEMMUX, MXF, MXF_OPATOM, MPEGVIDEO_PARSER MPEG2VIDEO_DECODER) 
+= fate-mxf-opatom-user-comments
 fate-mxf-opatom-user-comments: CMD = md5 -y -i 
$(TARGET_SAMPLES)/mxf/Sony-00001.mxf -an -vcodec copy -metadata 
"comment_test=value" -fflags +bitexact -f mxf_opatom
 
+# Exercise header rewriting, KAG rounding, and demuxing with reserved metadata 
space.
+FATE_MXF-$(call REMUX, MXF, MPEGVIDEO_PARSER MPEG2VIDEO_DECODER) += 
fate-mxf-header-reserve
+fate-mxf-header-reserve: CMD = transcode mxf 
$(TARGET_SAMPLES)/mxf/Sony-00001.mxf mxf "-c copy -t 0.12 -mxf_header_reserve 
65536" "-c copy"
+
+# A reserve above 16 MiB needs a longer BER length and flushes the AVIO buffer.
+FATE_MXF-$(call DEMMUX, MXF, MXF, MPEGVIDEO_PARSER MPEG2VIDEO_DECODER 
PIPE_PROTOCOL) += fate-mxf-header-reserve-pipe
+fate-mxf-header-reserve-pipe: CMD = md5pipe -i 
$(TARGET_SAMPLES)/mxf/Sony-00001.mxf -c copy -t 0.12 -mxf_header_reserve 
16777217 -fflags +bitexact -f mxf
+
+FATE_MXF-$(call TRANSCODE, MPEG2VIDEO, MXF_D10 MXF, MPEGVIDEO_PARSER 
EXTRACT_EXTRADATA_BSF) += fate-mxf-d10-header-reserve
+fate-mxf-d10-header-reserve: CMD = transcode mxf 
$(TARGET_SAMPLES)/mxf/Sony-00001.mxf mxf_d10 "-c:v mpeg2video -b:v 50000k 
-minrate:v 50000k -maxrate:v 50000k -bufsize:v 2000000 -rc_init_occupancy 
2000000 -g 1 -an -t 0.12 -mxf_header_reserve 1" "-c copy"
+
+FATE_MXF-$(call REMUX, MXF_OPATOM MXF, MPEGVIDEO_PARSER MPEG2VIDEO_DECODER) += 
fate-mxf-opatom-header-reserve
+fate-mxf-opatom-header-reserve: CMD = transcode mxf 
$(TARGET_SAMPLES)/mxf/Sony-00001.mxf mxf_opatom "-c copy -an -t 0.12 
-mxf_header_reserve 65537" "-c copy"
+
 FATE_SAMPLES_FFMPEG += $(FATE_MXF-yes)
 FATE_SAMPLES_FFMPEG_FFPROBE += $(FATE_MXF_FFMPEG_FFPROBE-yes)
 FATE_SAMPLES_FFPROBE += $(FATE_MXF_PROBE-yes)
diff --git a/tests/ref/fate/mxf-d10-header-reserve 
b/tests/ref/fate/mxf-d10-header-reserve
new file mode 100644
index 0000000000..626384c7dd
--- /dev/null
+++ b/tests/ref/fate/mxf-d10-header-reserve
@@ -0,0 +1,11 @@
+733ad8e5047b7b79e691ba3fcdf1a77d 
*tests/data/fate/mxf-d10-header-reserve.mxf_d10
+759341 tests/data/fate/mxf-d10-header-reserve.mxf_d10
+#extradata 0:       34, 0x6cb90561
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: mpeg2video
+#dimensions 0: 720x608
+#sar 0: 152/135
+0,         -1,          0,        1,   250000, 0x8bb844b0
+0,          0,          1,        1,   250000, 0xbeae1f65
+0,          1,          2,        1,   250000, 0xd25c8555
diff --git a/tests/ref/fate/mxf-header-reserve 
b/tests/ref/fate/mxf-header-reserve
new file mode 100644
index 0000000000..925007b73b
--- /dev/null
+++ b/tests/ref/fate/mxf-header-reserve
@@ -0,0 +1,19 @@
+2150815593aca5ecd899e3b30e9874e2 *tests/data/fate/mxf-header-reserve.mxf
+883257 tests/data/fate/mxf-header-reserve.mxf
+#extradata 0:       22, 0x32ea0490
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: mpeg2video
+#dimensions 0: 720x608
+#sar 0: 152/135
+#tb 1: 1/48000
+#media_type 1: audio
+#codec_id 1: pcm_s16le
+#sample_rate 1: 48000
+#channel_layout_name 1: 7.1
+0,          0,          0,        1,   237628, 0xeff25579
+1,          0,          0,     1920,    30720, 0xa38d58ed
+0,          1,          1,        1,   238066, 0xb2265f41
+1,       1920,       1920,     1920,    30720, 0x4f2a0ded
+0,          2,          2,        1,   237723, 0x00d7cd24
+1,       3840,       3840,     1920,    30720, 0x2ee79ff8
diff --git a/tests/ref/fate/mxf-header-reserve-pipe 
b/tests/ref/fate/mxf-header-reserve-pipe
new file mode 100644
index 0000000000..7e615d517d
--- /dev/null
+++ b/tests/ref/fate/mxf-header-reserve-pipe
@@ -0,0 +1 @@
+d7ec7c962a493fe45b0b35dcdaaaffd0
diff --git a/tests/ref/fate/mxf-opatom-header-reserve 
b/tests/ref/fate/mxf-opatom-header-reserve
new file mode 100644
index 0000000000..87c7823e39
--- /dev/null
+++ b/tests/ref/fate/mxf-opatom-header-reserve
@@ -0,0 +1,11 @@
+cd1900c0d97005a39b2ab8c65ac59eed 
*tests/data/fate/mxf-opatom-header-reserve.mxf_opatom
+786489 tests/data/fate/mxf-opatom-header-reserve.mxf_opatom
+#extradata 0:       22, 0x32ea0490
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: mpeg2video
+#dimensions 0: 720x608
+#sar 0: 152/135
+0,          0,          0,        1,   237628, 0xeff25579
+0,          1,          1,        1,   238066, 0xb2265f41
+0,          2,          2,        1,   237723, 0x00d7cd24
-- 
2.52.0

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

Reply via email to