[FFmpeg-devel] [PATCH] avfilter/scale: allow dynamic output via expressions

Gyan ffmpeg at gyani.pro
Tue Nov 12 15:50:47 EET 2019


This patch relies on the proposed addition to the eval API at 
https://patchwork.ffmpeg.org/patch/16109/

FATE passes.

Gyan
-------------- next part --------------
From a435e1ba5455cec67df16db85d5c47f7e4cd9797 Mon Sep 17 00:00:00 2001
From: Gyan Doshi <ffmpeg at gyani.pro>
Date: Tue, 12 Nov 2019 19:04:54 +0530
Subject: [PATCH] avfilter/scale: allow dynamic output via expressions

At present, dynamic output can be had from scale and scale2ref
filters by sending commands or varying the resolution of the
input (in scale) or reference stream (in scale2ref). This is
unwieldy when an interpolated or otherwise animated output is
desired.

This commit introduces standard temporal variables that can be used
in width and height expressions, so dynamic output can be configured
at initialization or via commands. In order to achieve this, external
dependency on libavfilter/scale.c has been removed and equivalent
functionality introduced inline.

Such varying output ought to be sent to filters which can
accommodate them, such as the overlay filter.
---
 libavfilter/Makefile   |   4 +-
 libavfilter/scale.c    |  72 +------
 libavfilter/vf_scale.c | 436 +++++++++++++++++++++++++++++++++++++----
 3 files changed, 408 insertions(+), 104 deletions(-)

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index fce930360d..f1f6994574 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -358,12 +358,12 @@ OBJS-$(CONFIG_ROBERTS_OPENCL_FILTER)         += vf_convolution_opencl.o opencl.o
                                                 opencl/convolution.o
 OBJS-$(CONFIG_ROTATE_FILTER)                 += vf_rotate.o
 OBJS-$(CONFIG_SAB_FILTER)                    += vf_sab.o
-OBJS-$(CONFIG_SCALE_FILTER)                  += vf_scale.o scale.o
+OBJS-$(CONFIG_SCALE_FILTER)                  += vf_scale.o
 OBJS-$(CONFIG_SCALE_CUDA_FILTER)             += vf_scale_cuda.o vf_scale_cuda.ptx.o
 OBJS-$(CONFIG_SCALE_NPP_FILTER)              += vf_scale_npp.o scale.o
 OBJS-$(CONFIG_SCALE_QSV_FILTER)              += vf_scale_qsv.o
 OBJS-$(CONFIG_SCALE_VAAPI_FILTER)            += vf_scale_vaapi.o scale.o vaapi_vpp.o
-OBJS-$(CONFIG_SCALE2REF_FILTER)              += vf_scale.o scale.o
+OBJS-$(CONFIG_SCALE2REF_FILTER)              += vf_scale.o
 OBJS-$(CONFIG_SCROLL_FILTER)                 += vf_scroll.o
 OBJS-$(CONFIG_SELECT_FILTER)                 += f_select.o
 OBJS-$(CONFIG_SELECTIVECOLOR_FILTER)         += vf_selectivecolor.o
diff --git a/libavfilter/scale.c b/libavfilter/scale.c
index eaee95fac6..668aa04622 100644
--- a/libavfilter/scale.c
+++ b/libavfilter/scale.c
@@ -60,49 +60,6 @@ enum var_name {
     VARS_NB
 };
 
-/**
- * This must be kept in sync with var_names so that it is always a
- * complete list of var_names with the scale2ref specific names
- * appended. scale2ref values must appear in the order they appear
- * in the var_name_scale2ref enum but also be below all of the
- * non-scale2ref specific values.
- */
-static const char *const var_names_scale2ref[] = {
-    "PI",
-    "PHI",
-    "E",
-    "in_w",   "iw",
-    "in_h",   "ih",
-    "out_w",  "ow",
-    "out_h",  "oh",
-    "a",
-    "sar",
-    "dar",
-    "hsub",
-    "vsub",
-    "ohsub",
-    "ovsub",
-    "main_w",
-    "main_h",
-    "main_a",
-    "main_sar",
-    "main_dar", "mdar",
-    "main_hsub",
-    "main_vsub",
-    NULL
-};
-
-enum var_name_scale2ref {
-    VAR_S2R_MAIN_W,
-    VAR_S2R_MAIN_H,
-    VAR_S2R_MAIN_A,
-    VAR_S2R_MAIN_SAR,
-    VAR_S2R_MAIN_DAR, VAR_S2R_MDAR,
-    VAR_S2R_MAIN_HSUB,
-    VAR_S2R_MAIN_VSUB,
-    VARS_S2R_NB
-};
-
 int ff_scale_eval_dimensions(void *log_ctx,
     const char *w_expr, const char *h_expr,
     AVFilterLink *inlink, AVFilterLink *outlink,
@@ -115,16 +72,7 @@ int ff_scale_eval_dimensions(void *log_ctx,
     int factor_w, factor_h;
     int eval_w, eval_h;
     int ret;
-    const char scale2ref = outlink->src->nb_inputs == 2 && outlink->src->inputs[1] == inlink;
-    double var_values[VARS_NB + VARS_S2R_NB], res;
-    const AVPixFmtDescriptor *main_desc;
-    const AVFilterLink *main_link;
-    const char *const *names = scale2ref ? var_names_scale2ref : var_names;
-
-    if (scale2ref) {
-        main_link = outlink->src->inputs[0];
-        main_desc = av_pix_fmt_desc_get(main_link->format);
-    }
+    double var_values[VARS_NB], res;
 
     var_values[VAR_PI]    = M_PI;
     var_values[VAR_PHI]   = M_PHI;
@@ -142,32 +90,20 @@ int ff_scale_eval_dimensions(void *log_ctx,
     var_values[VAR_OHSUB] = 1 << out_desc->log2_chroma_w;
     var_values[VAR_OVSUB] = 1 << out_desc->log2_chroma_h;
 
-    if (scale2ref) {
-        var_values[VARS_NB + VAR_S2R_MAIN_W] = main_link->w;
-        var_values[VARS_NB + VAR_S2R_MAIN_H] = main_link->h;
-        var_values[VARS_NB + VAR_S2R_MAIN_A] = (double) main_link->w / main_link->h;
-        var_values[VARS_NB + VAR_S2R_MAIN_SAR] = main_link->sample_aspect_ratio.num ?
-            (double) main_link->sample_aspect_ratio.num / main_link->sample_aspect_ratio.den : 1;
-        var_values[VARS_NB + VAR_S2R_MAIN_DAR] = var_values[VARS_NB + VAR_S2R_MDAR] =
-            var_values[VARS_NB + VAR_S2R_MAIN_A] * var_values[VARS_NB + VAR_S2R_MAIN_SAR];
-        var_values[VARS_NB + VAR_S2R_MAIN_HSUB] = 1 << main_desc->log2_chroma_w;
-        var_values[VARS_NB + VAR_S2R_MAIN_VSUB] = 1 << main_desc->log2_chroma_h;
-    }
-
     /* evaluate width and height */
     av_expr_parse_and_eval(&res, (expr = w_expr),
-                           names, var_values,
+                           var_names, var_values,
                            NULL, NULL, NULL, NULL, NULL, 0, log_ctx);
     eval_w = var_values[VAR_OUT_W] = var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
 
     if ((ret = av_expr_parse_and_eval(&res, (expr = h_expr),
-                                      names, var_values,
+                                      var_names, var_values,
                                       NULL, NULL, NULL, NULL, NULL, 0, log_ctx)) < 0)
         goto fail;
     eval_h = var_values[VAR_OUT_H] = var_values[VAR_OH] = (int) res == 0 ? inlink->h : (int) res;
     /* evaluate again the width, as it may depend on the output height */
     if ((ret = av_expr_parse_and_eval(&res, (expr = w_expr),
-                                      names, var_values,
+                                      var_names, var_values,
                                       NULL, NULL, NULL, NULL, NULL, 0, log_ctx)) < 0)
         goto fail;
     eval_w = (int) res == 0 ? inlink->w : (int) res;
diff --git a/libavfilter/vf_scale.c b/libavfilter/vf_scale.c
index 41ddec7661..cbacd34827 100644
--- a/libavfilter/vf_scale.c
+++ b/libavfilter/vf_scale.c
@@ -29,9 +29,9 @@
 #include "avfilter.h"
 #include "formats.h"
 #include "internal.h"
-#include "scale.h"
 #include "video.h"
 #include "libavutil/avstring.h"
+#include "libavutil/eval.h"
 #include "libavutil/internal.h"
 #include "libavutil/mathematics.h"
 #include "libavutil/opt.h"
@@ -41,6 +41,62 @@
 #include "libavutil/avassert.h"
 #include "libswscale/swscale.h"
 
+static const char *const var_names[] = {
+    "in_w",   "iw",
+    "in_h",   "ih",
+    "out_w",  "ow",
+    "out_h",  "oh",
+    "a",
+    "sar",
+    "dar",
+    "hsub",
+    "vsub",
+    "ohsub",
+    "ovsub",
+    "n",
+    "t",
+    "pos",
+    "main_w",
+    "main_h",
+    "main_a",
+    "main_sar",
+    "main_dar", "mdar",
+    "main_hsub",
+    "main_vsub",
+    "main_n",
+    "main_t",
+    "main_pos",
+    NULL
+};
+
+enum var_name {
+    VAR_IN_W,   VAR_IW,
+    VAR_IN_H,   VAR_IH,
+    VAR_OUT_W,  VAR_OW,
+    VAR_OUT_H,  VAR_OH,
+    VAR_A,
+    VAR_SAR,
+    VAR_DAR,
+    VAR_HSUB,
+    VAR_VSUB,
+    VAR_OHSUB,
+    VAR_OVSUB,
+    VAR_N,
+    VAR_T,
+    VAR_POS,
+    VAR_S2R_MAIN_W,
+    VAR_S2R_MAIN_H,
+    VAR_S2R_MAIN_A,
+    VAR_S2R_MAIN_SAR,
+    VAR_S2R_MAIN_DAR, VAR_S2R_MDAR,
+    VAR_S2R_MAIN_HSUB,
+    VAR_S2R_MAIN_VSUB,
+    VAR_S2R_MAIN_N,
+    VAR_S2R_MAIN_T,
+    VAR_S2R_MAIN_POS,
+    VARS_NB
+};
+
 enum EvalMode {
     EVAL_MODE_INIT,
     EVAL_MODE_FRAME,
@@ -72,6 +128,10 @@ typedef struct ScaleContext {
 
     char *w_expr;               ///< width  expression string
     char *h_expr;               ///< height expression string
+    AVExpr *w_pexpr;
+    AVExpr *h_pexpr;
+    double var_values[VARS_NB];
+
     char *flags_str;
 
     char *in_color_matrix;
@@ -96,6 +156,8 @@ typedef struct ScaleContext {
 
 AVFilter ff_vf_scale2ref;
 
+static int check_exprs(AVFilterContext *ctx);
+
 static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts)
 {
     ScaleContext *scale = ctx->priv;
@@ -107,8 +169,10 @@ static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts)
             return AVERROR(EINVAL);
     }
 
-    if (scale->w_expr && !scale->h_expr)
+    if (scale->w_expr && !scale->h_expr) {
+        av_log(ctx, AV_LOG_INFO, "No height expression found. Treating width expression as video size.\n");
         FFSWAP(char *, scale->w_expr, scale->size_str);
+    }
 
     if (scale->size_str) {
         char buf[32];
@@ -127,6 +191,26 @@ static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts)
     if (!scale->h_expr)
         av_opt_set(scale, "h", "ih", 0);
 
+    ret = av_expr_parse(&scale->w_pexpr, scale->w_expr,
+                            var_names,
+                            NULL, NULL, NULL, NULL, 0, ctx);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Cannot parse width expression: '%s'\n", scale->w_expr);
+        return ret;
+    }
+
+    ret = av_expr_parse(&scale->h_pexpr, scale->h_expr,
+                            var_names,
+                            NULL, NULL, NULL, NULL, 0, ctx);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Cannot parse height expression: '%s'\n", scale->h_expr);
+        return ret;
+    }
+
+    ret = check_exprs(ctx);
+    if (ret < 0)
+        return ret;
+
     av_log(ctx, AV_LOG_VERBOSE, "w:%s h:%s flags:'%s' interl:%d\n",
            scale->w_expr, scale->h_expr, (char *)av_x_if_null(scale->flags_str, ""), scale->interlaced);
 
@@ -149,6 +233,9 @@ static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts)
 static av_cold void uninit(AVFilterContext *ctx)
 {
     ScaleContext *scale = ctx->priv;
+    av_expr_free(scale->w_pexpr);
+    av_expr_free(scale->h_pexpr);
+    scale->w_pexpr = scale->h_pexpr = NULL;
     sws_freeContext(scale->sws);
     sws_freeContext(scale->isws[0]);
     sws_freeContext(scale->isws[1]);
@@ -194,6 +281,45 @@ static int query_formats(AVFilterContext *ctx)
     return 0;
 }
 
+static int check_exprs(AVFilterContext *ctx)
+{
+    ScaleContext *scale = ctx->priv;
+
+    if (av_expr_count_var(scale->w_pexpr, VAR_OUT_W, VAR_OW+1)) {
+        av_log(ctx, AV_LOG_ERROR, "Width expression cannot be self-referencing: '%s'.\n", scale->w_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if (av_expr_count_var(scale->h_pexpr, VAR_OUT_H, VAR_OH+1)) {
+        av_log(ctx, AV_LOG_ERROR, "Height expression cannot be self-referencing: '%s'.\n", scale->h_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if (av_expr_count_var(scale->w_pexpr, VAR_OUT_H, VAR_OH+1) &&
+        av_expr_count_var(scale->h_pexpr, VAR_OUT_W, VAR_OW+1)) {
+        av_log(ctx, AV_LOG_ERROR, "Circular expressions invalid for width '%s' and height '%s'.\n", scale->w_expr, scale->h_expr);
+        return AVERROR(EINVAL);
+    }
+
+    if (ctx->filter != &ff_vf_scale2ref &&
+        (av_expr_count_var(scale->w_pexpr, VAR_S2R_MAIN_W, VARS_NB) ||
+         av_expr_count_var(scale->h_pexpr, VAR_S2R_MAIN_W, VARS_NB))) {
+        av_log(ctx, AV_LOG_ERROR, "Expressions with scale2ref variables are not valid in scale filter.\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (scale->eval_mode == EVAL_MODE_INIT &&
+        (av_expr_count_var(scale->w_pexpr, VAR_N, VAR_POS+1) ||
+         av_expr_count_var(scale->w_pexpr, VAR_S2R_MAIN_N, VAR_S2R_MAIN_POS+1) ||
+         av_expr_count_var(scale->h_pexpr, VAR_N, VAR_POS+1) ||
+         av_expr_count_var(scale->h_pexpr, VAR_S2R_MAIN_N, VAR_S2R_MAIN_POS+1))) {
+        av_log(ctx, AV_LOG_ERROR, "Expressions with frame variables 'n', 't', 'pos' are not valid in init eval_mode.\n");
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
 static const int *parse_yuv_type(const char *s, enum AVColorSpace colorspace)
 {
     if (!s)
@@ -218,24 +344,105 @@ static const int *parse_yuv_type(const char *s, enum AVColorSpace colorspace)
     return sws_getCoefficients(colorspace);
 }
 
-static int config_props(AVFilterLink *outlink)
+static int scale_eval_dimensions(AVFilterContext *ctx)
 {
-    AVFilterContext *ctx = outlink->src;
-    AVFilterLink *inlink0 = outlink->src->inputs[0];
-    AVFilterLink *inlink  = ctx->filter == &ff_vf_scale2ref ?
-                            outlink->src->inputs[1] :
-                            outlink->src->inputs[0];
-    enum AVPixelFormat outfmt = outlink->format;
-    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
     ScaleContext *scale = ctx->priv;
+    const char scale2ref = ctx->filter == &ff_vf_scale2ref;
+    const AVFilterLink *inlink = scale2ref ? ctx->inputs[1] : ctx->inputs[0];
+    const AVFilterLink *outlink = ctx->outputs[0];
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
+    const AVPixFmtDescriptor *out_desc = av_pix_fmt_desc_get(outlink->format);
+    char *expr;
     int w, h;
+    int factor_w, factor_h;
+    int eval_w, eval_h;
     int ret;
 
-    if ((ret = ff_scale_eval_dimensions(ctx,
-                                        scale->w_expr, scale->h_expr,
-                                        inlink, outlink,
-                                        &w, &h)) < 0)
+    double res;
+    const AVPixFmtDescriptor *main_desc;
+    const AVFilterLink *main_link;
+
+    if (scale2ref) {
+        main_link = ctx->inputs[0];
+        main_desc = av_pix_fmt_desc_get(main_link->format);
+    }
+
+    scale->var_values[VAR_IN_W]  = scale->var_values[VAR_IW] = inlink->w;
+    scale->var_values[VAR_IN_H]  = scale->var_values[VAR_IH] = inlink->h;
+    scale->var_values[VAR_OUT_W] = scale->var_values[VAR_OW] = NAN;
+    scale->var_values[VAR_OUT_H] = scale->var_values[VAR_OH] = NAN;
+    scale->var_values[VAR_A]     = (double) inlink->w / inlink->h;
+    scale->var_values[VAR_SAR]   = inlink->sample_aspect_ratio.num ?
+        (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
+    scale->var_values[VAR_DAR]   = scale->var_values[VAR_A] * scale->var_values[VAR_SAR];
+    scale->var_values[VAR_HSUB]  = 1 << desc->log2_chroma_w;
+    scale->var_values[VAR_VSUB]  = 1 << desc->log2_chroma_h;
+    scale->var_values[VAR_OHSUB] = 1 << out_desc->log2_chroma_w;
+    scale->var_values[VAR_OVSUB] = 1 << out_desc->log2_chroma_h;
+
+    if (scale2ref) {
+        scale->var_values[VAR_S2R_MAIN_W] = main_link->w;
+        scale->var_values[VAR_S2R_MAIN_H] = main_link->h;
+        scale->var_values[VAR_S2R_MAIN_A] = (double) main_link->w / main_link->h;
+        scale->var_values[VAR_S2R_MAIN_SAR] = main_link->sample_aspect_ratio.num ?
+            (double) main_link->sample_aspect_ratio.num / main_link->sample_aspect_ratio.den : 1;
+        scale->var_values[VAR_S2R_MAIN_DAR] = scale->var_values[VAR_S2R_MDAR] =
+            scale->var_values[VAR_S2R_MAIN_A] * scale->var_values[VAR_S2R_MAIN_SAR];
+        scale->var_values[VAR_S2R_MAIN_HSUB] = 1 << main_desc->log2_chroma_w;
+        scale->var_values[VAR_S2R_MAIN_VSUB] = 1 << main_desc->log2_chroma_h;
+    }
+
+    if (!av_expr_count_var(scale->w_pexpr, VAR_OUT_H, VAR_OH+1)) {
+        res = av_expr_eval(scale->w_pexpr, scale->var_values, NULL);
+        if (isnan(res)) {
+        expr = scale->w_expr;
+            goto fail;
+        }
+        eval_w = scale->var_values[VAR_OUT_W] = scale->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
+    }
+
+    res = av_expr_eval(scale->h_pexpr, scale->var_values, NULL);
+    if (isnan(res)) {
+        expr = scale->h_expr;
         goto fail;
+    }
+    eval_h = scale->var_values[VAR_OUT_H] = scale->var_values[VAR_OH] = (int) res == 0 ? inlink->h : (int) res;
+
+    if (av_expr_count_var(scale->w_pexpr, VAR_OUT_H, VAR_OH+1)) {
+        res = av_expr_eval(scale->w_pexpr, scale->var_values, NULL);
+        if (isnan(res)) {
+        expr = scale->w_expr;
+            goto fail;
+        }
+        eval_w = scale->var_values[VAR_OUT_W] = scale->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res;
+    }
+
+    w = eval_w;
+    h = eval_h;
+
+    /* Check if it is requested that the result has to be divisible by a some
+     * factor (w or h = -n with n being the factor). */
+    factor_w = 1;
+    factor_h = 1;
+    if (w < -1) {
+        factor_w = -w;
+    }
+    if (h < -1) {
+        factor_h = -h;
+    }
+
+    if (w < 0 && h < 0) {
+        w = inlink->w;
+        h = inlink->h;
+    }
+
+    /* Make sure that the result is divisible by the factor we determined
+     * earlier. If no factor was set, nothing will happen as the default
+     * factor is 1 */
+    if (w < 0)
+        w = av_rescale(h, inlink->w, inlink->h * factor_w) * factor_w;
+    if (h < 0)
+        h = av_rescale(w, inlink->h, inlink->w * factor_h) * factor_h;
 
     /* Note that force_original_aspect_ratio may overwrite the previous set
      * dimensions so that it is not divisible by the set factors anymore
@@ -265,11 +472,42 @@ static int config_props(AVFilterLink *outlink)
 
     if (w > INT_MAX || h > INT_MAX ||
         (h * inlink->w) > INT_MAX  ||
-        (w * inlink->h) > INT_MAX)
+        (w * inlink->h) > INT_MAX) {
         av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n");
+        return AVERROR(EINVAL);
+    }
+    scale->w = w;
+    scale->h = h;
+
+    return 0;
 
-    outlink->w = w;
-    outlink->h = h;
+fail:
+    av_log(ctx, AV_LOG_ERROR,
+           "Error when evaluating the expression '%s'.\n", expr);
+    return ret;
+}
+
+static int config_props(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    ScaleContext *scale = ctx->priv;
+    AVFilterLink *inlink0 = ctx->inputs[0];
+    AVFilterLink *inlink  = ctx->filter == &ff_vf_scale2ref ?
+                            ctx->inputs[1] :
+                            ctx->inputs[0];
+    enum AVPixelFormat outfmt = outlink->format;
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
+    int ret;
+
+    if (scale->eval_mode == EVAL_MODE_INIT) {
+        if ((ret = scale_eval_dimensions(ctx)) < 0)
+            return ret;
+        outlink->w = scale->w;
+        outlink->h = scale->h;
+    } else {
+        outlink->w = scale->w ? scale->w : inlink->w;
+        outlink->h = scale->h ? scale->h : inlink->h;
+    }
 
     /* TODO: make algorithm configurable */
 
@@ -360,9 +598,6 @@ static int config_props(AVFilterLink *outlink)
            outlink->sample_aspect_ratio.num, outlink->sample_aspect_ratio.den,
            scale->flags);
     return 0;
-
-fail:
-    return ret;
 }
 
 static int config_props_ref(AVFilterLink *outlink)
@@ -412,30 +647,68 @@ static int scale_slice(AVFilterLink *link, AVFrame *out_buf, AVFrame *cur_pic, s
                          out,out_stride);
 }
 
+#define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts) * av_q2d(tb))
+
 static int scale_frame(AVFilterLink *link, AVFrame *in, AVFrame **frame_out)
 {
-    ScaleContext *scale = link->dst->priv;
+    AVFilterContext *ctx = link->dst;
+    ScaleContext *scale = ctx->priv;
     AVFilterLink *outlink = link->dst->outputs[0];
     AVFrame *out;
     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format);
     char buf[32];
     int in_range;
+    int frame_changed;
 
     *frame_out = NULL;
     if (in->colorspace == AVCOL_SPC_YCGCO)
         av_log(link->dst, AV_LOG_WARNING, "Detected unsupported YCgCo colorspace.\n");
 
-    if (  in->width  != link->w
-       || in->height != link->h
-       || in->format != link->format
-       || in->sample_aspect_ratio.den != link->sample_aspect_ratio.den || in->sample_aspect_ratio.num != link->sample_aspect_ratio.num) {
+
+    frame_changed = in->width  != link->w ||
+                    in->height != link->h ||
+                    in->format != link->format ||
+                    in->sample_aspect_ratio.den != link->sample_aspect_ratio.den ||
+                    in->sample_aspect_ratio.num != link->sample_aspect_ratio.num;
+
+    if (scale->eval_mode == EVAL_MODE_FRAME || frame_changed) {
         int ret;
 
+        if (scale->eval_mode == EVAL_MODE_FRAME &&
+            !frame_changed &&
+            ctx->filter != &ff_vf_scale2ref &&
+            !av_expr_count_var(scale->w_pexpr,VAR_N,VAR_POS+1) &&
+            !av_expr_count_var(scale->h_pexpr,VAR_N,VAR_POS+1) &&
+            scale->w && scale->h)
+            goto scale;
+
         if (scale->eval_mode == EVAL_MODE_INIT) {
             snprintf(buf, sizeof(buf)-1, "%d", outlink->w);
             av_opt_set(scale, "w", buf, 0);
             snprintf(buf, sizeof(buf)-1, "%d", outlink->h);
             av_opt_set(scale, "h", buf, 0);
+
+            av_expr_free(scale->w_pexpr);
+            av_expr_free(scale->h_pexpr);
+            scale->w_pexpr = scale->h_pexpr = NULL;
+
+            av_expr_parse(&scale->w_pexpr, scale->w_expr,
+                          var_names,
+                          NULL, NULL, NULL, NULL, 0, ctx);
+
+            av_expr_parse(&scale->h_pexpr, scale->h_expr,
+                          var_names,
+                          NULL, NULL, NULL, NULL, 0, ctx);
+        }
+
+        if (ctx->filter == &ff_vf_scale2ref) {
+            scale->var_values[VAR_S2R_MAIN_N] = link->frame_count_out;
+            scale->var_values[VAR_S2R_MAIN_T] = TS2T(in->pts, link->time_base);
+            scale->var_values[VAR_S2R_MAIN_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos;
+        } else {
+            scale->var_values[VAR_N] = link->frame_count_out;
+            scale->var_values[VAR_T] = TS2T(in->pts, link->time_base);
+            scale->var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos;
         }
 
         link->dst->inputs[0]->format = in->format;
@@ -445,10 +718,15 @@ static int scale_frame(AVFilterLink *link, AVFrame *in, AVFrame **frame_out)
         link->dst->inputs[0]->sample_aspect_ratio.den = in->sample_aspect_ratio.den;
         link->dst->inputs[0]->sample_aspect_ratio.num = in->sample_aspect_ratio.num;
 
+        if (scale->eval_mode == EVAL_MODE_FRAME &&
+            (ret = scale_eval_dimensions(ctx)) < 0)
+            return ret;
+
         if ((ret = config_props(outlink)) < 0)
             return ret;
     }
 
+scale:
     if (!scale->sws) {
         *frame_out = in;
         return 0;
@@ -555,7 +833,31 @@ static int filter_frame(AVFilterLink *link, AVFrame *in)
 
 static int filter_frame_ref(AVFilterLink *link, AVFrame *in)
 {
+    ScaleContext *scale = link->dst->priv;
     AVFilterLink *outlink = link->dst->outputs[1];
+    int frame_changed;
+
+    frame_changed = in->width  != link->w ||
+                    in->height != link->h ||
+                    in->format != link->format ||
+                    in->sample_aspect_ratio.den != link->sample_aspect_ratio.den ||
+                    in->sample_aspect_ratio.num != link->sample_aspect_ratio.num;
+
+    if (frame_changed) {
+        link->format = in->format;
+        link->w = in->width;
+        link->h = in->height;
+        link->sample_aspect_ratio.num = in->sample_aspect_ratio.num;
+        link->sample_aspect_ratio.den = in->sample_aspect_ratio.den;
+
+        config_props_ref(outlink);
+    }
+
+    if (scale->eval_mode == EVAL_MODE_FRAME) {
+        scale->var_values[VAR_N] = link->frame_count_out;
+        scale->var_values[VAR_T] = TS2T(in->pts, link->time_base);
+        scale->var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos;
+    }
 
     return ff_filter_frame(outlink, in);
 }
@@ -564,19 +866,85 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar
                            char *res, int res_len, int flags)
 {
     ScaleContext *scale = ctx->priv;
-    int ret;
+    AVFilterLink *outlink = ctx->outputs[0];
+    char *old_w_str, *old_h_str;
+    AVExpr *old_w_pexpr, *old_h_pexpr;
+    int ret, w = 0, h = 0;
 
-    if (   !strcmp(cmd, "width")  || !strcmp(cmd, "w")
-        || !strcmp(cmd, "height") || !strcmp(cmd, "h")) {
+    w = !strcmp(cmd, "width")  || !strcmp(cmd, "w");
+    h = !strcmp(cmd, "height")  || !strcmp(cmd, "h");
 
-        int old_w = scale->w;
-        int old_h = scale->h;
-        AVFilterLink *outlink = ctx->outputs[0];
+    if (w || h) {
+
+        if (w) {
+            old_w_str = av_strdup(scale->w_expr);
+            if (!old_w_str)
+                return AVERROR(ENOMEM);
+            old_w_pexpr = scale->w_pexpr;
+            scale->w_pexpr = NULL;
+        }
+
+        if (h) {
+            old_h_str = av_strdup(scale->h_expr);
+            if (!old_h_str)
+                return AVERROR(ENOMEM);
+            old_h_pexpr = scale->h_pexpr;
+            scale->h_pexpr = NULL;
+        }
 
         av_opt_set(scale, cmd, args, 0);
-        if ((ret = config_props(outlink)) < 0) {
-            scale->w = old_w;
-            scale->h = old_h;
+
+        if (w) {
+            ret = av_expr_parse(&scale->w_pexpr, scale->w_expr,
+                            var_names,
+                            NULL, NULL, NULL, NULL, 0, ctx);
+            if (ret < 0) {
+                av_log(ctx, AV_LOG_ERROR, "Cannot parse width expression: '%s'\n", scale->w_expr);
+                av_opt_set(scale, "w", old_w_str, 0);
+                av_free(old_w_str);
+                scale->w_pexpr = old_w_pexpr;
+                return ret;
+            }
+        }
+
+        if (h) {
+            ret = av_expr_parse(&scale->h_pexpr, scale->h_expr,
+                            var_names,
+                            NULL, NULL, NULL, NULL, 0, ctx);
+            if (ret < 0) {
+                av_log(ctx, AV_LOG_ERROR, "Cannot parse height expression: '%s'\n", scale->h_expr);
+                av_opt_set(scale, "h", old_h_str, 0);
+                av_free(old_h_str);
+                scale->h_pexpr = old_h_pexpr;
+                return ret;
+            }
+        }
+
+        if ((ret = check_exprs(ctx)) < 0 ||
+            (ret = config_props(outlink)) < 0) {
+            if (w) {
+                av_opt_set(scale, "w", old_w_str, 0);
+                av_free(old_w_str);
+                av_expr_free(scale->w_pexpr);
+                scale->w_pexpr = old_w_pexpr;
+            }
+            if (h) {
+                av_opt_set(scale, "h", old_h_str, 0);
+                av_free(old_h_str);
+                av_expr_free(scale->h_pexpr);
+                scale->h_pexpr = old_h_pexpr;
+            }
+            av_log(ctx, AV_LOG_ERROR, "Command failed. Continuing with existing parameters.\n");
+            return ret;
+        }
+
+        if (w) {
+            av_free(old_w_str);
+            av_expr_free(old_w_pexpr);
+        }
+        if (h) {
+            av_free(old_h_str);
+            av_expr_free(old_h_pexpr);
         }
     } else
         ret = AVERROR(ENOSYS);
-- 
2.24.0



More information about the ffmpeg-devel mailing list