graphparser.c 14.4 KB
Newer Older
1 2
/*
 * filter graph parser
3 4
 * Copyright (c) 2008 Vitor Sessak
 * Copyright (c) 2007 Bobby Bingham
5
 *
6
 * This file is part of Libav.
7
 *
8
 * Libav is free software; you can redistribute it and/or
9 10 11 12
 * 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.
 *
13
 * Libav is distributed in the hope that it will be useful,
14 15 16 17 18
 * 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
19
 * License along with Libav; if not, write to the Free Software
20 21 22 23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <string.h>
24
#include <stdio.h>
25

26
#include "libavutil/avstring.h"
27
#include "libavutil/mem.h"
28 29
#include "avfilter.h"

30 31
#define WHITESPACES " \n\t"

32 33 34 35 36
/**
 * Link two filters together.
 *
 * @see avfilter_link()
 */
37
static int link_filter(AVFilterContext *src, int srcpad,
38
                       AVFilterContext *dst, int dstpad,
39
                       void *log_ctx)
40
{
41 42
    int ret;
    if ((ret = avfilter_link(src, srcpad, dst, dstpad))) {
43
        av_log(log_ctx, AV_LOG_ERROR,
44
               "Cannot create the link %s:%d -> %s:%d\n",
45
               src->filter->name, srcpad, dst->filter->name, dstpad);
46
        return ret;
47 48 49 50 51 52
    }

    return 0;
}

/**
53 54 55 56
 * Parse the name of a link, which has the format "[linkname]".
 *
 * @return a pointer (that need to be freed after use) to the name
 * between parenthesis
57
 */
58
static char *parse_link_name(const char **buf, void *log_ctx)
59
{
Vitor Sessak's avatar
Vitor Sessak committed
60
    const char *start = *buf;
61
    char *name;
62 63
    (*buf)++;

64
    name = av_get_token(buf, "]");
65

Stefano Sabatini's avatar
Stefano Sabatini committed
66
    if (!name[0]) {
67
        av_log(log_ctx, AV_LOG_ERROR,
Vitor Sessak's avatar
Vitor Sessak committed
68
               "Bad (empty?) label found in the following: \"%s\".\n", start);
69
        goto fail;
Vitor Sessak's avatar
Vitor Sessak committed
70
    }
71

Stefano Sabatini's avatar
Stefano Sabatini committed
72
    if (*(*buf)++ != ']') {
73
        av_log(log_ctx, AV_LOG_ERROR,
Vitor Sessak's avatar
Vitor Sessak committed
74
               "Mismatched '[' found in the following: \"%s\".\n", start);
Vitor Sessak's avatar
Vitor Sessak committed
75
    fail:
76
        av_freep(&name);
Vitor Sessak's avatar
Vitor Sessak committed
77
    }
78 79

    return name;
80 81
}

Stefano Sabatini's avatar
Stefano Sabatini committed
82 83 84 85
/**
 * Create an instance of a filter, initialize and insert it in the
 * filtergraph in *ctx.
 *
86
 * @param filt_ctx put here a filter context in case of successful creation and configuration, NULL otherwise.
Stefano Sabatini's avatar
Stefano Sabatini committed
87 88 89 90 91
 * @param ctx the filtergraph context
 * @param index an index which is supposed to be unique for each filter instance added to the filtergraph
 * @param filt_name the name of the filter to create
 * @param args the arguments provided to the filter during its initialization
 * @param log_ctx the log context to use
92
 * @return 0 in case of success, a negative AVERROR code otherwise
Stefano Sabatini's avatar
Stefano Sabatini committed
93
 */
94
static int create_filter(AVFilterContext **filt_ctx, AVFilterGraph *ctx, int index,
95
                         const char *filt_name, const char *args, void *log_ctx)
96
{
97
    AVFilter *filt;
98
    char inst_name[30];
99
    char tmp_args[256];
100
    int ret;
101

102
    snprintf(inst_name, sizeof(inst_name), "Parsed filter %d %s", index, filt_name);
103

104
    filt = avfilter_get_by_name(filt_name);
105

Stefano Sabatini's avatar
Stefano Sabatini committed
106
    if (!filt) {
107
        av_log(log_ctx, AV_LOG_ERROR,
108
               "No such filter: '%s'\n", filt_name);
109
        return AVERROR(EINVAL);
110 111
    }

112
    *filt_ctx = avfilter_graph_alloc_filter(ctx, filt, inst_name);
113
    if (!*filt_ctx) {
114
        av_log(log_ctx, AV_LOG_ERROR,
115
               "Error creating filter '%s'\n", filt_name);
116
        return AVERROR(ENOMEM);
Vitor Sessak's avatar
Vitor Sessak committed
117
    }
118

119 120
    if (!strcmp(filt_name, "scale") && args && !strstr(args, "flags") &&
        ctx->scale_sws_opts) {
121 122 123 124 125
        snprintf(tmp_args, sizeof(tmp_args), "%s:%s",
                 args, ctx->scale_sws_opts);
        args = tmp_args;
    }

126
    if ((ret = avfilter_init_filter(*filt_ctx, args, NULL)) < 0) {
127
        av_log(log_ctx, AV_LOG_ERROR,
128
               "Error initializing filter '%s' with args '%s'\n", filt_name, args);
129
        return ret;
130 131
    }

132
    return 0;
133 134
}

135
/**
136 137 138 139
 * Parse a string of the form FILTER_NAME[=PARAMS], and create a
 * corresponding filter instance which is added to graph with
 * create_filter().
 *
140 141
 * @param filt_ctx Pointer that is set to the created and configured filter
 *                 context on success, set to NULL on failure.
142 143 144 145 146 147 148 149
 * @param filt_ctx put here a pointer to the created filter context on
 * success, NULL otherwise
 * @param buf pointer to the buffer to parse, *buf will be updated to
 * point to the char next after the parsed string
 * @param index an index which is assigned to the created filter
 * instance, and which is supposed to be unique for each filter
 * instance added to the filtergraph
 * @return 0 in case of success, a negative AVERROR code otherwise
150
 */
151
static int parse_filter(AVFilterContext **filt_ctx, const char **buf, AVFilterGraph *graph,
152
                        int index, void *log_ctx)
153
{
Vitor Sessak's avatar
Vitor Sessak committed
154
    char *opts = NULL;
155
    char *name = av_get_token(buf, "=,;[\n");
156
    int ret;
157

Stefano Sabatini's avatar
Stefano Sabatini committed
158
    if (**buf == '=') {
159
        (*buf)++;
160
        opts = av_get_token(buf, "[],;\n");
Vitor Sessak's avatar
Vitor Sessak committed
161
    }
162

163
    ret = create_filter(filt_ctx, graph, index, name, opts, log_ctx);
Vitor Sessak's avatar
Vitor Sessak committed
164 165
    av_free(name);
    av_free(opts);
166
    return ret;
167 168
}

169
AVFilterInOut *avfilter_inout_alloc(void)
170
{
171 172 173 174 175 176 177 178 179 180
    return av_mallocz(sizeof(AVFilterInOut));
}

void avfilter_inout_free(AVFilterInOut **inout)
{
    while (*inout) {
        AVFilterInOut *next = (*inout)->next;
        av_freep(&(*inout)->name);
        av_freep(inout);
        *inout = next;
181 182 183
    }
}

184 185 186 187
static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links)
{
    AVFilterInOut *ret;

188
    while (*links && (!(*links)->name || strcmp((*links)->name, label)))
Vitor Sessak's avatar
Vitor Sessak committed
189
        links = &((*links)->next);
190

Vitor Sessak's avatar
Vitor Sessak committed
191
    ret = *links;
192

193
    if (ret) {
Vitor Sessak's avatar
Vitor Sessak committed
194
        *links = ret->next;
195 196
        ret->next = NULL;
    }
197 198 199 200

    return ret;
}

201 202 203 204 205
static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element)
{
    element->next = *inouts;
    *inouts = element;
}
206

207 208 209 210 211 212 213 214 215 216 217 218
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;
}

219
static int link_filter_inouts(AVFilterContext *filt_ctx,
220
                              AVFilterInOut **curr_inputs,
221
                              AVFilterInOut **open_inputs, void *log_ctx)
222
{
223
    int pad, ret;
224

225
    for (pad = 0; pad < filt_ctx->nb_inputs; pad++) {
226
        AVFilterInOut *p = *curr_inputs;
227

228
        if (p) {
229
            *curr_inputs = (*curr_inputs)->next;
230 231
            p->next = NULL;
        } else if (!(p = av_mallocz(sizeof(*p))))
232
            return AVERROR(ENOMEM);
Vitor Sessak's avatar
Vitor Sessak committed
233

234
        if (p->filter_ctx) {
235
            ret = link_filter(p->filter_ctx, p->pad_idx, filt_ctx, pad, log_ctx);
Vitor Sessak's avatar
Vitor Sessak committed
236
            av_free(p->name);
237
            av_free(p);
238 239
            if (ret < 0)
                return ret;
240
        } else {
241
            p->filter_ctx = filt_ctx;
242
            p->pad_idx = pad;
243
            append_inout(open_inputs, &p);
244 245 246
        }
    }

Stefano Sabatini's avatar
Stefano Sabatini committed
247
    if (*curr_inputs) {
248 249
        av_log(log_ctx, AV_LOG_ERROR,
               "Too many inputs specified for the \"%s\" filter.\n",
250
               filt_ctx->filter->name);
251
        return AVERROR(EINVAL);
252 253
    }

254
    pad = filt_ctx->nb_outputs;
Stefano Sabatini's avatar
Stefano Sabatini committed
255
    while (pad--) {
256
        AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut));
257 258
        if (!currlinkn)
            return AVERROR(ENOMEM);
259
        currlinkn->filter_ctx  = filt_ctx;
260
        currlinkn->pad_idx = pad;
261
        insert_inout(curr_inputs, currlinkn);
262 263 264 265 266
    }

    return 0;
}

267
static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs,
268
                        AVFilterInOut **open_outputs, void *log_ctx)
269
{
270
    AVFilterInOut *parsed_inputs = NULL;
271 272
    int pad = 0;

Stefano Sabatini's avatar
Stefano Sabatini committed
273
    while (**buf == '[') {
274
        char *name = parse_link_name(buf, log_ctx);
Vitor Sessak's avatar
Vitor Sessak committed
275
        AVFilterInOut *match;
Vitor Sessak's avatar
Vitor Sessak committed
276

Stefano Sabatini's avatar
Stefano Sabatini committed
277
        if (!name)
278
            return AVERROR(EINVAL);
Vitor Sessak's avatar
Vitor Sessak committed
279

280 281
        /* First check if the label is not in the open_outputs list */
        match = extract_inout(name, open_outputs);
282

Stefano Sabatini's avatar
Stefano Sabatini committed
283
        if (match) {
Vitor Sessak's avatar
Vitor Sessak committed
284
            av_free(name);
285 286
        } else {
            /* Not in the list, so add it as an input */
287 288
            if (!(match = av_mallocz(sizeof(AVFilterInOut)))) {
                av_free(name);
289
                return AVERROR(ENOMEM);
290
            }
Vitor Sessak's avatar
Vitor Sessak committed
291 292
            match->name    = name;
            match->pad_idx = pad;
293
        }
294

295
        append_inout(&parsed_inputs, &match);
296

297
        *buf += strspn(*buf, WHITESPACES);
298
        pad++;
299
    }
300

301 302 303
    append_inout(&parsed_inputs, curr_inputs);
    *curr_inputs = parsed_inputs;

304 305 306
    return pad;
}

307 308
static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs,
                         AVFilterInOut **open_inputs,
309
                         AVFilterInOut **open_outputs, void *log_ctx)
310
{
311
    int ret, pad = 0;
312

Stefano Sabatini's avatar
Stefano Sabatini committed
313
    while (**buf == '[') {
314
        char *name = parse_link_name(buf, log_ctx);
315 316
        AVFilterInOut *match;

317
        AVFilterInOut *input = *curr_inputs;
318 319 320 321

        if (!name)
            return AVERROR(EINVAL);

322 323
        if (!input) {
            av_log(log_ctx, AV_LOG_ERROR,
324 325
                   "No output pad can be associated to link label '%s'.\n", name);
            av_free(name);
326 327
            return AVERROR(EINVAL);
        }
328
        *curr_inputs = (*curr_inputs)->next;
Vitor Sessak's avatar
Vitor Sessak committed
329

330 331
        /* First check if the label is not in the open_inputs list */
        match = extract_inout(name, open_inputs);
332

Stefano Sabatini's avatar
Stefano Sabatini committed
333
        if (match) {
334
            if ((ret = link_filter(input->filter_ctx, input->pad_idx,
335 336
                                   match->filter_ctx, match->pad_idx, log_ctx)) < 0) {
                av_free(name);
337
                return ret;
338
            }
Vitor Sessak's avatar
Vitor Sessak committed
339 340
            av_free(match->name);
            av_free(name);
341
            av_free(match);
Vitor Sessak's avatar
Vitor Sessak committed
342
            av_free(input);
343
        } else {
344
            /* Not in the list, so add the first input as a open_output */
Vitor Sessak's avatar
Vitor Sessak committed
345
            input->name = name;
346
            insert_inout(open_outputs, input);
347
        }
348
        *buf += strspn(*buf, WHITESPACES);
349
        pad++;
350
    }
351

352 353
    return pad;
}
354

355 356 357 358 359 360 361 362
static int parse_sws_flags(const char **buf, AVFilterGraph *graph)
{
    char *p = strchr(*buf, ';');

    if (strncmp(*buf, "sws_flags=", 10))
        return 0;

    if (!p) {
363
        av_log(graph, AV_LOG_ERROR, "sws_flags not terminated with ';'.\n");
364 365 366 367 368 369 370 371 372 373 374 375 376 377
        return AVERROR(EINVAL);
    }

    *buf += 4;  // keep the 'flags=' part

    av_freep(&graph->scale_sws_opts);
    if (!(graph->scale_sws_opts = av_mallocz(p - *buf + 1)))
        return AVERROR(ENOMEM);
    av_strlcpy(graph->scale_sws_opts, *buf, p - *buf + 1);

    *buf = p + 1;
    return 0;
}

378 379 380
int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters,
                          AVFilterInOut **inputs,
                          AVFilterInOut **outputs)
381
{
382
    int index = 0, ret;
383 384
    char chr = 0;

385
    AVFilterInOut *curr_inputs = NULL, *open_inputs = NULL, *open_outputs = NULL;
386

387 388 389 390 391
    filters += strspn(filters, WHITESPACES);

    if ((ret = parse_sws_flags(&filters, graph)) < 0)
        goto fail;

392
    do {
393
        AVFilterContext *filter;
394
        filters += strspn(filters, WHITESPACES);
395

396
        if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, graph)) < 0)
397 398
            goto fail;

399
        if ((ret = parse_filter(&filter, &filters, graph, index, graph)) < 0)
Vitor Sessak's avatar
Vitor Sessak committed
400 401
            goto fail;

402

403
        if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, graph)) < 0)
404
            goto fail;
Vitor Sessak's avatar
Vitor Sessak committed
405

406
        if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs,
407
                                 graph)) < 0)
Vitor Sessak's avatar
Vitor Sessak committed
408 409
            goto fail;

410
        filters += strspn(filters, WHITESPACES);
411 412
        chr = *filters++;

413 414
        if (chr == ';' && curr_inputs)
            append_inout(&open_outputs, &curr_inputs);
415
        index++;
Stefano Sabatini's avatar
Stefano Sabatini committed
416
    } while (chr == ',' || chr == ';');
417

418
    if (chr) {
419
        av_log(graph, AV_LOG_ERROR,
420 421
               "Unable to parse graph description substring: \"%s\"\n",
               filters - 1);
422
        ret = AVERROR(EINVAL);
423 424 425
        goto fail;
    }

426
    append_inout(&open_outputs, &curr_inputs);
427

428 429
    *inputs  = open_inputs;
    *outputs = open_outputs;
430 431 432
    return 0;

 fail:
433 434
    for (; graph->nb_filters > 0; graph->nb_filters--)
        avfilter_free(graph->filters[graph->nb_filters - 1]);
435
    av_freep(&graph->filters);
436 437 438
    avfilter_inout_free(&open_inputs);
    avfilter_inout_free(&open_outputs);
    avfilter_inout_free(&curr_inputs);
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470

    *inputs  = NULL;
    *outputs = NULL;

    return ret;
}

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);
471
        avfilter_inout_free(&match);
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
        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);
491
        avfilter_inout_free(&match);
492 493 494 495 496 497
        if (ret < 0)
            goto fail;
    }

 fail:
    if (ret < 0) {
498 499
        for (; graph->nb_filters > 0; graph->nb_filters--)
            avfilter_free(graph->filters[graph->nb_filters - 1]);
500 501
        av_freep(&graph->filters);
    }
502 503 504 505
    avfilter_inout_free(&inputs);
    avfilter_inout_free(&outputs);
    avfilter_inout_free(&open_inputs);
    avfilter_inout_free(&open_outputs);
506
    return ret;
507
}