lavfi: mv vf_select.c -> f_select.c
authorStefano Sabatini <stefasab@gmail.com>
Tue, 11 Dec 2012 22:26:01 +0000 (23:26 +0100)
committerStefano Sabatini <stefasab@gmail.com>
Wed, 12 Dec 2012 23:10:43 +0000 (00:10 +0100)
The file now contains also an audio select implementation. Also move the
aselect/select documentation from video filters to the multimedia filters
section.

doc/filters.texi
libavfilter/Makefile
libavfilter/f_select.c [new file with mode: 0644]
libavfilter/vf_select.c [deleted file]

index d7af64a..1043911 100644 (file)
@@ -3506,140 +3506,6 @@ scale='min(500\, iw*3/2):-1'
 @end example
 @end itemize
 
-@section aselect, select
-Select frames to pass in output.
-
-It accepts in input 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.
-
-The expression can contain the following constants:
-
-@table @option
-@item n
-the sequential number of the filtered frame, starting from 0
-
-@item selected_n
-the sequential number of the selected frame, starting from 0
-
-@item prev_selected_n
-the sequential number of the last selected frame, NAN if undefined
-
-@item TB
-timebase of the input timestamps
-
-@item pts
-the PTS (Presentation TimeStamp) of the filtered video frame,
-expressed in @var{TB} units, NAN if undefined
-
-@item t
-the PTS (Presentation TimeStamp) of the filtered video frame,
-expressed in seconds, NAN if undefined
-
-@item prev_pts
-the PTS of the previously filtered video frame, NAN if undefined
-
-@item prev_selected_pts
-the PTS of the last previously filtered video frame, NAN if undefined
-
-@item prev_selected_t
-the PTS of the last previously selected video frame, NAN if undefined
-
-@item start_pts
-the PTS of the first video frame in the video, NAN if undefined
-
-@item start_t
-the time of the first video frame in the video, NAN if undefined
-
-@item pict_type @emph{(video only)}
-the type of the filtered frame, can assume one of the following
-values:
-@table @option
-@item I
-@item P
-@item B
-@item S
-@item SI
-@item SP
-@item BI
-@end table
-
-@item interlace_type @emph{(video only)}
-the frame interlace type, can assume one of the following values:
-@table @option
-@item PROGRESSIVE
-the frame is progressive (not interlaced)
-@item TOPFIRST
-the frame is top-field-first
-@item BOTTOMFIRST
-the frame is bottom-field-first
-@end table
-
-@item consumed_sample_n @emph{(audio only)}
-the number of selected samples before the current frame
-
-@item samples_n @emph{(audio only)}
-the number of samples in the current frame
-
-@item sample_rate @emph{(audio only)}
-the input sample rate
-
-@item key
-1 if the filtered frame is a key-frame, 0 otherwise
-
-@item pos
-the position in the file of the filtered frame, -1 if the information
-is not available (e.g. for synthetic video)
-
-@item scene @emph{(video only)}
-value between 0 and 1 to indicate a new scene; a low value reflects a low
-probability for the current frame to introduce a new scene, while a higher
-value means the current frame is more likely to be one (see the example below)
-
-@end table
-
-The default value of the select expression is "1".
-
-Some examples follow:
-
-@example
-# select all frames in input
-select
-
-# the above is the same as:
-select=1
-
-# skip all frames:
-select=0
-
-# select only I-frames
-select='eq(pict_type\,I)'
-
-# select one frame every 100
-select='not(mod(n\,100))'
-
-# select only frames contained in the 10-20 time interval
-select='gte(t\,10)*lte(t\,20)'
-
-# select only I frames contained in the 10-20 time interval
-select='gte(t\,10)*lte(t\,20)*eq(pict_type\,I)'
-
-# select frames with a minimum distance of 10 seconds
-select='isnan(prev_selected_t)+gte(t-prev_selected_t\,10)'
-
-# use aselect to select only audio frames with samples number > 100
-aselect='gt(samples_n\,100)'
-@end example
-
-Complete example to create a mosaic of the first scenes:
-
-@example
-ffmpeg -i video.avi -vf select='gt(scene\,0.4)',scale=160:120,tile -frames:v 1 preview.png
-@end example
-
-Comparing @var{scene} against a value between 0.3 and 0.5 is generally a sane
-choice.
-
 @section setdar, setsar
 
 The @code{setdar} filter sets the Display Aspect Ratio for the filter
@@ -4768,6 +4634,140 @@ tools.
 
 Below is a description of the currently available multimedia filters.
 
+@section aselect, select
+Select frames to pass in output.
+
+It accepts in input 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.
+
+The expression can contain the following constants:
+
+@table @option
+@item n
+the sequential number of the filtered frame, starting from 0
+
+@item selected_n
+the sequential number of the selected frame, starting from 0
+
+@item prev_selected_n
+the sequential number of the last selected frame, NAN if undefined
+
+@item TB
+timebase of the input timestamps
+
+@item pts
+the PTS (Presentation TimeStamp) of the filtered video frame,
+expressed in @var{TB} units, NAN if undefined
+
+@item t
+the PTS (Presentation TimeStamp) of the filtered video frame,
+expressed in seconds, NAN if undefined
+
+@item prev_pts
+the PTS of the previously filtered video frame, NAN if undefined
+
+@item prev_selected_pts
+the PTS of the last previously filtered video frame, NAN if undefined
+
+@item prev_selected_t
+the PTS of the last previously selected video frame, NAN if undefined
+
+@item start_pts
+the PTS of the first video frame in the video, NAN if undefined
+
+@item start_t
+the time of the first video frame in the video, NAN if undefined
+
+@item pict_type @emph{(video only)}
+the type of the filtered frame, can assume one of the following
+values:
+@table @option
+@item I
+@item P
+@item B
+@item S
+@item SI
+@item SP
+@item BI
+@end table
+
+@item interlace_type @emph{(video only)}
+the frame interlace type, can assume one of the following values:
+@table @option
+@item PROGRESSIVE
+the frame is progressive (not interlaced)
+@item TOPFIRST
+the frame is top-field-first
+@item BOTTOMFIRST
+the frame is bottom-field-first
+@end table
+
+@item consumed_sample_n @emph{(audio only)}
+the number of selected samples before the current frame
+
+@item samples_n @emph{(audio only)}
+the number of samples in the current frame
+
+@item sample_rate @emph{(audio only)}
+the input sample rate
+
+@item key
+1 if the filtered frame is a key-frame, 0 otherwise
+
+@item pos
+the position in the file of the filtered frame, -1 if the information
+is not available (e.g. for synthetic video)
+
+@item scene @emph{(video only)}
+value between 0 and 1 to indicate a new scene; a low value reflects a low
+probability for the current frame to introduce a new scene, while a higher
+value means the current frame is more likely to be one (see the example below)
+
+@end table
+
+The default value of the select expression is "1".
+
+Some examples follow:
+
+@example
+# select all frames in input
+select
+
+# the above is the same as:
+select=1
+
+# skip all frames:
+select=0
+
+# select only I-frames
+select='eq(pict_type\,I)'
+
+# select one frame every 100
+select='not(mod(n\,100))'
+
+# select only frames contained in the 10-20 time interval
+select='gte(t\,10)*lte(t\,20)'
+
+# select only I frames contained in the 10-20 time interval
+select='gte(t\,10)*lte(t\,20)*eq(pict_type\,I)'
+
+# select frames with a minimum distance of 10 seconds
+select='isnan(prev_selected_t)+gte(t-prev_selected_t\,10)'
+
+# use aselect to select only audio frames with samples number > 100
+aselect='gt(samples_n\,100)'
+@end example
+
+Complete example to create a mosaic of the first scenes:
+
+@example
+ffmpeg -i video.avi -vf select='gt(scene\,0.4)',scale=160:120,tile -frames:v 1 preview.png
+@end example
+
+Comparing @var{scene} against a value between 0.3 and 0.5 is generally a sane
+choice.
+
 @section asendcmd, sendcmd
 
 Send commands to filters in the filtergraph.
index 3480b42..58f8954 100644 (file)
@@ -54,7 +54,7 @@ OBJS-$(CONFIG_AMERGE_FILTER)                 += af_amerge.o
 OBJS-$(CONFIG_AMIX_FILTER)                   += af_amix.o
 OBJS-$(CONFIG_ANULL_FILTER)                  += af_anull.o
 OBJS-$(CONFIG_ARESAMPLE_FILTER)              += af_aresample.o
-OBJS-$(CONFIG_ASELECT_FILTER)                += vf_select.o
+OBJS-$(CONFIG_ASELECT_FILTER)                += f_select.o
 OBJS-$(CONFIG_ASENDCMD_FILTER)               += f_sendcmd.o
 OBJS-$(CONFIG_ASETNSAMPLES_FILTER)           += af_asetnsamples.o
 OBJS-$(CONFIG_ASETPTS_FILTER)                += f_setpts.o
@@ -125,7 +125,7 @@ OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
 OBJS-$(CONFIG_PIXDESCTEST_FILTER)            += vf_pixdesctest.o
 OBJS-$(CONFIG_REMOVELOGO_FILTER)             += bbox.o lswsutils.o lavfutils.o vf_removelogo.o
 OBJS-$(CONFIG_SCALE_FILTER)                  += vf_scale.o
-OBJS-$(CONFIG_SELECT_FILTER)                 += vf_select.o
+OBJS-$(CONFIG_SELECT_FILTER)                 += f_select.o
 OBJS-$(CONFIG_SENDCMD_FILTER)                += f_sendcmd.o
 OBJS-$(CONFIG_SETDAR_FILTER)                 += vf_aspect.o
 OBJS-$(CONFIG_SETFIELD_FILTER)               += vf_setfield.o
diff --git a/libavfilter/f_select.c b/libavfilter/f_select.c
new file mode 100644 (file)
index 0000000..2fcf001
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * Copyright (c) 2011 Stefano Sabatini
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * filter for selecting which frame passes in the filterchain
+ */
+
+#include "libavutil/eval.h"
+#include "libavutil/fifo.h"
+#include "libavutil/internal.h"
+#include "avfilter.h"
+#include "audio.h"
+#include "formats.h"
+#include "internal.h"
+#include "video.h"
+
+#if CONFIG_AVCODEC
+#include "libavcodec/dsputil.h"
+#endif
+
+static const char *const var_names[] = {
+    "TB",                ///< timebase
+
+    "pts",               ///< original pts in the file of the frame
+    "start_pts",         ///< first PTS in the stream, expressed in TB units
+    "prev_pts",          ///< previous frame PTS
+    "prev_selected_pts", ///< previous selected frame PTS
+
+    "t",                 ///< first PTS in seconds
+    "start_t",           ///< first PTS in the stream, expressed in seconds
+    "prev_t",            ///< previous frame time
+    "prev_selected_t",   ///< previously selected time
+
+    "pict_type",         ///< the type of picture in the movie
+    "I",
+    "P",
+    "B",
+    "S",
+    "SI",
+    "SP",
+    "BI",
+
+    "interlace_type",    ///< the frame interlace type
+    "PROGRESSIVE",
+    "TOPFIRST",
+    "BOTTOMFIRST",
+
+    "consumed_samples_n",///< number of samples consumed by the filter (only audio)
+    "samples_n",         ///< number of samples in the current frame (only audio)
+    "sample_rate",       ///< sample rate (only audio)
+
+    "n",                 ///< frame number (starting from zero)
+    "selected_n",        ///< selected frame number (starting from zero)
+    "prev_selected_n",   ///< number of the last selected frame
+
+    "key",               ///< tell if the frame is a key frame
+    "pos",               ///< original position in the file of the frame
+
+    "scene",
+
+    NULL
+};
+
+enum var_name {
+    VAR_TB,
+
+    VAR_PTS,
+    VAR_START_PTS,
+    VAR_PREV_PTS,
+    VAR_PREV_SELECTED_PTS,
+
+    VAR_T,
+    VAR_START_T,
+    VAR_PREV_T,
+    VAR_PREV_SELECTED_T,
+
+    VAR_PICT_TYPE,
+    VAR_PICT_TYPE_I,
+    VAR_PICT_TYPE_P,
+    VAR_PICT_TYPE_B,
+    VAR_PICT_TYPE_S,
+    VAR_PICT_TYPE_SI,
+    VAR_PICT_TYPE_SP,
+    VAR_PICT_TYPE_BI,
+
+    VAR_INTERLACE_TYPE,
+    VAR_INTERLACE_TYPE_P,
+    VAR_INTERLACE_TYPE_T,
+    VAR_INTERLACE_TYPE_B,
+
+    VAR_CONSUMED_SAMPLES_N,
+    VAR_SAMPLES_N,
+    VAR_SAMPLE_RATE,
+
+    VAR_N,
+    VAR_SELECTED_N,
+    VAR_PREV_SELECTED_N,
+
+    VAR_KEY,
+    VAR_POS,
+
+    VAR_SCENE,
+
+    VAR_VARS_NB
+};
+
+typedef struct {
+    AVExpr *expr;
+    double var_values[VAR_VARS_NB];
+    int do_scene_detect;            ///< 1 if the expression requires scene detection variables, 0 otherwise
+#if CONFIG_AVCODEC
+    AVCodecContext *avctx;          ///< codec context required for the DSPContext (scene detect only)
+    DSPContext c;                   ///< context providing optimized SAD methods   (scene detect only)
+    double prev_mafd;               ///< previous MAFD                             (scene detect only)
+#endif
+    AVFilterBufferRef *prev_picref; ///< previous frame                            (scene detect only)
+    double select;
+} SelectContext;
+
+static av_cold int init(AVFilterContext *ctx, const char *args)
+{
+    SelectContext *select = ctx->priv;
+    int ret;
+
+    if ((ret = av_expr_parse(&select->expr, args ? args : "1",
+                             var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", args);
+        return ret;
+    }
+
+    select->do_scene_detect = args && strstr(args, "scene");
+    if (select->do_scene_detect && !CONFIG_AVCODEC) {
+        av_log(ctx, AV_LOG_ERROR, "Scene detection is not available without libavcodec.\n");
+        return AVERROR(EINVAL);
+    }
+    return 0;
+}
+
+#define INTERLACE_TYPE_P 0
+#define INTERLACE_TYPE_T 1
+#define INTERLACE_TYPE_B 2
+
+static int config_input(AVFilterLink *inlink)
+{
+    SelectContext *select = inlink->dst->priv;
+
+    select->var_values[VAR_N]          = 0.0;
+    select->var_values[VAR_SELECTED_N] = 0.0;
+
+    select->var_values[VAR_TB] = av_q2d(inlink->time_base);
+
+    select->var_values[VAR_PREV_PTS]          = NAN;
+    select->var_values[VAR_PREV_SELECTED_PTS] = NAN;
+    select->var_values[VAR_PREV_SELECTED_T]   = NAN;
+    select->var_values[VAR_START_PTS]         = NAN;
+    select->var_values[VAR_START_T]           = NAN;
+
+    select->var_values[VAR_PICT_TYPE_I]  = AV_PICTURE_TYPE_I;
+    select->var_values[VAR_PICT_TYPE_P]  = AV_PICTURE_TYPE_P;
+    select->var_values[VAR_PICT_TYPE_B]  = AV_PICTURE_TYPE_B;
+    select->var_values[VAR_PICT_TYPE_SI] = AV_PICTURE_TYPE_SI;
+    select->var_values[VAR_PICT_TYPE_SP] = AV_PICTURE_TYPE_SP;
+
+    select->var_values[VAR_INTERLACE_TYPE_P] = INTERLACE_TYPE_P;
+    select->var_values[VAR_INTERLACE_TYPE_T] = INTERLACE_TYPE_T;
+    select->var_values[VAR_INTERLACE_TYPE_B] = INTERLACE_TYPE_B;
+
+    select->var_values[VAR_SAMPLE_RATE] =
+        inlink->type == AVMEDIA_TYPE_AUDIO ? inlink->sample_rate : NAN;
+
+    if (CONFIG_AVCODEC && select->do_scene_detect) {
+        select->avctx = avcodec_alloc_context3(NULL);
+        if (!select->avctx)
+            return AVERROR(ENOMEM);
+        dsputil_init(&select->c, select->avctx);
+    }
+    return 0;
+}
+
+#if CONFIG_AVCODEC
+static double get_scene_score(AVFilterContext *ctx, AVFilterBufferRef *picref)
+{
+    double ret = 0;
+    SelectContext *select = ctx->priv;
+    AVFilterBufferRef *prev_picref = select->prev_picref;
+
+    if (prev_picref &&
+        picref->video->h    == prev_picref->video->h &&
+        picref->video->w    == prev_picref->video->w &&
+        picref->linesize[0] == prev_picref->linesize[0]) {
+        int x, y, nb_sad = 0;
+        int64_t sad = 0;
+        double mafd, diff;
+        uint8_t *p1 =      picref->data[0];
+        uint8_t *p2 = prev_picref->data[0];
+        const int linesize = picref->linesize[0];
+
+        for (y = 0; y < picref->video->h - 8; y += 8) {
+            for (x = 0; x < picref->video->w*3 - 8; x += 8) {
+                sad += select->c.sad[1](select, p1 + x, p2 + x,
+                                        linesize, 8);
+                nb_sad += 8 * 8;
+            }
+            p1 += 8 * linesize;
+            p2 += 8 * linesize;
+        }
+        emms_c();
+        mafd = nb_sad ? sad / nb_sad : 0;
+        diff = fabs(mafd - select->prev_mafd);
+        ret  = av_clipf(FFMIN(mafd, diff) / 100., 0, 1);
+        select->prev_mafd = mafd;
+        avfilter_unref_buffer(prev_picref);
+    }
+    select->prev_picref = avfilter_ref_buffer(picref, ~0);
+    return ret;
+}
+#endif
+
+#define D2TS(d)  (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d))
+#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
+
+static int select_frame(AVFilterContext *ctx, AVFilterBufferRef *picref)
+{
+    SelectContext *select = ctx->priv;
+    AVFilterLink *inlink = ctx->inputs[0];
+    double res;
+
+    if (isnan(select->var_values[VAR_START_PTS]))
+        select->var_values[VAR_START_PTS] = TS2D(picref->pts);
+    if (isnan(select->var_values[VAR_START_T]))
+        select->var_values[VAR_START_T] = TS2D(picref->pts) * av_q2d(inlink->time_base);
+
+    select->var_values[VAR_PTS] = TS2D(picref->pts);
+    select->var_values[VAR_T  ] = TS2D(picref->pts) * av_q2d(inlink->time_base);
+    select->var_values[VAR_POS] = picref->pos == -1 ? NAN : picref->pos;
+    select->var_values[VAR_PREV_PTS] = TS2D(picref ->pts);
+
+    switch (inlink->type) {
+    case AVMEDIA_TYPE_AUDIO:
+        select->var_values[VAR_SAMPLES_N] = picref->audio->nb_samples;
+        break;
+
+    case AVMEDIA_TYPE_VIDEO:
+        select->var_values[VAR_INTERLACE_TYPE] =
+            !picref->video->interlaced     ? INTERLACE_TYPE_P :
+        picref->video->top_field_first ? INTERLACE_TYPE_T : INTERLACE_TYPE_B;
+        select->var_values[VAR_PICT_TYPE] = picref->video->pict_type;
+        if (CONFIG_AVCODEC && select->do_scene_detect) {
+            char buf[32];
+            select->var_values[VAR_SCENE] = get_scene_score(ctx, picref);
+            // TODO: document metadata
+            snprintf(buf, sizeof(buf), "%f", select->var_values[VAR_SCENE]);
+            av_dict_set(&picref->metadata, "lavfi.scene_score", buf, 0);
+        }
+        break;
+    }
+
+    res = av_expr_eval(select->expr, select->var_values, NULL);
+    av_log(inlink->dst, AV_LOG_DEBUG,
+           "n:%d pts:%d t:%f pos:%d key:%d",
+           (int)select->var_values[VAR_N],
+           (int)select->var_values[VAR_PTS],
+           select->var_values[VAR_T],
+           (int)select->var_values[VAR_POS],
+           (int)select->var_values[VAR_KEY]);
+
+    switch (inlink->type) {
+    case AVMEDIA_TYPE_VIDEO:
+        av_log(inlink->dst, AV_LOG_DEBUG, " interlace_type:%c pict_type:%c",
+               select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_P ? 'P' :
+               select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_T ? 'T' :
+               select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_B ? 'B' : '?',
+               av_get_picture_type_char(select->var_values[VAR_PICT_TYPE]));
+        break;
+    case AVMEDIA_TYPE_AUDIO:
+        av_log(inlink->dst, AV_LOG_DEBUG, " samples_n:%d consumed_samples_n:%d",
+               (int)select->var_values[VAR_SAMPLES_N],
+               (int)select->var_values[VAR_CONSUMED_SAMPLES_N]);
+        break;
+    }
+
+    av_log(inlink->dst, AV_LOG_DEBUG, " -> select:%f\n", res);
+
+    if (res) {
+        select->var_values[VAR_PREV_SELECTED_N]   = select->var_values[VAR_N];
+        select->var_values[VAR_PREV_SELECTED_PTS] = select->var_values[VAR_PTS];
+        select->var_values[VAR_PREV_SELECTED_T]   = select->var_values[VAR_T];
+        select->var_values[VAR_SELECTED_N] += 1.0;
+        if (inlink->type == AVMEDIA_TYPE_AUDIO)
+            select->var_values[VAR_CONSUMED_SAMPLES_N] += picref->audio->nb_samples;
+    }
+
+    select->var_values[VAR_N] += 1.0;
+
+    return res;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *frame)
+{
+    SelectContext *select = inlink->dst->priv;
+
+    select->select = select_frame(inlink->dst, frame);
+    if (select->select)
+        return ff_filter_frame(inlink->dst->outputs[0], frame);
+
+    avfilter_unref_bufferp(&frame);
+    return 0;
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    SelectContext *select = ctx->priv;
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    select->select = 0;
+
+    do {
+        int ret = ff_request_frame(inlink);
+        if (ret < 0)
+            return ret;
+    } while (!select->select);
+
+    return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    SelectContext *select = ctx->priv;
+
+    av_expr_free(select->expr);
+    select->expr = NULL;
+
+    if (select->do_scene_detect) {
+        avfilter_unref_bufferp(&select->prev_picref);
+        if (select->avctx) {
+            avcodec_close(select->avctx);
+            av_freep(&select->avctx);
+        }
+    }
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    SelectContext *select = ctx->priv;
+
+    if (!select->do_scene_detect) {
+        return ff_default_query_formats(ctx);
+    } else {
+        static const enum AVPixelFormat pix_fmts[] = {
+            AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
+            AV_PIX_FMT_NONE
+        };
+        ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
+    }
+    return 0;
+}
+
+#if CONFIG_ASELECT_FILTER
+static const AVFilterPad avfilter_af_aselect_inputs[] = {
+    {
+        .name             = "default",
+        .type             = AVMEDIA_TYPE_AUDIO,
+        .get_audio_buffer = ff_null_get_audio_buffer,
+        .config_props     = config_input,
+        .filter_frame     = filter_frame,
+    },
+    { 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."),
+    .init      = init,
+    .uninit    = uninit,
+    .priv_size = sizeof(SelectContext),
+    .inputs    = avfilter_af_aselect_inputs,
+    .outputs   = avfilter_af_aselect_outputs,
+};
+#endif /* CONFIG_ASELECT_FILTER */
+
+#if CONFIG_SELECT_FILTER
+static const AVFilterPad avfilter_vf_select_inputs[] = {
+    {
+        .name             = "default",
+        .type             = AVMEDIA_TYPE_VIDEO,
+        .get_video_buffer = ff_null_get_video_buffer,
+        .min_perms        = AV_PERM_PRESERVE,
+        .config_props     = config_input,
+        .filter_frame     = filter_frame,
+    },
+    { 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."),
+    .init      = init,
+    .uninit    = uninit,
+    .query_formats = query_formats,
+
+    .priv_size = sizeof(SelectContext),
+
+    .inputs    = avfilter_vf_select_inputs,
+    .outputs   = avfilter_vf_select_outputs,
+};
+#endif /* CONFIG_SELECT_FILTER */
diff --git a/libavfilter/vf_select.c b/libavfilter/vf_select.c
deleted file mode 100644 (file)
index 2fcf001..0000000
+++ /dev/null
@@ -1,441 +0,0 @@
-/*
- * Copyright (c) 2011 Stefano Sabatini
- *
- * This file is part of FFmpeg.
- *
- * FFmpeg is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * FFmpeg is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with FFmpeg; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/**
- * @file
- * filter for selecting which frame passes in the filterchain
- */
-
-#include "libavutil/eval.h"
-#include "libavutil/fifo.h"
-#include "libavutil/internal.h"
-#include "avfilter.h"
-#include "audio.h"
-#include "formats.h"
-#include "internal.h"
-#include "video.h"
-
-#if CONFIG_AVCODEC
-#include "libavcodec/dsputil.h"
-#endif
-
-static const char *const var_names[] = {
-    "TB",                ///< timebase
-
-    "pts",               ///< original pts in the file of the frame
-    "start_pts",         ///< first PTS in the stream, expressed in TB units
-    "prev_pts",          ///< previous frame PTS
-    "prev_selected_pts", ///< previous selected frame PTS
-
-    "t",                 ///< first PTS in seconds
-    "start_t",           ///< first PTS in the stream, expressed in seconds
-    "prev_t",            ///< previous frame time
-    "prev_selected_t",   ///< previously selected time
-
-    "pict_type",         ///< the type of picture in the movie
-    "I",
-    "P",
-    "B",
-    "S",
-    "SI",
-    "SP",
-    "BI",
-
-    "interlace_type",    ///< the frame interlace type
-    "PROGRESSIVE",
-    "TOPFIRST",
-    "BOTTOMFIRST",
-
-    "consumed_samples_n",///< number of samples consumed by the filter (only audio)
-    "samples_n",         ///< number of samples in the current frame (only audio)
-    "sample_rate",       ///< sample rate (only audio)
-
-    "n",                 ///< frame number (starting from zero)
-    "selected_n",        ///< selected frame number (starting from zero)
-    "prev_selected_n",   ///< number of the last selected frame
-
-    "key",               ///< tell if the frame is a key frame
-    "pos",               ///< original position in the file of the frame
-
-    "scene",
-
-    NULL
-};
-
-enum var_name {
-    VAR_TB,
-
-    VAR_PTS,
-    VAR_START_PTS,
-    VAR_PREV_PTS,
-    VAR_PREV_SELECTED_PTS,
-
-    VAR_T,
-    VAR_START_T,
-    VAR_PREV_T,
-    VAR_PREV_SELECTED_T,
-
-    VAR_PICT_TYPE,
-    VAR_PICT_TYPE_I,
-    VAR_PICT_TYPE_P,
-    VAR_PICT_TYPE_B,
-    VAR_PICT_TYPE_S,
-    VAR_PICT_TYPE_SI,
-    VAR_PICT_TYPE_SP,
-    VAR_PICT_TYPE_BI,
-
-    VAR_INTERLACE_TYPE,
-    VAR_INTERLACE_TYPE_P,
-    VAR_INTERLACE_TYPE_T,
-    VAR_INTERLACE_TYPE_B,
-
-    VAR_CONSUMED_SAMPLES_N,
-    VAR_SAMPLES_N,
-    VAR_SAMPLE_RATE,
-
-    VAR_N,
-    VAR_SELECTED_N,
-    VAR_PREV_SELECTED_N,
-
-    VAR_KEY,
-    VAR_POS,
-
-    VAR_SCENE,
-
-    VAR_VARS_NB
-};
-
-typedef struct {
-    AVExpr *expr;
-    double var_values[VAR_VARS_NB];
-    int do_scene_detect;            ///< 1 if the expression requires scene detection variables, 0 otherwise
-#if CONFIG_AVCODEC
-    AVCodecContext *avctx;          ///< codec context required for the DSPContext (scene detect only)
-    DSPContext c;                   ///< context providing optimized SAD methods   (scene detect only)
-    double prev_mafd;               ///< previous MAFD                             (scene detect only)
-#endif
-    AVFilterBufferRef *prev_picref; ///< previous frame                            (scene detect only)
-    double select;
-} SelectContext;
-
-static av_cold int init(AVFilterContext *ctx, const char *args)
-{
-    SelectContext *select = ctx->priv;
-    int ret;
-
-    if ((ret = av_expr_parse(&select->expr, args ? args : "1",
-                             var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
-        av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", args);
-        return ret;
-    }
-
-    select->do_scene_detect = args && strstr(args, "scene");
-    if (select->do_scene_detect && !CONFIG_AVCODEC) {
-        av_log(ctx, AV_LOG_ERROR, "Scene detection is not available without libavcodec.\n");
-        return AVERROR(EINVAL);
-    }
-    return 0;
-}
-
-#define INTERLACE_TYPE_P 0
-#define INTERLACE_TYPE_T 1
-#define INTERLACE_TYPE_B 2
-
-static int config_input(AVFilterLink *inlink)
-{
-    SelectContext *select = inlink->dst->priv;
-
-    select->var_values[VAR_N]          = 0.0;
-    select->var_values[VAR_SELECTED_N] = 0.0;
-
-    select->var_values[VAR_TB] = av_q2d(inlink->time_base);
-
-    select->var_values[VAR_PREV_PTS]          = NAN;
-    select->var_values[VAR_PREV_SELECTED_PTS] = NAN;
-    select->var_values[VAR_PREV_SELECTED_T]   = NAN;
-    select->var_values[VAR_START_PTS]         = NAN;
-    select->var_values[VAR_START_T]           = NAN;
-
-    select->var_values[VAR_PICT_TYPE_I]  = AV_PICTURE_TYPE_I;
-    select->var_values[VAR_PICT_TYPE_P]  = AV_PICTURE_TYPE_P;
-    select->var_values[VAR_PICT_TYPE_B]  = AV_PICTURE_TYPE_B;
-    select->var_values[VAR_PICT_TYPE_SI] = AV_PICTURE_TYPE_SI;
-    select->var_values[VAR_PICT_TYPE_SP] = AV_PICTURE_TYPE_SP;
-
-    select->var_values[VAR_INTERLACE_TYPE_P] = INTERLACE_TYPE_P;
-    select->var_values[VAR_INTERLACE_TYPE_T] = INTERLACE_TYPE_T;
-    select->var_values[VAR_INTERLACE_TYPE_B] = INTERLACE_TYPE_B;
-
-    select->var_values[VAR_SAMPLE_RATE] =
-        inlink->type == AVMEDIA_TYPE_AUDIO ? inlink->sample_rate : NAN;
-
-    if (CONFIG_AVCODEC && select->do_scene_detect) {
-        select->avctx = avcodec_alloc_context3(NULL);
-        if (!select->avctx)
-            return AVERROR(ENOMEM);
-        dsputil_init(&select->c, select->avctx);
-    }
-    return 0;
-}
-
-#if CONFIG_AVCODEC
-static double get_scene_score(AVFilterContext *ctx, AVFilterBufferRef *picref)
-{
-    double ret = 0;
-    SelectContext *select = ctx->priv;
-    AVFilterBufferRef *prev_picref = select->prev_picref;
-
-    if (prev_picref &&
-        picref->video->h    == prev_picref->video->h &&
-        picref->video->w    == prev_picref->video->w &&
-        picref->linesize[0] == prev_picref->linesize[0]) {
-        int x, y, nb_sad = 0;
-        int64_t sad = 0;
-        double mafd, diff;
-        uint8_t *p1 =      picref->data[0];
-        uint8_t *p2 = prev_picref->data[0];
-        const int linesize = picref->linesize[0];
-
-        for (y = 0; y < picref->video->h - 8; y += 8) {
-            for (x = 0; x < picref->video->w*3 - 8; x += 8) {
-                sad += select->c.sad[1](select, p1 + x, p2 + x,
-                                        linesize, 8);
-                nb_sad += 8 * 8;
-            }
-            p1 += 8 * linesize;
-            p2 += 8 * linesize;
-        }
-        emms_c();
-        mafd = nb_sad ? sad / nb_sad : 0;
-        diff = fabs(mafd - select->prev_mafd);
-        ret  = av_clipf(FFMIN(mafd, diff) / 100., 0, 1);
-        select->prev_mafd = mafd;
-        avfilter_unref_buffer(prev_picref);
-    }
-    select->prev_picref = avfilter_ref_buffer(picref, ~0);
-    return ret;
-}
-#endif
-
-#define D2TS(d)  (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d))
-#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
-
-static int select_frame(AVFilterContext *ctx, AVFilterBufferRef *picref)
-{
-    SelectContext *select = ctx->priv;
-    AVFilterLink *inlink = ctx->inputs[0];
-    double res;
-
-    if (isnan(select->var_values[VAR_START_PTS]))
-        select->var_values[VAR_START_PTS] = TS2D(picref->pts);
-    if (isnan(select->var_values[VAR_START_T]))
-        select->var_values[VAR_START_T] = TS2D(picref->pts) * av_q2d(inlink->time_base);
-
-    select->var_values[VAR_PTS] = TS2D(picref->pts);
-    select->var_values[VAR_T  ] = TS2D(picref->pts) * av_q2d(inlink->time_base);
-    select->var_values[VAR_POS] = picref->pos == -1 ? NAN : picref->pos;
-    select->var_values[VAR_PREV_PTS] = TS2D(picref ->pts);
-
-    switch (inlink->type) {
-    case AVMEDIA_TYPE_AUDIO:
-        select->var_values[VAR_SAMPLES_N] = picref->audio->nb_samples;
-        break;
-
-    case AVMEDIA_TYPE_VIDEO:
-        select->var_values[VAR_INTERLACE_TYPE] =
-            !picref->video->interlaced     ? INTERLACE_TYPE_P :
-        picref->video->top_field_first ? INTERLACE_TYPE_T : INTERLACE_TYPE_B;
-        select->var_values[VAR_PICT_TYPE] = picref->video->pict_type;
-        if (CONFIG_AVCODEC && select->do_scene_detect) {
-            char buf[32];
-            select->var_values[VAR_SCENE] = get_scene_score(ctx, picref);
-            // TODO: document metadata
-            snprintf(buf, sizeof(buf), "%f", select->var_values[VAR_SCENE]);
-            av_dict_set(&picref->metadata, "lavfi.scene_score", buf, 0);
-        }
-        break;
-    }
-
-    res = av_expr_eval(select->expr, select->var_values, NULL);
-    av_log(inlink->dst, AV_LOG_DEBUG,
-           "n:%d pts:%d t:%f pos:%d key:%d",
-           (int)select->var_values[VAR_N],
-           (int)select->var_values[VAR_PTS],
-           select->var_values[VAR_T],
-           (int)select->var_values[VAR_POS],
-           (int)select->var_values[VAR_KEY]);
-
-    switch (inlink->type) {
-    case AVMEDIA_TYPE_VIDEO:
-        av_log(inlink->dst, AV_LOG_DEBUG, " interlace_type:%c pict_type:%c",
-               select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_P ? 'P' :
-               select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_T ? 'T' :
-               select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_B ? 'B' : '?',
-               av_get_picture_type_char(select->var_values[VAR_PICT_TYPE]));
-        break;
-    case AVMEDIA_TYPE_AUDIO:
-        av_log(inlink->dst, AV_LOG_DEBUG, " samples_n:%d consumed_samples_n:%d",
-               (int)select->var_values[VAR_SAMPLES_N],
-               (int)select->var_values[VAR_CONSUMED_SAMPLES_N]);
-        break;
-    }
-
-    av_log(inlink->dst, AV_LOG_DEBUG, " -> select:%f\n", res);
-
-    if (res) {
-        select->var_values[VAR_PREV_SELECTED_N]   = select->var_values[VAR_N];
-        select->var_values[VAR_PREV_SELECTED_PTS] = select->var_values[VAR_PTS];
-        select->var_values[VAR_PREV_SELECTED_T]   = select->var_values[VAR_T];
-        select->var_values[VAR_SELECTED_N] += 1.0;
-        if (inlink->type == AVMEDIA_TYPE_AUDIO)
-            select->var_values[VAR_CONSUMED_SAMPLES_N] += picref->audio->nb_samples;
-    }
-
-    select->var_values[VAR_N] += 1.0;
-
-    return res;
-}
-
-static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *frame)
-{
-    SelectContext *select = inlink->dst->priv;
-
-    select->select = select_frame(inlink->dst, frame);
-    if (select->select)
-        return ff_filter_frame(inlink->dst->outputs[0], frame);
-
-    avfilter_unref_bufferp(&frame);
-    return 0;
-}
-
-static int request_frame(AVFilterLink *outlink)
-{
-    AVFilterContext *ctx = outlink->src;
-    SelectContext *select = ctx->priv;
-    AVFilterLink *inlink = outlink->src->inputs[0];
-    select->select = 0;
-
-    do {
-        int ret = ff_request_frame(inlink);
-        if (ret < 0)
-            return ret;
-    } while (!select->select);
-
-    return 0;
-}
-
-static av_cold void uninit(AVFilterContext *ctx)
-{
-    SelectContext *select = ctx->priv;
-
-    av_expr_free(select->expr);
-    select->expr = NULL;
-
-    if (select->do_scene_detect) {
-        avfilter_unref_bufferp(&select->prev_picref);
-        if (select->avctx) {
-            avcodec_close(select->avctx);
-            av_freep(&select->avctx);
-        }
-    }
-}
-
-static int query_formats(AVFilterContext *ctx)
-{
-    SelectContext *select = ctx->priv;
-
-    if (!select->do_scene_detect) {
-        return ff_default_query_formats(ctx);
-    } else {
-        static const enum AVPixelFormat pix_fmts[] = {
-            AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
-            AV_PIX_FMT_NONE
-        };
-        ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
-    }
-    return 0;
-}
-
-#if CONFIG_ASELECT_FILTER
-static const AVFilterPad avfilter_af_aselect_inputs[] = {
-    {
-        .name             = "default",
-        .type             = AVMEDIA_TYPE_AUDIO,
-        .get_audio_buffer = ff_null_get_audio_buffer,
-        .config_props     = config_input,
-        .filter_frame     = filter_frame,
-    },
-    { 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."),
-    .init      = init,
-    .uninit    = uninit,
-    .priv_size = sizeof(SelectContext),
-    .inputs    = avfilter_af_aselect_inputs,
-    .outputs   = avfilter_af_aselect_outputs,
-};
-#endif /* CONFIG_ASELECT_FILTER */
-
-#if CONFIG_SELECT_FILTER
-static const AVFilterPad avfilter_vf_select_inputs[] = {
-    {
-        .name             = "default",
-        .type             = AVMEDIA_TYPE_VIDEO,
-        .get_video_buffer = ff_null_get_video_buffer,
-        .min_perms        = AV_PERM_PRESERVE,
-        .config_props     = config_input,
-        .filter_frame     = filter_frame,
-    },
-    { 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."),
-    .init      = init,
-    .uninit    = uninit,
-    .query_formats = query_formats,
-
-    .priv_size = sizeof(SelectContext),
-
-    .inputs    = avfilter_vf_select_inputs,
-    .outputs   = avfilter_vf_select_outputs,
-};
-#endif /* CONFIG_SELECT_FILTER */