diff options
author | Stefano Sabatini <stefasab@gmail.com> | 2013-04-08 12:58:56 +0200 |
---|---|---|
committer | Stefano Sabatini <stefasab@gmail.com> | 2013-04-17 22:34:05 +0200 |
commit | dcc1b3236168cc7baf97ae9074b9d1b90a03be9b (patch) | |
tree | bc73492f76a6312c297a8b0acf9e247fc0a4d360 | |
parent | 565c50ac7b51112032cd48e81af4560cda72b5b0 (diff) | |
download | ffmpeg-dcc1b3236168cc7baf97ae9074b9d1b90a03be9b.tar.gz |
lavfi/select: add support for dynamic number of outputs
-rw-r--r-- | doc/filters.texi | 22 | ||||
-rw-r--r-- | libavfilter/f_select.c | 67 | ||||
-rw-r--r-- | libavfilter/version.h | 2 |
3 files changed, 61 insertions, 30 deletions
diff --git a/doc/filters.texi b/doc/filters.texi index bc78492e72..adf6000935 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -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 diff --git a/libavfilter/f_select.c b/libavfilter/f_select.c index ca9bdc9533..9413f059c2 100644 --- a/libavfilter/f_select.c +++ b/libavfilter/f_select.c @@ -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 */ diff --git a/libavfilter/version.h b/libavfilter/version.h index b396075566..ba1ec0b786 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -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, \ |