lavfi/select: add support for dynamic number of outputs
authorStefano Sabatini <stefasab@gmail.com>
Mon, 8 Apr 2013 10:58:56 +0000 (12:58 +0200)
committerStefano Sabatini <stefasab@gmail.com>
Wed, 17 Apr 2013 20:34:05 +0000 (22:34 +0200)
doc/filters.texi
libavfilter/f_select.c
libavfilter/version.h

index bc78492..adf6000 100644 (file)
@@ -6721,10 +6721,20 @@ This filter accepts the following options:
 @table @option
 
 @item expr, e
-An expression, which is evaluated for each input frame. If the expression is
-evaluated to a non-zero value, the frame is selected and passed to the output,
-otherwise it is discarded.
+Set expression, which is evaluated for each input frame.
 
+If the expression is evaluated to zero, the frame is discarded.
+
+If the evaluation result is negative or NaN, the frame is sent to the
+first output; otherwise it is sent to the output with index
+@code{ceil(val)-1}, assuming that the input index starts from 0.
+
+For example a value of @code{1.2} corresponds to the output with index
+@code{ceil(1.2)-1 = 2-1 = 1}, that is the second output.
+
+@item outputs, n
+Set the number of outputs. The output to which to send the selected
+frame is based on the result of the evaluation. Default value is 1.
 @end table
 
 The expression can contain the following constants:
@@ -6878,6 +6888,12 @@ ffmpeg -i video.avi -vf select='gt(scene\,0.4)',scale=160:120,tile -frames:v 1 p
 
 Comparing @var{scene} against a value between 0.3 and 0.5 is generally a sane
 choice.
+
+@item
+Send even and odd frames to separate outputs, and compose them:
+@example
+select=n=2:e='mod(n, 2)+1' [odd][even]; [odd] pad=h=2*ih [tmp]; [tmp][even] overlay=y=h
+@end example
 @end itemize
 
 @section asendcmd, sendcmd
index ca9bdc9..9413f05 100644 (file)
@@ -23,6 +23,7 @@
  * filter for selecting which frame passes in the filterchain
  */
 
+#include "libavutil/avstring.h"
 #include "libavutil/eval.h"
 #include "libavutil/fifo.h"
 #include "libavutil/internal.h"
@@ -136,13 +137,16 @@ typedef struct {
 #endif
     AVFrame *prev_picref; ///< previous frame                            (scene detect only)
     double select;
+    int select_out;                 ///< mark the selected output pad index
+    int nb_outputs;
 } SelectContext;
 
+static int request_frame(AVFilterLink *outlink);
 
 static av_cold int init(AVFilterContext *ctx)
 {
     SelectContext *select = ctx->priv;
-    int ret;
+    int i, ret;
 
     if ((ret = av_expr_parse(&select->expr, select->expr_str,
                              var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
@@ -152,6 +156,17 @@ static av_cold int init(AVFilterContext *ctx)
     }
     select->do_scene_detect = !!strstr(select->expr_str, "scene");
 
+    for (i = 0; i < select->nb_outputs; i++) {
+        AVFilterPad pad = { 0 };
+
+        pad.name = av_asprintf("output%d", i);
+        if (!pad.name)
+            return AVERROR(ENOMEM);
+        pad.type = ctx->filter->inputs[0].type;
+        pad.request_frame = request_frame;
+        ff_insert_outpad(ctx, i, &pad);
+    }
+
     return 0;
 }
 
@@ -308,7 +323,15 @@ static void select_frame(AVFilterContext *ctx, AVFrame *frame)
         break;
     }
 
-    av_log(inlink->dst, AV_LOG_DEBUG, " -> select:%f\n", res);
+    if (res == 0) {
+        select->select_out = -1; /* drop */
+    } else if (isnan(res) || res < 0) {
+        select->select_out = 0; /* first output */
+    } else {
+        select->select_out = FFMIN(ceilf(res)-1, select->nb_outputs-1); /* other outputs */
+    }
+
+    av_log(inlink->dst, AV_LOG_DEBUG, " -> select:%f select_out:%d\n", res, select->select_out);
 
     if (res) {
         select->var_values[VAR_PREV_SELECTED_N]   = select->var_values[VAR_N];
@@ -326,11 +349,12 @@ static void select_frame(AVFilterContext *ctx, AVFrame *frame)
 
 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
 {
-    SelectContext *select = inlink->dst->priv;
+    AVFilterContext *ctx = inlink->dst;
+    SelectContext *select = ctx->priv;
 
-    select_frame(inlink->dst, frame);
+    select_frame(ctx, frame);
     if (select->select)
-        return ff_filter_frame(inlink->dst->outputs[0], frame);
+        return ff_filter_frame(ctx->outputs[select->select_out], frame);
 
     av_frame_free(&frame);
     return 0;
@@ -341,13 +365,13 @@ static int request_frame(AVFilterLink *outlink)
     AVFilterContext *ctx = outlink->src;
     SelectContext *select = ctx->priv;
     AVFilterLink *inlink = outlink->src->inputs[0];
-    select->select = 0;
+    int out_no = FF_OUTLINK_IDX(outlink);
 
     do {
         int ret = ff_request_frame(inlink);
         if (ret < 0)
             return ret;
-    } while (!select->select);
+    } while (select->select_out != out_no);
 
     return 0;
 }
@@ -355,10 +379,14 @@ static int request_frame(AVFilterLink *outlink)
 static av_cold void uninit(AVFilterContext *ctx)
 {
     SelectContext *select = ctx->priv;
+    int i;
 
     av_expr_free(select->expr);
     select->expr = NULL;
 
+    for (i = 0; i < ctx->nb_outputs; i++)
+        av_freep(&ctx->output_pads[i].name);
+
 #if CONFIG_AVCODEC
     if (select->do_scene_detect) {
         av_frame_free(&select->prev_picref);
@@ -393,6 +421,8 @@ static int query_formats(AVFilterContext *ctx)
 static const AVOption aselect_options[] = {
     { "expr", "An expression to use for selecting frames", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "1" }, .flags = AFLAGS },
     { "e",    "An expression to use for selecting frames", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "1" }, .flags = AFLAGS },
+    { "outputs", "set the number of outputs", OFFSET(nb_outputs), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, AFLAGS },
+    { "n",       "set the number of outputs", OFFSET(nb_outputs), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, AFLAGS },
     { NULL },
 };
 AVFILTER_DEFINE_CLASS(aselect);
@@ -424,14 +454,6 @@ static const AVFilterPad avfilter_af_aselect_inputs[] = {
     { NULL }
 };
 
-static const AVFilterPad avfilter_af_aselect_outputs[] = {
-    {
-        .name = "default",
-        .type = AVMEDIA_TYPE_AUDIO,
-    },
-    { NULL }
-};
-
 AVFilter avfilter_af_aselect = {
     .name      = "aselect",
     .description = NULL_IF_CONFIG_SMALL("Select audio frames to pass in output."),
@@ -439,8 +461,8 @@ AVFilter avfilter_af_aselect = {
     .uninit    = uninit,
     .priv_size = sizeof(SelectContext),
     .inputs    = avfilter_af_aselect_inputs,
-    .outputs   = avfilter_af_aselect_outputs,
     .priv_class = &aselect_class,
+    .flags     = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
 };
 #endif /* CONFIG_ASELECT_FILTER */
 
@@ -451,6 +473,8 @@ AVFilter avfilter_af_aselect = {
 static const AVOption select_options[] = {
     { "expr", "An expression to use for selecting frames", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "1" }, .flags = FLAGS },
     { "e",    "An expression to use for selecting frames", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "1" }, .flags = FLAGS },
+    { "outputs", "set the number of outputs", OFFSET(nb_outputs), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, FLAGS },
+    { "n",       "set the number of outputs", OFFSET(nb_outputs), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, FLAGS },
     { NULL },
 };
 
@@ -483,15 +507,6 @@ static const AVFilterPad avfilter_vf_select_inputs[] = {
     { NULL }
 };
 
-static const AVFilterPad avfilter_vf_select_outputs[] = {
-    {
-        .name          = "default",
-        .type          = AVMEDIA_TYPE_VIDEO,
-        .request_frame = request_frame,
-    },
-    { NULL }
-};
-
 AVFilter avfilter_vf_select = {
     .name      = "select",
     .description = NULL_IF_CONFIG_SMALL("Select video frames to pass in output."),
@@ -503,6 +518,6 @@ AVFilter avfilter_vf_select = {
     .priv_class = &select_class,
 
     .inputs    = avfilter_vf_select_inputs,
-    .outputs   = avfilter_vf_select_outputs,
+    .flags     = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
 };
 #endif /* CONFIG_SELECT_FILTER */
index b396075..ba1ec0b 100644 (file)
@@ -30,7 +30,7 @@
 
 #define LIBAVFILTER_VERSION_MAJOR  3
 #define LIBAVFILTER_VERSION_MINOR  56
-#define LIBAVFILTER_VERSION_MICRO 102
+#define LIBAVFILTER_VERSION_MICRO 103
 
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
                                                LIBAVFILTER_VERSION_MINOR, \