avfiltergraph: Properly handle memory allocation failure
[ffmpeg.git] / libavfilter / avf_concat.c
index 8812c9e..4b02ac8 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 #include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
 #include "libavutil/channel_layout.h"
 #include "libavutil/opt.h"
 #include "avfilter.h"
@@ -67,8 +68,8 @@ static const AVOption concat_options[] = {
       AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, A|F},
     { "unsafe", "enable unsafe mode",
       OFFSET(unsafe),
-      AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, A|A|F},
-    { 0 }
+      AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, V|A|F},
+    { NULL }
 };
 
 AVFILTER_DEFINE_CLASS(concat);
@@ -77,8 +78,8 @@ static int query_formats(AVFilterContext *ctx)
 {
     ConcatContext *cat = ctx->priv;
     unsigned type, nb_str, idx0 = 0, idx, str, seg;
-    AVFilterFormats *formats, *rates;
-    AVFilterChannelLayouts *layouts;
+    AVFilterFormats *formats, *rates = NULL;
+    AVFilterChannelLayouts *layouts = NULL;
 
     for (type = 0; type < TYPE_ALL; type++) {
         nb_str = cat->nb_streams[type];
@@ -133,10 +134,13 @@ static int config_output(AVFilterLink *outlink)
     outlink->format              = inlink->format;
     for (seg = 1; seg < cat->nb_segments; seg++) {
         inlink = ctx->inputs[in_no += ctx->nb_outputs];
+        if (!outlink->sample_aspect_ratio.num)
+            outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
         /* possible enhancement: unsafe mode, do not check */
         if (outlink->w                       != inlink->w                       ||
             outlink->h                       != inlink->h                       ||
-            outlink->sample_aspect_ratio.num != inlink->sample_aspect_ratio.num ||
+            outlink->sample_aspect_ratio.num != inlink->sample_aspect_ratio.num &&
+                                                inlink->sample_aspect_ratio.num ||
             outlink->sample_aspect_ratio.den != inlink->sample_aspect_ratio.den) {
             av_log(ctx, AV_LOG_ERROR, "Input link %s parameters "
                    "(size %dx%d, SAR %d:%d) do not match the corresponding "
@@ -155,8 +159,7 @@ static int config_output(AVFilterLink *outlink)
     return 0;
 }
 
-static void push_frame(AVFilterContext *ctx, unsigned in_no,
-                       AVFilterBufferRef *buf)
+static int push_frame(AVFilterContext *ctx, unsigned in_no, AVFrame *buf)
 {
     ConcatContext *cat = ctx->priv;
     unsigned out_no = in_no % ctx->nb_outputs;
@@ -170,7 +173,7 @@ static void push_frame(AVFilterContext *ctx, unsigned in_no,
     /* add duration to input PTS */
     if (inlink->sample_rate)
         /* use number of audio samples */
-        in->pts += av_rescale_q(buf->audio->nb_samples,
+        in->pts += av_rescale_q(buf->nb_samples,
                                 (AVRational){ 1, inlink->sample_rate },
                                 outlink->time_base);
     else if (in->nb_frames >= 2)
@@ -178,19 +181,10 @@ static void push_frame(AVFilterContext *ctx, unsigned in_no,
         in->pts = av_rescale(in->pts, in->nb_frames, in->nb_frames - 1);
 
     buf->pts += cat->delta_ts;
-    switch (buf->type) {
-    case AVMEDIA_TYPE_VIDEO:
-        ff_start_frame(outlink, buf);
-        ff_draw_slice(outlink, 0, outlink->h, 1);
-        ff_end_frame(outlink);
-        break;
-    case AVMEDIA_TYPE_AUDIO:
-        ff_filter_samples(outlink, buf);
-        break;
-    }
+    return ff_filter_frame(outlink, buf);
 }
 
-static void process_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
+static int process_frame(AVFilterLink *inlink, AVFrame *buf)
 {
     AVFilterContext *ctx  = inlink->dst;
     ConcatContext *cat    = ctx->priv;
@@ -199,55 +193,36 @@ static void process_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
     if (in_no < cat->cur_idx) {
         av_log(ctx, AV_LOG_ERROR, "Frame after EOF on input %s\n",
                ctx->input_pads[in_no].name);
-        avfilter_unref_buffer(buf);
+        av_frame_free(&buf);
     } else if (in_no >= cat->cur_idx + ctx->nb_outputs) {
         ff_bufqueue_add(ctx, &cat->in[in_no].queue, buf);
     } else {
-        push_frame(ctx, in_no, buf);
+        return push_frame(ctx, in_no, buf);
     }
+    return 0;
 }
 
-static AVFilterBufferRef *get_video_buffer(AVFilterLink *inlink, int perms,
-                                           int w, int h)
+static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h)
 {
     AVFilterContext *ctx = inlink->dst;
     unsigned in_no = FF_INLINK_IDX(inlink);
     AVFilterLink *outlink = ctx->outputs[in_no % ctx->nb_outputs];
 
-    return ff_get_video_buffer(outlink, perms, w, h);
+    return ff_get_video_buffer(outlink, w, h);
 }
 
-static AVFilterBufferRef *get_audio_buffer(AVFilterLink *inlink, int perms,
-                                           int nb_samples)
+static AVFrame *get_audio_buffer(AVFilterLink *inlink, int nb_samples)
 {
     AVFilterContext *ctx = inlink->dst;
     unsigned in_no = FF_INLINK_IDX(inlink);
     AVFilterLink *outlink = ctx->outputs[in_no % ctx->nb_outputs];
 
-    return ff_get_audio_buffer(outlink, perms, nb_samples);
-}
-
-static int start_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
-{
-    return 0;
-}
-
-static int draw_slice(AVFilterLink *inlink, int y, int h, int dir)
-{
-    return 0;
-}
-
-static int end_frame(AVFilterLink *inlink)
-{
-    process_frame(inlink, inlink->cur_buf);
-    inlink->cur_buf = NULL;
-    return 0;
+    return ff_get_audio_buffer(outlink, nb_samples);
 }
 
-static int filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf)
+static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
 {
-    process_frame(inlink, buf);
-    return 0; /* enhancement: handle error return */
+    return process_frame(inlink, buf);
 }
 
 static void close_input(AVFilterContext *ctx, unsigned in_no)
@@ -260,7 +235,7 @@ static void close_input(AVFilterContext *ctx, unsigned in_no)
            ctx->input_pads[in_no].name, cat->nb_in_active);
 }
 
-static void find_next_delta_ts(AVFilterContext *ctx)
+static void find_next_delta_ts(AVFilterContext *ctx, int64_t *seg_delta)
 {
     ConcatContext *cat = ctx->priv;
     unsigned i = cat->cur_idx;
@@ -271,44 +246,51 @@ static void find_next_delta_ts(AVFilterContext *ctx)
     for (; i < imax; i++)
         pts = FFMAX(pts, cat->in[i].pts);
     cat->delta_ts += pts;
+    *seg_delta = pts;
 }
 
-static void send_silence(AVFilterContext *ctx, unsigned in_no, unsigned out_no)
+static int send_silence(AVFilterContext *ctx, unsigned in_no, unsigned out_no,
+                        int64_t seg_delta)
 {
     ConcatContext *cat = ctx->priv;
     AVFilterLink *outlink = ctx->outputs[out_no];
-    int64_t base_pts = cat->in[in_no].pts + cat->delta_ts;
+    int64_t base_pts = cat->in[in_no].pts + cat->delta_ts - seg_delta;
     int64_t nb_samples, sent = 0;
-    int frame_nb_samples;
+    int frame_nb_samples, ret;
     AVRational rate_tb = { 1, ctx->inputs[in_no]->sample_rate };
-    AVFilterBufferRef *buf;
+    AVFrame *buf;
     int nb_channels = av_get_channel_layout_nb_channels(outlink->channel_layout);
 
     if (!rate_tb.den)
-        return;
-    nb_samples = av_rescale_q(cat->delta_ts - base_pts,
+        return AVERROR_BUG;
+    nb_samples = av_rescale_q(seg_delta - cat->in[in_no].pts,
                               outlink->time_base, rate_tb);
     frame_nb_samples = FFMAX(9600, rate_tb.den / 5); /* arbitrary */
     while (nb_samples) {
         frame_nb_samples = FFMIN(frame_nb_samples, nb_samples);
-        buf = ff_get_audio_buffer(outlink, AV_PERM_WRITE, frame_nb_samples);
+        buf = ff_get_audio_buffer(outlink, frame_nb_samples);
         if (!buf)
-            return;
+            return AVERROR(ENOMEM);
         av_samples_set_silence(buf->extended_data, 0, frame_nb_samples,
                                nb_channels, outlink->format);
         buf->pts = base_pts + av_rescale_q(sent, rate_tb, outlink->time_base);
-        ff_filter_samples(outlink, buf);
+        ret = ff_filter_frame(outlink, buf);
+        if (ret < 0)
+            return ret;
         sent       += frame_nb_samples;
         nb_samples -= frame_nb_samples;
     }
+    return 0;
 }
 
-static void flush_segment(AVFilterContext *ctx)
+static int flush_segment(AVFilterContext *ctx)
 {
+    int ret;
     ConcatContext *cat = ctx->priv;
     unsigned str, str_max;
+    int64_t seg_delta;
 
-    find_next_delta_ts(ctx);
+    find_next_delta_ts(ctx, &seg_delta);
     cat->cur_idx += ctx->nb_outputs;
     cat->nb_in_active = ctx->nb_outputs;
     av_log(ctx, AV_LOG_VERBOSE, "Segment finished at pts=%"PRId64"\n",
@@ -318,15 +300,24 @@ static void flush_segment(AVFilterContext *ctx)
         /* pad audio streams with silence */
         str = cat->nb_streams[AVMEDIA_TYPE_VIDEO];
         str_max = str + cat->nb_streams[AVMEDIA_TYPE_AUDIO];
-        for (; str < str_max; str++)
-            send_silence(ctx, cat->cur_idx - ctx->nb_outputs + str, str);
+        for (; str < str_max; str++) {
+            ret = send_silence(ctx, cat->cur_idx - ctx->nb_outputs + str, str,
+                               seg_delta);
+            if (ret < 0)
+                return ret;
+        }
         /* flush queued buffers */
         /* possible enhancement: flush in PTS order */
         str_max = cat->cur_idx + ctx->nb_outputs;
-        for (str = cat->cur_idx; str < str_max; str++)
-            while (cat->in[str].queue.available)
-                push_frame(ctx, str, ff_bufqueue_get(&cat->in[str].queue));
+        for (str = cat->cur_idx; str < str_max; str++) {
+            while (cat->in[str].queue.available) {
+                ret = push_frame(ctx, str, ff_bufqueue_get(&cat->in[str].queue));
+                if (ret < 0)
+                    return ret;
+            }
+        }
     }
+    return 0;
 }
 
 static int request_frame(AVFilterLink *outlink)
@@ -360,25 +351,17 @@ static int request_frame(AVFilterLink *outlink)
             else if (ret < 0)
                 return ret;
         }
-        flush_segment(ctx);
+        ret = flush_segment(ctx);
+        if (ret < 0)
+            return ret;
         in_no += ctx->nb_outputs;
     }
 }
 
-static av_cold int init(AVFilterContext *ctx, const char *args)
+static av_cold int init(AVFilterContext *ctx)
 {
     ConcatContext *cat = ctx->priv;
-    int ret;
     unsigned seg, type, str;
-    char name[32];
-
-    cat->class = &concat_class;
-    av_opt_set_defaults(cat);
-    ret = av_set_options_string(cat, args, "=", ":");
-    if (ret < 0) {
-        av_log(ctx, AV_LOG_ERROR, "Error parsing options: '%s'\n", args);
-        return ret;
-    }
 
     /* create input pads */
     for (seg = 0; seg < cat->nb_segments; seg++) {
@@ -386,19 +369,11 @@ static av_cold int init(AVFilterContext *ctx, const char *args)
             for (str = 0; str < cat->nb_streams[type]; str++) {
                 AVFilterPad pad = {
                     .type             = type,
-                    .min_perms        = AV_PERM_READ | AV_PERM_PRESERVE,
                     .get_video_buffer = get_video_buffer,
                     .get_audio_buffer = get_audio_buffer,
+                    .filter_frame     = filter_frame,
                 };
-                snprintf(name, sizeof(name), "in%d:%c%d", seg, "va"[type], str);
-                pad.name = av_strdup(name);
-                if (type == AVMEDIA_TYPE_VIDEO) {
-                    pad.start_frame = start_frame;
-                    pad.draw_slice  = draw_slice;
-                    pad.end_frame   = end_frame;
-                } else {
-                    pad.filter_samples = filter_samples;
-                }
+                pad.name = av_asprintf("in%d:%c%d", seg, "va"[type], str);
                 ff_insert_inpad(ctx, ctx->nb_inputs, &pad);
             }
         }
@@ -411,8 +386,7 @@ static av_cold int init(AVFilterContext *ctx, const char *args)
                 .config_props  = config_output,
                 .request_frame = request_frame,
             };
-            snprintf(name, sizeof(name), "out:%c%d", "va"[type], str);
-            pad.name = av_strdup(name);
+            pad.name = av_asprintf("out:%c%d", "va"[type], str);
             ff_insert_outpad(ctx, ctx->nb_outputs, &pad);
         }
     }
@@ -445,7 +419,8 @@ AVFilter avfilter_avf_concat = {
     .uninit        = uninit,
     .query_formats = query_formats,
     .priv_size     = sizeof(ConcatContext),
-    .inputs        = (const AVFilterPad[]) { { .name = NULL } },
-    .outputs       = (const AVFilterPad[]) { { .name = NULL } },
+    .inputs        = NULL,
+    .outputs       = NULL,
     .priv_class    = &concat_class,
+    .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_DYNAMIC_OUTPUTS,
 };