From: cenzhanquan1 via ffmpeg-devel <ffmpeg-devel@ffmpeg.org>
To: ffmpeg-devel@ffmpeg.org
Cc: cenzhanquan1 <code@ffmpeg.org>
Subject: [FFmpeg-devel] [PATCH] fate: add tests for asendcmd/sendcmd propagation modes. (PR #21156)
Date: Wed, 10 Dec 2025 15:26:33 -0000
Message-ID: <176538039395.39.15344170555500452830@2cb04c0e5124> (raw)
PR #21156 opened by cenzhanquan1
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21156
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21156.patch
Add FATE tests for the new 'forward' and 'backward' command propagation
modes in asendcmd/sendcmd, which allow directional traversal of the
filter graph to control multiple filters with a single command.
- fate-filter-sendcmd-graph: tests mode=graph (existing behavior)
- fate-filter-sendcmd-forward: tests mode=forward (propagate downstream)
- fate-filter-sendcmd-backward: tests mode=backward (propagate upstream)
These tests verify that volume adjustments are correctly applied across
the filter chain according to the specified propagation direction, using
sine wave inputs and amix for deterministic output.
The tests depend on ASENDCMD, VOLUME, ANULL, ADELAY, and AMIX filters,
and use framecrc for reproducible verification.
Signed-off-by: cenzhanquan1 <cenzhanquan1@xiaomi.com>
>From ca1bb9407ecfc8e52cd895ba2ae2a30ed8e0ff93 Mon Sep 17 00:00:00 2001
From: cenzhanquan1 <cenzhanquan1@xiaomi.com>
Date: Wed, 10 Dec 2025 23:08:00 +0800
Subject: [PATCH] fate: add tests for asendcmd/sendcmd propagation modes.
Add FATE tests for the new 'forward' and 'backward' command propagation
modes in asendcmd/sendcmd, which allow directional traversal of the
filter graph to control multiple filters with a single command.
- fate-filter-sendcmd-graph: tests mode=graph (existing behavior)
- fate-filter-sendcmd-forward: tests mode=forward (propagate downstream)
- fate-filter-sendcmd-backward: tests mode=backward (propagate upstream)
These tests verify that volume adjustments are correctly applied across
the filter chain according to the specified propagation direction, using
sine wave inputs and amix for deterministic output.
The tests depend on ASENDCMD, VOLUME, ANULL, ADELAY, and AMIX filters,
and use framecrc for reproducible verification.
Signed-off-by: cenzhanquan1 <cenzhanquan1@xiaomi.com>
---
libavfilter/f_sendcmd.c | 69 ++++++++++++++++++++++++++++++++-----
tests/fate/filter-audio.mak | 42 ++++++++++++++++++++++
2 files changed, 103 insertions(+), 8 deletions(-)
diff --git a/libavfilter/f_sendcmd.c b/libavfilter/f_sendcmd.c
index 304658ae3d..7d841d0e87 100644
--- a/libavfilter/f_sendcmd.c
+++ b/libavfilter/f_sendcmd.c
@@ -98,6 +98,12 @@ typedef struct Interval {
int enabled; ///< current time detected inside this interval
} Interval;
+typedef enum {
+ CMD_MODE_GRAPH = 0,
+ CMD_MODE_FORWARD,
+ CMD_MODE_BACKWARD
+} SendCmdMode;
+
typedef struct SendCmdContext {
const AVClass *class;
Interval *intervals;
@@ -105,6 +111,8 @@ typedef struct SendCmdContext {
char *commands_filename;
char *commands_str;
+
+ int mode;
} SendCmdContext;
#define OFFSET(x) offsetof(SendCmdContext, x)
@@ -114,6 +122,10 @@ static const AVOption options[] = {
{ "c", "set commands", OFFSET(commands_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
{ "filename", "set commands file", OFFSET(commands_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
{ "f", "set commands file", OFFSET(commands_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
+ { "mode", "set command propagation mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=CMD_MODE_GRAPH}, CMD_MODE_GRAPH, CMD_MODE_BACKWARD, FLAGS, "mode" },
+ { "graph", "graph send command mode", 0, AV_OPT_TYPE_CONST, {.i64=CMD_MODE_GRAPH}, 0, 0, FLAGS, "mode" },
+ { "forward", "forward traversal mode", 0, AV_OPT_TYPE_CONST, {.i64=CMD_MODE_FORWARD}, 0, 0, FLAGS, "mode" },
+ { "backward", "backward traversal mode", 0, AV_OPT_TYPE_CONST, {.i64=CMD_MODE_BACKWARD}, 0, 0, FLAGS, "mode" },
{ NULL }
};
@@ -530,6 +542,7 @@ 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;
+ int send_flags = 0;
char buf[1024];
if (cmd->flags & flags) {
@@ -561,16 +574,56 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *ref)
return AVERROR(ENOMEM);
}
}
+
+ if (s->mode == CMD_MODE_GRAPH)
+ send_flags = AVFILTER_CMD_FLAG_ONE;
+ else if (s->mode == CMD_MODE_FORWARD)
+ send_flags = AVFILTER_CMD_FLAG_FORWARD;
+ else if (s->mode == CMD_MODE_BACKWARD)
+ send_flags = AVFILTER_CMD_FLAG_BACKWARD;
+
av_log(ctx, AV_LOG_VERBOSE,
- "Processing command #%d target:%s command:%s arg:%s\n",
- cmd->index, cmd->target, cmd->command, cmd_arg);
- ret = avfilter_graph_send_command(inl->graph,
- cmd->target, cmd->command, cmd_arg,
- buf, sizeof(buf),
- AVFILTER_CMD_FLAG_ONE);
+ "Processing command #%d target:%s command:%s arg:%s\n",
+ cmd->index, cmd->target, cmd->command, cmd_arg);
+
+ if (s->mode == CMD_MODE_GRAPH) {
+ ret = avfilter_graph_send_command(inl->graph,
+ cmd->target, cmd->command, cmd_arg,
+ buf, sizeof(buf),
+ send_flags);
+ } else {
+ AVFilterContext *start_filter = NULL;
+
+ if (cmd->target && cmd->target[0]) {
+ for (int k = 0; k < inl->graph->nb_filters; k++) {
+ AVFilterContext *filter = inl->graph->filters[k];
+
+ if (filter->name && strcmp(filter->name, cmd->target) == 0) {
+ start_filter = filter;
+ break;
+ }
+ }
+ }
+
+ if (!start_filter) {
+ av_log(ctx, AV_LOG_ERROR,
+ "Cannot find target filter instance '%s' for command #%d in non-GRAPH mode\n",
+ cmd->target ? cmd->target : "(null)", j);
+
+ if (cmd->flags & COMMAND_FLAG_EXPR)
+ av_freep(&cmd_arg);
+
+ continue;
+ }
+
+ ret = avfilter_process_command(start_filter, cmd->command,
+ cmd_arg, buf, sizeof(buf), send_flags);
+ }
+
av_log(ctx, AV_LOG_VERBOSE,
- "Command reply for command #%d: ret:%s res:%s\n",
- cmd->index, av_err2str(ret), buf);
+ "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);
}
diff --git a/tests/fate/filter-audio.mak b/tests/fate/filter-audio.mak
index 526645a634..4db1594bab 100644
--- a/tests/fate/filter-audio.mak
+++ b/tests/fate/filter-audio.mak
@@ -451,3 +451,45 @@ FATE_AFILTER-yes := $(if $(call FRAMECRC), $(FATE_AFILTER-yes))
FATE_SAMPLES_AVCONV += $(FATE_AFILTER_SAMPLES-yes)
FATE_FFMPEG += $(FATE_AFILTER-yes)
fate-afilter: $(FATE_AFILTER-yes) $(FATE_AFILTER_SAMPLES-yes)
+
+# FATE tests for sendcmd/asendcmd with new propagation modes
+
+# Test 'graph' mode: commands affect multiple filters in a graph,
+# demonstrating cross-filter parameter propagation.
+FATE_AFILTER-$(call FILTERDEMDECENCMUX, ASENDCMD VOLUME ANULL AMIX, WAV, PCM_S16LE, PCM_S16LE, WAV) += fate-filter-sendcmd-graph
+fate-filter-sendcmd-graph: tests/data/filtergraphs/sendcmd-graph
+fate-filter-sendcmd-graph: CMD = ffmpeg \
+ -f lavfi -i "sine=frequency=440:sample_rate=44100:duration=5" \
+ -f lavfi -i "sine=frequency=880:sample_rate=44100:duration=5" \
+ -filter_complex_script $(TARGET_PATH)/tests/data/filtergraphs/sendcmd-graph \
+ -map "[mixout]" -t 5 -f framecrc -
+
+tests/data/filtergraphs/sendcmd-graph: TAG = GEN
+tests/data/filtergraphs/sendcmd-graph: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data/filtergraphs
+ $(M)echo '[0:a]asendcmd=mode=graph:commands=0.0 volume volume 0.5,anull,volume=precision=fixed:volume=1.0[a1];[1:a]asendcmd=mode=graph:commands=0.0 volume volume 0.3,anull,volume=precision=fixed:volume=1.0[a2];[a1][a2]amix=inputs=2[mixout]' > $(TARGET_PATH)/tests/data/filtergraphs/sendcmd-graph
+
+# Test 'forward' mode: command propagates forward to filters downstream.
+FATE_AFILTER-$(call FILTERDEMDECENCMUX, ASENDCMD VOLUME ADELAY AMIX, WAV, PCM_S16LE, PCM_S16LE, WAV) += fate-filter-sendcmd-forward
+fate-filter-sendcmd-forward: tests/data/filtergraphs/sendcmd-forward
+fate-filter-sendcmd-forward: CMD = ffmpeg \
+ -f lavfi -i "sine=frequency=440:sample_rate=44100:duration=5" \
+ -f lavfi -i "sine=frequency=880:sample_rate=44100:duration=5" \
+ -filter_complex_script $(TARGET_PATH)/tests/data/filtergraphs/sendcmd-forward \
+ -map "[mixout]" -t 5 -f framecrc -
+
+tests/data/filtergraphs/sendcmd-forward: TAG = GEN
+tests/data/filtergraphs/sendcmd-forward: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data/filtergraphs
+ $(M)echo '[0:a]asendcmd=mode=forward:commands=0.0 volume volume 0.5,volume=precision=fixed:volume=1.0,aformat=sample_fmts=fltp,adelay=1000,volume=precision=fixed:volume=1.0[a1];[1:a]volume=precision=fixed:volume=1.0,aformat=sample_fmts=fltp,adelay=2000,volume=precision=fixed:volume=1.0[a2];[a1][a2]amix=inputs=2[mixout]' > $(TARGET_PATH)/tests/data/filtergraphs/sendcmd-forward
+
+# Test 'backward' mode: command propagates backward to upstream filters.
+FATE_AFILTER-$(call FILTERDEMDECENCMUX, ASENDCMD VOLUME ADELAY AMIX, WAV, PCM_S16LE, PCM_S16LE, WAV) += fate-filter-sendcmd-backward
+fate-filter-sendcmd-backward: tests/data/filtergraphs/sendcmd-backward
+fate-filter-sendcmd-backward: CMD = ffmpeg \
+ -f lavfi -i "sine=frequency=440:sample_rate=44100:duration=5" \
+ -f lavfi -i "sine=frequency=880:sample_rate=44100:duration=5" \
+ -filter_complex_script $(TARGET_PATH)/tests/data/filtergraphs/sendcmd-backward \
+ -map "[mixout]" -t 5 -f framecrc -
+
+tests/data/filtergraphs/sendcmd-backward: TAG = GEN
+tests/data/filtergraphs/sendcmd-backward: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data/filtergraphs
+ $(M)echo '[0:a]volume=precision=fixed:volume=1.0,aformat=sample_fmts=fltp,adelay=1000,volume=precision=fixed:volume=1.0[a1];[1:a]volume=precision=fixed:volume=1.0,aformat=sample_fmts=fltp,adelay=2000,volume=precision=fixed:volume=1.0[a2];[a1][a2]amix=inputs=2,asendcmd=mode=backward:commands=0.0 volume volume 0.3[mixout]' > $(TARGET_PATH)/tests/data/filtergraphs/sendcmd-backward
\ No newline at end of file
--
2.49.1
_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
next reply other threads:[~2025-12-10 15:27 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-10 15:26 cenzhanquan1 via ffmpeg-devel [this message]
2025-12-10 15:57 ` [FFmpeg-devel] " Nicolas George via ffmpeg-devel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=176538039395.39.15344170555500452830@2cb04c0e5124 \
--to=ffmpeg-devel@ffmpeg.org \
--cc=code@ffmpeg.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git