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 | * A linked-list of the inputs/outputs of the filter chain. | 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 | 94 | * This is mainly useful for avfilter_graph_parse() / avfilter_graph_parse2(), |
| 95 | * function may accept a description of a graph with not connected | 95 | * where it is used to communicate open (unlinked) inputs and outputs from and |
| 96 | * input/output pads. This struct specifies, per each not connected | 96 | * to the caller. |
| 97 | * pad contained in the graph, the filter context and the pad index | 97 | * This struct specifies, per each not connected pad contained in the graph, the |
| 98 | * required for establishing a link. | 98 | * filter context and the pad index required for establishing a link. |
| 99 | */ | 99 | */ |
| 100 | typedef struct AVFilterInOut { | 100 | typedef struct AVFilterInOut { |
| 101 | /** unique name for this input/output in the list */ | 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 | AVFilterInOut *inputs, AVFilterInOut *outputs, | 124 | AVFilterInOut *inputs, AVFilterInOut *outputs, |
| 125 | void *log_ctx); | 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 | #endif /* AVFILTER_AVFILTERGRAPH_H */ | 161 | #endif /* AVFILTER_AVFILTERGRAPH_H */ |
| @@ -184,13 +184,15 @@ static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links) · 183 unmodified lines | |||
| 184 | { | 184 | { |
| 185 | AVFilterInOut *ret; | 185 | AVFilterInOut *ret; |
| 186 | 186 | ||
| 187 | while (*links && strcmp((*links)->name, label)) | 187 | while (*links && (!(*links)->name || strcmp((*links)->name, label))) |
| 188 | links = &((*links)->next); | 188 | links = &((*links)->next); |
| 189 | 189 | ||
| 190 | ret = *links; | 190 | ret = *links; |
| 191 | 191 | ||
| 192 | if (ret) | 192 | if (ret) { |
| 193 | *links = ret->next; | 193 | *links = ret->next; |
| 194 | ret->next = NULL; | ||
| 195 | } | ||
| 194 | 196 | ||
| 195 | return ret; | 197 | return ret; |
| 196 | } | 198 | } |
| @@ -201,6 +203,18 @@ static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element) · 4 unmodified lines | |||
| 201 | *inouts = element; | 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 | static int link_filter_inouts(AVFilterContext *filt_ctx, | 218 | static int link_filter_inouts(AVFilterContext *filt_ctx, |
| 205 | AVFilterInOut **curr_inputs, | 219 | AVFilterInOut **curr_inputs, |
| 206 | AVFilterInOut **open_inputs, void *log_ctx) | 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 | while (pad--) { | 224 | while (pad--) { |
| 211 | AVFilterInOut *p = *curr_inputs; | 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 | if (p->filter_ctx) { | 232 | if (p->filter_ctx) { |
| 222 | if ((ret = link_filter(p->filter_ctx, p->pad_idx, filt_ctx, pad, log_ctx)) < 0) | 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 | return pad; | 340 | return pad; |
| 330 | } | 341 | } |
| 331 | 342 | ||
| 332 | int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, | 343 | #if FF_API_GRAPH_AVCLASS |
| 333 | AVFilterInOut *open_inputs, | 344 | #define log_ctx graph |
| 334 | AVFilterInOut *open_outputs, void *log_ctx) | 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 | int index = 0, ret; | 352 | int index = 0, ret; |
| 337 | char chr = 0; | 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 | do { | 357 | do { |
| 342 | AVFilterContext *filter; | 358 | AVFilterContext *filter; |
| 343 | const char *filterchain = filters; | ||
| 344 | filters += strspn(filters, WHITESPACES); | 359 | filters += strspn(filters, WHITESPACES); |
| 345 | 360 | ||
| 346 | if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx)) < 0) | 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 | if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0) | 364 | if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0) |
| 350 | goto fail; | 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 | if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx)) < 0) | 368 | if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx)) < 0) |
| 360 | goto fail; | 369 | goto fail; |
| @@ -366,13 +375,8 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, · 5 unmodified lines | |||
| 366 | filters += strspn(filters, WHITESPACES); | 375 | filters += strspn(filters, WHITESPACES); |
| 367 | chr = *filters++; | 376 | chr = *filters++; |
| 368 | 377 | ||
| 369 | if (chr == ';' && curr_inputs) { | 378 | if (chr == ';' && curr_inputs) |
| 370 | av_log(log_ctx, AV_LOG_ERROR, | 379 | append_inout(&open_outputs, &curr_inputs); |
| 371 | "Invalid filterchain containing an unlabelled output pad: \"%s\"\n", | ||
| 372 | filterchain); | ||
| 373 | ret = AVERROR(EINVAL); | ||
| 374 | goto fail; | ||
| 375 | } | ||
| 376 | index++; | 380 | index++; |
| 377 | } while (chr == ',' || chr == ';'); | 381 | } while (chr == ',' || chr == ';'); |
| 378 | 382 | ||
| @@ -384,14 +388,10 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, · 5 unmodified lines | |||
| 384 | goto fail; | 388 | goto fail; |
| 385 | } | 389 | } |
| 386 | 390 | ||
| 387 | if (open_inputs && !strcmp(open_inputs->name, "out") && curr_inputs) { | 391 | append_inout(&open_outputs, &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 | } | ||
| 394 | 392 | ||
| 393 | *inputs = open_inputs; | ||
| 394 | *outputs = open_outputs; | ||
| 395 | return 0; | 395 | return 0; |
| 396 | 396 | ||
| 397 | fail: | 397 | fail: |
| @@ -401,5 +401,73 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, · 3 unmodified lines | |||
| 401 | free_inout(open_inputs); | 401 | free_inout(open_inputs); |
| 402 | free_inout(open_outputs); | 402 | free_inout(open_outputs); |
| 403 | free_inout(curr_inputs); | 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 | return ret; | 472 | return ret; |
| 405 | } | 473 | } |