[FFmpeg-devel] [PATCH] [2/??] [3/3] Filter graphs - Parser for a graph description

Vitor Sessak vitor1001
Fri Apr 11 18:43:10 CEST 2008


Hi, and thanks for the review

Michael Niedermayer wrote:
> On Thu, Apr 10, 2008 at 08:55:24PM +0200, Vitor Sessak wrote:
>> Michael Niedermayer wrote:
>>> On Sun, Apr 06, 2008 at 10:39:00PM +0200, Vitor Sessak wrote:
> [...]
>>> [...]
>>>> /**
>>>>  * A linked-list of the inputs/outputs of the filter chain.
>>>>  */
>>>> typedef struct AVFilterInOut {
>>>>     enum LinkType type;
>>>>     char *name;
>>>>     AVFilterContext *filter;
>>>>     int pad_idx;
>>>>
>>>>     struct AVFilterInOut *next;
>>>> } AVFilterInOut;
>>> I wonder if an array would be simpler than a linked list ...
>> I'm not sure it would be really simpler. For me, it will only save one or 
>> two malloc lines...
> 
> Well, i will think more about this, but as is the parser currently looks
> too complex for what it supports ...

I agree it looks kind of complex for what it does, but a good part of 
the complexity comes from whitespace handling and giving decent error 
messages.

> [...]
>> Also, are you ok to commit it without svn history? It was rewritten from 
>> scratch...
> 
> Iam ok with ommiting unrelated history / what is prior to your rewrite ...
> 
> 
> [...]
>> /**
>>  * For use in av_log
>>  */
>> static const char *log_name(void *p)
>> {
>>     return "Filter parser";
>> }
>>
>> static const AVClass filter_parser_class = {
>>     "Filter parser",
>>     log_name
>> };
>>
>> static const AVClass *log_ctx = &filter_parser_class;
> 
> I do not like this. This is a hack not the correct way to use av_log()

Options:

1- Make AVFilterGraph a context for av_log
     a) Good points: Instance specific
     b) Bad  points: Has nothing specifically to do with the parser

2- Make a AVFilterInOut linked list a member of a context
     a) Good points: The best candidate for a parser context
     b) Bad  points: Useless struct

4- Malloc'ing a instance of AVClass in the beginning of 
avfilter_parse_graph and passing it to every function
     a) Good points: Instance specific
     b) Bad  points: Useless function parameters

5- av_log(NULL, ...)

>> static AVFilterContext *create_filter(AVFilterGraph *ctx, int index,
>>                                       char *name, char *args)
> 
> shouldnt name and args be const

Yes

> 
> [...]
>> /**
>>  * Consumes a string from *buf.
>>  * @return a copy of the consumed string, which should be free'd after use
>>  */
>> static char *consume_string(const char **buf)
>> {
>>     char *out = av_malloc(strlen(*buf) + 1);
>>     const char *in = *buf;
>>     char *ret = out;
>>
>>     consume_whitespace(buf);
>>
>>     do{
>>         char c = *in++;
> 
> you change buf but use in, also why arent you useing just one variable ...

My original idea was to avoid constructs like *(*buf)++ = , but I'm not 
against it, so changed.

> 
>>         switch (c) {
>>         case '\\':
>>             *out++= *in++;
>>             break;
>>         case '\'':
>>             while(*in && *in != '\'')
>>                 *out++= *in++;
>>             if(*in) in++;
>>             break;
>>         case 0:
>>         case ']':
>>         case '[':
>>         case '=':
>>         case ',':
>>             *out++= 0;
>>             break;
>>         default:
>>             *out++= c;
>>         }
>>     } while(out[-1]);
> 
> What about trailing whitespace?

Weren't handled properly (it was a nice thing of consume_char()). Fixed.

> 
> 
>>     *buf = in-1;
>>     return ret;
>> }
>>
> 
>> /**
>>  * Parse "[linkname]"
>>  * @arg name a pointer (that need to be free'd after use) to the name between
>>  *           parenthesis
>>  */
>> static void parse_link_name(const char **buf, char **name)
>> {
>>     const char *start = *buf;
>>     (*buf)++;
>>
>>     *name = consume_string(buf);
>>
>>     if(!*name[0]) {
>>         av_log(&log_ctx, AV_LOG_ERROR,
>>                "Bad (empty?) label found in the following: \"%s\".\n", start);
>>         goto fail;
>>     }
>>
>>     if(*(*buf)++ != ']') {
>>         av_log(&log_ctx, AV_LOG_ERROR,
>>                "Mismatched '[' found in the following: \"%s\".\n", start);
>>         goto fail;
>>     }
>>
>>     return;
>>
>>  fail:
>>     av_freep(name);
>> }
> 
> you can at least move the fail: free to where the second goto is, this also
> makes the return redundant.

consume_string() will alloc a string in both cases (an empty one in the 
first goto).

> [...]
>> /**
>>  * Parse "[a1][link2] ... [etc]"
>>  */
>> static int parse_inouts(const char **buf, AVFilterInOut **inout, int pad,
>>                         enum LinkType type, AVFilterContext *filter)
>> {
>>     while (**buf == '[') {
>>         AVFilterInOut *inoutn = av_malloc(sizeof(AVFilterInOut));
>>         parse_link_name(buf, &inoutn->name);
>>
>>         if (!inoutn->name) {
>>             av_free(inoutn);
>>             return -1;
>>         }
>>
> 
>>         inoutn->type = type;
>>         inoutn->filter = filter;
>>         inoutn->pad_idx = pad++;
>>         inoutn->next = *inout;
> 
> vertical align

done

> 
> [...]
>> /**
>>  * Parse a string describing a filter graph.
>>  */
>> int avfilter_parse_graph(AVFilterGraph *graph, const char *filters,
>>                          AVFilterContext *in, int inpad,
>>                          AVFilterContext *out, int outpad)
>> {
>>     AVFilterInOut *inout=NULL;
>>     AVFilterInOut  *head=NULL;
>>
>>     int index = 0;
>>     char chr = 0;
>>     int pad = 0;
>>     int has_out = 0;
>>
>>     AVFilterContext *last_filt = NULL;
>>
>>     consume_whitespace(&filters);
>>
>>     do {
>>         AVFilterContext *filter;
>>         int oldpad = pad;
>>         const char *inouts = filters;
>>
>>         // We need to parse the inputs of the filter after we create it, so
>>         // skip it by now
>>         filters = skip_inouts(filters);
>>
>>         if(!(filter = parse_filter(&filters, graph, index)))
>>             goto fail;
>>
>>         pad = parse_inouts(&inouts, &inout, chr == ',', LinkTypeIn, filter);
>>
>>         if(pad < 0)
>>             goto fail;
>>
>>         // If the first filter has an input and none was given, it is
>>         // implicitly the input of the whole graph.
>>         if(pad == 0 && filter->input_count == 1) {
>>             if(link_filter(in, inpad, filter, 0))
>>                 goto fail;
>>         }
>>
>>         if(chr == ',') {
>>             if(link_filter(last_filt, oldpad, filter, 0) < 0)
>>                 goto fail;
>>         }
>>
>>         pad = parse_inouts(&filters, &inout, 0, LinkTypeOut, filter);
>>         chr = *filters++;
>>         index++;
>>         last_filt = filter;
>>     } while (chr == ',' || chr == ';');
>>
>>     head = inout;
>>     for (; inout != NULL; inout = inout->next) {
> 
> Why dont you build the graph in 1 pass?

Why? In the 2 pass version, I can be sure to find a match for every 
label. Also, it makes two nicely separated blocks of code, the linking 
can be cleanly moved to another function, for example.

-Vitor

-------------- next part --------------
A non-text attachment was scrubbed...
Name: avfiltergraph.c
Type: text/x-csrc
Size: 4871 bytes
Desc: not available
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20080411/eb53af7d/attachment.c>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: avfiltergraph.h
Type: text/x-chdr
Size: 1624 bytes
Desc: not available
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20080411/eb53af7d/attachment.h>



More information about the ffmpeg-devel mailing list