[FFmpeg-devel] [PATCH] lavfi/WIP: timeline support.

Clément Bœsch ubitux at gmail.com
Sun Apr 14 23:56:52 CEST 2013


---
Here is a small working PoC to enable timeline filtering.

The idea is to add a generic "enable" option to enable some filters in a
timelaps, such as
  -vf 'curves = vintage : enable=between(t\,2\,5),
       hue    = -30     : enable=between(t\,3\,7)'

 => curves filter enabled from second 2 to 5 and hue filter enabled from second
    3 to 7

The patch works fine as is, but it might be relevant to add a flag to mark
filters supporting it, and allow filters to have a special callback passthrough
(typically select filter which needs to increment the frame counter for every
frame).

I should be done with the patch pretty soon.

Comments welcome.
---
 libavfilter/avfilter.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 libavfilter/avfilter.h |  6 +++++-
 2 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index f392613..a312d1d 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -23,6 +23,7 @@
 #include "libavutil/avstring.h"
 #include "libavutil/channel_layout.h"
 #include "libavutil/common.h"
+#include "libavutil/eval.h"
 #include "libavutil/imgutils.h"
 #include "libavutil/opt.h"
 #include "libavutil/pixdesc.h"
@@ -483,12 +484,20 @@ static const AVClass *filter_child_class_next(const AVClass *prev)
     return NULL;
 }
 
+#define OFFSET(x) offsetof(AVFilterContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM
+static const AVOption filters_common_options[] = {
+    { "enable", "set enable expression", OFFSET(enable_str), AV_OPT_TYPE_STRING, {.str="1"}, .flags = FLAGS },
+    { NULL },
+};
+
 static const AVClass avfilter_class = {
     .class_name = "AVFilter",
     .item_name  = default_filter_name,
     .version    = LIBAVUTIL_VERSION_INT,
     .category   = AV_CLASS_CATEGORY_FILTER,
     .child_next = filter_child_next,
+    .option     = filters_common_options,
     .child_class_next = filter_child_class_next,
 };
 
@@ -618,9 +627,23 @@ void avfilter_free(AVFilterContext *filter)
     while(filter->command_queue){
         ff_command_queue_pop(filter);
     }
+    av_opt_free(filter);
+    av_expr_free(filter->enable);
+    filter->enable = NULL;
+    av_freep(&filter->var_values);
     av_free(filter);
 }
 
+static const char *const var_names[] = {
+    "t",
+    NULL
+};
+
+enum var_name {
+    VAR_T,
+    VAR_VARS_NB
+};
+
 static int process_options(AVFilterContext *ctx, AVDictionary **options,
                            const char *args)
 {
@@ -665,6 +688,12 @@ static int process_options(AVFilterContext *ctx, AVDictionary **options,
         }
 
         av_log(ctx, AV_LOG_DEBUG, "Setting '%s' to value '%s'\n", key, value);
+
+        if (av_opt_find(ctx, key, NULL, 0, 0)) {
+            ret = av_opt_set(ctx, key, value, 0);
+            if (ret < 0)
+                return ret;
+        } else {
         av_dict_set(options, key, value, 0);
         if ((ret = av_opt_set(ctx->priv, key, value, 0)) < 0) {
             if (!av_opt_find(ctx->priv, key, NULL, 0, AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ)) {
@@ -675,11 +704,22 @@ static int process_options(AVFilterContext *ctx, AVDictionary **options,
             return ret;
             }
         }
+        }
 
         av_free(value);
         av_free(parsed_key);
         count++;
     }
+
+    if (ctx->enable_str) {
+        ctx->var_values = av_calloc(VAR_VARS_NB, sizeof(*ctx->var_values));
+        if (!ctx->var_values)
+            return AVERROR(ENOMEM);
+        ret = av_expr_parse((AVExpr**)&ctx->enable, ctx->enable_str, var_names,
+                            NULL, NULL, NULL, NULL, 0, ctx->priv);
+        if (ret < 0)
+            return ret;
+    }
     return count;
 }
 
@@ -852,6 +892,7 @@ static int default_filter_frame(AVFilterLink *link, AVFrame *frame)
 static int ff_filter_frame_framed(AVFilterLink *link, AVFrame *frame)
 {
     int (*filter_frame)(AVFilterLink *, AVFrame *);
+    AVFilterContext *dstctx = link->dst;
     AVFilterPad *dst = link->dstpad;
     AVFrame *out;
     int ret;
@@ -914,6 +955,11 @@ static int ff_filter_frame_framed(AVFilterLink *link, AVFrame *frame)
     }
 
     pts = out->pts;
+    if (dstctx->enable_str) {
+        dstctx->var_values[VAR_T] = pts == AV_NOPTS_VALUE ? NAN : pts * av_q2d(link->time_base);
+        if (!((int)av_expr_eval(dstctx->enable, dstctx->var_values, NULL)))
+            filter_frame = default_filter_frame;
+    }
     ret = filter_frame(link, out);
     link->frame_requested = 0;
     ff_update_link_current_pts(link, pts);
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index 0b970d0..c38a125 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -522,7 +522,7 @@ typedef struct AVFilter {
 
 /** An instance of a filter */
 struct AVFilterContext {
-    const AVClass *av_class;        ///< needed for av_log()
+    const AVClass *av_class;        ///< needed for av_log() and filters common options
 
     const AVFilter *filter;         ///< the AVFilter of which this is an instance
 
@@ -547,6 +547,10 @@ struct AVFilterContext {
     struct AVFilterGraph *graph;    ///< filtergraph this filter belongs to
 
     struct AVFilterCommand *command_queue;
+
+    char *enable_str;
+    void *enable;
+    double *var_values;
 };
 
 /**
-- 
1.8.2.1



More information about the ffmpeg-devel mailing list