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]

Reply via email to