X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavfilter%2Fgraphparser.c;h=ad1e8d473ae0b1db338fa073262d493a6bea38e2;hb=eee68d96d4e592634562860534acb12247b9a454;hp=c0fdb95b563079bedf7dba30af7f577fd8ae36a3;hpb=e84f0b628768942d8f2e8e1013edbe75965746f1;p=ffmpeg diff --git a/libavfilter/graphparser.c b/libavfilter/graphparser.c index c0fdb95b563..ad1e8d473ae 100644 --- a/libavfilter/graphparser.c +++ b/libavfilter/graphparser.c @@ -26,23 +26,9 @@ #include "avfilter.h" #include "avfiltergraph.h" -/** - * 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; - static AVFilterContext *create_filter(AVFilterGraph *ctx, int index, - const char *name, const char *args) + const char *name, const char *args, + AVClass *log_ctx) { AVFilterContext *filt; @@ -52,13 +38,13 @@ static AVFilterContext *create_filter(AVFilterGraph *ctx, int index, snprintf(inst_name, sizeof(inst_name), "Parsed filter %d", index); if(!(filterdef = avfilter_get_by_name(name))) { - av_log(&log_ctx, AV_LOG_ERROR, + av_log(log_ctx, AV_LOG_ERROR, "no such filter: '%s'\n", name); return NULL; } if(!(filt = avfilter_open(filterdef, inst_name))) { - av_log(&log_ctx, AV_LOG_ERROR, + av_log(log_ctx, AV_LOG_ERROR, "error creating filter '%s'\n", name); return NULL; } @@ -67,7 +53,7 @@ static AVFilterContext *create_filter(AVFilterGraph *ctx, int index, return NULL; if(avfilter_init_filter(filt, args, NULL)) { - av_log(&log_ctx, AV_LOG_ERROR, + av_log(log_ctx, AV_LOG_ERROR, "error initializing filter '%s' with args '%s'\n", name, args); return NULL; } @@ -76,10 +62,11 @@ static AVFilterContext *create_filter(AVFilterGraph *ctx, int index, } static int link_filter(AVFilterContext *src, int srcpad, - AVFilterContext *dst, int dstpad) + AVFilterContext *dst, int dstpad, + AVClass *log_ctx) { if(avfilter_link(src, srcpad, dst, dstpad)) { - av_log(&log_ctx, AV_LOG_ERROR, + av_log(log_ctx, AV_LOG_ERROR, "cannot create the link %s:%d -> %s:%d\n", src->filter->name, srcpad, dst->filter->name, dstpad); return -1; @@ -120,6 +107,7 @@ static char *consume_string(const char **buf) case '[': case '=': case ',': + case ';': case ' ': case '\n': *out++= 0; @@ -140,7 +128,7 @@ static char *consume_string(const char **buf) * @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) +static void parse_link_name(const char **buf, char **name, AVClass *log_ctx) { const char *start = *buf; (*buf)++; @@ -148,40 +136,19 @@ static void parse_link_name(const char **buf, char **name) *name = consume_string(buf); if(!*name[0]) { - av_log(&log_ctx, AV_LOG_ERROR, + 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, + av_log(log_ctx, AV_LOG_ERROR, "Mismatched '[' found in the following: \"%s\".\n", start); fail: av_freep(name); } } -/** - * Parse "filter=params" - * @arg name a pointer (that need to be free'd after use) to the name of the - * filter - * @arg ars a pointer (that need to be free'd after use) to the args of the - * filter - */ -static AVFilterContext *parse_filter(const char **buf, AVFilterGraph *graph, int index) -{ - char *name, *opts; - name = consume_string(buf); - - if(**buf == '=') { - (*buf)++; - opts = consume_string(buf); - } else { - opts = NULL; - } - - return create_filter(graph, index, name, opts); -} enum LinkType { LinkTypeIn, @@ -193,7 +160,7 @@ enum LinkType { */ typedef struct AVFilterInOut { enum LinkType type; - char *name; + const char *name; AVFilterContext *filter; int pad_idx; @@ -209,176 +176,268 @@ static void free_inout(AVFilterInOut *head) } } -/** - * Process a link. This funcion looks for a matching label in the *inout - * linked list. If none is found, it adds this link to the list. - */ -static int handle_link(char *name, AVFilterInOut **inout, int pad, - enum LinkType type, AVFilterContext *filter) +static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links) { - AVFilterInOut *p = *inout; + AVFilterInOut *ret; + + while(*links && strcmp((*links)->name, label)) + links = &((*links)->next); + + ret = *links; - for (; p && strcmp(p->name, name); p = p->next); + if(ret) + *links = ret->next; - if(!p) { - // First label apearence, add it to the linked list - AVFilterInOut *inoutn = av_malloc(sizeof(AVFilterInOut)); + return ret; +} + + +static int link_filter_inouts(AVFilterContext *filter, + AVFilterInOut **currInputs, + AVFilterInOut **openLinks, AVClass *log_ctx) +{ + int pad = 0; - inoutn->name = name; - inoutn->type = type; - inoutn->filter = filter; - inoutn->pad_idx = pad; - inoutn->next = *inout; - *inout = inoutn; - return 0; + pad = filter->input_count; + while(pad--) { + AVFilterInOut *p= *currInputs; + *currInputs = (*currInputs)->next; + if(!p) { + av_log(log_ctx, AV_LOG_ERROR, + "Not enough inputs specified for the \"%s\" filter.\n", + filter->name); + return -1; + } + + if(p->filter) { + if(link_filter(p->filter, p->pad_idx, filter, pad, log_ctx)) + return -1; + av_free(p); + } else { + p->filter = filter; + p->pad_idx = pad; + p->next = *openLinks; + *openLinks = p; + } } - if(p->type == LinkTypeIn && type == LinkTypeOut) { - if(link_filter(filter, pad, p->filter, p->pad_idx) < 0) - goto fail; - } else if(p->type == LinkTypeOut && type == LinkTypeIn) { - if(link_filter(p->filter, p->pad_idx, filter, pad) < 0) - goto fail; - } else { - av_log(&log_ctx, AV_LOG_ERROR, - "Two links named '%s' are either both input or both output\n", - name); - goto fail; + + if(*currInputs) { + av_log(log_ctx, AV_LOG_ERROR, + "Too many inputs specified for the \"%s\" filter.\n", + filter->name); + return -1; } - p->filter = NULL; + pad = filter->output_count; + while(pad--) { + AVFilterInOut *currlinkn = av_malloc(sizeof(AVFilterInOut)); + currlinkn->name = NULL; + currlinkn->type = LinkTypeOut; + currlinkn->filter = filter; + currlinkn->pad_idx = pad; + currlinkn->next = *currInputs; + *currInputs = currlinkn; + } return 0; - fail: - return -1; } - /** - * Parse "[a1][link2] ... [etc]" + * Parse "filter=params" + * @arg name a pointer (that need to be free'd after use) to the name of the + * filter + * @arg ars a pointer (that need to be free'd after use) to the args of the + * filter */ -static int parse_inouts(const char **buf, AVFilterInOut **inout, int pad, - enum LinkType type, AVFilterContext *filter) +static AVFilterContext *parse_filter(const char **buf, AVFilterGraph *graph, + int index, AVClass *log_ctx) { + char *opts; + char *name = consume_string(buf); + + if(**buf == '=') { + (*buf)++; + opts = consume_string(buf); + } else { + opts = NULL; + } + + return create_filter(graph, index, name, opts, log_ctx); +} + +static int parse_inputs(const char **buf, AVFilterInOut **currInputs, + AVFilterInOut **openLinks, AVClass *log_ctx) +{ + int pad = 0; + while (**buf == '[') { char *name; + AVFilterInOut *link_to_add; + AVFilterInOut *match; - parse_link_name(buf, &name); + parse_link_name(buf, &name, log_ctx); - if (!name) + if(!name) return -1; - handle_link(name, inout, pad++, type, filter); + /* First check if the label is not in the openLinks list */ + match = extract_inout(name, openLinks); + + if(match) { + /* A label of a open link. Make it one of the inputs of the next + filter */ + if (match->type != LinkTypeOut) { + av_log(log_ctx, AV_LOG_ERROR, + "Label \"%s\" appears twice as input!\n", match->name); + return -1; + } + + link_to_add = match; + } else { + /* Not in the list, so add it as an input */ + link_to_add = av_malloc(sizeof(AVFilterInOut)); + + link_to_add->name = name; + link_to_add->type = LinkTypeIn; + link_to_add->filter = NULL; + link_to_add->pad_idx = pad; + } + link_to_add->next = *currInputs; + *currInputs = link_to_add; consume_whitespace(buf); + pad++; } + return pad; } -static const char *skip_inouts(const char *buf) +static int parse_outputs(const char **buf, AVFilterInOut **currInputs, + AVFilterInOut **openLinks, AVClass *log_ctx) { - while (*buf == '[') { - buf += strcspn(buf, "]") + 1; - consume_whitespace(&buf); + int pad = 0; + + while (**buf == '[') { + char *name; + AVFilterInOut *match; + + AVFilterInOut *input = *currInputs; + *currInputs = (*currInputs)->next; + + parse_link_name(buf, &name, log_ctx); + + if(!name) + return -1; + + /* First check if the label is not in the openLinks list */ + match = extract_inout(name, openLinks); + + if(match) { + /* A label of a open link. Link it. */ + if (match->type != LinkTypeIn) { + av_log(log_ctx, AV_LOG_ERROR, + "Label \"%s\" appears twice as output!\n", match->name); + return -1; + } + + if(link_filter(input->filter, input->pad_idx, + match->filter, match->pad_idx, log_ctx) < 0) + return -1; + av_free(match); + av_free(input); + } else { + /* Not in the list, so add the first input as a openLink */ + input->next = *openLinks; + input->type = LinkTypeOut; + input->name = name; + *openLinks = input; + } + consume_whitespace(buf); + pad++; } - return buf; -} + return pad; +} /** * Parse a string describing a filter graph. */ int avfilter_parse_graph(AVFilterGraph *graph, const char *filters, AVFilterContext *in, int inpad, - AVFilterContext *out, int outpad) + AVFilterContext *out, int outpad, + AVClass *log_ctx) { - AVFilterInOut *inout=NULL; - AVFilterInOut *head=NULL; - int index = 0; char chr = 0; int pad = 0; - int has_out = 0; - AVFilterContext *last_filt = NULL; + AVFilterInOut *currInputs=NULL; + AVFilterInOut *openLinks = av_malloc(sizeof(AVFilterInOut)); + + openLinks->name = "in"; + openLinks->filter = in; + openLinks->type = LinkTypeOut; + openLinks->pad_idx = inpad; + openLinks->next = av_malloc(sizeof(AVFilterInOut)); + + openLinks->next->name = "out"; + openLinks->next->filter = out; + openLinks->next->type = LinkTypeIn; + openLinks->next->pad_idx = outpad; + openLinks->next->next = NULL; do { AVFilterContext *filter; - int oldpad = pad; - const char *inouts; - consume_whitespace(&filters); - inouts = filters; - // We need to parse the inputs of the filter after we create it, so - // skip it by now - filters = skip_inouts(filters); + pad = parse_inputs(&filters, &currInputs, &openLinks, log_ctx); - if(!(filter = parse_filter(&filters, graph, index))) + if(pad < 0) goto fail; - pad = parse_inouts(&inouts, &inout, chr == ',', LinkTypeIn, filter); - - if(pad < 0) + if(!(filter = parse_filter(&filters, graph, index, log_ctx))) 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)) + if(filter->input_count == 1 && !currInputs && !index) { + // First input can be ommitted if it is "[in]" + const char *tmp = "[in]"; + pad = parse_inputs(&tmp, &currInputs, &openLinks, log_ctx); + if (pad < 0) goto fail; } - if(chr == ',') { - if(link_filter(last_filt, oldpad, filter, 0) < 0) - goto fail; - } + if(link_filter_inouts(filter, &currInputs, &openLinks, log_ctx) < 0) + goto fail; - pad = parse_inouts(&filters, &inout, 0, LinkTypeOut, filter); + pad = parse_outputs(&filters, &currInputs, &openLinks, log_ctx); if(pad < 0) goto fail; consume_whitespace(&filters); - chr = *filters++; - index++; - last_filt = filter; - } while (chr == ',' || chr == ';'); - - head = inout; - // Process remaining labels. Only inputs and outputs should be left. - for (; inout; inout = inout->next) { - if(!inout->filter) - continue; // Already processed - - if(!strcmp(inout->name, "in")) { - if(link_filter(in, inpad, inout->filter, inout->pad_idx)) - goto fail; - - } else if(!strcmp(inout->name, "out")) { - has_out = 1; - - if(link_filter(inout->filter, inout->pad_idx, out, outpad)) - goto fail; - } else { - av_log(&log_ctx, AV_LOG_ERROR, "Unmatched link: %s.\n", - inout->name); - goto fail; + if (chr == ';' && currInputs) { + av_log(log_ctx, AV_LOG_ERROR, + "Could not find a output to link when parsing \"%s\"\n", + filters - 1); + goto fail; } - } - - free_inout(head); + index++; + } while (chr == ',' || chr == ';'); - if(!has_out) { - if(link_filter(last_filt, pad, out, outpad)) + if(openLinks && !strcmp(openLinks->name, "out") && currInputs) { + // Last output can be ommitted if it is "[out]" + const char *tmp = "[out]"; + if(parse_outputs(&tmp, &currInputs, &openLinks, log_ctx) < 0) goto fail; } return 0; fail: - free_inout(head); avfilter_destroy_graph(graph); + free_inout(openLinks); + free_inout(currInputs); return -1; }