[FFmpeg-devel] [PATCH] lavfi: add asendcmd and sendcmd filters

Stefano Sabatini stefasab at gmail.com
Tue Aug 14 20:55:56 CEST 2012


On date Tuesday 2012-08-14 15:03:24 +0200, Nicolas George encoded:
> L'octidi 28 thermidor, an CCXX, Stefano Sabatini a écrit :
> > ---
> >  doc/filters.texi         |   70 ++++++++++
> >  libavfilter/Makefile     |    2 +
> >  libavfilter/allfilters.c |    2 +
> >  libavfilter/f_sendcmd.c  |  332 ++++++++++++++++++++++++++++++++++++++++++++++
> >  4 files changed, 406 insertions(+), 0 deletions(-)
> >  create mode 100644 libavfilter/f_sendcmd.c
> > 
> > diff --git a/doc/filters.texi b/doc/filters.texi
> > index 763085c..ae5af7c 100644
> > --- a/doc/filters.texi
> > +++ b/doc/filters.texi
> > @@ -4064,6 +4064,76 @@ tools.
> >  
> >  Below is a description of the currently available multimedia filters.
> >  
> > + at section asendcmd, sendcmd
> > +
> > +Send commands to filters in the filtergraph.
> > +
> > +These filters read commands to be sent to other filters in the
> > +filtergraph.
> > +
> 
> > + at code{asendcmd} must be inserted between two audio filters,
> > + at code{sendcmd} must be inserted between two video filters, but apart
> > +from that they act the same.
> 
> If I undestand correctly, the placement of the filters in the graph only
> affect the time they see on the frames, right?

Yes.

> 
> > +
> > +The specification of commands can be specified in the filter arguments
> > +with the @var{commands} option, or in a file specified with the
> > + at var{filename} option.
> > +
> 
> > +Commands are sent the first time when a frame with time greater or
> > +equal to the specified command time is processed by the filter.
> 
> Hum. It works for encoding, but not for interactive playback where seeks
> backwards happen. A lot of filters have that problem, though.

Well, I suppose we could detect when the time goes back in case t2 <=
t1, and seek back to the first command with time >= t2.

[...]
> > +/**
> > + * @file
> > + * send commands filter
> > + */
> > +
> > +#include "libavutil/avstring.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"
> > +
> > +typedef struct {
> > +    char *buf;                  ///< allocated buffer
> > +    char *target, *command, *arg;
> 
> > +    double t;
> 
> Any reason to use a double and not just an int64_t in AV_TIME_BASE or in
> link->time_base? Floats are tricky.

Changed to 1/1000000 units.

[...] 
> > +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 DELIMS " \f\t"
> 
> What happens if your lines are terminated by \r\n?

Syntax error (a line containing only "\r" doesn't contain enough
information.

[...]
> > +static int parse_commands(Command **cmds, int *nb_cmds,
> > +                          const char *cmds_buf, size_t cmds_bufsize,
> > +                          void *log_ctx)
> > +{
> > +    char *line = NULL;
> > +    size_t line_size = 0;
> > +    int line_count;
> > +    int ret, n = 0;
> > +    const char *p = cmds_buf;
> > +    const char *pend = cmds_buf + cmds_bufsize;
> > +
> > +    *nb_cmds = 0;
> > +    if (!p)
> > +        return 0;
> > +    for (line_count = 0; *p; line_count++) {
> > +        Command cmd;
> > +        int i = 0;
> > +
> 
> > +        /* skip empty lines && # comments */
> 
> I believe this comment goes a few lines later.

Yes.
 
> > +        while (p[i] != '\n' && p+i < pend)
> > +            i++;
> > +        line_size = i;
> 
> It makes multiline commands impossible, does it not?

Yes. At the moment I don't know how to support commands which spans
for more than one line. I suppose we could make use of some form of
\n escaping.
 
> > +
> > +        if (*p == '\n' || *p == '#') {
> > +            p += line_size+1;
> > +            continue;
> > +        }
> 
> What happens if a line starts with a space and then a comment?

It will abort since the line doesn't contain a command (# not at the
begin of the line are not treated as comments).

> > +
> > +        line = av_realloc_f(line, line_size+1, 1);
> > +        if (!line) {
> > +            ret = AVERROR(ENOMEM);
> > +            goto end;
> > +        }
> > +        memcpy(line, p, line_size);
> > +        line[line_size] = 0;
> > +
> > +        if ((ret = parse_command(&cmd, line, log_ctx)) < 0) {
> > +            av_log(log_ctx, AV_LOG_ERROR,
> > +                   "Error occurred parsing line %d: %s\n", line_count, line);
> > +            goto end;
> > +        }
> > +
> > +        /* (re)allocate commands array if required*/
> > +        if (*nb_cmds == n) {
> > +            n = FFMAX(16, 2*n);
> > +            *cmds = av_realloc_f(*cmds, n, 2*sizeof(Command));
> 
> > +            if (!*cmds) {
> > +                ret = AVERROR(ENOMEM);
> > +            }
> 
> It looks like there is a goto missing, or *cmds will be dereferenced anyway.

Well spotted.

> 
> > +        }
> > +        (*cmds)[(*nb_cmds)++] = cmd;
> > +        p += line_size+1;
> > +    }
> > +
> > +end:
> > +    av_free(line);
> > +    return ret;
> > +}
> > +
> > +static av_cold int init(AVFilterContext *ctx, const char *args)
> > +{
> > +    SendCmdContext *sendcmd = ctx->priv;
> > +    int ret, i;
> > +    char *buf;
> > +    size_t bufsize;
> > +
> > +    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) {
> > +        ret = av_file_map(sendcmd->commands_filename,
> > +                          &sendcmd->commands_file_buf,
> > +                          &sendcmd->commands_file_bufsize, 0, ctx);
> > +        if (ret < 0)
> > +            return ret;
> > +        buf     = sendcmd->commands_file_buf;
> > +        bufsize = sendcmd->commands_file_bufsize;
> > +    } else {
> > +        buf     = sendcmd->commands_str;
> > +        bufsize = strlen(sendcmd->commands_str) + 1;
> > +    }
> > +
> > +    if ((ret = parse_commands(&sendcmd->commands, &sendcmd->nb_commands,
> > +                              buf, bufsize, ctx)) < 0)
> > +        return ret;
> > +
> 
> > +    qsort(sendcmd->commands, sendcmd->nb_commands, sizeof(Command), cmp_commands);
> 
> The user can specify several commands for the same time, and the order may
> be relevant: a stable sort would be required.

I don't know if we have already a mergesort implementation in our
codebase or if I should hack it.

> > +
> > +    av_log(ctx, AV_LOG_DEBUG, "Parsed commands:\n");
> > +    for (i = 0; i < sendcmd->nb_commands; i++) {
> > +        Command cmd = sendcmd->commands[i];
> > +        av_log(ctx, AV_LOG_DEBUG, "target:%s time:%f command:%s arg:%s\n",
> > +               cmd.target, cmd.t, cmd.command, cmd.arg);
> > +    }
> > +
> > +    return 0;
> > +}
> > +
[...]
> > +static int end_frame(AVFilterLink *inlink)
> > +{
> > +    inlink->cur_buf = NULL;
> > +    return ff_end_frame(inlink->dst->outputs[0]);
> > +}
> 
> Any reason not to clear cur_buf in process_frame?

Good idea, fixed.

[...] 
> That is an useful feature, thanks.

Thanks (I implemented it mainly as a testing device, but can be useful
in many reconfiguration scenarios).

Patch updated. Todo:

1. stable sort of commands
2. ecaping of newlines (useful for commands which contains newlines in
the argument
3. silly \r\n line ending support??
4. more meaningful drawtext examples
-- 
FFmpeg = Fantastic & Forgiving Meaningless Programmable Elegant Gadget
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-lavfi-add-asendcmd-and-sendcmd-filters.patch
Type: text/x-diff
Size: 15297 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20120814/1e45ec1d/attachment.bin>


More information about the ffmpeg-devel mailing list