summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul B Mahol <onemda@gmail.com>2020-02-28 23:11:32 +0100
committerPaul B Mahol <onemda@gmail.com>2020-03-03 20:38:56 +0100
commit70209000fd1c7033b2039f4c906d0d0c6cd500b4 (patch)
tree5fa911e028e9a8e74fb3dd0c6eaca045ba6d805b
parent3117f47f19d051d47ba29c9b78c2ca525f0fdb45 (diff)
avfilter/f_sendcmd: implement expr flag
Make possible to parse expressions and store results as arguments for target filters.
-rw-r--r--doc/filters.texi23
-rw-r--r--libavfilter/f_sendcmd.c54
2 files changed, 74 insertions, 3 deletions
diff --git a/doc/filters.texi b/doc/filters.texi
index 433abe296d..f9baa0716b 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -23666,6 +23666,29 @@ The command is sent when the current frame timestamp leaves the
specified interval. In other words, the command is sent when the
previous frame timestamp was in the given interval, and the
current is not.
+
+@item expr
+The command @var{ARG} is interpreted as expression and result of
+expression is passed as @var{ARG}.
+
+The expression is evaluated through the eval API and can contain the following
+constants:
+
+@table @option
+@item POS
+Original position in the file of the frame, or undefined if undefined
+for the current frame.
+
+@item PTS
+The presentation timestamp in input.
+
+@item N
+The count of the input frame for video or audio, starting from 0.
+
+@item T
+The time in seconds of the current frame.
+@end table
+
@end table
If @var{FLAGS} is not specified, a default value of @code{[enter]} is
diff --git a/libavfilter/f_sendcmd.c b/libavfilter/f_sendcmd.c
index b8740e8883..5a62a338ee 100644
--- a/libavfilter/f_sendcmd.c
+++ b/libavfilter/f_sendcmd.c
@@ -25,6 +25,7 @@
#include "libavutil/avstring.h"
#include "libavutil/bprint.h"
+#include "libavutil/eval.h"
#include "libavutil/file.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
@@ -35,10 +36,27 @@
#define COMMAND_FLAG_ENTER 1
#define COMMAND_FLAG_LEAVE 2
+#define COMMAND_FLAG_EXPR 4
+
+static const char *const var_names[] = {
+ "N", /* frame number */
+ "T", /* frame time in seconds */
+ "POS", /* original position in the file of the frame */
+ "PTS", /* frame pts */
+ NULL
+};
+
+enum var_name {
+ VAR_N,
+ VAR_T,
+ VAR_POS,
+ VAR_PTS,
+ VAR_VARS_NB
+};
static inline char *make_command_flags_str(AVBPrint *pbuf, int flags)
{
- static const char * const flag_strings[] = { "enter", "leave" };
+ static const char * const flag_strings[] = { "enter", "leave", "expr" };
int i, is_first = 1;
av_bprint_init(pbuf, 0, AV_BPRINT_SIZE_AUTOMATIC);
@@ -129,6 +147,7 @@ static int parse_command(Command *cmd, int cmd_count, int interval_count,
if (!strncmp(*buf, "enter", strlen("enter"))) cmd->flags |= COMMAND_FLAG_ENTER;
else if (!strncmp(*buf, "leave", strlen("leave"))) cmd->flags |= COMMAND_FLAG_LEAVE;
+ else if (!strncmp(*buf, "expr", strlen("expr"))) cmd->flags |= COMMAND_FLAG_EXPR;
else {
char flag_buf[64];
av_strlcpy(flag_buf, *buf, sizeof(flag_buf));
@@ -450,6 +469,9 @@ static av_cold void uninit(AVFilterContext *ctx)
av_freep(&s->intervals);
}
+#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
+#define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts)*av_q2d(tb))
+
static int filter_frame(AVFilterLink *inlink, AVFrame *ref)
{
AVFilterContext *ctx = inlink->dst;
@@ -476,6 +498,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *ref)
flags += COMMAND_FLAG_LEAVE;
interval->enabled = 0;
}
+ if (interval->enabled)
+ flags += COMMAND_FLAG_EXPR;
if (flags) {
AVBPrint pbuf;
@@ -487,19 +511,43 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *ref)
for (j = 0; flags && j < interval->nb_commands; j++) {
Command *cmd = &interval->commands[j];
+ char *cmd_arg = cmd->arg;
char buf[1024];
if (cmd->flags & flags) {
+ if (cmd->flags & COMMAND_FLAG_EXPR) {
+ double var_values[VAR_VARS_NB], res;
+
+ var_values[VAR_N] = inlink->frame_count_in;
+ var_values[VAR_POS] = ref->pkt_pos == -1 ? NAN : ref->pkt_pos;
+ var_values[VAR_PTS] = TS2D(ref->pts);
+ var_values[VAR_T] = TS2T(ref->pts, inlink->time_base);
+
+ if ((ret = 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 = av_asprintf("%g", res);
+ if (!cmd_arg) {
+ av_frame_free(&ref);
+ return AVERROR(ENOMEM);
+ }
+ }
av_log(ctx, AV_LOG_VERBOSE,
"Processing command #%d target:%s command:%s arg:%s\n",
- cmd->index, cmd->target, cmd->command, cmd->arg);
+ cmd->index, cmd->target, cmd->command, cmd_arg);
ret = avfilter_graph_send_command(inlink->graph,
- cmd->target, cmd->command, cmd->arg,
+ cmd->target, cmd->command, cmd_arg,
buf, sizeof(buf),
AVFILTER_CMD_FLAG_ONE);
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)
+ av_freep(&cmd_arg);
}
}
}