From 740c99528880c1d101167f82eddcfe8ce5453605 Mon Sep 17 00:00:00 2001 From: Stefano Sabatini Date: Mon, 13 Aug 2012 20:13:26 +0200 Subject: [PATCH] lavfi: add asendcmd and sendcmd filters --- Changelog | 1 + doc/filters.texi | 131 +++++++++ libavfilter/Makefile | 2 + libavfilter/allfilters.c | 2 + libavfilter/f_sendcmd.c | 577 +++++++++++++++++++++++++++++++++++++++ libavfilter/version.h | 4 +- 6 files changed, 715 insertions(+), 2 deletions(-) create mode 100644 libavfilter/f_sendcmd.c diff --git a/Changelog b/Changelog index bd37f87fbe..e8ea6799d4 100644 --- a/Changelog +++ b/Changelog @@ -62,6 +62,7 @@ version next: - RTP depacketization of JPEG - Smooth Streaming live segmenter muxer - F4V muxer +- sendcmd and asendcmd filters version 0.11: diff --git a/doc/filters.texi b/doc/filters.texi index e202d383d9..916aa6f8b9 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -4248,6 +4248,137 @@ tools. Below is a description of the currently available multimedia filters. +@section asendcmd, sendcmd + +Send commands to filters in the filtergraph. + +These filters read commands to be sent to other filters in the +filtergraph. + +@code{asendcmd} must be inserted between two audio filters, +@code{sendcmd} must be inserted between two video filters, but apart +from that they act the same way. + +The specification of commands can be provided in the filter arguments +with the @var{commands} option, or in a file specified by the +@var{filename} option. + +These filters accept the following options: +@table @option +@item commands, c +Set the commands to be read and sent to the other filters. +@item filename, f +Set the filename of the commands to be read and sent to the other +filters. +@end table + +@subsection Commands syntax + +A commands description consists of a sequence of interval +specifications, comprising a list of commands to be executed when a +particular event related to that interval occurs. The occurring event +is typically the current frame time entering or leaving a given time +interval. + +An interval is specified by the following syntax: +@example +@var{START}[-@var{END}] @var{COMMANDS}; +@end example + +The time interval is specified by the @var{START} and @var{END} times. +@var{END} is optional and defaults to the maximum time. + +The current frame time is considered within the specified interval if +it is included in the interval [@var{START}, @var{END}), that is when +the time is greater or equal to @var{START} and is lesser than +@var{END}. + +@var{COMMANDS} consists of a sequence of one or more command +specifications, separated by ",", relating to that interval. The +syntax of a command specification is given by: +@example +[@var{FLAGS}] @var{TARGET} @var{COMMAND} @var{ARG} +@end example + +@var{FLAGS} is optional and specifies the type of events relating to +the time interval which enable sending the specified command, and must +be a non-null sequence of identifier flags separated by "+" or "|" and +enclosed between "[" and "]". + +The following flags are recognized: +@table @option +@item enter +The command is sent when the current frame timestamp enters the +specified interval. In other words, the command is sent when the +previous frame timestamp was not in the given interval, and the +current is. + +@item leave +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. +@end table + +If @var{FLAGS} is not specified, a default value of @code{[enter]} is +assumed. + +@var{TARGET} specifies the target of the command, usually the name of +the filter class or a specific filter instance name. + +@var{COMMAND} specifies the name of the command for the target filter. + +@var{ARG} is optional and specifies the optional list of argument for +the given @var{COMMAND}. + +Between one interval specification and another, whitespaces, or +sequences of characters starting with @code{#} until the end of line, +are ignored and can be used to annotate comments. + +A simplified BNF description of the commands specification syntax +follows: +@example +@var{COMMAND_FLAG} ::= "enter" | "leave" +@var{COMMAND_FLAGS} ::= @var{COMMAND_FLAG} [(+|"|")@var{COMMAND_FLAG}] +@var{COMMAND} ::= ["[" @var{COMMAND_FLAGS} "]"] @var{TARGET} @var{COMMAND} [@var{ARG}] +@var{COMMANDS} ::= @var{COMMAND} [,@var{COMMANDS}] +@var{INTERVAL} ::= @var{START}[-@var{END}] @var{COMMANDS} +@var{INTERVALS} ::= @var{INTERVAL}[;@var{INTERVALS}] +@end example + +@subsection Examples + +@itemize +@item +Specify audio tempo change at second 4: +@example +asendcmd=c='4.0 atempo tempo 1.5',atempo +@end example + +@item +Specify a list of drawtext and hue commands in a file. +@example +# show text in the interval 5-10 +5.0-10.0 [enter] drawtext reinit 'fontfile=FreeSerif.ttf:text=hello world', + [leave] drawtext reinit 'fontfile=FreeSerif.ttf:text='; + +# desaturate the image in the interval 15-20 +15.0-20.0 [enter] hue reinit s=0, + [enter] drawtext reinit 'fontfile=FreeSerif.ttf:text=nocolor', + [leave] hue reinit s=1, + [leave] drawtext reinit 'fontfile=FreeSerif.ttf:text=color'; + +# apply an exponential saturation fade-out effect, starting from time 25 +25 [enter] hue s=exp(t-25) +@end example + +A filtergraph allowing to read and process the above command list +stored in a file @file{test.cmd}, can be specified with: +@example +sendcmd=f=test.cmd,drawtext=fontfile=FreeSerif.ttf:text='',hue +@end example +@end itemize + @section asetpts, setpts Change the PTS (presentation timestamp) of the input frames. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 82d39e4848..3477ecab6b 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -53,6 +53,7 @@ OBJS-$(CONFIG_AMERGE_FILTER) += af_amerge.o OBJS-$(CONFIG_AMIX_FILTER) += af_amix.o OBJS-$(CONFIG_ANULL_FILTER) += af_anull.o OBJS-$(CONFIG_ARESAMPLE_FILTER) += af_aresample.o +OBJS-$(CONFIG_ASENDCMD_FILTER) += f_sendcmd.o OBJS-$(CONFIG_ASETNSAMPLES_FILTER) += af_asetnsamples.o OBJS-$(CONFIG_ASETPTS_FILTER) += f_setpts.o OBJS-$(CONFIG_ASETTB_FILTER) += f_settb.o @@ -122,6 +123,7 @@ OBJS-$(CONFIG_PIXDESCTEST_FILTER) += vf_pixdesctest.o OBJS-$(CONFIG_REMOVELOGO_FILTER) += bbox.o lswsutils.o lavfutils.o vf_removelogo.o OBJS-$(CONFIG_SCALE_FILTER) += vf_scale.o OBJS-$(CONFIG_SELECT_FILTER) += vf_select.o +OBJS-$(CONFIG_SENDCMD_FILTER) += f_sendcmd.o OBJS-$(CONFIG_SETDAR_FILTER) += vf_aspect.o OBJS-$(CONFIG_SETFIELD_FILTER) += vf_setfield.o OBJS-$(CONFIG_SETPTS_FILTER) += f_setpts.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 6842ec95d5..b28c024c63 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -42,6 +42,7 @@ void avfilter_register_all(void) REGISTER_FILTER (AMIX, amix, af); REGISTER_FILTER (ANULL, anull, af); REGISTER_FILTER (ARESAMPLE, aresample, af); + REGISTER_FILTER (ASENDCMD, asendcmd, af); REGISTER_FILTER (ASETNSAMPLES, asetnsamples, af); REGISTER_FILTER (ASETPTS, asetpts, af); REGISTER_FILTER (ASETTB, asettb, af); @@ -113,6 +114,7 @@ void avfilter_register_all(void) REGISTER_FILTER (REMOVELOGO, removelogo, vf); REGISTER_FILTER (SCALE, scale, vf); REGISTER_FILTER (SELECT, select, vf); + REGISTER_FILTER (SENDCMD, sendcmd, vf); REGISTER_FILTER (SETDAR, setdar, vf); REGISTER_FILTER (SETFIELD, setfield, vf); REGISTER_FILTER (SETPTS, setpts, vf); diff --git a/libavfilter/f_sendcmd.c b/libavfilter/f_sendcmd.c new file mode 100644 index 0000000000..7081de815d --- /dev/null +++ b/libavfilter/f_sendcmd.c @@ -0,0 +1,577 @@ +/* + * Copyright (c) 2012 Stefano Sabatini + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * send commands filter + */ + +#include "libavutil/avstring.h" +#include "libavutil/bprint.h" +#include "libavutil/file.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "avfilter.h" +#include "internal.h" +#include "avfiltergraph.h" +#include "audio.h" +#include "video.h" + +#define COMMAND_FLAG_ENTER 1 +#define COMMAND_FLAG_LEAVE 2 + +static inline char *make_command_flags_str(AVBPrint *pbuf, int flags) +{ + const char *flag_strings[] = { "enter", "leave" }; + int i, is_first = 1; + + av_bprint_init(pbuf, 0, AV_BPRINT_SIZE_AUTOMATIC); + for (i = 0; i < FF_ARRAY_ELEMS(flag_strings); i++) { + if (flags & 1<str; +} + +typedef struct { + int flags; + char *target, *command, *arg; + int index; +} Command; + +typedef struct { + int64_t start_ts; ///< start timestamp expressed as microseconds units + int64_t end_ts; ///< end timestamp expressed as microseconds units + int index; ///< unique index for these interval commands + Command *commands; + int nb_commands; + int enabled; ///< current time detected inside this interval +} Interval; + +typedef struct { + const AVClass *class; + Interval *intervals; + int nb_intervals; + + char *commands_filename; + char *commands_str; +} SendCmdContext; + +#define OFFSET(x) offsetof(SendCmdContext, x) + +static const AVOption sendcmd_options[]= { + { "commands", "set commands", OFFSET(commands_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 }, + { "c", "set commands", OFFSET(commands_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 }, + { "filename", "set commands file", OFFSET(commands_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 }, + { "f", "set commands file", OFFSET(commands_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 }, + {NULL}, +}; + +AVFILTER_DEFINE_CLASS(sendcmd); + +#define SPACES " \f\t\n\r" + +static void skip_comments(const char **buf) +{ + while (**buf) { + /* skip leading spaces */ + *buf += strspn(*buf, SPACES); + if (**buf != '#') + break; + + (*buf)++; + + /* skip comment until the end of line */ + *buf += strcspn(*buf, "\n"); + if (**buf) + (*buf)++; + } +} + +#define COMMAND_DELIMS " \f\t\n\r,;" + +static int parse_command(Command *cmd, int cmd_count, int interval_count, + const char **buf, void *log_ctx) +{ + int ret; + + memset(cmd, 0, sizeof(Command)); + cmd->index = cmd_count; + + /* format: [FLAGS] target command arg */ + *buf += strspn(*buf, SPACES); + + /* parse flags */ + if (**buf == '[') { + (*buf)++; /* skip "[" */ + + while (**buf) { + int len = strcspn(*buf, "|+]"); + + if (!strncmp(*buf, "enter", strlen("enter"))) cmd->flags |= COMMAND_FLAG_ENTER; + else if (!strncmp(*buf, "leave", strlen("leave"))) cmd->flags |= COMMAND_FLAG_LEAVE; + else { + char flag_buf[64]; + av_strlcpy(flag_buf, *buf, sizeof(flag_buf)); + av_log(log_ctx, AV_LOG_ERROR, + "Unknown flag '%s' in in interval #%d, command #%d\n", + flag_buf, interval_count, cmd_count); + return AVERROR(EINVAL); + } + *buf += len; + if (**buf == ']') + break; + if (!strspn(*buf, "+|")) { + av_log(log_ctx, AV_LOG_ERROR, + "Invalid flags char '%c' in interval #%d, command #%d\n", + **buf, interval_count, cmd_count); + return AVERROR(EINVAL); + } + if (**buf) + (*buf)++; + } + + if (**buf != ']') { + av_log(log_ctx, AV_LOG_ERROR, + "Missing flag terminator or extraneous data found at the end of flags " + "in interval #%d, command #%d\n", interval_count, cmd_count); + return AVERROR(EINVAL); + } + (*buf)++; /* skip "]" */ + } else { + cmd->flags = COMMAND_FLAG_ENTER; + } + + *buf += strspn(*buf, SPACES); + cmd->target = av_get_token(buf, COMMAND_DELIMS); + if (!cmd->target || !cmd->target[0]) { + av_log(log_ctx, AV_LOG_ERROR, + "No target specified in in interval #%d, command #%d\n", + interval_count, cmd_count); + ret = AVERROR(EINVAL); + goto fail; + } + + *buf += strspn(*buf, SPACES); + cmd->command = av_get_token(buf, COMMAND_DELIMS); + if (!cmd->command || !cmd->command[0]) { + av_log(log_ctx, AV_LOG_ERROR, + "No command specified in in interval #%d, command #%d\n", + interval_count, cmd_count); + ret = AVERROR(EINVAL); + goto fail; + } + + *buf += strspn(*buf, SPACES); + cmd->arg = av_get_token(buf, COMMAND_DELIMS); + + return 1; + +fail: + av_freep(&cmd->target); + av_freep(&cmd->command); + av_freep(&cmd->arg); + return ret; +} + +static int parse_commands(Command **cmds, int *nb_cmds, int interval_count, + const char **buf, void *log_ctx) +{ + int cmd_count = 0; + int ret, n = 0; + AVBPrint pbuf; + + *cmds = NULL; + *nb_cmds = 0; + + while (**buf) { + Command cmd; + + if ((ret = parse_command(&cmd, cmd_count, interval_count, buf, log_ctx)) < 0) + return ret; + cmd_count++; + + /* (re)allocate commands array if required */ + if (*nb_cmds == n) { + n = FFMAX(16, 2*n); /* first allocation = 16, or double the number */ + *cmds = av_realloc_f(*cmds, n, 2*sizeof(Command)); + if (!*cmds) { + av_log(log_ctx, AV_LOG_ERROR, + "Could not (re)allocate command array\n"); + return AVERROR(ENOMEM); + } + } + + (*cmds)[(*nb_cmds)++] = cmd; + + *buf += strspn(*buf, SPACES); + if (**buf && **buf != ';' && **buf != ',') { + av_log(log_ctx, AV_LOG_ERROR, + "Missing separator or extraneous data found at the end of " + "interval #%d, in command #%d\n", + interval_count, cmd_count); + av_log(log_ctx, AV_LOG_ERROR, + "Command was parsed as: flags:[%s] target:%s command:%s arg:%s\n", + make_command_flags_str(&pbuf, cmd.flags), cmd.target, cmd.command, cmd.arg); + return AVERROR(EINVAL); + } + if (**buf == ';') + break; + if (**buf == ',') + (*buf)++; + } + + return 0; +} + +#define DELIMS " \f\t\n\r,;" + +static int parse_interval(Interval *interval, int interval_count, + const char **buf, void *log_ctx) +{ + char *intervalstr; + int ret; + + *buf += strspn(*buf, SPACES); + if (!**buf) + return 0; + + /* reset data */ + memset(interval, 0, sizeof(Interval)); + interval->index = interval_count; + + /* format: INTERVAL COMMANDS */ + + /* parse interval */ + intervalstr = av_get_token(buf, DELIMS); + if (intervalstr && intervalstr[0]) { + char *start, *end; + + start = av_strtok(intervalstr, "-", &end); + if ((ret = av_parse_time(&interval->start_ts, start, 1)) < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "Invalid start time specification '%s' in interval #%d\n", + start, interval_count); + goto end; + } + + if (end) { + if ((ret = av_parse_time(&interval->end_ts, end, 1)) < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "Invalid end time specification '%s' in interval #%d\n", + end, interval_count); + goto end; + } + } else { + interval->end_ts = INT64_MAX; + } + if (interval->end_ts < interval->start_ts) { + av_log(log_ctx, AV_LOG_ERROR, + "Invalid end time '%s' in interval #%d: " + "cannot be lesser than start time '%s'\n", + end, interval_count, start); + ret = AVERROR(EINVAL); + goto end; + } + } else { + av_log(log_ctx, AV_LOG_ERROR, + "No interval specified for interval #%d\n", interval_count); + ret = AVERROR(EINVAL); + goto end; + } + + /* parse commands */ + ret = parse_commands(&interval->commands, &interval->nb_commands, + interval_count, buf, log_ctx); + +end: + av_free(intervalstr); + return ret; +} + +static int parse_intervals(Interval **intervals, int *nb_intervals, + const char *buf, void *log_ctx) +{ + int interval_count = 0; + int ret, n = 0; + + *intervals = NULL; + *nb_intervals = 0; + + while (1) { + Interval interval; + + skip_comments(&buf); + if (!(*buf)) + break; + + if ((ret = parse_interval(&interval, interval_count, &buf, log_ctx)) < 0) + return ret; + + buf += strspn(buf, SPACES); + if (*buf) { + if (*buf != ';') { + av_log(log_ctx, AV_LOG_ERROR, + "Missing terminator or extraneous data found at the end of interval #%d\n", + interval_count); + return AVERROR(EINVAL); + } + buf++; /* skip ';' */ + } + interval_count++; + + /* (re)allocate commands array if required */ + if (*nb_intervals == n) { + n = FFMAX(16, 2*n); /* first allocation = 16, or double the number */ + *intervals = av_realloc_f(*intervals, n, 2*sizeof(Interval)); + if (!*intervals) { + av_log(log_ctx, AV_LOG_ERROR, + "Could not (re)allocate intervals array\n"); + return AVERROR(ENOMEM); + } + } + + (*intervals)[(*nb_intervals)++] = interval; + } + + return 0; +} + +static int cmp_intervals(const void *a, const void *b) +{ + const Interval *i1 = a; + const Interval *i2 = b; + int64_t ts_diff = i1->start_ts - i2->start_ts; + int ret; + + ret = ts_diff > 0 ? 1 : ts_diff < 0 ? -1 : 0; + return ret == 0 ? i1->index - i2->index : ret; +} + +static av_cold int init(AVFilterContext *ctx, const char *args) +{ + SendCmdContext *sendcmd = ctx->priv; + int ret, i, j; + char *buf; + + sendcmd->class = &sendcmd_class; + av_opt_set_defaults(sendcmd); + + if ((ret = av_set_options_string(sendcmd, args, "=", ":")) < 0) + return ret; + + if (sendcmd->commands_filename && sendcmd->commands_str) { + av_log(ctx, AV_LOG_ERROR, + "Only one of the filename or commands options must be specified\n"); + return AVERROR(EINVAL); + } + + if (sendcmd->commands_filename) { + uint8_t *file_buf; + size_t file_bufsize; + ret = av_file_map(sendcmd->commands_filename, + &file_buf, &file_bufsize, 0, ctx); + if (ret < 0) + return ret; + + /* create a 0-terminated string based on the read file */ + buf = av_malloc(file_bufsize + 1); + if (!buf) + return AVERROR(ENOMEM); + memcpy(buf, file_buf, file_bufsize); + buf[file_bufsize] = 0; + av_file_unmap(file_buf, file_bufsize); + sendcmd->commands_str = buf; + } + + if ((ret = parse_intervals(&sendcmd->intervals, &sendcmd->nb_intervals, + sendcmd->commands_str, ctx)) < 0) + return ret; + + qsort(sendcmd->intervals, sendcmd->nb_intervals, sizeof(Interval), cmp_intervals); + + av_log(ctx, AV_LOG_DEBUG, "Parsed commands:\n"); + for (i = 0; i < sendcmd->nb_intervals; i++) { + AVBPrint pbuf; + Interval *interval = &sendcmd->intervals[i]; + av_log(ctx, AV_LOG_VERBOSE, "start_time:%f end_time:%f index:%d\n", + (double)interval->start_ts/1000000, (double)interval->end_ts/1000000, interval->index); + for (j = 0; j < interval->nb_commands; j++) { + Command *cmd = &interval->commands[j]; + av_log(ctx, AV_LOG_VERBOSE, + " [%s] target:%s command:%s arg:%s index:%d\n", + make_command_flags_str(&pbuf, cmd->flags), cmd->target, cmd->command, cmd->arg, cmd->index); + } + } + + return 0; +} + +static void av_cold uninit(AVFilterContext *ctx) +{ + SendCmdContext *sendcmd = ctx->priv; + int i, j; + + av_opt_free(sendcmd); + + for (i = 0; i < sendcmd->nb_intervals; i++) { + Interval *interval = &sendcmd->intervals[i]; + for (j = 0; j < interval->nb_commands; j++) { + Command *cmd = &interval->commands[j]; + av_free(cmd->target); + av_free(cmd->command); + av_free(cmd->arg); + } + av_free(interval->commands); + } + av_freep(&sendcmd->intervals); +} + +static int process_frame(AVFilterLink *inlink, AVFilterBufferRef *ref) +{ + AVFilterContext *ctx = inlink->dst; + SendCmdContext *sendcmd = ctx->priv; + int64_t ts; + int i, j, ret; + + if (ref->pts == AV_NOPTS_VALUE) + goto end; + + ts = av_rescale_q(ref->pts, inlink->time_base, AV_TIME_BASE_Q); + +#define WITHIN_INTERVAL(ts, start_ts, end_ts) ((ts) >= (start_ts) && (ts) < (end_ts)) + + for (i = 0; i < sendcmd->nb_intervals; i++) { + Interval *interval = &sendcmd->intervals[i]; + int flags = 0; + + if (!interval->enabled && WITHIN_INTERVAL(ts, interval->start_ts, interval->end_ts)) { + flags += COMMAND_FLAG_ENTER; + interval->enabled = 1; + } + if (interval->enabled && !WITHIN_INTERVAL(ts, interval->start_ts, interval->end_ts)) { + flags += COMMAND_FLAG_LEAVE; + interval->enabled = 0; + } + + if (flags) { + AVBPrint pbuf; + av_log(ctx, AV_LOG_VERBOSE, + "[%s] interval #%d start_ts:%f end_ts:%f ts:%f\n", + make_command_flags_str(&pbuf, flags), interval->index, + (double)interval->start_ts/1000000, (double)interval->end_ts/1000000, + (double)ts/1000000); + + for (j = 0; flags && j < interval->nb_commands; j++) { + Command *cmd = &interval->commands[j]; + char buf[1024]; + + if (cmd->flags & flags) { + 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(inlink->graph, + 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); + } + } + } + } + +end: + /* give the reference away, do not store in cur_buf */ + inlink->cur_buf = NULL; + + switch (inlink->type) { + case AVMEDIA_TYPE_VIDEO: return ff_start_frame (inlink->dst->outputs[0], ref); + case AVMEDIA_TYPE_AUDIO: return ff_filter_samples(inlink->dst->outputs[0], ref); + } + return AVERROR(ENOSYS); +} + +#if CONFIG_SENDCMD_FILTER + +AVFilter avfilter_vf_sendcmd = { + .name = "sendcmd", + .description = NULL_IF_CONFIG_SMALL("Send commands to filters."), + + .init = init, + .uninit = uninit, + .priv_size = sizeof(SendCmdContext), + + .inputs = (const AVFilterPad[]) { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .get_video_buffer = ff_null_get_video_buffer, + .start_frame = process_frame, + .end_frame = ff_null_end_frame, + }, + { .name = NULL } + }, + .outputs = (const AVFilterPad[]) { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { .name = NULL } + }, +}; + +#endif + +#if CONFIG_ASENDCMD_FILTER + +AVFilter avfilter_af_asendcmd = { + .name = "asendcmd", + .description = NULL_IF_CONFIG_SMALL("Send commands to filters."), + + .init = init, + .uninit = uninit, + .priv_size = sizeof(SendCmdContext), + + .inputs = (const AVFilterPad[]) { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .get_audio_buffer = ff_null_get_audio_buffer, + .filter_samples = process_frame, + }, + { .name = NULL } + }, + .outputs = (const AVFilterPad[]) { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + }, + { .name = NULL } + }, +}; + +#endif diff --git a/libavfilter/version.h b/libavfilter/version.h index 8b7a6700c4..3d3d1c9d20 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -29,8 +29,8 @@ #include "libavutil/avutil.h" #define LIBAVFILTER_VERSION_MAJOR 3 -#define LIBAVFILTER_VERSION_MINOR 16 -#define LIBAVFILTER_VERSION_MICRO 106 +#define LIBAVFILTER_VERSION_MINOR 17 +#define LIBAVFILTER_VERSION_MICRO 100 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ LIBAVFILTER_VERSION_MINOR, \