Commit ed1c9f553a for ffmpeg

commit ed1c9f553a2ec35be30a5dc7a043189bbf9673ba
Author: Michael Niedermayer <michael@niedermayer.cc>
Date:   Fri Nov 6 03:35:38 2026 +0100

    avfilter/avf_concat: send the padding silence one frame at a time

    Fixes: OOM
    Fixes: rYbksRctTGL7
    Fixes: swarm-Unbounded-audio-silence-allocation-driven-by-crafted-PTS-c5e945

    Found-by: zhang xingxing <2388969553@qq.com>

    OOM Replicated through UnModified FFmpeg with RSS monitoring

    Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>

diff --git a/libavfilter/avf_concat.c b/libavfilter/avf_concat.c
index 00855d8888..63ffc70afd 100644
--- a/libavfilter/avf_concat.c
+++ b/libavfilter/avf_concat.c
@@ -248,12 +248,21 @@ static void find_next_delta_ts(AVFilterContext *ctx, int64_t *seg_delta)
     *seg_delta = pts;
 }

+static int silence_pending(AVFilterContext *ctx, unsigned out_no)
+{
+    ConcatContext *cat = ctx->priv;
+
+    return cat->silence[out_no].nb_samples &&
+           !ff_outlink_get_status(ctx->outputs[out_no]);
+}
+
 static int queue_silence(AVFilterContext *ctx, unsigned in_no, unsigned out_no,
                          int64_t seg_delta)
 {
     ConcatContext *cat = ctx->priv;
     AVFilterLink *outlink = ctx->outputs[out_no];
     struct concat_silence *silence = &cat->silence[out_no];
+    int64_t nb_samples;
     AVRational rate_tb = { 1, ctx->inputs[in_no]->sample_rate };

     if (!rate_tb.den)
@@ -262,9 +271,12 @@ static int queue_silence(AVFilterContext *ctx, unsigned in_no, unsigned out_no,
         return AVERROR_INVALIDDATA;
     if (seg_delta < cat->in[in_no].pts)
         return AVERROR_INVALIDDATA;
-    silence->nb_samples = av_rescale_q(seg_delta - cat->in[in_no].pts,
-                                       outlink->time_base, rate_tb);
-    silence->end_pts    = cat->delta_ts;
+    nb_samples = av_rescale_q(seg_delta - cat->in[in_no].pts,
+                              outlink->time_base, rate_tb);
+    if (nb_samples < 0 || nb_samples > INT64_MAX - silence->nb_samples)
+        return AVERROR_INVALIDDATA;
+    silence->nb_samples += nb_samples;
+    silence->end_pts     = cat->delta_ts;
     return 0;
 }

@@ -310,11 +322,6 @@ static int flush_segment(AVFilterContext *ctx)
                                 seg_delta);
             if (ret < 0)
                 return ret;
-            while (cat->silence[str].nb_samples) {
-                ret = send_silence(ctx, str);
-                if (ret < 0)
-                    return ret;
-            }
         }
     }
     return 0;
@@ -398,6 +405,8 @@ static int activate(AVFilterContext *ctx)
     /* Forward available frames */
     if (cat->cur_idx < ctx->nb_inputs) {
         for (i = 0; i < ctx->nb_outputs; i++) {
+            if (silence_pending(ctx, i))
+                continue;
             ret = ff_inlink_consume_frame(ctx->inputs[cat->cur_idx + i], &frame);
             if (ret < 0)
                 return ret;
@@ -413,6 +422,8 @@ static int activate(AVFilterContext *ctx)
         for (i = 0; i < ctx->nb_outputs; i++) {
             AVFilterLink *inlink = ctx->inputs[cat->cur_idx + i];

+            if (silence_pending(ctx, i))
+                continue;
             ret = ff_inlink_acknowledge_status(inlink, &status, &pts);
             /* TODO use pts */
             if (ret > 0) {
@@ -437,10 +448,15 @@ static int activate(AVFilterContext *ctx)
     ret = FFERROR_NOT_READY;
     for (i = 0; i < ctx->nb_outputs; i++) {
         if (ff_outlink_frame_wanted(ctx->outputs[i])) {
+            if (silence_pending(ctx, i))
+                return send_silence(ctx, i);
             if (cat->in[cat->cur_idx + i].eof) {
-                for (j = 0; j < ctx->nb_outputs; j++)
+                for (j = 0; j < ctx->nb_outputs; j++) {
+                    if (silence_pending(ctx, j))
+                        return send_silence(ctx, j);
                     if (!cat->in[cat->cur_idx + j].eof)
                         ff_inlink_request_frame(ctx->inputs[cat->cur_idx + j]);
+                }
                 return 0;
             } else {
                 ff_inlink_request_frame(ctx->inputs[cat->cur_idx + i]);