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.
| ... | ... |
@@ -91,11 +91,11 @@ void avfilter_graph_free(AVFilterGraph **graph); |
| 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, |
| 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) |
| 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) |
| 201 | 201 |
*inouts = element; |
| 202 | 202 |
} |
| 203 | 203 |
|
| 204 |
+static void append_inout(AVFilterInOut **inouts, AVFilterInOut **element) |
|
| 205 |
+{
|
|
| 206 |
+ while (*inouts && (*inouts)->next) |
|
| 207 |
+ inouts = &((*inouts)->next); |
|
| 208 |
+ |
|
| 209 |
+ if (!*inouts) |
|
| 210 |
+ *inouts = *element; |
|
| 211 |
+ else |
|
| 212 |
+ (*inouts)->next = *element; |
|
| 213 |
+ *element = NULL; |
|
| 214 |
+} |
|
| 215 |
+ |
|
| 204 | 216 |
static int link_filter_inouts(AVFilterContext *filt_ctx, |
| 205 | 217 |
AVFilterInOut **curr_inputs, |
| 206 | 218 |
AVFilterInOut **open_inputs, void *log_ctx) |
| ... | ... |
@@ -209,14 +223,11 @@ static int link_filter_inouts(AVFilterContext *filt_ctx, |
| 209 | 209 |
|
| 210 | 210 |
while (pad--) {
|
| 211 | 211 |
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 | 212 |
|
| 219 |
- *curr_inputs = (*curr_inputs)->next; |
|
| 213 |
+ if (p) |
|
| 214 |
+ *curr_inputs = (*curr_inputs)->next; |
|
| 215 |
+ else if (!(p = av_mallocz(sizeof(*p)))) |
|
| 216 |
+ return AVERROR(ENOMEM); |
|
| 220 | 217 |
|
| 221 | 218 |
if (p->filter_ctx) {
|
| 222 | 219 |
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, |
| 329 | 329 |
return pad; |
| 330 | 330 |
} |
| 331 | 331 |
|
| 332 |
-int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, |
|
| 333 |
- AVFilterInOut *open_inputs, |
|
| 334 |
- AVFilterInOut *open_outputs, void *log_ctx) |
|
| 332 |
+#if FF_API_GRAPH_AVCLASS |
|
| 333 |
+#define log_ctx graph |
|
| 334 |
+#else |
|
| 335 |
+#define log_ctx NULL |
|
| 336 |
+#endif |
|
| 337 |
+int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters, |
|
| 338 |
+ AVFilterInOut **inputs, |
|
| 339 |
+ AVFilterInOut **outputs) |
|
| 335 | 340 |
{
|
| 336 | 341 |
int index = 0, ret; |
| 337 | 342 |
char chr = 0; |
| 338 | 343 |
|
| 339 |
- AVFilterInOut *curr_inputs = NULL; |
|
| 344 |
+ AVFilterInOut *curr_inputs = NULL, *open_inputs = NULL, *open_outputs = NULL; |
|
| 340 | 345 |
|
| 341 | 346 |
do {
|
| 342 | 347 |
AVFilterContext *filter; |
| 343 |
- const char *filterchain = filters; |
|
| 344 | 348 |
filters += strspn(filters, WHITESPACES); |
| 345 | 349 |
|
| 346 | 350 |
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, |
| 349 | 349 |
if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0) |
| 350 | 350 |
goto fail; |
| 351 | 351 |
|
| 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 | 352 |
|
| 359 | 353 |
if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx)) < 0) |
| 360 | 354 |
goto fail; |
| ... | ... |
@@ -366,13 +375,8 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, |
| 366 | 366 |
filters += strspn(filters, WHITESPACES); |
| 367 | 367 |
chr = *filters++; |
| 368 | 368 |
|
| 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 |
- } |
|
| 369 |
+ if (chr == ';' && curr_inputs) |
|
| 370 |
+ append_inout(&open_outputs, &curr_inputs); |
|
| 376 | 371 |
index++; |
| 377 | 372 |
} while (chr == ',' || chr == ';'); |
| 378 | 373 |
|
| ... | ... |
@@ -384,14 +388,10 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, |
| 384 | 384 |
goto fail; |
| 385 | 385 |
} |
| 386 | 386 |
|
| 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 |
- } |
|
| 387 |
+ append_inout(&open_outputs, &curr_inputs); |
|
| 394 | 388 |
|
| 389 |
+ *inputs = open_inputs; |
|
| 390 |
+ *outputs = open_outputs; |
|
| 395 | 391 |
return 0; |
| 396 | 392 |
|
| 397 | 393 |
fail: |
| ... | ... |
@@ -401,5 +401,73 @@ int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, |
| 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 |
} |