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

Use moof-relative addressing when global sidx shifts fragmented data, and add 
regression coverage for empty_moov and delay_moov.


The bug is caused by an invalid fragment offset after writing a global sidx 
atom.
With global_sidx, FFmpeg writes the fragmented MP4 first and inserts the global 
sidx in a second pass. This insertion shifts every moof and
mdat atom forward in the file. However, the tfhd.base_data_offset fields were 
already serialized using the original absolute moof offsets and
are not updated after the shift.
As a result, the demuxer calculates sample positions from stale offsets. It 
reads bytes from the trun metadata area instead of the
corresponding mdat payload. For H.264, those metadata bytes are interpreted as 
NAL-unit length fields, producing errors such as:
Invalid NAL unit size
Error splitting the input into NAL units
The issue is exposed by combinations such as:
global_sidx + frag_keyframe + empty_moov
global_sidx + frag_keyframe + delay_moov
Using default_base_moof or omit_tfhd_offset avoids the problem because fragment 
addressing no longer depends on the stale absolute
base_data_offset. The fix makes FFmpeg use default-base-is-moof automatically 
when global_sidx is enabled for fragmented output.


>From 211e3183cafe33497fa561ce16c36aaad37781e4 Mon Sep 17 00:00:00 2001
From: jiangjie <[email protected]>
Date: Tue, 15 Sep 2026 19:14:18 +0800
Subject: [PATCH] avformat/movenc: fix global sidx fragment offsets

Use moof-relative addressing when global sidx shifts fragmented data, and add 
regression coverage for empty_moov and delay_moov.

Fixes issue #24438.
---
 libavformat/movenc.c |  7 +++++++
 tests/fate/mov.mak   | 15 +++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index ecc6544bad..a64d54adc6 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -8273,6 +8273,13 @@ static int mov_init(AVFormatContext *s)
         mov->flags |= FF_MOV_FLAG_FRAGMENT | FF_MOV_FLAG_EMPTY_MOOV |
                       FF_MOV_FLAG_DEFAULT_BASE_MOOF | 
FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS;
 
+    /* global_sidx is inserted in a second pass, shifting all fragments. Use
+     * moof-relative addressing so that tfhd offsets remain valid after that
+     * shift. */
+    if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX &&
+        mov->flags & FF_MOV_FLAG_FRAGMENT)
+        mov->flags |= FF_MOV_FLAG_DEFAULT_BASE_MOOF;
+
     if (mov->flags & FF_MOV_FLAG_EMPTY_MOOV && s->flags & AVFMT_FLAG_AUTO_BSF) 
{
         av_log(s, AV_LOG_VERBOSE, "Empty MOOV enabled; disabling automatic 
bitstream filtering\n");
         s->flags &= ~AVFMT_FLAG_AUTO_BSF;
diff --git a/tests/fate/mov.mak b/tests/fate/mov.mak
index 3c0ac3ec13..dc3f62806c 100644
--- a/tests/fate/mov.mak
+++ b/tests/fate/mov.mak
@@ -322,6 +322,21 @@ fate-mov-vfr: CMD = md5 -filter_complex 
testsrc=size=2x2:duration=1,setpts=N*N:s
 fate-mov-vfr: CMP = oneline
 fate-mov-vfr: REF = 1558b4a9398d8635783c93f84eb5a60d
 
+# Verify that inserting a global sidx does not invalidate fragment offsets.
+FATE_MOV_FFMPEG-$(call ALLYES, LAVFI_INDEV TESTSRC_FILTER MPEG4_ENCODER \
+                                      MP4_MUXER MOV_DEMUXER NULL_MUXER 
FILE_PROTOCOL) \
+                                      += fate-mov-global-sidx 
fate-mov-global-sidx-delay-moov
+fate-mov-global-sidx: CMP = null
+fate-mov-global-sidx: CMD = run_with_temp \
+    "$(FFMPEG) -nostdin -hide_banner -loglevel error -f lavfi -i 
testsrc=size=32x24:rate=25 \
+    -frames:v 3 -c:v mpeg4 -movflags +global_sidx+frag_keyframe+empty_moov -f 
mp4 -y" \
+    "$(FFMPEG) -nostdin -hide_banner -loglevel error -f null - -i" mp4
+fate-mov-global-sidx-delay-moov: CMP = null
+fate-mov-global-sidx-delay-moov: CMD = run_with_temp \
+    "$(FFMPEG) -nostdin -hide_banner -loglevel error -f lavfi -i 
testsrc=size=32x24:rate=25 \
+    -frames:v 3 -c:v mpeg4 -movflags +global_sidx+frag_keyframe+delay_moov -f 
mp4 -y" \
+    "$(FFMPEG) -nostdin -hide_banner -loglevel error -f null - -i" mp4
+
 FATE_MOV_FFMPEG_FFPROBE-$(call ALLYES, COLOR_FILTER SETPTS_FILTER 
MPEG4_ENCODER \
                                       MOV_MUXER MOV_DEMUXER FILE_PROTOCOL)     
 \
                                       += fate-mov-vfr-bframes-derived-duration
-- 
2.52.0

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

Reply via email to