@@ -17201,6 +17201,19 @@ evaluate expressions for each incoming frame
Default value is @samp{init}.
@end table
+@subsection Commands
+
+This filter supports the following command:
+
+@table @option
+@item dims
+Updates all eight coordinate values from one colon-separated parameter.
+@example
+0.0 [enter] perspective dims 0:0:300:0:0:300:300:300;
+5.0 [enter] perspective dims 0:0:300:50:0:300:300:350;
+@end example
+@end table
+
@section phase
Delay interlaced video by one field time so that the field order changes.
@@ -19,13 +19,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
+#include <libavutil/avstring.h>
#include "libavutil/avassert.h"
#include "libavutil/eval.h"
#include "libavutil/imgutils.h"
#include "libavutil/pixdesc.h"
#include "libavutil/opt.h"
#include "avfilter.h"
-#include "formats.h"
#include "internal.h"
#include "video.h"
@@ -36,9 +36,13 @@
Store expressions to avoid parsing them for each change. Support a command re-assigning all corners for the perspective. Signed-off-by: Simon Binder <oss@simonbinder.eu> --- doc/filters.texi | 13 ++++ libavfilter/vf_perspective.c | 114 ++++++++++++++++++++++++++++------- 2 files changed, 106 insertions(+), 21 deletions(-) #define LINEAR 0 #define CUBIC 1 +static const char *const var_names[] = { "W", "H", "in", "on", NULL }; +enum { VAR_W, VAR_H, VAR_IN, VAR_ON, VAR_VARS_NB }; + typedef struct PerspectiveContext { const AVClass *class; char *expr_str[4][2]; + AVExpr* expr[4][2]; double ref[4][2]; int32_t (*pv)[2]; int32_t coeff[SUB_PIXELS][4]; @@ -49,6 +53,7 @@ typedef struct PerspectiveContext { int nb_planes; int sense; int eval_mode; + double var_values[VAR_VARS_NB]; int (*perspective)(AVFilterContext *ctx, void *arg, int job, int nb_jobs); @@ -117,34 +122,31 @@ static inline double get_coeff(double d) return coeff; } -static const char *const var_names[] = { "W", "H", "in", "on", NULL }; -enum { VAR_W, VAR_H, VAR_IN, VAR_ON, VAR_VARS_NB }; - -static int calc_persp_luts(AVFilterContext *ctx, AVFilterLink *inlink) +static void set_variables_for_link(AVFilterContext *ctx, AVFilterLink *inlink) { PerspectiveContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; + + s->var_values[VAR_W] = inlink->w; + s->var_values[VAR_H] = inlink->h; + s->var_values[VAR_IN] = inlink->frame_count_out + 1; + s->var_values[VAR_ON] = outlink->frame_count_in + 1; +} + +static int calc_persp_luts(AVFilterContext *ctx) +{ + PerspectiveContext *s = ctx->priv; double (*ref)[2] = s->ref; - double values[VAR_VARS_NB] = { [VAR_W] = inlink->w, [VAR_H] = inlink->h, - [VAR_IN] = inlink->frame_count_out + 1, - [VAR_ON] = outlink->frame_count_in + 1 }; - const int h = values[VAR_H]; - const int w = values[VAR_W]; + const int h = s->var_values[VAR_H]; + const int w = s->var_values[VAR_W]; double x0, x1, x2, x3, x4, x5, x6, x7, x8, q; double t0, t1, t2, t3; - int x, y, i, j, ret; + int x, y, i, j; for (i = 0; i < 4; i++) { for (j = 0; j < 2; j++) { - if (!s->expr_str[i][j]) - return AVERROR(EINVAL); - ret = av_expr_parse_and_eval(&s->ref[i][j], s->expr_str[i][j], - var_names, &values[0], - NULL, NULL, NULL, NULL, - 0, 0, ctx); - if (ret < 0) - return ret; + s->ref[i][j] = av_expr_eval(s->expr[i][j], s->var_values, s); } } @@ -238,8 +240,27 @@ static int config_input(AVFilterLink *inlink) if (!s->pv) return AVERROR(ENOMEM); + // Parse coordinate expressions + for (i = 0; i < 4; i++) { + for (j = 0; j < 2; j++) { + av_expr_free(s->expr[i][j]); + s->expr[i][j] = NULL; + + if (!s->expr_str[i][j]) + return AVERROR(EINVAL); + + if ((ret = av_expr_parse(&s->expr[i][j], s->expr_str[i][j], var_names, + NULL, NULL, NULL, NULL, 0, ctx)) < 0) { + av_log(ctx, AV_LOG_ERROR, + "Error occurred parsing coordinate '%s'\n", s->expr_str[i][j]); + return ret; + } + } + } + if (s->eval_mode == EVAL_MODE_INIT) { - if ((ret = calc_persp_luts(ctx, inlink)) < 0) { + set_variables_for_link(ctx, inlink); + if ((ret = calc_persp_luts(ctx)) < 0) { return ret; } } @@ -437,6 +458,49 @@ static av_cold int init(AVFilterContext *ctx) return 0; } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *arg, + char *res, int res_len, int flags) +{ + PerspectiveContext *s = ctx->priv; + int ret; + char *token; + AVExpr *old; + + if (!strcmp(cmd, "dims")) { + // Expect arg to be an 8-token argument containing the new coordinates + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 2; j++) { + if (!*arg) { + av_log(ctx, AV_LOG_ERROR,"Expected 8 expressions for the new coordinates\n"); + return AVERROR(EINVAL); + } + + token = av_get_token(&arg, ":"); + if (!token) + return AVERROR(ENOMEM); + arg++; + + old = s->expr[i][j]; + + if ((ret = av_expr_parse(&s->expr[i][j], token, var_names, + NULL, NULL, NULL, NULL, 0, ctx)) < 0) { + av_log(ctx, AV_LOG_ERROR, + "Error occurred parsing coordinate '%s'\n", token); + av_free(token); + return ret; + } + av_free(token); + av_expr_free(old); + } + } + + ret = calc_persp_luts(ctx); + } else + ret = AVERROR(ENOSYS); + + return ret; +} + static int filter_frame(AVFilterLink *inlink, AVFrame *frame) { AVFilterContext *ctx = inlink->dst; @@ -454,7 +518,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) av_frame_copy_props(out, frame); if (s->eval_mode == EVAL_MODE_FRAME) { - if ((ret = calc_persp_luts(ctx, inlink)) < 0) { + set_variables_for_link(ctx, inlink); + if ((ret = calc_persp_luts(ctx)) < 0) { av_frame_free(&out); return ret; } @@ -484,6 +549,12 @@ static av_cold void uninit(AVFilterContext *ctx) PerspectiveContext *s = ctx->priv; av_freep(&s->pv); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 2; j++) { + av_expr_free(s->expr[i][j]); + s->expr[i][j] = NULL; + } + } } static const AVFilterPad perspective_inputs[] = { @@ -508,6 +579,7 @@ const AVFilter ff_vf_perspective = { .priv_size = sizeof(PerspectiveContext), .init = init, .uninit = uninit, + .process_command = &process_command, FILTER_INPUTS(perspective_inputs), FILTER_OUTPUTS(perspective_outputs), FILTER_PIXFMTS_ARRAY(pix_fmts), -- 2.35.1