PR #25312 opened by Marti2203 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25312 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25312.patch
The smoothstreaming muxer drops two kinds of errors. `ism_write()`, the write callback of the per-stream AVIOContext, ignores the return value of `ffurl_write()`. When a write fails (ENOSPC, EIO, a quota), every fragment is silently truncated, the Manifest still lists all of them and ffmpeg exits 0. A file size limit reproduces it without filling a disk: ``` (trap '' XFSZ; ulimit -f 16; ffmpeg -f lavfi -i sine=d=30 -c:a aac -b:a 128k -f smoothstreaming out.ism) exit status 0 16384 Fragments(audio=0) 16384 Fragments(audio=50155103) ... (every fragment cut at 16 KiB) chunks in Manifest: 7 ``` The mp4 muxer under the same limit reports "File too large" and fails. The first patch returns the error from `ism_write()`, checks the AVIOContext error after flushing a fragment in `ism_flush()`, and returns the result of the final flush from `ism_write_trailer()`, which so far always returned 0. With it the same command fails with "Error submitting a packet to the muxer: File too large" (exit status 229). `ism_flush()` also ignores the return value of `add_fragment()`. If its allocation fails, the fragment has already been renamed into place but is not listed, so it is missing from the Manifest and ffmpeg still exits 0 (7 fragment files, 6 chunks in the Manifest, with one injected failure of the Fragment allocation via an LD_PRELOAD shim). The second patch returns the error, so muxing fails with "Cannot allocate memory". Testing: the reproducer script (file size limit and allocation failure injection) fails before and reports the error after; normal output, also with `window_size` and `remove_at_exit`, is byte-identical before and after; fate-lavf-ismv, the mov/mp4/flv FATE tests and fate-source pass. Write and allocation failures are not something FATE can exercise, so there is no new test. Caveats: after a failure the files written so far stay on disk (the fragment being flushed remains as `temp`, or as an orphan fragment in the allocation case); the change only makes the failure visible. `copy_moof()`'s return value is still ignored, which I left for a separate change. This issue was found by an LLM-Enhanced Static Analysis Engine and verified manually. 🤖 Generated with [Claude Code](https://claude.com/claude-code) From bdbb51dc03d4ba66843b054ef6f10765b18b6a14 Mon Sep 17 00:00:00 2001 From: Martin Mirchev <[email protected]> Date: Wed, 21 Oct 2026 19:10:19 +0800 Subject: [PATCH 1/2] avformat/smoothstreamingenc: propagate write errors ism_write() discarded the return value of ffurl_write(), so a failed write (ENOSPC, EIO, EFBIG, ...) silently truncated the fragment files, while the Manifest still listed every fragment and ffmpeg exited successfully. Return the error from the write callback, check the AVIOContext error after flushing a fragment in ism_flush(), and return the result of the final ism_flush() from ism_write_trailer() so that the failure is reported. Signed-off-by: Martin Mirchev <[email protected]> --- libavformat/smoothstreamingenc.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/libavformat/smoothstreamingenc.c b/libavformat/smoothstreamingenc.c index 118761bfc0b..d77df1a563d 100644 --- a/libavformat/smoothstreamingenc.c +++ b/libavformat/smoothstreamingenc.c @@ -80,10 +80,17 @@ typedef struct SmoothStreamingContext { static int ism_write(void *opaque, const uint8_t *buf, int buf_size) { OutputStream *os = opaque; - if (os->out) - ffurl_write(os->out, buf, buf_size); - if (os->out2) - ffurl_write(os->out2, buf, buf_size); + int ret; + if (os->out) { + ret = ffurl_write(os->out, buf, buf_size); + if (ret < 0) + return ret; + } + if (os->out2) { + ret = ffurl_write(os->out2, buf, buf_size); + if (ret < 0) + return ret; + } os->cur_pos += buf_size; if (os->cur_pos >= os->tail_pos) os->tail_pos = os->cur_pos; @@ -510,9 +517,15 @@ static int ism_flush(AVFormatContext *s, int final) if (ret < 0) break; os->cur_start_pos = os->tail_pos; - av_write_frame(os->ctx, NULL); + ret = av_write_frame(os->ctx, NULL); avio_flush(os->ctx->pb); os->packets_written = 0; + if (ret >= 0) + ret = os->ctx->pb->error; + if (ret < 0) { + ffurl_closep(&os->out); + break; + } if (!os->out || os->tail_out) return AVERROR(EIO); @@ -605,7 +618,7 @@ static int ism_write_packet(AVFormatContext *s, AVPacket *pkt) static int ism_write_trailer(AVFormatContext *s) { SmoothStreamingContext *c = s->priv_data; - ism_flush(s, 1); + int ret = ism_flush(s, 1); if (c->remove_at_exit) { char filename[1024]; @@ -614,7 +627,7 @@ static int ism_write_trailer(AVFormatContext *s) rmdir(s->url); } - return 0; + return ret; } #define OFFSET(x) offsetof(SmoothStreamingContext, x) -- 2.54.0 From 9692a6e1b51aee45d0594911eaecad83814ed36f Mon Sep 17 00:00:00 2001 From: Martin Mirchev <[email protected]> Date: Wed, 21 Oct 2026 19:10:30 +0800 Subject: [PATCH 2/2] avformat/smoothstreamingenc: check the return value of add_fragment() If add_fragment() fails to allocate, the fragment has already been renamed into place but is not added to the list, so it is missing from the Manifest while muxing carries on and ffmpeg exits successfully. Return the error instead. Signed-off-by: Martin Mirchev <[email protected]> --- libavformat/smoothstreamingenc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libavformat/smoothstreamingenc.c b/libavformat/smoothstreamingenc.c index d77df1a563d..0fc70b5832c 100644 --- a/libavformat/smoothstreamingenc.c +++ b/libavformat/smoothstreamingenc.c @@ -559,8 +559,10 @@ static int ism_flush(AVFormatContext *s, int final) ret = ff_rename(filename, target_filename, s); if (ret < 0) break; - add_fragment(os, target_filename, header_filename, start_ts, duration, - os->cur_start_pos, size); + ret = add_fragment(os, target_filename, header_filename, start_ts, duration, + os->cur_start_pos, size); + if (ret < 0) + break; } if (c->window_size || (final && c->remove_at_exit)) { -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
