PR #25365 opened by Forgejo_Fairy URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25365 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25365.patch
Copying artwork into an M4A/M4B can make ten-minute QuickTime chapters disappear. With 22050 Hz audio and a cover stream using a 90 kHz time base, automatic movie-timescale selection chooses 4410000 ticks per second. Chapter durations then exceed the muxer's INT_MAX packet-duration limit and are rejected. Exclude cover images from the common-timescale calculation using the existing is_cover_image() predicate. Artwork has no presentation timeline. Timed thumbnails still participate, and explicit movie_timescale settings retain their behavior. Add fate-mov-cover-long-chapters using generated short audio/artwork and a staged metadata fixture containing two ten-minute chapters. The reference checks audio, artwork, the text chapter track and chapter boundaries. Update the existing cover-image checksum for the changed movie timing fields; its packet checksums and stream information are unchanged. Fixes #25317. The regression was verified at 51f5f60443ee against its direct parent 2153c6795cf9. Validation on Linux/AArch64: - The supplied reproduction script fails on master 303510f9d1f and passes all five conversions with this patch. - The new FATE test fails with the source fix removed and passes with it applied. - All 11 tests enabled in the focused build's fate-mov group pass, including mov-mp4-chapters and mov-cover-image. - Release/9.0 reproduces the issue; release/8.1 does not. This fixes the cover-induced timescale inflation. The existing packet-duration limit and chapter-write error handling are outside this patch's scope. No new external FATE sample is needed. >From 4734bb08fc5142d40ec339f96d3835a0216eb68e Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Thu, 22 Oct 2026 17:10:37 +0000 Subject: [PATCH] avformat/movenc: exclude cover images from the movie timescale Cover images are stored as metadata and do not have a presentation timeline. Including their time base in the common movie timescale can raise it unnecessarily: 22050 Hz audio and a 90 kHz cover stream select 4410000 ticks per second. QuickTime chapter packets use the movie timescale. At that rate a ten-minute chapter exceeds the INT_MAX packet duration limit and is rejected, leaving the output without its chapter track. Exclude cover images using the same predicate as the artwork writer, while retaining timed thumbnail streams and explicit movie_timescale settings. Add a generated-media FATE regression test checking the audio, artwork, chapter track and chapter times. Update the existing cover-image checksum for the changed movie timing fields. Fixes: 51f5f60443eec9944c9d0cf24e0766edf403d7ef Fixes: https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/25317 Assisted-by: Fairy --- libavformat/movenc.c | 4 +++- tests/fate/mov.mak | 23 +++++++++++++++++++++ tests/mov-cover-long-chapters.ffmeta | 11 ++++++++++ tests/ref/fate/mov-cover-image | 2 +- tests/ref/fate/mov-cover-long-chapters | 28 ++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 tests/mov-cover-long-chapters.ffmeta create mode 100644 tests/ref/fate/mov-cover-long-chapters diff --git a/libavformat/movenc.c b/libavformat/movenc.c index debe1acb7e0..438dc60b9e5 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -8640,7 +8640,9 @@ static int mov_init(AVFormatContext *s) track->par = st->codecpar; } - movie_timescale = av_gcd_q(movie_timescale, st->time_base, INT_MAX, (AVRational){1,0}); + /* Cover images are metadata and have no presentation timeline. */ + if (!is_cover_image(st)) + movie_timescale = av_gcd_q(movie_timescale, st->time_base, INT_MAX, (AVRational){1,0}); } if (!movie_timescale.den) movie_timescale = MOV_TIMESCALE_Q; diff --git a/tests/fate/mov.mak b/tests/fate/mov.mak index 9aa879fc390..325e8b479dd 100644 --- a/tests/fate/mov.mak +++ b/tests/fate/mov.mak @@ -575,6 +575,29 @@ fate-mov-stereo3d-sbs-mp4: CMD = transcode rawvideo $(TARGET_PATH)/tests/data/vs "-c:v mpeg4 -qscale:v 10 -stereo3d:v sbsl -strict unofficial -frames:v 2" \ "-c:v copy" "-show_entries stream_side_data_list" "" "" "-s 352x288 -pix_fmt yuv420p" +# Including artwork's 90 kHz time base would make the movie/chapter timescale +# so large that ten-minute chapter durations no longer fit in INT_MAX. +FATE_MOV_FFMPEG_FFPROBE-$(call ALLYES, LAVFI_INDEV SINE_FILTER COLOR_FILTER \ + PCM_S16$(if $(filter yes,$(HAVE_BIGENDIAN)),BE,LE)_DECODER \ + WRAPPED_AVFRAME_DECODER AAC_ENCODER MJPEG_ENCODER \ + ARESAMPLE_FILTER SCALE_FILTER FFMETADATA_DEMUXER \ + IPOD_MUXER MOV_DEMUXER FILE_PROTOCOL) \ + += fate-mov-cover-long-chapters + +tests/data/mov-cover-long-chapters.ffmeta: TAG = COPY +tests/data/mov-cover-long-chapters.ffmeta: $(SRC_PATH)/tests/mov-cover-long-chapters.ffmeta | tests/data + $(M)cp $< $@ +fate-mov-cover-long-chapters: tests/data/mov-cover-long-chapters.ffmeta +fate-mov-cover-long-chapters: CMD = run_with_temp \ + "$(FFMPEG) -nostdin -v error \ + -f lavfi -i sine=r=22050:d=0.2 -f lavfi -i color=c=red:s=16x16:d=0.04 \ + -i $(TARGET_PATH)/tests/data/mov-cover-long-chapters.ffmeta \ + -map 0:a -map 1:v -map_chapters 2 -c:a aac -c:v mjpeg \ + -fps_mode:v passthrough -enc_time_base:v 1/90000 -disposition:v attached_pic \ + -threads 1 -f ipod -y" \ + "ffprobe$(PROGSSUF)$(EXESUF) -v error \ + -show_entries stream=codec_name,codec_tag_string,time_base:stream_disposition=attached_pic:chapter=start_time,end_time:chapter_tags=title" m4b + FATE_FFMPEG += $(FATE_MOV_FFMPEG-yes) FATE_FFMPEG_FFPROBE += $(FATE_MOV_FFMPEG_FFPROBE-yes) diff --git a/tests/mov-cover-long-chapters.ffmeta b/tests/mov-cover-long-chapters.ffmeta new file mode 100644 index 00000000000..49e100fdf7e --- /dev/null +++ b/tests/mov-cover-long-chapters.ffmeta @@ -0,0 +1,11 @@ +;FFMETADATA1 +[CHAPTER] +TIMEBASE=1/1000 +START=0 +END=600000 +title=CH 1 +[CHAPTER] +TIMEBASE=1/1000 +START=600000 +END=1200000 +title=CH 2 diff --git a/tests/ref/fate/mov-cover-image b/tests/ref/fate/mov-cover-image index 6c6607c39c1..0811b5594a9 100644 --- a/tests/ref/fate/mov-cover-image +++ b/tests/ref/fate/mov-cover-image @@ -1,4 +1,4 @@ -124d92893a4a6dc8e0b5a6c384536e2e *tests/data/fate/mov-cover-image.mp4 +863c657c1ac377cada881745c3220eb8 *tests/data/fate/mov-cover-image.mp4 1024065 tests/data/fate/mov-cover-image.mp4 #extradata 0: 2, 0x00340022 #tb 0: 1/44100 diff --git a/tests/ref/fate/mov-cover-long-chapters b/tests/ref/fate/mov-cover-long-chapters new file mode 100644 index 00000000000..c70b4fd690f --- /dev/null +++ b/tests/ref/fate/mov-cover-long-chapters @@ -0,0 +1,28 @@ +[STREAM] +codec_name=aac +codec_tag_string=mp4a +time_base=1/22050 +DISPOSITION:attached_pic=0 +[/STREAM] +[STREAM] +codec_name=bin_data +codec_tag_string=text +time_base=1/22050 +DISPOSITION:attached_pic=0 +[/STREAM] +[STREAM] +codec_name=mjpeg +codec_tag_string=[0][0][0][0] +time_base=1/90000 +DISPOSITION:attached_pic=1 +[/STREAM] +[CHAPTER] +start_time=0.000000 +end_time=600.000000 +TAG:title=CH 1 +[/CHAPTER] +[CHAPTER] +start_time=600.000000 +end_time=1200.000000 +TAG:title=CH 2 +[/CHAPTER] -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
