diff options
author | Anton Khirnov <anton@khirnov.net> | 2012-03-21 10:46:56 +0400 |
---|---|---|
committer | Anton Khirnov <anton@khirnov.net> | 2012-04-14 11:25:46 +0400 |
commit | d7bcc71dadea71d86a2b95a4f46aedd392f8b948 (patch) | |
tree | d9ceee7b910f008a59074bd17b93b81d6f0b8272 /libavfilter | |
parent | 7ae7c41413beb601714a06b4fd18cdb4610b2eda (diff) |
graphparser: add avfilter_graph_parse2().
Unlike avfilter_graph_parse(), it returns unlinked inputs and outputs
to the caller, which allows parsing of graphs where inputs/outputs are
not known in advance.
Diffstat (limited to 'libavfilter')
-rw-r--r-- | libavfilter/avfiltergraph.h | 44 | ||||
-rw-r--r-- | libavfilter/graphparser.c | 136 |
2 files changed, 141 insertions, 39 deletions
diff --git a/libavfilter/avfiltergraph.h b/libavfilter/avfiltergraph.h index 733d1c4a51..0d250df614 100644 --- a/libavfilter/avfiltergraph.h +++ b/libavfilter/avfiltergraph.h @@ -91,11 +91,11 @@ void avfilter_graph_free(AVFilterGraph **graph); /** * A linked-list of the inputs/outputs of the filter chain. * - * This is mainly useful for avfilter_graph_parse(), since this - * function may accept a description of a graph with not connected - * input/output pads. This struct specifies, per each not connected - * pad contained in the graph, the filter context and the pad index - * required for establishing a link. + * This is mainly useful for avfilter_graph_parse() / avfilter_graph_parse2(), + * where it is used to communicate open (unlinked) inputs and outputs from and + * to the caller. + * This struct specifies, per each not connected pad contained in the graph, the + * filter context and the pad index required for establishing a link. */ typedef struct AVFilterInOut { /** unique name for this input/output in the list */ @@ -124,4 +124,38 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, AVFilterInOut *inputs, AVFilterInOut *outputs, void *log_ctx); +/** + * Add a graph described by a string to a graph. + * + * @param[in] graph the filter graph where to link the parsed graph context + * @param[in] filters string to be parsed + * @param[out] inputs a linked list of all free (unlinked) inputs of the + * parsed graph will be returned here. It is to be freed + * by the caller using avfilter_inout_free(). + * @param[out] outputs a linked list of all free (unlinked) outputs of the + * parsed graph will be returned here. It is to be freed by the + * caller using avfilter_inout_free(). + * @return zero on success, a negative AVERROR code on error + * + * @note the difference between avfilter_graph_parse2() and + * avfilter_graph_parse() is that in avfilter_graph_parse(), the caller provides + * the lists of inputs and outputs, which therefore must be known before calling + * the function. On the other hand, avfilter_graph_parse2() \em returns the + * inputs and outputs that are left unlinked after parsing the graph and the + * caller then deals with them. Another difference is that in + * avfilter_graph_parse(), the inputs parameter describes inputs of the + * <em>already existing</em> part of the graph; i.e. from the point of view of + * the newly created part, they are outputs. Similarly the outputs parameter + * describes outputs of the already existing filters, which are provided as + * inputs to the parsed filters. + * avfilter_graph_parse2() takes the opposite approach -- it makes no reference + * whatsoever to already existing parts of the graph and the inputs parameter + * will on return contain inputs of the newly parsed part of the graph. + * Analogously the outputs parameter will contain outputs of the newly created + * filters. + */ +int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters, + AVFilterInOut **inputs, + AVFilterInOut **outputs); + #endif /* AVFILTER_AVFILTERGRAPH_H */ diff --git a/libavfilter/graphparser.c b/libavfilter/graphparser.c index 90f2936590..7a899a815c 100644 --- a/libavfilter/graphparser.c +++ b/libavfilter/graphparser.c @@ -184,13 +184,15 @@ static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links) { AVFilterInOut *ret; - while (*links && strcmp((*links)->name, label)) + while (*links && (!(*links)->name || strcmp((*links)->name, label))) links = &((*links)->next); ret = *links; - if (ret) + if (ret) { *links = ret->next; + ret->next = NULL; + } return ret; } @@ -201,6 +203,18 @@ static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element) *inouts = element; } +static void append_inout(AVFilterInOut **inouts, AVFilterInOut **element) +{ + while (*inouts && (*inouts)->next) + inouts = &((*inouts)->next); + + if (!*inouts) + *inouts = *element; + else + (*inouts)->next = *element; + *element = NULL; +} + static int link_filter_inouts(AVFilterContext *filt_ctx, AVFilterInOut **curr_inputs, AVFilterInOut **open_inputs, void *log_ctx) @@ -209,14 +223,11 @@ static int link_filter_inouts(AVFilterContext *filt_ctx, while (pad--) { AVFilterInOut *p = *curr_inputs; - if (!p) { - av_log(log_ctx, AV_LOG_ERROR, - "Not enough inputs specified for the \"%s\" filter.\n", - filt_ctx->filter->name); - return AVERROR(EINVAL); - } - *curr_inputs = (*curr_inputs)->next; + if (p) + *curr_inputs = (*curr_inputs)->next; + else if (!(p = av_mallocz(sizeof(*p)))) + return AVERROR(ENOMEM); if (p->filter_ctx) { if ((ret = link_filter(p->filter_ctx, p->pad_idx, filt_ctx, pad, log_ctx)) < 0) @@ -329,18 +340,22 @@ static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs, return pad; } -int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, - AVFilterInOut *open_inputs, - AVFilterInOut *open_outputs, void *log_ctx) +#if FF_API_GRAPH_AVCLASS +#define log_ctx graph +#else +#define log_ctx NULL +#endif +int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters, + AVFilterInOut **inputs, + AVFilterInOut **outputs) { int index = 0, ret; char chr = 0; - AVFilterInOut *curr_inputs = NULL; + AVFilterInOut *curr_inputs = NULL, *open_inputs = NULL, *open_outputs = NULL; do { AVFilterContext *filter; - const char *filterchain = filters; filters += strspn(filters, WHITESPACES); if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx)) < 0) @@ -349,12 +364,6 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0) goto fail; - if (filter->input_count == 1 && !curr_inputs && !index) { - /* First input can be omitted if it is "[in]" */ - const char *tmp = "[in]"; - if ((ret = parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx)) < 0) - goto fail; - } if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx)) < 0) goto fail; @@ -366,13 +375,8 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, filters += strspn(filters, WHITESPACES); chr = *filters++; - if (chr == ';' && curr_inputs) { - av_log(log_ctx, AV_LOG_ERROR, - "Invalid filterchain containing an unlabelled output pad: \"%s\"\n", - filterchain); - ret = AVERROR(EINVAL); - goto fail; - } + if (chr == ';' && curr_inputs) + append_inout(&open_outputs, &curr_inputs); index++; } while (chr == ',' || chr == ';'); @@ -384,14 +388,10 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, goto fail; } - if (open_inputs && !strcmp(open_inputs->name, "out") && curr_inputs) { - /* Last output can be omitted if it is "[out]" */ - const char *tmp = "[out]"; - if ((ret = parse_outputs(&tmp, &curr_inputs, &open_inputs, &open_outputs, - log_ctx)) < 0) - goto fail; - } + append_inout(&open_outputs, &curr_inputs); + *inputs = open_inputs; + *outputs = open_outputs; return 0; fail: @@ -401,5 +401,73 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, free_inout(open_inputs); free_inout(open_outputs); free_inout(curr_inputs); + + *inputs = NULL; + *outputs = NULL; + + return ret; +} +#undef log_ctx + +int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, + AVFilterInOut *open_inputs, + AVFilterInOut *open_outputs, void *log_ctx) +{ + int ret; + AVFilterInOut *cur, *match, *inputs = NULL, *outputs = NULL; + + if ((ret = avfilter_graph_parse2(graph, filters, &inputs, &outputs)) < 0) + goto fail; + + /* First input can be omitted if it is "[in]" */ + if (inputs && !inputs->name) + inputs->name = av_strdup("in"); + for (cur = inputs; cur; cur = cur->next) { + if (!cur->name) { + av_log(log_ctx, AV_LOG_ERROR, + "Not enough inputs specified for the \"%s\" filter.\n", + cur->filter_ctx->filter->name); + ret = AVERROR(EINVAL); + goto fail; + } + if (!(match = extract_inout(cur->name, &open_outputs))) + continue; + ret = avfilter_link(match->filter_ctx, match->pad_idx, + cur->filter_ctx, cur->pad_idx); + free_inout(match); + if (ret < 0) + goto fail; + } + + /* Last output can be omitted if it is "[out]" */ + if (outputs && !outputs->name) + outputs->name = av_strdup("out"); + for (cur = outputs; cur; cur = cur->next) { + if (!cur->name) { + av_log(log_ctx, AV_LOG_ERROR, + "Invalid filterchain containing an unlabelled output pad: \"%s\"\n", + filters); + ret = AVERROR(EINVAL); + goto fail; + } + if (!(match = extract_inout(cur->name, &open_inputs))) + continue; + ret = avfilter_link(cur->filter_ctx, cur->pad_idx, + match->filter_ctx, match->pad_idx); + free_inout(match); + if (ret < 0) + goto fail; + } + + fail: + if (ret < 0) { + for (; graph->filter_count > 0; graph->filter_count--) + avfilter_free(graph->filters[graph->filter_count - 1]); + av_freep(&graph->filters); + } + free_inout(inputs); + free_inout(outputs); + free_inout(open_inputs); + free_inout(open_outputs); return ret; } |