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

Fragmented MP4 recordings with occasional explicitly zero-sized samples fail 
during input opening after 03d81a044ad5. The sample in #23807 contains two such 
video samples; the immediate parent of that commit remuxes it successfully.

Allow explicit zero sample sizes, whose count is bounded by the existing trun 
payload checks. Continue rejecting nonempty runs that inherit a zero default 
size, but perform that check before allocating or modifying the sample index. 
Retain empty samples in the index to preserve sample numbering and timing.

Add `fate-mov-trun-zero-sample-size`, which generates a small fragmented MP4, 
sets the last sample size in its first fragment to zero, and checks packet 
sizes, positions, and timestamps through the following fragment. It requires no 
external sample.

Validation on Linux/AArch64:

- The generated regression input fails on unmodified master and passes with the 
patch.
- The full #23807 recording remuxes without warnings. Its 35,677 video packets 
and 55,782 audio packets match the working parent revision's remux in packet 
sizes and SHA-256 payload hashes.
- All 12 enabled MOV FATE tests and `fate-api-movenc` pass.
- Generated zero-default-size runs remain rejected, including a large-count 
case; a run with zero entries remains accepted.

Fixes #23807.


>From 7ce7c8c2c99ab6f13c39b6e7ee571dfbe06a2376 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sun, 20 Sep 2026 12:44:33 +0000
Subject: [PATCH] avformat/mov: allow explicit zero sample sizes in trun

03d81a044ad587ea83567f75dc36bc3d64278199 rejected every zero-sized
sample to avoid pathological runs of empty samples. This also prevents
opening fragmented recordings with occasional explicit zero sizes.

Allow explicit zero sample sizes, whose count is bounded by the trun
payload checks. Continue rejecting nonempty runs that inherit a zero
default size, but do so before allocating or updating the sample index.
Keep empty samples in the index to preserve sample numbering and timing.

Add a generated FATE test with an empty sample at the end of one fragment
followed by another fragment. No external sample is needed.

Fixes #23807.

Assisted-by: Fairy
---
 libavformat/mov.c                        |  7 +++++--
 tests/fate/mov.mak                       | 16 +++++++++++++++-
 tests/ref/fate/mov-trun-zero-sample-size | 10 ++++++++++
 3 files changed, 30 insertions(+), 3 deletions(-)
 create mode 100644 tests/ref/fate/mov-trun-zero-sample-size

diff --git a/libavformat/mov.c b/libavformat/mov.c
index 6abd3f3513..968ac0a090 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -6255,6 +6255,11 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, 
MOVAtom atom)
     entries = avio_rb32(pb);
     av_log(c->fc, AV_LOG_TRACE, "flags 0x%x entries %u\n", flags, entries);
 
+    /* Explicit zero sizes are bounded by the trun payload. Reject a zero
+     * default size before allocating or updating the sample index. */
+    if (entries && !frag->size && !(flags & MOV_TRUN_SAMPLE_SIZE))
+        return AVERROR_INVALIDDATA;
+
     if ((uint64_t)entries+sc->tts_count >= UINT_MAX/sizeof(*sc->tts_data))
         return AVERROR_INVALIDDATA;
     if (flags & MOV_TRUN_DATA_OFFSET)        data_offset        = 
avio_rb32(pb);
@@ -6465,8 +6470,6 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, 
MOVAtom atom)
         distance++;
         if (av_sat_add64(dts, sample_duration) != dts + 
(uint64_t)sample_duration)
             return AVERROR_INVALIDDATA;
-        if (!sample_size)
-            return AVERROR_INVALIDDATA;
         dts += sample_duration;
         offset += sample_size;
         sc->data_size += sample_size;
diff --git a/tests/fate/mov.mak b/tests/fate/mov.mak
index ecbf686814..b2c5b0a0e9 100644
--- a/tests/fate/mov.mak
+++ b/tests/fate/mov.mak
@@ -340,7 +340,8 @@ FATE_MOV_FFMPEG_FFPROBE-$(call ALLYES, COLOR_FILTER 
SETPTS_FILTER MPEG4_ENCODER
 
 FATE_MOV_FFMPEG_FFPROBE-$(call ALLYES, COLOR_FILTER SETPTS_FILTER 
MPEG4_ENCODER \
                                       MP4_MUXER MOV_DEMUXER FILE_PROTOCOL)     
 \
-                                      += fate-mov-trun-large-sample-duration
+                                      += fate-mov-trun-large-sample-duration \
+                                         fate-mov-trun-zero-sample-size
 
 # Generate a fragmented VFR file, then replace the last sample duration in
 # its first trun with UINT32_MAX.
@@ -354,6 +355,19 @@ fate-mov-trun-large-sample-duration: CMD = 
run_with_patched_temp \
     -show_entries packet=pts,dts,duration -print_format compact \
     -select_streams v -v 0" mp4 923 "\\0377\\0377\\0377\\0377"
 
+# An explicitly empty sample must not prevent reading the following fragment.
+# Set the last sample size in the first trun to zero; its unreferenced payload
+# remains in mdat. Disable parsing to observe the empty sample and its 
duration.
+fate-mov-trun-zero-sample-size: CMD = run_with_patched_temp \
+    "$(FFMPEG) -nostdin -v error \
+    -filter_complex color=c=black:s=2x2:r=1,setpts=N*N \
+    -frames:v 10 -fps_mode vfr -c:v mpeg4 -g 5 -bf 0 -q:v 2 -threads 1 \
+    -flags +bitexact -fflags +bitexact \
+    -movflags empty_moov+frag_keyframe+default_base_moof -f mp4 -y" \
+    "ffprobe$(PROGSSUF)$(EXESUF) -fflags +noparse+nofillin -show_packets \
+    -show_entries packet=pts,dts,duration,size,pos -print_format compact \
+    -select_streams v -v 0" mp4 927 "\\0000\\0000\\0000\\0000"
+
 # Create VFR B-frames whose presentation durations are not a permutation of
 # the STTS sample deltas.
 tests/data/mov-vfr-bframes-derived-duration.mov: TAG = GEN
diff --git a/tests/ref/fate/mov-trun-zero-sample-size 
b/tests/ref/fate/mov-trun-zero-sample-size
new file mode 100644
index 0000000000..7838fa6b93
--- /dev/null
+++ b/tests/ref/fate/mov-trun-zero-sample-size
@@ -0,0 +1,10 @@
+packet|pts=0|dts=0|duration=16384|size=17|pos=939
+packet|pts=16384|dts=16384|duration=49152|size=7|pos=956
+packet|pts=65536|dts=65536|duration=81920|size=7|pos=963
+packet|pts=147456|dts=147456|duration=114688|size=8|pos=970
+packet|pts=262144|dts=262144|duration=147456|size=0|pos=978
+packet|pts=409600|dts=409600|duration=180224|size=17|pos=1138
+packet|pts=589824|dts=589824|duration=212992|size=8|pos=1155
+packet|pts=802816|dts=802816|duration=245760|size=9|pos=1163
+packet|pts=1048576|dts=1048576|duration=278528|size=9|pos=1172
+packet|pts=1327104|dts=1327104|duration=16384|size=9|pos=1181
-- 
2.52.0

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

Reply via email to