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.
Unified · Split
| @@ -91,11 +91,11 @@ void avfilter_graph_free(AVFilterGraph **graph); · 90 unmodified lines | ||
| 91 | 91 | /** |
| 92 | 92 | * A linked-list of the inputs/outputs of the filter chain. |
| 93 | 93 | * |
| 94 | − * This is mainly useful for avfilter_graph_parse(), since this | |
| 95 | − * function may accept a description of a graph with not connected | |
| 96 | − * input/output pads. This struct specifies, per each not connected | |
| 97 | − * pad contained in the graph, the filter context and the pad index | |
| 98 | − * required for establishing a link. | |
| 94 | + * This is mainly useful for avfilter_graph_parse() / avfilter_graph_parse2(), | |
| 95 | + * where it is used to communicate open (unlinked) inputs and outputs from and | |
| 96 | + * to the caller. | |
| 97 | + * This struct specifies, per each not connected pad contained in the graph, the | |
| 98 | + * filter context and the pad index required for establishing a link. | |
| 99 | 99 | */ |
| 100 | 100 | typedef struct AVFilterInOut { |
| 101 | 101 | /** unique name for this input/output in the list */ |
| @@ -124,4 +124,38 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, · 22 unmodified lines | ||
| 124 | 124 | AVFilterInOut *inputs, AVFilterInOut *outputs, |
| 125 | 125 | void *log_ctx); |
| 126 | 126 | |
| 127 | +/** | |
| 128 | + * Add a graph described by a string to a graph. | |
| 129 | + * | |
| 130 | + * @param[in] graph the filter graph where to link the parsed graph context | |
| 131 | + * @param[in] filters string to be parsed | |
| 132 | + * @param[out] inputs a linked list of all free (unlinked) inputs of the | |
| 133 | + * parsed graph will be returned here. It is to be freed | |
| 134 | + * by the caller using avfilter_inout_free(). | |
| 135 | + * @param[out] outputs a linked list of all free (unlinked) outputs of the | |
| 136 | + * parsed graph will be returned here. It is to be freed by the | |
| 137 | + * caller using avfilter_inout_free(). | |
| 138 | + * @return zero on success, a negative AVERROR code on error | |
| 139 | + * | |
| 140 | + * @note the difference between avfilter_graph_parse2() and | |
| 141 | + * avfilter_graph_parse() is that in avfilter_graph_parse(), the caller provides | |
| 142 | + * the lists of inputs and outputs, which therefore must be known before calling | |
| 143 | + * the function. On the other hand, avfilter_graph_parse2() \em returns the | |
| 144 | + * inputs and outputs that are left unlinked after parsing the graph and the | |
| 145 | + * caller then deals with them. Another difference is that in | |
| 146 | + * avfilter_graph_parse(), the inputs parameter describes inputs of the | |
| 147 | + * <em>already existing</em> part of the graph; i.e. from the point of view of | |
| 148 | + * the newly created part, they are outputs. Similarly the outputs parameter | |
| 149 | + * describes outputs of the already existing filters, which are provided as | |
| 150 | + * inputs to the parsed filters. | |
| 151 | + * avfilter_graph_parse2() takes the opposite approach -- it makes no reference | |
| 152 | + * whatsoever to already existing parts of the graph and the inputs parameter | |
| 153 | + * will on return contain inputs of the newly parsed part of the graph. | |
| 154 | + * Analogously the outputs parameter will contain outputs of the newly created | |
| 155 | + * filters. | |
| 156 | + */ | |
| 157 | +int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters, | |
| 158 | + AVFilterInOut **inputs, | |
| 159 | + AVFilterInOut **outputs); | |
| 160 | + | |
| 127 | 161 | #endif /* AVFILTER_AVFILTERGRAPH_H */ |
| @@ -184,13 +184,15 @@ static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links) · 183 unmodified lines | ||
| 184 | 184 | { |
| 185 | 185 | AVFilterInOut *ret; |
| 186 | 186 | |
| 187 | − while (*links && strcmp((*links)->name, label)) | |
| 187 | + while (*links && (!(*links)->name || strcmp((*links)->name, label))) | |
| 188 | 188 | links = &((*links)->next); |
| 189 | 189 | |
| 190 | 190 | ret = *links; |
| 191 | 191 | |
| 192 | − if (ret) | |
| 192 | + if (ret) { | |
| 193 | 193 | *links = ret->next; |
| 194 | + ret->next = NULL; | |
| 195 | + } | |
| 194 | 196 | |
| 195 | 197 | return ret; |
| 196 | 198 | } |
| @@ -201,6 +203,18 @@ static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element) · 4 unmodified lines | ||
| 201 | 203 | *inouts = element; |
| 202 | 204 | } |
| 203 | 205 | |
| 206 | +static void append_inout(AVFilterInOut **inouts, AVFilterInOut **element) | |
| 207 | +{ | |
| 208 | + while (*inouts && (*inouts)->next) | |
| 209 | + inouts = &((*inouts)->next); | |
| 210 | + | |
| 211 | + if (!*inouts) | |
| 212 | + *inouts = *element; | |
| 213 | + else | |
| 214 | + (*inouts)->next = *element; | |
| 215 | + *element = NULL; | |
| 216 | +} | |
| 217 | + | |
| 204 | 218 | static int link_filter_inouts(AVFilterContext *filt_ctx, |
| 205 | 219 | AVFilterInOut **curr_inputs, |
| 206 | 220 | AVFilterInOut **open_inputs, void *log_ctx) |
| @@ -209,14 +223,11 @@ static int link_filter_inouts(AVFilterContext *filt_ctx, · 2 unmodified lines | ||
| 209 | 223 | |
| 210 | 224 | while (pad--) { |
| 211 | 225 | AVFilterInOut *p = *curr_inputs; |
| 212 | − if (!p) { | |
| 213 | − av_log(log_ctx, AV_LOG_ERROR, | |
| 214 | − "Not enough inputs specified for the \"%s\" filter.\n", | |
| 215 | − filt_ctx->filter->name); | |
| 216 | − return AVERROR(EINVAL); | |
| 217 | − } | |
| 218 | 226 | |
| 219 | − *curr_inputs = (*curr_inputs)->next; | |
| 227 | + if (p) | |
| 228 | + *curr_inputs = (*curr_inputs)->next; | |
| 229 | + else if (!(p = av_mallocz(sizeof(*p)))) | |
| 230 | + return AVERROR(ENOMEM); | |
| 220 | 231 | |
| 221 | 232 | if (p->filter_ctx) { |
| 222 | 233 | 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, · 106 unmodified lines | ||
| 329 | 340 | return pad; |
| 330 | 341 | } |
| 331 | 342 | |
| 332 | −int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, | |
| 333 | − AVFilterInOut *open_inputs, | |
| 334 | − AVFilterInOut *open_outputs, void *log_ctx) | |
| 343 | +#if FF_API_GRAPH_AVCLASS | |
| 344 | +#define log_ctx graph | |
| 345 | +#else | |
| 346 | +#define log_ctx NULL | |
| 347 | +#endif | |
| 348 | +int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters, | |
| 349 | + AVFilterInOut **inputs, | |
| 350 | + AVFilterInOut **outputs) | |
| 335 | 351 | { |
| 336 | 352 | int index = 0, ret; |
| 337 | 353 | char chr = 0; |
| 338 | 354 | |
| 339 | − AVFilterInOut *curr_inputs = NULL; | |
| 355 | + AVFilterInOut *curr_inputs = NULL, *open_inputs = NULL, *open_outputs = NULL; | |
| 340 | 356 | |
| 341 | 357 | do { |
| 342 | 358 | AVFilterContext *filter; |
| 343 | − const char *filterchain = filters; | |
| 344 | 359 | filters += strspn(filters, WHITESPACES); |
| 345 | 360 | |
| 346 | 361 | 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, · 2 unmodified lines | ||
| 349 | 364 | if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0) |
| 350 | 365 | goto fail; |
| 351 | 366 | |
| 352 | − if (filter->input_count == 1 && !curr_inputs && !index) { | |
| 353 | − /* First input can be omitted if it is "[in]" */ | |
| 354 | − const char *tmp = "[in]"; | |
| 355 | − if ((ret = parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx)) < 0) | |
| 356 | − goto fail; | |
| 357 | − } | |
| 358 | 367 | |
| 359 | 368 | if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx)) < 0) |
| 360 | 369 | goto fail; |
| @@ -366,13 +375,8 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, · 5 unmodified lines | ||
| 366 | 375 | filters += strspn(filters, WHITESPACES); |
| 367 | 376 | chr = *filters++; |
| 368 | 377 | |
| 369 | − if (chr == ';' && curr_inputs) { | |
| 370 | − av_log(log_ctx, AV_LOG_ERROR, | |
| 371 | − "Invalid filterchain containing an unlabelled output pad: \"%s\"\n", | |
| 372 | − filterchain); | |
| 373 | − ret = AVERROR(EINVAL); | |
| 374 | − goto fail; | |
| 375 | − } | |
| 378 | + if (chr == ';' && curr_inputs) | |
| 379 | + append_inout(&open_outputs, &curr_inputs); | |
| 376 | 380 | index++; |
| 377 | 381 | } while (chr == ',' || chr == ';'); |
| 378 | 382 | |
| @@ -384,14 +388,10 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, · 5 unmodified lines | ||
| 384 | 388 | goto fail; |
| 385 | 389 | } |
| 386 | 390 | |
| 387 | − if (open_inputs && !strcmp(open_inputs->name, "out") && curr_inputs) { | |
| 388 | − /* Last output can be omitted if it is "[out]" */ | |
| 389 | − const char *tmp = "[out]"; | |
| 390 | − if ((ret = parse_outputs(&tmp, &curr_inputs, &open_inputs, &open_outputs, | |
| 391 | − log_ctx)) < 0) | |
| 392 | − goto fail; | |
| 393 | − } | |
| 391 | + append_inout(&open_outputs, &curr_inputs); | |
| 394 | 392 | |
| 393 | + *inputs = open_inputs; | |
| 394 | + *outputs = open_outputs; | |
| 395 | 395 | return 0; |
| 396 | 396 | |
| 397 | 397 | fail: |
| @@ -401,5 +401,73 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, · 3 unmodified lines | ||
| 401 | 401 | free_inout(open_inputs); |
| 402 | 402 | free_inout(open_outputs); |
| 403 | 403 | free_inout(curr_inputs); |
| 404 | + | |
| 405 | + *inputs = NULL; | |
| 406 | + *outputs = NULL; | |
| 407 | + | |
| 408 | + return ret; | |
| 409 | +} | |
| 410 | +#undef log_ctx | |
| 411 | + | |
| 412 | +int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, | |
| 413 | + AVFilterInOut *open_inputs, | |
| 414 | + AVFilterInOut *open_outputs, void *log_ctx) | |
| 415 | +{ | |
| 416 | + int ret; | |
| 417 | + AVFilterInOut *cur, *match, *inputs = NULL, *outputs = NULL; | |
| 418 | + | |
| 419 | + if ((ret = avfilter_graph_parse2(graph, filters, &inputs, &outputs)) < 0) | |
| 420 | + goto fail; | |
| 421 | + | |
| 422 | + /* First input can be omitted if it is "[in]" */ | |
| 423 | + if (inputs && !inputs->name) | |
| 424 | + inputs->name = av_strdup("in"); | |
| 425 | + for (cur = inputs; cur; cur = cur->next) { | |
| 426 | + if (!cur->name) { | |
| 427 | + av_log(log_ctx, AV_LOG_ERROR, | |
| 428 | + "Not enough inputs specified for the \"%s\" filter.\n", | |
| 429 | + cur->filter_ctx->filter->name); | |
| 430 | + ret = AVERROR(EINVAL); | |
| 431 | + goto fail; | |
| 432 | + } | |
| 433 | + if (!(match = extract_inout(cur->name, &open_outputs))) | |
| 434 | + continue; | |
| 435 | + ret = avfilter_link(match->filter_ctx, match->pad_idx, | |
| 436 | + cur->filter_ctx, cur->pad_idx); | |
| 437 | + free_inout(match); | |
| 438 | + if (ret < 0) | |
| 439 | + goto fail; | |
| 440 | + } | |
| 441 | + | |
| 442 | + /* Last output can be omitted if it is "[out]" */ | |
| 443 | + if (outputs && !outputs->name) | |
| 444 | + outputs->name = av_strdup("out"); | |
| 445 | + for (cur = outputs; cur; cur = cur->next) { | |
| 446 | + if (!cur->name) { | |
| 447 | + av_log(log_ctx, AV_LOG_ERROR, | |
| 448 | + "Invalid filterchain containing an unlabelled output pad: \"%s\"\n", | |
| 449 | + filters); | |
| 450 | + ret = AVERROR(EINVAL); | |
| 451 | + goto fail; | |
| 452 | + } | |
| 453 | + if (!(match = extract_inout(cur->name, &open_inputs))) | |
| 454 | + continue; | |
| 455 | + ret = avfilter_link(cur->filter_ctx, cur->pad_idx, | |
| 456 | + match->filter_ctx, match->pad_idx); | |
| 457 | + free_inout(match); | |
| 458 | + if (ret < 0) | |
| 459 | + goto fail; | |
| 460 | + } | |
| 461 | + | |
| 462 | + fail: | |
| 463 | + if (ret < 0) { | |
| 464 | + for (; graph->filter_count > 0; graph->filter_count--) | |
| 465 | + avfilter_free(graph->filters[graph->filter_count - 1]); | |
| 466 | + av_freep(&graph->filters); | |
| 467 | + } | |
| 468 | + free_inout(inputs); | |
| 469 | + free_inout(outputs); | |
| 470 | + free_inout(open_inputs); | |
| 471 | + free_inout(open_outputs); | |
| 404 | 472 | return ret; |
| 405 | 473 | } |