PR #23320 opened by yuyong05
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23320
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23320.patch
This would otherwise cause the DTS timeline counter to overshoot the edit
list segment duration, producing Time-To-Sample (TTS / STTS) table entries
with incorrect durations.
This didn't cause any issues previously for standard framerates where the
overshoot was negligible, but for low-framerate videos, the discrepancy
became large enough to disrupt audio/video sync and fail transcoding
duration validation.
To generate test file:
ffmpeg -f lavfi -i color=c=blue:s=854x480 -frames:v 1 pixel.png -y
ffmpeg -loop 1 -r 1/15 -i pixel.png -f lavfi -i
anullsrc=r=44100:cl=stereo \
-t 200 -c:v libx264 -c:a pcm_s16le -pix_fmt yuv420p raw_200s.mov -y
ffmpeg -ss 0 -t 15 -i raw_200s.mov -i raw_200s.mov -map 0:v -map 1:a \
-c copy output.mov -y
To verify (Before Fix):
1. Remux output using unfixed ffmpeg:
unfixed_ffmpeg -i output.mov -c copy remuxed_unfixed.mov -y
2. Inspect the video stream duration:
unfixed_ffprobe -v error -show_entries
format=duration:stream=duration \
-of default=noprint_wrappers=1 remuxed_unfixed.mov
To verify (After Fix):
1. Remux output using fixed ffmpeg:
fixed_ffmpeg -i output.mov -c copy remuxed_fixed.mov -y
2. Inspect the video stream duration:
fixed_ffprobe -v error -show_entries format=duration:stream=duration
\
-of default=noprint_wrappers=1 remuxed_fixed.mov
Verification results summary:
+--------------------+-----------------------+---------------------------+-----------------------+
| Version | Video Stream Duration | Last Packet
duration_time | Remux Completion Time |
+--------------------+-----------------------+---------------------------+-----------------------+
| Unfixed (Reverted) | 45.000000 seconds | 15.000000 seconds
| time=00:00:15.00 |
| Fixed | 75.000000 seconds | 45.000000 seconds
| time=00:00:45.00 |
+--------------------+-----------------------+---------------------------+-----------------------+
# Summary of changes
Briefly describe what this PR does and why.
<!--
If this PR requires new FATE test samples, attach them to the PR and
list their target paths below (relative to the fate-suite root).
Attached filenames must match the sample's filename:
```fate-samples
# e.g. vorbis/new-sample.ogg
```
-->
>From 14a4643e49b5b03f2b7a66aeeec393f20ede464c Mon Sep 17 00:00:00 2001
From: Yong Yu <[email protected]>
Date: Wed, 3 Jun 2026 15:57:02 +0000
Subject: [PATCH] avformat/mov: fix DTS overshoot and TTS duration mismatches
This would otherwise cause the DTS timeline counter to overshoot the edit
list segment duration, producing Time-To-Sample (TTS / STTS) table entries
with incorrect durations.
This didn't cause any issues previously for standard framerates where the
overshoot was negligible, but for low-framerate videos, the discrepancy
became large enough to disrupt audio/video sync and fail transcoding
duration validation.
To generate test file:
ffmpeg -f lavfi -i color=c=blue:s=854x480 -frames:v 1 pixel.png -y
ffmpeg -loop 1 -r 1/15 -i pixel.png -f lavfi -i
anullsrc=r=44100:cl=stereo \
-t 200 -c:v libx264 -c:a pcm_s16le -pix_fmt yuv420p raw_200s.mov -y
ffmpeg -ss 0 -t 15 -i raw_200s.mov -i raw_200s.mov -map 0:v -map 1:a \
-c copy output.mov -y
To verify (Before Fix):
1. Remux output using unfixed ffmpeg:
unfixed_ffmpeg -i output.mov -c copy remuxed_unfixed.mov -y
2. Inspect the video stream duration:
unfixed_ffprobe -v error -show_entries
format=duration:stream=duration \
-of default=noprint_wrappers=1 remuxed_unfixed.mov
To verify (After Fix):
1. Remux output using fixed ffmpeg:
fixed_ffmpeg -i output.mov -c copy remuxed_fixed.mov -y
2. Inspect the video stream duration:
fixed_ffprobe -v error -show_entries format=duration:stream=duration
\
-of default=noprint_wrappers=1 remuxed_fixed.mov
Verification results summary:
+--------------------+-----------------------+---------------------------+-----------------------+
| Version | Video Stream Duration | Last Packet
duration_time | Remux Completion Time |
+--------------------+-----------------------+---------------------------+-----------------------+
| Unfixed (Reverted) | 45.000000 seconds | 15.000000 seconds
| time=00:00:15.00 |
| Fixed | 75.000000 seconds | 45.000000 seconds
| time=00:00:45.00 |
+--------------------+-----------------------+---------------------------+-----------------------+
---
libavformat/mov.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 46dac965a2..89126b6b53 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -4385,6 +4385,7 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
// check if frame outside edit list mark it for discard
frame_duration = (current + 1 < e_old_end) ?
((current + 1)->timestamp - current->timestamp) :
edit_list_duration;
+ frame_duration = FFMIN(frame_duration, FFMAX(0,
edit_list_dts_entry_end - edit_list_dts_counter));
flags = current->flags;
@@ -4402,7 +4403,7 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
if (add_tts_entry(&msc->tts_data, &msc->tts_count,
&msc->tts_allocated_size,
tts_data_old[tts_index_old].count -
edit_list_start_tts_sample,
- tts_data_old[tts_index_old].offset,
tts_data_old[tts_index_old].duration) == -1) {
+ tts_data_old[tts_index_old].offset,
frame_duration) == -1) {
av_log(mov->fc, AV_LOG_ERROR, "Cannot add Time To
Sample entry %"PRId64" - {%"PRId64", %d}\n",
tts_index_old,
tts_data_old[tts_index_old].count -
edit_list_start_tts_sample,
@@ -4509,7 +4510,7 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
if (add_tts_entry(&msc->tts_data, &msc->tts_count,
&msc->tts_allocated_size,
tts_sample_old -
edit_list_start_tts_sample,
- tts_data_old[tts_index_old].offset,
tts_data_old[tts_index_old].duration) == -1) {
+ tts_data_old[tts_index_old].offset,
frame_duration) == -1) {
av_log(mov->fc, AV_LOG_ERROR, "Cannot add Time To
Sample entry %"PRId64" - {%"PRId64", %d}\n",
tts_index_old, tts_sample_old -
edit_list_start_tts_sample,
tts_data_old[tts_index_old].offset);
--
2.52.0
_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]