From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id 7524C4E677 for ; Wed, 11 Jun 2025 11:53:41 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id 61E3368C216; Wed, 11 Jun 2025 14:53:31 +0300 (EEST) Received: from mail.qult.net (unknown [78.193.33.39]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id 2ACB368BDA7 for ; Wed, 11 Jun 2025 14:53:25 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=qult.net; s=x; h=Content-Transfer-Encoding:Content-Type:MIME-Version:Message-ID:Subject :To:From:Date:Sender:Reply-To:Cc:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To: References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post: List-Owner:List-Archive; bh=QTdHTXhnmJwWt38XMlzS5u8gkWLgBm+b1ftcUiygOjU=; b=l UdRF9arxLPFcZ/jaXV8Kvc2bx2P/aAzoeqizmsPjm7wDViglz5YeynDP9WvDimnHbzPEQoa6L/whH jAsuo8ToBzhYJT/B3W8Es/ZHfu14Ww1CkdZh0yd2vGQ4ShgZK1YlSgOuqgfKf30ZXkXaCsg8vOADg h6BOfNjIaXpE25Pc=; Received: from [2001:470:60ee:0:da5e:d3ff:fe0e:33f5] (helo=zenon.qult.net) by mail.qult.net with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.97) (envelope-from ) id 1uPJcY-0000000BvLE-0bM4 for ffmpeg-devel@ffmpeg.org; Wed, 11 Jun 2025 13:27:54 +0200 Received: from ig by zenon.qult.net with local (Exim 4.98.1) (envelope-from ) id 1uPJcS-00000008kwa-0ZlL for ffmpeg-devel@ffmpeg.org; Wed, 11 Jun 2025 13:27:48 +0200 Date: Wed, 11 Jun 2025 13:27:48 +0200 From: Ignacy =?utf-8?B?R2F3xJlkemtp?= To: ffmpeg-devel@ffmpeg.org Message-ID: <7qh4tdsvzgwnkcvibx57skqo462ewupskuezbecq3megy4wubo@awbf6liszgu2> MIME-Version: 1.0 Content-Disposition: inline Subject: [FFmpeg-devel] [RFC 1/1] avfilter/f_sendcmd: add format expressions X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Content-Type: text/plain; charset="iso-8859-2" Content-Transfer-Encoding: quoted-printable Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Archived-At: List-Archive: List-Post: Add support for a "format" flag (exclusive with "expr") which indicates that the argument is a format string followed by expressions to be evaluated and formatted and the result sent as a string. Only simple 'f' format is supported as expressions are evaluated to double. Signed-off-by: Ignacy Gaw=EAdzki --- libavfilter/f_sendcmd.c | 161 +++++++++++++++++++++++++++++++++++----- 1 file changed, 143 insertions(+), 18 deletions(-) diff --git a/libavfilter/f_sendcmd.c b/libavfilter/f_sendcmd.c index 9201fb5381..fadf1a4956 100644 --- a/libavfilter/f_sendcmd.c +++ b/libavfilter/f_sendcmd.c @@ -37,9 +37,10 @@ #include "audio.h" #include "video.h" = -#define COMMAND_FLAG_ENTER 1 -#define COMMAND_FLAG_LEAVE 2 -#define COMMAND_FLAG_EXPR 4 +#define COMMAND_FLAG_ENTER 1 +#define COMMAND_FLAG_LEAVE 2 +#define COMMAND_FLAG_EXPR 4 +#define COMMAND_FLAG_FORMAT 8 = static const char *const var_names[] =3D { "N", /* frame number */ @@ -67,7 +68,8 @@ enum var_name { = static inline char *make_command_flags_str(AVBPrint *pbuf, int flags) { - static const char * const flag_strings[] =3D { "enter", "leave", "expr= " }; + static const char * const flag_strings[] =3D { "enter", "leave", + "expr", "format" }; int i, is_first =3D 1; = av_bprint_init(pbuf, 0, AV_BPRINT_SIZE_AUTOMATIC); @@ -159,6 +161,7 @@ static int parse_command(Command *cmd, int cmd_count, i= nt interval_count, if (!strncmp(*buf, "enter", strlen("enter"))) cmd->flags = |=3D COMMAND_FLAG_ENTER; else if (!strncmp(*buf, "leave", strlen("leave"))) cmd->flags = |=3D COMMAND_FLAG_LEAVE; else if (!strncmp(*buf, "expr", strlen("expr"))) cmd->flags = |=3D COMMAND_FLAG_EXPR; + else if (!strncmp(*buf, "format", strlen("format"))) cmd->flag= s |=3D COMMAND_FLAG_FORMAT; else { char flag_buf[64]; av_strlcpy(flag_buf, *buf, sizeof(flag_buf)); @@ -191,6 +194,13 @@ static int parse_command(Command *cmd, int cmd_count, = int interval_count, cmd->flags =3D COMMAND_FLAG_ENTER; } = + if ((cmd->flags & COMMAND_FLAG_EXPR) + && (cmd->flags & COMMAND_FLAG_FORMAT)) { + av_log(log_ctx, AV_LOG_ERROR, + "Invalid flags: expr and format are mutually exclusive\n"); + return AVERROR(EINVAL); + } + *buf +=3D strspn(*buf, SPACES); cmd->target =3D av_get_token(buf, COMMAND_DELIMS); if (!cmd->target || !cmd->target[0]) { @@ -480,6 +490,65 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&s->intervals); } = +static int format_expr(char **dest, char *format, + int dargc, const double dargv[]) +{ + char *p =3D format, *begin =3D format; + size_t size =3D 64, len; + int n =3D 0; + *dest =3D av_realloc_f(*dest, size, sizeof (char)); + **dest =3D 0; + if (!*dest) + return AVERROR(ENOMEM); + while (*p) { + if (*p =3D=3D '%') { + if (!p[1]) + return AVERROR(EINVAL); + if (p[1] =3D=3D '%') { + p +=3D 2; + continue; + } + len =3D 1 + strspn(p + 1, "0123456789"); + len +=3D strspn(p + len, "."); + len +=3D strspn(p + len, "0123456789"); + if (!strspn(p + len, "fF")) + return AVERROR(EINVAL); + if (n) { + *p =3D 0; + len =3D strlen(*dest); + if (av_strlcatf(*dest, size, begin, dargv[n - 1]) > size -= 1) { + if (size > SIZE_MAX / 2) + return AVERROR(ENOMEM); + size *=3D 2; + *dest =3D av_realloc_f(*dest, size, sizeof (char)); + if (!*dest) + return AVERROR(ENOMEM); + (*dest)[len] =3D 0; + av_strlcatf(*dest, size, begin, dargv[n - 1]); + } + *p =3D '%'; + begin =3D p; + } + ++n; + } + ++p; + } + if (begin < p) { + len =3D strlen(*dest); + if (av_strlcatf(*dest, size, begin, dargv[n - 1]) > size - 1) { + if (size > SIZE_MAX / 2) + return AVERROR(ENOMEM); + size *=3D 2; + *dest =3D av_realloc_f(*dest, size, sizeof (char)); + if (!*dest) + return AVERROR(ENOMEM); + (*dest)[len] =3D 0; + av_strlcatf(*dest, size, begin, dargv[n - 1]); + } + } + return 0; +} + static int filter_frame(AVFilterLink *inlink, AVFrame *ref) { FilterLink *inl =3D ff_filter_link(inlink); @@ -508,7 +577,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *= ref) interval->enabled =3D 0; } if (interval->enabled) - flags +=3D COMMAND_FLAG_EXPR; + flags +=3D COMMAND_FLAG_EXPR | COMMAND_FLAG_FORMAT; = if (flags) { AVBPrint pbuf; @@ -524,7 +593,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *= ref) char buf[1024]; = if (cmd->flags & flags) { - if (cmd->flags & COMMAND_FLAG_EXPR) { + if (cmd->flags & (COMMAND_FLAG_EXPR | COMMAND_FLAG_FOR= MAT)) { double var_values[VAR_VARS_NB], res; double start =3D TS2T(interval->start_ts, AV_TIME_= BASE_Q); double end =3D TS2T(interval->end_ts, AV_TIME_BASE= _Q); @@ -539,17 +608,73 @@ static int filter_frame(AVFilterLink *inlink, AVFrame= *ref) var_values[VAR_W] =3D ref->width; var_values[VAR_H] =3D ref->height; = - if ((ret =3D av_expr_parse_and_eval(&res, cmd->arg= , var_names, var_values, - NULL, NULL, NULL= , NULL, NULL, 0, NULL)) < 0) { - av_log(ctx, AV_LOG_ERROR, "Invalid expression = '%s' for command argument.\n", cmd->arg); - av_frame_free(&ref); - return AVERROR(EINVAL); - } - - cmd_arg =3D av_asprintf("%g", res); - if (!cmd_arg) { - av_frame_free(&ref); - return AVERROR(ENOMEM); + if (cmd->flags & COMMAND_FLAG_FORMAT) { + const char *args =3D cmd->arg; + char *format =3D av_get_token(&args, ",= "); + double *dargv =3D NULL; + int dargc =3D 0; + ret =3D 0; + do { + args +=3D strspn(args, SPACES); + if (*args =3D=3D ',') + ++args; + char *arg =3D av_get_token(&args, ","); + if (!arg) { + ret =3D AVERROR(ENOMEM); + goto end_format; + } + if (arg[0] =3D=3D 0) { + av_free(arg); + break; + } + if ((ret =3D av_expr_parse_and_eval(&res, = arg, var_names, var_values, + NULL, NU= LL, NULL, NULL, NULL, 0, NULL)) < 0) { + av_log(ctx, AV_LOG_ERROR, "Invalid exp= ression '%s' for command argument.\n", arg); + av_free(arg); + ret =3D AVERROR(EINVAL); + goto end_format; + } + av_free(arg); + dargv =3D av_realloc(dargv, ++dargc * size= of (double)); + if (!dargv) { + ret =3D AVERROR(ENOMEM); + goto end_format; + } + dargv[dargc - 1] =3D res; + } while (1); + if (dargc =3D=3D 0) { + ret =3D AVERROR(EINVAL); + av_log(ctx, AV_LOG_ERROR, "Invalid express= ion '%s' with no arguments for command.\n", cmd->arg); + goto end_format; + } + cmd_arg =3D NULL; + if ((ret =3D format_expr(&cmd_arg, format, dar= gc, dargv))) { + av_free(cmd_arg); + if (AVUNERROR(ret) =3D=3D EINVAL) { + av_log(ctx, AV_LOG_ERROR, "Invalid for= mat expression '%s' for command argument.\n", cmd->arg); + } + } + end_format: + av_free(dargv); + av_free(format); + + if (ret) { + av_frame_free(&ref); + return ret; + } + } else { + if ((ret =3D av_expr_parse_and_eval(&res, cmd-= >arg, var_names, var_values, + NULL, NULL, = NULL, NULL, NULL, 0, NULL)) < 0) { + av_log(ctx, AV_LOG_ERROR, "Invalid express= ion '%s' for command argument.\n", cmd->arg); + av_frame_free(&ref); + return AVERROR(EINVAL); + } + + cmd_arg =3D av_asprintf("%g", res); + if (!cmd_arg) { + av_frame_free(&ref); + return AVERROR(ENOMEM); + } } } av_log(ctx, AV_LOG_VERBOSE, @@ -562,7 +687,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *= ref) av_log(ctx, AV_LOG_VERBOSE, "Command reply for command #%d: ret:%s res:%s\n= ", cmd->index, av_err2str(ret), buf); - if (cmd->flags & COMMAND_FLAG_EXPR) + if (cmd->flags & (COMMAND_FLAG_EXPR | COMMAND_FLAG_FOR= MAT)) av_freep(&cmd_arg); } } -- = 2.48.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".