[FFmpeg-devel] [PATCH 1/3] libavfilter/scale2ref: Add constants for the non-ref input

Kevin Mark kmark937 at gmail.com
Sat May 27 17:10:44 EEST 2017


Variables pertaining to the non-reference video are now available when
using the scale2ref filter. This allows scaling a video with another
as a reference point while maintaining the original aspect ratio of
the non-reference video.

Consider the following graph: scale2ref=iw/6:-1 [scaled][ref]
This will scale [scaled] to 1/6 the width of [ref] while maintaining
the aspect ratio. This works well when the AR of [ref] is equal to
the AR of [scaled] only. What the above filter really does is
maintain the AR of [ref] when scaling [scaled]. So in all non-same-AR
situations [scaled] will appear stretched or compressed to conform to
the same AR of the reference video. Without doing this calculation
externally there is no way to scale in reference to another input
while maintaining AR in libavfilter.

To make this possible, we introduce seven new constants to be used
in the w and h expressions only in the scale2ref filter:

 * other_w/other_h: width/height of the input video being scaled
 * other_a: aspect ratio of the input video being scaled
 * other_sar: sample aspect ratio of the input video being scaled
 * other_dar: display aspect ratio of the input video being scaled
 * other_hsub/other_vsub: horiz/vert chroma subsample vals of input

Of course the only new constants needed for maintaining the AR is
other_w/other_h or other_a but adding additional constants in line of
what is available for in/out allows for other scaling possibilities I
have not imagined.

So to now scale a video to 1/6 the size of another video using the
width and maintaining its own aspect ratio you can do this:

scale2ref=iw/6:iw/(6*other_a) [scaled][ref]

This is ideal for picture-in-picture configurations where you could
have a square or 4:3 video overlaid on a corner of a larger 16:9
feed all while keeping the scaled video in the corner at its correct
aspect ratio and always the same size relative to the larger video.

I am not fully sold on the other_ prefix. It works but I'm open to
suggestions on something that is more concise or clear. Unfortunately
in_ is most clear but that is taken by the reference input, which in
my opinion, should've been labeled as such (not as in_ but perhaps
ref_). What is the best way to refer to this? The other input? The
scaled input? The non-reference input?

I've tried to re-use as much code as possible. I could not find a way
to avoid duplication of the var_names array. It must now be kept in
sync with the other (the normal one and the scale2ref one) for
everything to work which does not seem ideal. For every new variable
introduced/removed into/from the normal scale filter one must be
added/removed to/from the scale2ref version. Suggestions on how to
avoid var_names duplication are welcome.

var_values has been increased to always be large enough for the
additional scale2ref variables. I do not forsee this being a problem
as the names variable will always be the correct size. From my
understanding of av_expr_parse_and_eval it will stop processing
variables when it runs out of names even though there may be
additional (potentially uninitialized) entries in the values array.
The ideal solution here would be using a variable-length array but
that is unsupported in C90.

This patch does not remove any functionality and is strictly a
feature patch. There are no API changes. Behavior does not change for
any previously valid inputs.

Signed-off-by: Kevin Mark <kmark937 at gmail.com>
---
 libavfilter/scale.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 68 insertions(+), 4 deletions(-)

diff --git a/libavfilter/scale.c b/libavfilter/scale.c
index 50cd442849..9b4846719d 100644
--- a/libavfilter/scale.c
+++ b/libavfilter/scale.c
@@ -60,6 +60,49 @@ 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",
+    "other_w",
+    "other_h",
+    "other_a",
+    "other_sar",
+    "other_dar",
+    "other_hsub",
+    "other_vsub",
+    NULL
+};
+
+enum var_name_scale2ref {
+    VAR_S2R_OTHER_W,
+    VAR_S2R_OTHER_H,
+    VAR_S2R_OTHER_A,
+    VAR_S2R_OTHER_SAR,
+    VAR_S2R_OTHER_DAR,
+    VAR_S2R_OTHER_HSUB,
+    VAR_S2R_OTHER_VSUB,
+    VARS_S2R_NB
+};
+
 int ff_scale_eval_dimensions(void *log_ctx,
     const char *w_expr, const char *h_expr,
     AVFilterLink *inlink, AVFilterLink *outlink,
@@ -72,7 +115,16 @@ int ff_scale_eval_dimensions(void *log_ctx,
     int factor_w, factor_h;
     int eval_w, eval_h;
     int ret;
-    double var_values[VARS_NB], res;
+    const char scale2ref = outlink->src->inputs[1] == inlink;
+    double var_values[VARS_NB + VARS_S2R_NB], res;
+    const AVPixFmtDescriptor *other_desc;
+    const AVFilterLink *other;
+    const char *const *names = scale2ref ? var_names_scale2ref : var_names;
+
+    if (scale2ref) {
+        other = outlink->src->inputs[0];
+        other_desc = av_pix_fmt_desc_get(other->format);
+    }
 
     var_values[VAR_PI]    = M_PI;
     var_values[VAR_PHI]   = M_PHI;
@@ -90,20 +142,32 @@ 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_OTHER_W] = other->w;
+        var_values[VARS_NB + VAR_S2R_OTHER_H] = other->h;
+        var_values[VARS_NB + VAR_S2R_OTHER_A] = (double) other->w / other->h;
+        var_values[VARS_NB + VAR_S2R_OTHER_SAR] = other->sample_aspect_ratio.num ?
+            (double) other->sample_aspect_ratio.num / other->sample_aspect_ratio.den : 1;
+        var_values[VARS_NB + VAR_S2R_OTHER_DAR] = var_values[VARS_NB + VAR_S2R_OTHER_A] *
+            var_values[VARS_NB + VAR_S2R_OTHER_SAR];
+        var_values[VARS_NB + VAR_S2R_OTHER_HSUB] = 1 << other_desc->log2_chroma_w;
+        var_values[VARS_NB + VAR_S2R_OTHER_VSUB] = 1 << other_desc->log2_chroma_h;
+    }
+
     /* evaluate width and height */
     av_expr_parse_and_eval(&res, (expr = w_expr),
-                           var_names, var_values,
+                           names, var_values,
                            NULL, NULL, NULL, NULL, NULL, 0, log_ctx);
     eval_w = var_values[VAR_OUT_W] = var_values[VAR_OW] = res;
 
     if ((ret = av_expr_parse_and_eval(&res, (expr = h_expr),
-                                      var_names, var_values,
+                                      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] = res;
     /* evaluate again the width, as it may depend on the output height */
     if ((ret = av_expr_parse_and_eval(&res, (expr = w_expr),
-                                      var_names, var_values,
+                                      names, var_values,
                                       NULL, NULL, NULL, NULL, NULL, 0, log_ctx)) < 0)
         goto fail;
     eval_w = res;
-- 
2.13.0



More information about the ffmpeg-devel mailing list