]> git.sesse.net Git - ffmpeg/blobdiff - libavfilter/avfilter.c
Merge commit '28663511c99b3cdaf9387a15032259879474f5f4'
[ffmpeg] / libavfilter / avfilter.c
index cf0aefe3a49bda73cea12a6bc564cc681c140650..06d92b2df5ad500ebbe1a13353ee168b8b110676 100644 (file)
@@ -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"
@@ -365,6 +366,49 @@ int ff_poll_frame(AVFilterLink *link)
     return min;
 }
 
+static const char *const var_names[] = {   "t",   "n",   "pos",        NULL };
+enum                                   { VAR_T, VAR_N, VAR_POS, VAR_VARS_NB };
+
+static int set_enable_expr(AVFilterContext *ctx, const char *expr)
+{
+    int ret;
+    char *expr_dup;
+    AVExpr *old = ctx->enable;
+
+    if (!(ctx->filter->flags & AVFILTER_FLAG_SUPPORT_TIMELINE)) {
+        av_log(ctx, AV_LOG_ERROR, "Timeline ('enable' option) not supported "
+               "with filter '%s'\n", ctx->filter->name);
+        return AVERROR_PATCHWELCOME;
+    }
+
+    expr_dup = av_strdup(expr);
+    if (!expr_dup)
+        return AVERROR(ENOMEM);
+
+    if (!ctx->var_values) {
+        ctx->var_values = av_calloc(VAR_VARS_NB, sizeof(*ctx->var_values));
+        if (!ctx->var_values) {
+            av_free(expr_dup);
+            return AVERROR(ENOMEM);
+        }
+    }
+
+    ret = av_expr_parse((AVExpr**)&ctx->enable, expr_dup, var_names,
+                        NULL, NULL, NULL, NULL, 0, ctx->priv);
+    if (ret < 0) {
+        av_log(ctx->priv, AV_LOG_ERROR,
+               "Error when evaluating the expression '%s' for enable\n",
+               expr_dup);
+        av_free(expr_dup);
+        return ret;
+    }
+
+    av_expr_free(old);
+    av_free(ctx->enable_str);
+    ctx->enable_str = expr_dup;
+    return 0;
+}
+
 void ff_update_link_current_pts(AVFilterLink *link, int64_t pts)
 {
     if (pts == AV_NOPTS_VALUE)
@@ -380,63 +424,66 @@ int avfilter_process_command(AVFilterContext *filter, const char *cmd, const cha
     if(!strcmp(cmd, "ping")){
         av_strlcatf(res, res_len, "pong from:%s %s\n", filter->filter->name, filter->name);
         return 0;
+    }else if(!strcmp(cmd, "enable")) {
+        return set_enable_expr(filter, arg);
     }else if(filter->filter->process_command) {
         return filter->filter->process_command(filter, cmd, arg, res, res_len, flags);
     }
     return AVERROR(ENOSYS);
 }
 
-#define MAX_REGISTERED_AVFILTERS_NB 256
-
-static AVFilter *registered_avfilters[MAX_REGISTERED_AVFILTERS_NB + 1];
-
-static int next_registered_avfilter_idx = 0;
+static AVFilter *first_filter;
 
 AVFilter *avfilter_get_by_name(const char *name)
 {
-    int i;
+    AVFilter *f = NULL;
 
-    for (i = 0; registered_avfilters[i]; i++)
-        if (!strcmp(registered_avfilters[i]->name, name))
-            return registered_avfilters[i];
+    if (!name)
+        return NULL;
+
+    while ((f = avfilter_next(f)))
+        if (!strcmp(f->name, name))
+            return f;
 
     return NULL;
 }
 
 int avfilter_register(AVFilter *filter)
 {
+    AVFilter **f = &first_filter;
     int i;
 
-    if (next_registered_avfilter_idx == MAX_REGISTERED_AVFILTERS_NB) {
-        av_log(NULL, AV_LOG_ERROR,
-               "Maximum number of registered filters %d reached, "
-               "impossible to register filter with name '%s'\n",
-               MAX_REGISTERED_AVFILTERS_NB, filter->name);
-        return AVERROR(ENOMEM);
-    }
-
     for(i=0; filter->inputs && filter->inputs[i].name; i++) {
         const AVFilterPad *input = &filter->inputs[i];
         av_assert0(     !input->filter_frame
                     || (!input->start_frame && !input->end_frame));
     }
 
-    registered_avfilters[next_registered_avfilter_idx++] = filter;
+    while (*f)
+        f = &(*f)->next;
+    *f = filter;
+    filter->next = NULL;
+
     return 0;
 }
 
+const AVFilter *avfilter_next(const AVFilter *prev)
+{
+    return prev ? prev->next : first_filter;
+}
+
+#if FF_API_OLD_FILTER_REGISTER
 AVFilter **av_filter_next(AVFilter **filter)
 {
-    return filter ? ++filter : &registered_avfilters[0];
+    return filter ? &(*filter)->next : &first_filter;
 }
 
 void avfilter_uninit(void)
 {
-    memset(registered_avfilters, 0, sizeof(registered_avfilters));
-    next_registered_avfilter_idx = 0;
 }
+#endif
 
-static int pad_count(const AVFilterPad *pads)
+int avfilter_pad_count(const AVFilterPad *pads)
 {
     int count;
 
@@ -463,30 +510,39 @@ static void *filter_child_next(void *obj, void *prev)
 
 static const AVClass *filter_child_class_next(const AVClass *prev)
 {
-    AVFilter **f = NULL;
+    AVFilter *f = NULL;
 
     /* find the filter that corresponds to prev */
-    while (prev && *(f = av_filter_next(f)))
-        if ((*f)->priv_class == prev)
+    while (prev && (f = avfilter_next(f)))
+        if (f->priv_class == prev)
             break;
 
     /* could not find filter corresponding to prev */
-    if (prev && !(*f))
+    if (prev && !f)
         return NULL;
 
     /* find next filter with specific options */
-    while (*(f = av_filter_next(f)))
-        if ((*f)->priv_class)
-            return (*f)->priv_class;
+    while ((f = avfilter_next(f)))
+        if (f->priv_class)
+            return f->priv_class;
+
     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=NULL}, .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,
 };
 
@@ -515,7 +571,7 @@ AVFilterContext *ff_filter_alloc(const AVFilter *filter, const char *inst_name)
         av_opt_set_defaults(ret->priv);
     }
 
-    ret->nb_inputs = pad_count(filter->inputs);
+    ret->nb_inputs = avfilter_pad_count(filter->inputs);
     if (ret->nb_inputs ) {
         ret->input_pads   = av_malloc(sizeof(AVFilterPad) * ret->nb_inputs);
         if (!ret->input_pads)
@@ -526,7 +582,7 @@ AVFilterContext *ff_filter_alloc(const AVFilter *filter, const char *inst_name)
             goto err;
     }
 
-    ret->nb_outputs = pad_count(filter->outputs);
+    ret->nb_outputs = avfilter_pad_count(filter->outputs);
     if (ret->nb_outputs) {
         ret->output_pads  = av_malloc(sizeof(AVFilterPad) * ret->nb_outputs);
         if (!ret->output_pads)
@@ -604,7 +660,7 @@ void avfilter_free(AVFilterContext *filter)
         avfilter_link_free(&link);
     }
 
-    if (filter->filter->priv_class || filter->filter->shorthand)
+    if (filter->filter->priv_class)
         av_opt_free(filter->priv);
 
     av_freep(&filter->name);
@@ -616,6 +672,10 @@ 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);
 }
 
@@ -628,6 +688,8 @@ static int process_options(AVFilterContext *ctx, AVDictionary **options,
     const char *key;
     int offset= -1;
 
+    av_opt_set_defaults(ctx);
+
     if (!args)
         return 0;
 
@@ -663,6 +725,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)) {
@@ -673,31 +741,21 @@ static int process_options(AVFilterContext *ctx, AVDictionary **options,
             return ret;
             }
         }
+        }
 
         av_free(value);
         av_free(parsed_key);
         count++;
     }
+
+    if (ctx->enable_str) {
+        ret = set_enable_expr(ctx, ctx->enable_str);
+        if (ret < 0)
+            return ret;
+    }
     return count;
 }
 
-// TODO: drop me
-static const char *const filters_left_to_update[] = {
-#if FF_API_ACONVERT_FILTER
-    "aconvert",
-#endif
-    "pan",
-};
-
-static int filter_use_deprecated_init(const char *name)
-{
-    int i;
-    for (i = 0; i < FF_ARRAY_ELEMS(filters_left_to_update); i++)
-        if (!strcmp(name, filters_left_to_update[i]))
-            return 1;
-    return 0;
-}
-#if 0
 #if FF_API_AVFILTER_INIT_FILTER
 int avfilter_init_filter(AVFilterContext *filter, const char *args, void *opaque)
 {
@@ -705,35 +763,35 @@ int avfilter_init_filter(AVFilterContext *filter, const char *args, void *opaque
 }
 #endif
 
-int avfilter_init_str(AVFilterContext *filter, const char *args)
-#else
-int avfilter_init_str(AVFilterContext *filter, const char *args)
+int avfilter_init_dict(AVFilterContext *ctx, AVDictionary **options)
 {
-    return avfilter_init_filter(filter, args, NULL);
+    int ret = 0;
+
+    if (ctx->filter->priv_class) {
+        ret = av_opt_set_dict(ctx->priv, options);
+        if (ret < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Error applying options to the filter.\n");
+            return ret;
+        }
+    }
+
+    if (ctx->filter->init_opaque)
+        ret = ctx->filter->init_opaque(ctx, NULL);
+    else if (ctx->filter->init)
+        ret = ctx->filter->init(ctx);
+    else if (ctx->filter->init_dict)
+        ret = ctx->filter->init_dict(ctx, options);
+
+    return ret;
 }
 
-int avfilter_init_filter(AVFilterContext *filter, const char *args, void *opaque)
-#endif
+int avfilter_init_str(AVFilterContext *filter, const char *args)
 {
     AVDictionary *options = NULL;
     AVDictionaryEntry *e;
     int ret=0;
-    int deprecated_init = filter_use_deprecated_init(filter->filter->name);
-
-    // TODO: remove old shorthand
-    if (filter->filter->shorthand) {
-        av_assert0(filter->priv);
-        av_assert0(filter->filter->priv_class);
-        *(const AVClass **)filter->priv = filter->filter->priv_class;
-        av_opt_set_defaults(filter->priv);
-        ret = av_opt_set_from_string(filter->priv, args,
-                                     filter->filter->shorthand, "=", ":");
-        if (ret < 0)
-            return ret;
-        args = NULL;
-    }
 
-    if (!deprecated_init && args && *args) {
+    if (args && *args) {
         if (!filter->filter->priv_class) {
             av_log(filter, AV_LOG_ERROR, "This filter does not take any "
                    "options, but options were provided: %s.\n", args);
@@ -741,37 +799,12 @@ int avfilter_init_filter(AVFilterContext *filter, const char *args, void *opaque
         }
 
 #if FF_API_OLD_FILTER_OPTS
-        if (!strcmp(filter->filter->name, "scale") &&
-            strchr(args, ':') < strchr(args, '=')) {
-            /* old w:h:flags=<flags> syntax */
-            char *copy = av_strdup(args);
-            char *p;
-
-            av_log(filter, AV_LOG_WARNING, "The <w>:<h>:flags=<flags> option "
-                   "syntax is deprecated. Use either <w>:<h>:<flags> or "
-                   "w=<w>:h=<h>:flags=<flags>.\n");
-
-            if (!copy) {
-                ret = AVERROR(ENOMEM);
-                goto fail;
-            }
-
-            p = strrchr(copy, ':');
-            if (p) {
-                *p++ = 0;
-                ret = av_dict_parse_string(&options, p, "=", ":", 0);
-            }
-            if (ret >= 0)
-                ret = process_options(filter, &options, copy);
-            av_freep(&copy);
-
-            if (ret < 0)
-                goto fail;
-        } else if (!strcmp(filter->filter->name, "format")     ||
+            if (   !strcmp(filter->filter->name, "format")     ||
                    !strcmp(filter->filter->name, "noformat")   ||
                    !strcmp(filter->filter->name, "frei0r")     ||
                    !strcmp(filter->filter->name, "frei0r_src") ||
                    !strcmp(filter->filter->name, "ocv")        ||
+                   !strcmp(filter->filter->name, "pan")        ||
                    !strcmp(filter->filter->name, "pp")         ||
                    !strcmp(filter->filter->name, "aevalsrc")) {
             /* a hack for compatibility with the old syntax
@@ -858,20 +891,7 @@ int avfilter_init_filter(AVFilterContext *filter, const char *args, void *opaque
         }
     }
 
-    if (!deprecated_init && filter->filter->priv_class) {
-        ret = av_opt_set_dict(filter->priv, &options);
-        if (ret < 0) {
-            av_log(filter, AV_LOG_ERROR, "Error applying options to the filter.\n");
-            goto fail;
-        }
-    }
-
-    if (filter->filter->init_opaque)
-        ret = filter->filter->init_opaque(filter, args, opaque);
-    else if (filter->filter->init)
-        ret = filter->filter->init(filter, args);
-    else if (filter->filter->init_dict)
-        ret = filter->filter->init_dict(filter, &options);
+    ret = avfilter_init_dict(filter, &options);
     if (ret < 0)
         goto fail;
 
@@ -905,6 +925,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;
@@ -967,7 +988,17 @@ static int ff_filter_frame_framed(AVFilterLink *link, AVFrame *frame)
     }
 
     pts = out->pts;
+    if (dstctx->enable_str) {
+        int64_t pos = av_frame_get_pkt_pos(out);
+        dstctx->var_values[VAR_N] = link->frame_count;
+        dstctx->var_values[VAR_T] = pts == AV_NOPTS_VALUE ? NAN : pts * av_q2d(link->time_base);
+        dstctx->var_values[VAR_POS] = pos == -1 ? NAN : pos;
+        if (!av_expr_eval(dstctx->enable, dstctx->var_values, NULL))
+            filter_frame = dst->passthrough_filter_frame ? dst->passthrough_filter_frame
+                                                         : default_filter_frame;
+    }
     ret = filter_frame(link, out);
+    link->frame_count++;
     link->frame_requested = 0;
     ff_update_link_current_pts(link, pts);
     return ret;