PR #24822 opened by Forgejo_Fairy URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24822 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24822.patch
With `-use_wallclock_as_timestamps 1`, raw PCM packets carry timestamps even though the demuxer declares `AVFMT_NOTIMESTAMPS`. Stream probing currently skips first-DTS discovery in this case and can return with an unknown input start time. When combined with separately probed H.264 video, the ffmpeg CLI normalizes video near zero while retaining epoch-based audio timestamps, causing the scheduler to hold the audio demuxer back indefinitely. Include wallclock-generated timestamps in the existing first-DTS probing condition. This addresses the persistent audio starvation in #24053, exposed for raw PCM by 2892815c45c1. The older repeated video-write delays in that issue remain unresolved; this PR should not close the entire issue. Validation on Linux AArch64: - The new sample-free `fate-lavf-wallclock` test reports unknown format/stream start times against unpatched master and known start times with this change. Expected output resides in `tests/ref/fate/lavf-wallclock`; no absolute wallclock values or timing thresholds are compared. - All six enabled libavformat FATE tests pass in the minimal build. - The supplied live reproducer on master 09f879b07428 times out an audio write at 38.8 seconds elapsed. With this patch, it completes 120 seconds without an audio timeout, while retaining the older video-write delays. - Both 2892815c45c1 and its immediate parent f4a1243481d1 were built and tested: only the changed commit produces the persistent audio timeout. Investigation logs and reproduction instructions are preserved on `fairy/issue24053-tooling` under `tools/issue24053/`. >From 872e2889b1c6ecb77ea8b5e3d906e1b09739f1f7 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Tue, 29 Sep 2026 22:02:48 +0000 Subject: [PATCH] avformat/demux: probe wallclock timestamps for untimestamped formats When use_wallclock_as_timestamps is enabled, packets have timestamps even if the demuxer declares AVFMT_NOTIMESTAMPS. Do not skip first-DTS probing in this case. For raw PCM, skipping the probe can leave the input start time unknown. The ffmpeg CLI then keeps epoch-based audio timestamps while normalizing a separately probed video input near zero. The scheduler holds the audio demuxer back indefinitely waiting for video to catch up. This addresses the persistent audio starvation reported in #24053, exposed for raw PCM by 2892815c45c1cc5f668ef5cbd7d370ac64e0e40c. The older repeated video-write delays in that issue remain separate. Add a sample-free FATE test that checks start-time discovery and buffering of the first packet without comparing nondeterministic wallclock values. Assisted-by: Fairy --- libavformat/Makefile | 1 + libavformat/demux.c | 3 +- libavformat/tests/wallclock.c | 90 +++++++++++++++++++++++++++++++++++ tests/fate/libavformat.mak | 4 ++ tests/ref/fate/lavf-wallclock | 4 ++ 5 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 libavformat/tests/wallclock.c create mode 100644 tests/ref/fate/lavf-wallclock diff --git a/libavformat/Makefile b/libavformat/Makefile index 437c3edb5a..a319f34815 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -799,6 +799,7 @@ TESTPROGS-$(CONFIG_HTTP_PROTOCOL) += http TESTPROGS-$(CONFIG_NETWORK) += noproxy TESTPROGS-$(CONFIG_SRTP) += srtp TESTPROGS-$(CONFIG_IMF_DEMUXER) += imf +TESTPROGS-$(CONFIG_PCM_MULAW_DEMUXER) += wallclock TESTPROGS-$(CONFIG_OGG_MUXER) += ogg_pages TOOLS = aviocat \ diff --git a/libavformat/demux.c b/libavformat/demux.c index 97eaa5f9bc..49ead83c6a 100644 --- a/libavformat/demux.c +++ b/libavformat/demux.c @@ -2766,7 +2766,8 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) extract_extradata_check(st)) break; if (sti->first_dts == AV_NOPTS_VALUE && - (!(ic->iformat->flags & AVFMT_NOTIMESTAMPS) || sti->need_parsing == AVSTREAM_PARSE_FULL_RAW) && + (!(ic->iformat->flags & AVFMT_NOTIMESTAMPS) || ic->use_wallclock_as_timestamps || + sti->need_parsing == AVSTREAM_PARSE_FULL_RAW) && sti->codec_info_nb_frames < ((st->disposition & AV_DISPOSITION_ATTACHED_PIC) ? 1 : ic->max_ts_probe) && (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)) diff --git a/libavformat/tests/wallclock.c b/libavformat/tests/wallclock.c new file mode 100644 index 0000000000..8983beb62b --- /dev/null +++ b/libavformat/tests/wallclock.c @@ -0,0 +1,90 @@ +/* + * Wallclock timestamp probing test + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <string.h> + +#include "libavutil/mem.h" +#include "libavformat/avformat.h" + +static int read_packet(void *opaque, uint8_t *buf, int size) +{ + int *remaining = opaque; + + if (!*remaining) + return AVERROR_EOF; + size = FFMIN(size, *remaining); + memset(buf, 0xff, size); + *remaining -= size; + return size; +} + +int main(void) +{ + AVFormatContext *fmt = avformat_alloc_context(); + AVIOContext *io = NULL; + AVDictionary *opts = NULL; + AVPacket *pkt = av_packet_alloc(); + uint8_t *buffer = NULL; + int remaining = 4096; + int ret = AVERROR(ENOMEM); + + if (!fmt || !pkt) + goto end; + buffer = av_malloc(4096); + if (!buffer) + goto end; + io = avio_alloc_context(buffer, 4096, 0, &remaining, read_packet, NULL, NULL); + if (!io) + goto end; + buffer = NULL; + fmt->pb = io; + fmt->use_wallclock_as_timestamps = 1; + ret = av_dict_set(&opts, "sample_rate", "8000", 0); + if (ret < 0) + goto end; + ret = avformat_open_input(&fmt, NULL, av_find_input_format("mulaw"), &opts); + if (ret < 0) + goto end; + ret = avformat_find_stream_info(fmt, NULL); + if (ret < 0) + goto end; + + /* Print properties, not the nondeterministic wallclock values. */ + printf("format start time known: %d\n", fmt->start_time != AV_NOPTS_VALUE); + printf("stream start time known: %d\n", fmt->streams[0]->start_time != AV_NOPTS_VALUE); + + ret = av_read_frame(fmt, pkt); + if (ret < 0) + goto end; + printf("first packet PTS known: %d\n", pkt->pts != AV_NOPTS_VALUE); + printf("first packet matches stream start: %d\n", + pkt->pts == fmt->streams[0]->start_time); + ret = 0; +end: + av_packet_free(&pkt); + av_dict_free(&opts); + avformat_close_input(&fmt); + if (io) + av_freep(&io->buffer); + avio_context_free(&io); + av_free(buffer); + return ret < 0; +} diff --git a/tests/fate/libavformat.mak b/tests/fate/libavformat.mak index f306e19245..7abe3bec51 100644 --- a/tests/fate/libavformat.mak +++ b/tests/fate/libavformat.mak @@ -65,6 +65,10 @@ fate-seek_utils: libavformat/tests/seek_utils$(EXESUF) fate-seek_utils: CMD = run libavformat/tests/seek_utils$(EXESUF) fate-seek_utils: CMP = null +FATE_LIBAVFORMAT-$(call DEMDEC, PCM_MULAW, PCM_MULAW) += fate-lavf-wallclock +fate-lavf-wallclock: libavformat/tests/wallclock$(EXESUF) +fate-lavf-wallclock: CMD = run libavformat/tests/wallclock$(EXESUF) + FATE_LIBAVFORMAT += $(FATE_LIBAVFORMAT-yes) FATE-$(CONFIG_AVFORMAT) += $(FATE_LIBAVFORMAT) fate-libavformat: $(FATE_LIBAVFORMAT) diff --git a/tests/ref/fate/lavf-wallclock b/tests/ref/fate/lavf-wallclock new file mode 100644 index 0000000000..5cfc6ae435 --- /dev/null +++ b/tests/ref/fate/lavf-wallclock @@ -0,0 +1,4 @@ +format start time known: 1 +stream start time known: 1 +first packet PTS known: 1 +first packet matches stream start: 1 -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
