X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavfilter%2Favfiltergraph.c;h=8c43251c4cc11ce32fc1ef3a41fe0961351513ec;hb=70749c483c53312faa83ff3890fa4e2f7135453d;hp=a2dc93fafcbba141160a0a3884ffdd6de7c04eb5;hpb=c4913b812ddac9b85fb0b213fc5433c75bf81fa8;p=ffmpeg diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index a2dc93fafcb..8c43251c4cc 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -1,22 +1,22 @@ /* * filter graphs - * copyright (c) 2008 Vitor Sessak - * copyright (c) 2007 Bobby Bingham + * Copyright (c) 2008 Vitor Sessak + * Copyright (c) 2007 Bobby Bingham * - * This file is part of FFmpeg. + * This file is part of Libav. * - * FFmpeg is free software; you can redistribute it and/or + * Libav 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, + * Libav 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 + * License along with Libav; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -25,137 +25,161 @@ #include "avfilter.h" #include "avfiltergraph.h" +#include "internal.h" +AVFilterGraph *avfilter_graph_alloc(void) +{ + return av_mallocz(sizeof(AVFilterGraph)); +} -/** Linked-list of filters to create for an AVFilterGraphDesc */ -typedef struct AVFilterGraphDescFilter +void avfilter_graph_free(AVFilterGraph **graph) { - int index; ///< filter instance index - char *filter; ///< name of filter type - char *args; ///< filter parameters - struct AVFilterGraphDescFilter *next; -} AVFilterGraphDescFilter; - -/** Linked-list of links between filters */ -typedef struct AVFilterGraphDescLink + if (!*graph) + return; + for (; (*graph)->filter_count > 0; (*graph)->filter_count--) + avfilter_free((*graph)->filters[(*graph)->filter_count - 1]); + av_freep(&(*graph)->scale_sws_opts); + av_freep(&(*graph)->filters); + av_freep(graph); +} + +int avfilter_graph_add_filter(AVFilterGraph *graph, AVFilterContext *filter) { - /* TODO: allow referencing pads by name, not just by index */ - int src; ///< index of the source filter - unsigned srcpad; ///< index of the output pad on the source filter + AVFilterContext **filters = av_realloc(graph->filters, + sizeof(AVFilterContext*) * (graph->filter_count+1)); + if (!filters) + return AVERROR(ENOMEM); - int dst; ///< index of the dest filter - unsigned dstpad; ///< index of the input pad on the dest filter + graph->filters = filters; + graph->filters[graph->filter_count++] = filter; - struct AVFilterGraphDescLink *next; -} AVFilterGraphDescLink; + return 0; +} -/** Linked-list of filter pads to be exported from the graph */ -typedef struct AVFilterGraphDescExport +int avfilter_graph_create_filter(AVFilterContext **filt_ctx, AVFilter *filt, + const char *name, const char *args, void *opaque, + AVFilterGraph *graph_ctx) { - /* TODO: allow referencing pads by name, not just by index */ - char *name; ///< name of the exported pad - int filter; ///< index of the filter - unsigned pad; ///< index of the pad to be exported + int ret; - struct AVFilterGraphDescExport *next; -} AVFilterGraphDescExport; + if ((ret = avfilter_open(filt_ctx, filt, name)) < 0) + goto fail; + if ((ret = avfilter_init_filter(*filt_ctx, args, opaque)) < 0) + goto fail; + if ((ret = avfilter_graph_add_filter(graph_ctx, *filt_ctx)) < 0) + goto fail; + return 0; -/** Description of a graph to be loaded from a file, etc */ -typedef struct -{ - AVFilterGraphDescFilter *filters; ///< filters in the graph - AVFilterGraphDescLink *links; ///< links between the filters - AVFilterGraphDescExport *inputs; ///< inputs to export - AVFilterGraphDescExport *outputs; ///< outputs to export -} AVFilterGraphDesc; - -/** - * For use in av_log - */ -static const char *log_name(void *p) -{ - return "Filter parser"; +fail: + if (*filt_ctx) + avfilter_free(*filt_ctx); + *filt_ctx = NULL; + return ret; } -static const AVClass filter_parser_class = { - "Filter parser", - log_name -}; +int ff_avfilter_graph_check_validity(AVFilterGraph *graph, AVClass *log_ctx) +{ + AVFilterContext *filt; + int i, j; -static const AVClass *log_ctx = &filter_parser_class; + for (i = 0; i < graph->filter_count; i++) { + filt = graph->filters[i]; -void avfilter_destroy_graph(AVFilterGraph *graph) -{ - for(; graph->filter_count > 0; graph->filter_count --) - avfilter_destroy(graph->filters[graph->filter_count - 1]); - av_freep(&graph->filters); + for (j = 0; j < filt->input_count; j++) { + if (!filt->inputs[j] || !filt->inputs[j]->src) { + av_log(log_ctx, AV_LOG_ERROR, + "Input pad \"%s\" for the filter \"%s\" of type \"%s\" not connected to any source\n", + filt->input_pads[j].name, filt->name, filt->filter->name); + return AVERROR(EINVAL); + } + } + + for (j = 0; j < filt->output_count; j++) { + if (!filt->outputs[j] || !filt->outputs[j]->dst) { + av_log(log_ctx, AV_LOG_ERROR, + "Output pad \"%s\" for the filter \"%s\" of type \"%s\" not connected to any destination\n", + filt->output_pads[j].name, filt->name, filt->filter->name); + return AVERROR(EINVAL); + } + } + } + + return 0; } -/* TODO: insert in sorted order */ -void avfilter_graph_add_filter(AVFilterGraph *graph, AVFilterContext *filter) +int ff_avfilter_graph_config_links(AVFilterGraph *graph, AVClass *log_ctx) { - graph->filters = av_realloc(graph->filters, - sizeof(AVFilterContext*) * ++graph->filter_count); - graph->filters[graph->filter_count - 1] = filter; + AVFilterContext *filt; + int i, ret; + + for (i=0; i < graph->filter_count; i++) { + filt = graph->filters[i]; + + if (!filt->output_count) { + if ((ret = avfilter_config_links(filt))) + return ret; + } + } + + return 0; } -/* search intelligently, once we insert in order */ AVFilterContext *avfilter_graph_get_filter(AVFilterGraph *graph, char *name) { int i; - if(!name) - return NULL; - - for(i = 0; i < graph->filter_count; i ++) - if(graph->filters[i]->name && !strcmp(name, graph->filters[i]->name)) + for (i = 0; i < graph->filter_count; i++) + if (graph->filters[i]->name && !strcmp(name, graph->filters[i]->name)) return graph->filters[i]; return NULL; } -static int query_formats(AVFilterGraph *graph) +static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) { - int i, j; + int i, j, ret; + int scaler_count = 0; + char inst_name[30]; - /* ask all the sub-filters for their supported colorspaces */ - for(i = 0; i < graph->filter_count; i ++) { - if(graph->filters[i]->filter->query_formats) + /* ask all the sub-filters for their supported media formats */ + for (i = 0; i < graph->filter_count; i++) { + if (graph->filters[i]->filter->query_formats) graph->filters[i]->filter->query_formats(graph->filters[i]); else avfilter_default_query_formats(graph->filters[i]); } /* go through and merge as many format lists as possible */ - for(i = 0; i < graph->filter_count; i ++) { + for (i = 0; i < graph->filter_count; i++) { AVFilterContext *filter = graph->filters[i]; - for(j = 0; j < filter->input_count; j ++) { - AVFilterLink *link; - if(!(link = filter->inputs[j])) - continue; - if(link->in_formats != link->out_formats) { - if(!avfilter_merge_formats(link->in_formats, - link->out_formats)) { - /* couldn't merge format lists. auto-insert scale filter */ + for (j = 0; j < filter->input_count; j++) { + AVFilterLink *link = filter->inputs[j]; + if (link && link->in_formats != link->out_formats) { + if (!avfilter_merge_formats(link->in_formats, + link->out_formats)) { AVFilterContext *scale; + char scale_args[256]; + /* couldn't merge format lists. auto-insert scale filter */ + snprintf(inst_name, sizeof(inst_name), "auto-inserted scaler %d", + scaler_count++); + snprintf(scale_args, sizeof(scale_args), "0:0:%s", graph->scale_sws_opts); + if ((ret = avfilter_graph_create_filter(&scale, avfilter_get_by_name("scale"), + inst_name, scale_args, NULL, graph)) < 0) + return ret; + if ((ret = avfilter_insert_filter(link, scale, 0, 0)) < 0) + return ret; - if(!(scale = - avfilter_open(avfilter_get_by_name("scale"), NULL))) - return -1; - if(scale->filter->init(scale, NULL, NULL) || - avfilter_insert_filter(link, scale, 0, 0)) { - avfilter_destroy(scale); - return -1; - } - - avfilter_graph_add_filter(graph, scale); scale->filter->query_formats(scale); - if(!avfilter_merge_formats(scale-> inputs[0]->in_formats, - scale-> inputs[0]->out_formats) || - !avfilter_merge_formats(scale->outputs[0]->in_formats, - scale->outputs[0]->out_formats)) - return -1; + if (((link = scale-> inputs[0]) && + !avfilter_merge_formats(link->in_formats, link->out_formats)) || + ((link = scale->outputs[0]) && + !avfilter_merge_formats(link->in_formats, link->out_formats))) { + av_log(log_ctx, AV_LOG_ERROR, + "Impossible to convert between the formats supported by the filter " + "'%s' and the filter '%s'\n", link->src->name, link->dst->name); + return AVERROR(EINVAL); + } } } } @@ -166,7 +190,7 @@ static int query_formats(AVFilterGraph *graph) static void pick_format(AVFilterLink *link) { - if(!link || !link->in_formats) + if (!link || !link->in_formats) return; link->in_formats->format_count = 1; @@ -180,474 +204,41 @@ static void pick_formats(AVFilterGraph *graph) { int i, j; - for(i = 0; i < graph->filter_count; i ++) { + for (i = 0; i < graph->filter_count; i++) { AVFilterContext *filter = graph->filters[i]; - for(j = 0; j < filter->input_count; j ++) + for (j = 0; j < filter->input_count; j++) pick_format(filter->inputs[j]); - for(j = 0; j < filter->output_count; j ++) + for (j = 0; j < filter->output_count; j++) pick_format(filter->outputs[j]); } } -int avfilter_graph_config_formats(AVFilterGraph *graph) +int ff_avfilter_graph_config_formats(AVFilterGraph *graph, AVClass *log_ctx) { + int ret; + /* find supported formats from sub-filters, and merge along links */ - if(query_formats(graph)) - return -1; + if ((ret = query_formats(graph, log_ctx)) < 0) + return ret; /* Once everything is merged, it's possible that we'll still have - * multiple valid colorspace choices. We pick the first one. */ + * multiple valid media format choices. We pick the first one. */ pick_formats(graph); return 0; } -static int create_filter(AVFilterGraph *ctx, int index, char *name, - char *args) -{ - AVFilterContext *filt; - - AVFilter *filterdef; - char tmp[20]; - - snprintf(tmp, 20, "%d", index); - if(!(filterdef = avfilter_get_by_name(name)) || - !(filt = avfilter_open(filterdef, tmp))) { - av_log(&log_ctx, AV_LOG_ERROR, - "error creating filter '%s'\n", name); - return -1; - } - avfilter_graph_add_filter(ctx, filt); - if(avfilter_init_filter(filt, args, NULL)) { - av_log(&log_ctx, AV_LOG_ERROR, - "error initializing filter '%s'\n", name); - return -1; - } - - return 0; -} - -static int link_filter(AVFilterGraph *ctx, int src, int srcpad, - int dst, int dstpad) -{ - AVFilterContext *filt, *filtb; - - char tmp[20]; - - snprintf(tmp, 20, "%d", src); - if(!(filt = avfilter_graph_get_filter(ctx, tmp))) { - av_log(&log_ctx, AV_LOG_ERROR, "link source does not exist in graph\n"); - return -1; - } - snprintf(tmp, 20, "%d", dst); - if(!(filtb = avfilter_graph_get_filter(ctx, tmp))) { - av_log(&log_ctx, AV_LOG_ERROR, "link destination does not exist in graph\n"); - return -1; - } - if(avfilter_link(filt, srcpad, filtb, dstpad)) { - av_log(&log_ctx, AV_LOG_ERROR, "cannot create link between source and destination filters\n"); - return -1; - } - - return 0; -} - -static int load_from_desc(AVFilterGraph *graph, AVFilterGraphDesc *desc, AVFilterContext *in, int inpad, AVFilterContext *out, int outpad) -{ - AVFilterGraphDescExport *curpad; - char tmp[20]; - AVFilterContext *filt; - AVFilterGraphDescFilter *curfilt; - AVFilterGraphDescLink *curlink; - - - /* create all filters */ - for(curfilt = desc->filters; curfilt; curfilt = curfilt->next) { - if (create_filter(graph, curfilt->index, curfilt->filter, - curfilt->args) < 0) - goto fail; - } - - /* create all links */ - for(curlink = desc->links; curlink; curlink = curlink->next) { - if (link_filter(graph, curlink->src, curlink->srcpad, - curlink->dst, curlink->dstpad) < 0) - goto fail; - } - - /* export all input pads */ - for(curpad = desc->inputs; curpad; curpad = curpad->next) { - snprintf(tmp, 20, "%d", curpad->filter); - if(!(filt = avfilter_graph_get_filter(graph, tmp))) { - av_log(&log_ctx, AV_LOG_ERROR, "filter owning exported pad does not exist\n"); - goto fail; - } - if(avfilter_link(in, inpad, filt, curpad->pad)) { - av_log(&log_ctx, AV_LOG_ERROR, "cannot create link between source and destination filters\n"); - goto fail; - } - } - - /* export all output pads */ - for(curpad = desc->outputs; curpad; curpad = curpad->next) { - snprintf(tmp, 20, "%d", curpad->filter); - if(!(filt = avfilter_graph_get_filter(graph, tmp))) { - av_log(&log_ctx, AV_LOG_ERROR, "filter owning exported pad does not exist\n"); - goto fail; - } - - if(avfilter_link(filt, curpad->pad, out, outpad)) { - av_log(&log_ctx, AV_LOG_ERROR, "cannot create link between source and destination filters\n"); - goto fail; - } - } - - return 0; - -fail: - avfilter_destroy_graph(graph); - return -1; -} - - -static void consume_whitespace(const char **buf) +int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx) { - *buf += strspn(*buf, " \n\t"); -} - -/** - * get the next non-whitespace char - */ -static char consume_char(const char **buf) -{ - char out; - consume_whitespace(buf); - - out = **buf; - - if (out) - (*buf)++; + int ret; - return out; -} - -/** - * remove the quotation marks from a string. Ex: "aaa'bb'cc" -> "aaabbcc" - */ -static void unquote(char *str) -{ - char *p1, *p2; - p1=p2=str; - while (*p1 != 0) { - if (*p1 != '\'') - *p2++ = *p1; - p1++; - } - - *p2 = 0; -} - -/** - * 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) -{ - const char *start; - char *ret; - int size; - - consume_whitespace(buf); - - if (!(**buf)) - return av_mallocz(1); - - start = *buf; - - *buf += strcspn(*buf, " ()=,'"); - - if (**buf == '\'') { - char *p = strchr(*buf + 1, '\''); - if (p) - *buf = p + 1; - else - *buf += strlen(*buf); // Move the pointer to the null end byte - } - - size = *buf - start + 1; - ret = av_malloc(size); - memcpy(ret, start, size - 1); - ret[size-1] = 0; - - unquote(ret); - - 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) -{ - consume_char(buf); - - *name = consume_string(buf); - - if (!*name[0]) - goto fail; - - if (consume_char(buf) != ')') - goto fail; - - return; - fail: - av_freep(name); - av_log(&log_ctx, AV_LOG_ERROR, "Could not parse link name!\n"); -} - -/** - * 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 void parse_filter(const char **buf, char **name, char **opts) -{ - *name = consume_string(buf); - - if (**buf == '=') { - consume_char(buf); - *opts = consume_string(buf); - } else { - *opts = NULL; - } - -} - -enum LinkType { - LinkTypeIn, - LinkTypeOut, -}; - -/** - * A linked-list of the inputs/outputs of the filter chain. - */ -typedef struct AVFilterInOut { - enum LinkType type; - char *name; - int instance; - int pad_idx; - - struct AVFilterInOut *next; -} AVFilterInOut; - -static void free_inout(AVFilterInOut *head) -{ - while (head) { - AVFilterInOut *next; - next = head->next; - av_free(head); - head = next; - } -} - -/** - * Parse "(a1)(link2) ... (etc)" - */ -static int parse_inouts(const char **buf, AVFilterInOut **inout, int firstpad, - enum LinkType type, int instance) -{ - int pad = firstpad; - while (**buf == '(') { - AVFilterInOut *inoutn = av_malloc(sizeof(AVFilterInOut)); - parse_link_name(buf, &inoutn->name); - inoutn->type = type; - inoutn->instance = instance; - inoutn->pad_idx = pad++; - inoutn->next = *inout; - *inout = inoutn; - } - return pad; -} - -/** - * Free a graph description. - */ -static void free_desc(AVFilterGraphDesc *desc) -{ - void *next; - - while(desc->filters) { - next = desc->filters->next; - av_free(desc->filters->filter); - av_free(desc->filters->args); - av_free(desc->filters); - desc->filters = next; - } - - while(desc->links) { - next = desc->links->next; - av_free(desc->links); - desc->links = next; - } - - while(desc->inputs) { - next = desc->inputs->next; - av_free(desc->inputs); - desc->inputs = next; - } - - while(desc->outputs) { - next = desc->outputs->next; - av_free(desc->outputs); - desc->outputs = next; - } -} - -static AVFilterGraphDesc *parse_chain(const char *filters, int has_in) -{ - AVFilterGraphDesc *ret; - AVFilterGraphDescFilter **filterp, *filtern; - AVFilterGraphDescLink **linkp, *linkn; - AVFilterInOut *inout=NULL; - AVFilterInOut *head; - - int index = 0; - char chr = 0; - int pad = 0; - int has_out = 0; - - consume_whitespace(&filters); - - if(!(ret = av_mallocz(sizeof(AVFilterGraphDesc)))) - return NULL; - - filterp = &ret->filters; - linkp = &ret->links; - - do { - if(chr == ',') { - linkn = av_mallocz(sizeof(AVFilterGraphDescLink)); - linkn->src = index-1; - linkn->srcpad = pad; - linkn->dst = index; - linkn->dstpad = 0; - - *linkp = linkn; - linkp = &linkn->next; - } - pad = parse_inouts(&filters, &inout, chr == ',' || (!has_in), - LinkTypeIn, index); - - filtern = av_mallocz(sizeof(AVFilterGraphDescFilter)); - filtern->index = index; - parse_filter(&filters, &filtern->filter, &filtern->args); - *filterp = filtern; - filterp = &filtern->next; - - pad = parse_inouts(&filters, &inout, 0, - LinkTypeOut, index); - chr = consume_char(&filters); - index++; - } while (chr == ',' || chr == ';'); - - head = inout; - for (; inout != NULL; inout = inout->next) { - if (inout->instance == -1) - continue; // Already processed - - if (!strcmp(inout->name, "in")) { - if (!has_in) - goto fail; - ret->inputs = av_mallocz(sizeof(AVFilterGraphDescExport)); - ret->inputs->filter = inout->instance; - ret->inputs->pad = inout->pad_idx; - } else if (!strcmp(inout->name, "out")) { - has_out = 1; - ret->outputs = av_mallocz(sizeof(AVFilterGraphDescExport)); - ret->outputs->filter = inout->instance; - ret->outputs->pad = inout->pad_idx; - } else { - AVFilterInOut *p, *src, *dst; - for (p = inout->next; - p && strcmp(p->name,inout->name); p = p->next); - - if (!p) { - av_log(&log_ctx, AV_LOG_ERROR, "Unmatched link: %s.\n", - inout->name); - goto fail; - } - - if (p->type == LinkTypeIn && inout->type == LinkTypeOut) { - src = inout; - dst = p; - } else if (p->type == LinkTypeOut && inout->type == LinkTypeIn) { - src = p; - dst = inout; - } else { - av_log(&log_ctx, AV_LOG_ERROR, "Two links named '%s' are either both input or both output\n", - inout->name); - goto fail; - } - linkn = av_mallocz(sizeof(AVFilterGraphDescLink)); - - linkn->src = src->instance; - linkn->srcpad = src->pad_idx; - linkn->dst = dst->instance; - linkn->dstpad = dst->pad_idx; - - *linkp = linkn; - linkp = &linkn->next; - - src->instance = -1; - dst->instance = -1; - } - } - - free_inout(head); - - if (!has_in) { - ret->inputs = av_mallocz(sizeof(AVFilterGraphDescExport)); - ret->inputs->filter = 0; - } - if (!has_out) { - ret->outputs = av_mallocz(sizeof(AVFilterGraphDescExport)); - ret->outputs->filter = index-1; - } - - return ret; - - fail: - free_inout(head); - - free_desc(ret); - return NULL; -} - -/** - * Parse a string describing a filter graph. - */ -int avfilter_graph_parse_chain(AVFilterGraph *graph, const char *filters, AVFilterContext *in, int inpad, AVFilterContext *out, int outpad) -{ - AVFilterGraphDesc *desc; - - /* Try first to parse supposing there is no (in) element */ - if (!(desc = parse_chain(filters, 0))) { - /* If it didn't work, parse supposing there is an (in) element */ - desc = parse_chain(filters, 1); - } - if (!desc) - return -1; - - if (load_from_desc(graph, desc, in, inpad, out, outpad) < 0) { - free_desc(desc); - return -1; - } + if ((ret = ff_avfilter_graph_check_validity(graphctx, log_ctx))) + return ret; + if ((ret = ff_avfilter_graph_config_formats(graphctx, log_ctx))) + return ret; + if ((ret = ff_avfilter_graph_config_links(graphctx, log_ctx))) + return ret; - free_desc(desc); return 0; }