Hi,

patch attached.
From 07e5e6ef03dc4c5705fe2b76859d8aa9caaaf518 Mon Sep 17 00:00:00 2001
From: Paul B Mahol <one...@gmail.com>
Date: Fri, 5 Feb 2016 12:54:06 +0100
Subject: [PATCH] avfilter: add conditional filter

Signed-off-by: Paul B Mahol <one...@gmail.com>
---
 libavfilter/Makefile         |   1 +
 libavfilter/allfilters.c     |   1 +
 libavfilter/vf_conditional.c | 375 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 377 insertions(+)
 create mode 100644 libavfilter/vf_conditional.c

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index e76d18e..8ffd693 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -123,6 +123,7 @@ OBJS-$(CONFIG_COLORCHANNELMIXER_FILTER)      += vf_colorchannelmixer.o
 OBJS-$(CONFIG_COLORKEY_FILTER)               += vf_colorkey.o
 OBJS-$(CONFIG_COLORLEVELS_FILTER)            += vf_colorlevels.o
 OBJS-$(CONFIG_COLORMATRIX_FILTER)            += vf_colormatrix.o
+OBJS-$(CONFIG_CONDITIONAL_FILTER)            += vf_conditional.o
 OBJS-$(CONFIG_CONVOLUTION_FILTER)            += vf_convolution.o
 OBJS-$(CONFIG_COPY_FILTER)                   += vf_copy.o
 OBJS-$(CONFIG_COVER_RECT_FILTER)             += vf_cover_rect.o lavfutils.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 27d54bc..d4d37c9 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -144,6 +144,7 @@ void avfilter_register_all(void)
     REGISTER_FILTER(COLORKEY,       colorkey,       vf);
     REGISTER_FILTER(COLORLEVELS,    colorlevels,    vf);
     REGISTER_FILTER(COLORMATRIX,    colormatrix,    vf);
+    REGISTER_FILTER(CONDITIONAL,    conditional,    vf);
     REGISTER_FILTER(CONVOLUTION,    convolution,    vf);
     REGISTER_FILTER(COPY,           copy,           vf);
     REGISTER_FILTER(COVER_RECT,     cover_rect,     vf);
diff --git a/libavfilter/vf_conditional.c b/libavfilter/vf_conditional.c
new file mode 100644
index 0000000..e93ba4c
--- /dev/null
+++ b/libavfilter/vf_conditional.c
@@ -0,0 +1,375 @@
+/*
+ * 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
+ * conditional video filter
+ */
+
+#include "libavutil/attributes.h"
+#include "libavutil/avstring.h"
+#include "libavutil/avassert.h"
+#include "libavutil/eval.h"
+#include "libavutil/opt.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/internal.h"
+#include "libavutil/time.h"
+#include "libavutil/timestamp.h"
+#include "libavformat/avformat.h"
+#include "audio.h"
+#include "avfilter.h"
+#include "buffersink.h"
+#include "buffersrc.h"
+#include "formats.h"
+#include "internal.h"
+#include "video.h"
+
+static const char *const var_names[] = {
+    "FRAME_RATE",  ///< defined only for constant frame-rate video
+    "INTERLACED",  ///< tell if the current frame is interlaced
+    "N_IN",        ///< number of consumed frame, starting at zero
+    "N_OUT",       ///< number of returned frame, starting at zero
+    "POS",         ///< original position in the file of the frame
+    "PTS",         ///< original pts in the file of the frame
+    "STARTPTS",    ///< PTS at start of movie
+    "STARTT",      ///< time at start of movie
+    "T",           ///< original time in the file of the frame
+    "TB",          ///< timebase
+    "RTCTIME",
+    "RTCSTART",
+    NULL
+};
+
+enum var_name {
+    VAR_FRAME_RATE,
+    VAR_INTERLACED,
+    VAR_N_IN,
+    VAR_N_OUT,
+    VAR_POS,
+    VAR_PTS,
+    VAR_STARTPTS,
+    VAR_STARTT,
+    VAR_T,
+    VAR_TB,
+    VAR_RTCTIME,
+    VAR_RTCSTART,
+    VAR_VARS_NB
+};
+
+typedef struct ConditionalContext {
+    const AVClass *class;
+
+    char *expr;
+    char *filter_name[2];
+    char *options[2];
+
+    AVExpr *e;
+    int cg;
+    double var_values[VAR_VARS_NB];
+
+    AVFilterContext *sink[2];
+    AVFilterContext *src[2];
+    AVFilterContext *filter[2];
+    AVFilterGraph *graph[2];
+} ConditionalContext;
+
+#define OFFSET(x) offsetof(ConditionalContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM
+
+static const AVOption conditional_options[]= {
+    { "expr",  "specify the expression",                        OFFSET(expr),           AV_OPT_TYPE_STRING, {.str = "1"    }, .flags = FLAGS },
+    { "tf",    "specify filter to call if expression is true",  OFFSET(filter_name[0]), AV_OPT_TYPE_STRING, {.str = "null" }, .flags = FLAGS },
+    { "ff",    "specify filter to call if expression is false", OFFSET(filter_name[1]), AV_OPT_TYPE_STRING, {.str = "null" }, .flags = FLAGS },
+    { "to",    "specify filter options if expression is true",  OFFSET(options[0]),     AV_OPT_TYPE_STRING, {.str = NULL   }, .flags = FLAGS },
+    { "fo",    "specify filter options if expression is false", OFFSET(options[0]),     AV_OPT_TYPE_STRING, {.str = NULL   }, .flags = FLAGS },
+    { NULL },
+};
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    ConditionalContext *s = ctx->priv;
+    AVFilter *sink, *filter[2];
+    const char *sink_name, *filter_name;
+    int ret, i;
+
+    if (!s->expr || !s->filter_name[0] || !s->filter_name[1])
+        return AVERROR(EINVAL);
+
+    ret = av_expr_parse(&s->e, s->expr, var_names,
+                        NULL, NULL, NULL, NULL, 0, ctx);
+    if (ret < 0)
+        return ret;
+
+    s->graph[0] = avfilter_graph_alloc();
+    s->graph[1] = avfilter_graph_alloc();
+    if (!s->graph[0] || !s->graph[1])
+        return AVERROR(ENOMEM);
+
+    sink = avfilter_get_by_name("buffersink");
+    if (!sink) {
+        av_log(ctx, AV_LOG_ERROR, "Couldn't find sink filter\n");
+        return AVERROR(EINVAL);
+    }
+
+    for (i = 0; i < 2; i++) {
+        filter[i] = avfilter_get_by_name(s->filter_name[i]);
+        if (!filter[i]) {
+            av_log(ctx, AV_LOG_ERROR, "Couldn't find filter %s\n", s->filter_name[i]);
+            return AVERROR(EINVAL);
+        }
+    }
+
+    for (i = 0; i < 2; i++) {
+        filter_name = av_asprintf("filter%d", i);
+        s->filter[i] = avfilter_graph_alloc_filter(s->graph[i], filter[i], filter_name);
+        av_freep(&filter_name);
+        if (!s->filter[i]) {
+            av_log(ctx, AV_LOG_ERROR, "Error allocating filter: %s\n", s->filter_name[i]);
+            return AVERROR(ENOMEM);
+        }
+        ret = avfilter_init_str(s->filter[i], s->options[i]);
+        if (ret < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Error initializing filter: %s\n", s->filter_name[i]);
+            return ret;
+        }
+
+        sink_name = av_asprintf("out%d", i);
+        s->sink[i] = avfilter_graph_alloc_filter(s->graph[i], sink, sink_name);
+        av_freep(&sink_name);
+        if (!s->sink[i]) {
+            av_log(ctx, AV_LOG_ERROR, "Error allocating sink%d filter\n", i);
+            return AVERROR(ENOMEM);
+        }
+        ret = avfilter_init_str(s->sink[i], NULL);
+        if (ret < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Error initializing sink%d filter\n", i);
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
+#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
+#define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts)*av_q2d(tb))
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    ConditionalContext *s = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+    AVFrame *out;
+    int ret = 0;
+
+    if (isnan(s->var_values[VAR_STARTPTS])) {
+        s->var_values[VAR_STARTPTS] = TS2D(frame->pts);
+        s->var_values[VAR_STARTT] = TS2T(frame->pts, inlink->time_base);
+    }
+    s->var_values[VAR_PTS] = TS2D(frame->pts);
+    s->var_values[VAR_T] = TS2T(frame->pts, inlink->time_base);
+    s->var_values[VAR_POS] = av_frame_get_pkt_pos(frame) == -1 ? NAN : av_frame_get_pkt_pos(frame);
+    s->var_values[VAR_INTERLACED] = frame->interlaced_frame;
+    s->var_values[VAR_RTCTIME] = av_gettime();
+    s->var_values[VAR_N_IN] = inlink->frame_count;
+    s->var_values[VAR_N_OUT] = outlink->frame_count;
+
+    s->cg = !av_expr_eval(s->e, s->var_values, NULL);
+
+    ret = av_buffersrc_add_frame_flags(s->src[s->cg], frame,
+                                       AV_BUFFERSRC_FLAG_PUSH |
+                                       AV_BUFFERSRC_FLAG_KEEP_REF);
+    av_frame_free(&frame);
+
+    while (ret >= 0) {
+        out = av_frame_alloc();
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        ret = av_buffersink_get_frame_flags(s->sink[s->cg], out, 0);
+        if (ret == AVERROR(EAGAIN)) {
+            av_frame_free(&out);
+            ret = 0;
+            break;
+        } else if (ret < 0) {
+            av_frame_free(&out);
+            return ret;
+        }
+        ret = ff_filter_frame(outlink, out);
+    }
+
+    return ret;
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    ConditionalContext *s = ctx->priv;
+    int ret;
+
+    ret = ff_request_frame(ctx->inputs[0]);
+    if (ret == AVERROR_EOF) {
+        AVFrame *out;
+
+        ret = av_buffersrc_add_frame_flags(s->src[s->cg], NULL, 0);
+        if (ret < 0)
+            return ret;
+
+        ret = 0;
+        while (ret >= 0) {
+            out = av_frame_alloc();
+            if (!out)
+                return AVERROR(ENOMEM);
+
+            ret = av_buffersink_get_frame_flags(s->sink[s->cg], out, 0);
+            if (ret == AVERROR(EAGAIN)) {
+                av_frame_free(&out);
+                ret = 0;
+                break;
+            } else if (ret < 0) {
+                av_frame_free(&out);
+                return ret;
+            }
+            ret = ff_filter_frame(ctx->outputs[0], out);
+        }
+    }
+
+    return ret;
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    ConditionalContext *s = ctx->priv;
+    const char *src_name;
+    AVFilter *src;
+    int i, ret;
+
+    src = avfilter_get_by_name("buffer");
+    if (!src) {
+        av_log(ctx, AV_LOG_ERROR, "Couldn't find buffer filter\n");
+        return AVERROR(EINVAL);
+    }
+
+    for (i = 0; i < 2; i++) {
+        src_name = av_asprintf("in%d", i);
+        s->src[i] = avfilter_graph_alloc_filter(s->graph[i], src, src_name);
+        av_freep(&src_name);
+        if (!s->src[i]) {
+            av_log(ctx, AV_LOG_ERROR, "Error allocating src%d filter\n", i);
+            return AVERROR(ENOMEM);
+        }
+
+        av_opt_set_int(s->src[i], "width",     inlink->w,                   AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_int(s->src[i], "height",    inlink->h,                   AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_q  (s->src[i], "time_base", inlink->time_base,           AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_int(s->src[i], "pix_fmt",   inlink->format,              AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_q  (s->src[i], "sar",       inlink->sample_aspect_ratio, AV_OPT_SEARCH_CHILDREN);
+
+        ret = avfilter_init_str(s->src[i], NULL);
+        if (ret < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Error initializing src%d filter\n", i);
+            return ret;
+        }
+    }
+
+    for (i = 0; i < 2; i++) {
+        ret = avfilter_link(s->src[i], 0, s->filter[i], 0);
+        if (ret < 0)
+            return ret;
+        ret = avfilter_link(s->filter[i], 0, s->sink[i], 0);
+        if (ret < 0)
+            return ret;
+
+        ret = avfilter_graph_config(s->graph[i], ctx);
+        if (ret < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Error configuring the filter graph %d\n", i);
+            return ret;
+        }
+    }
+
+    s->var_values[VAR_TB] = av_q2d(inlink->time_base);
+    s->var_values[VAR_RTCSTART] = av_gettime();
+    s->var_values[VAR_FRAME_RATE] = inlink->frame_rate.num && inlink->frame_rate.den ?
+                                        av_q2d(inlink->frame_rate) : NAN;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    ConditionalContext *s = ctx->priv;
+    AVFilterLink *tsinklink = s->sink[0]->inputs[0];
+    AVFilterLink *fsinklink = s->sink[1]->inputs[0];
+
+    if (tsinklink->w != fsinklink->w ||
+        tsinklink->h != fsinklink->h) {
+        av_log(ctx, AV_LOG_ERROR, "Video sizes of both filters are not same\n");
+        return AVERROR(EINVAL);
+    }
+
+    outlink->w = tsinklink->w;
+    outlink->h = tsinklink->h;
+    outlink->time_base = tsinklink->time_base;
+    outlink->frame_rate = tsinklink->frame_rate;
+    outlink->sample_aspect_ratio = tsinklink->sample_aspect_ratio;
+
+    return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    ConditionalContext *s = ctx->priv;
+
+    av_expr_free(s->e);
+    avfilter_graph_free(&s->graph[0]);
+    avfilter_graph_free(&s->graph[1]);
+}
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+    { NULL }
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .request_frame = request_frame,
+        .config_props = config_output,
+    },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(conditional);
+
+AVFilter ff_vf_conditional = {
+    .name        = "conditional",
+    .description = NULL_IF_CONFIG_SMALL("Conditional video filtering."),
+    .priv_size   = sizeof(ConditionalContext),
+    .priv_class  = &conditional_class,
+    .init        = init,
+    .uninit      = uninit,
+    .inputs      = inputs,
+    .outputs     = outputs,
+};
-- 
1.9.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to