graphparser.c 14.5 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

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

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
}

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.
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
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

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 127
    ret = avfilter_init_str(*filt_ctx, args);
    if (ret < 0) {
128
        av_log(log_ctx, AV_LOG_ERROR,
129 130 131 132
               "Error initializing filter '%s'", filt_name);
        if (args)
            av_log(log_ctx, AV_LOG_ERROR, " with args '%s'", args);
        av_log(log_ctx, AV_LOG_ERROR, "\n");
133
        avfilter_free(*filt_ctx);
134
        return ret;
135 136
    }

137
    return 0;
138 139
}

140
/**
141 142 143 144
 * Parse a string of the form FILTER_NAME[=PARAMS], and create a
 * corresponding filter instance which is added to graph with
 * create_filter().
 *
145 146
 * @param filt_ctx Pointer that is set to the created and configured filter
 *                 context on success, set to NULL on failure.
147 148 149 150 151 152 153 154
 * @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
155
 */
156
static int parse_filter(AVFilterContext **filt_ctx, const char **buf, AVFilterGraph *graph,
157
                        int index, void *log_ctx)
158
{
Vitor Sessak's avatar
Vitor Sessak committed
159
    char *opts = NULL;
160
    char *name = av_get_token(buf, "=,;[\n");
161
    int ret;
162

163
    if (**buf == '=') {
164
        (*buf)++;
165
        opts = av_get_token(buf, "[],;\n");
Vitor Sessak's avatar
Vitor Sessak committed
166
    }
167

168
    ret = create_filter(filt_ctx, graph, index, name, opts, log_ctx);
Vitor Sessak's avatar
Vitor Sessak committed
169 170
    av_free(name);
    av_free(opts);
171
    return ret;
172 173
}

174
AVFilterInOut *avfilter_inout_alloc(void)
175
{
176 177 178 179 180 181 182 183 184 185
    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;
186 187 188
    }
}

189 190 191 192
static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links)
{
    AVFilterInOut *ret;

193
    while (*links && (!(*links)->name || strcmp((*links)->name, label)))
Vitor Sessak's avatar
Vitor Sessak committed
194
        links = &((*links)->next);
195

Vitor Sessak's avatar
Vitor Sessak committed
196
    ret = *links;
197

198
    if (ret) {
Vitor Sessak's avatar
Vitor Sessak committed
199
        *links = ret->next;
200 201
        ret->next = NULL;
    }
202 203 204 205

    return ret;
}

206 207 208 209 210
static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element)
{
    element->next = *inouts;
    *inouts = element;
}
211

212 213 214 215 216 217 218 219 220 221 222 223
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;
}

224
static int link_filter_inouts(AVFilterContext *filt_ctx,
225
                              AVFilterInOut **curr_inputs,
226
                              AVFilterInOut **open_inputs, void *log_ctx)
227
{
228
    int pad, ret;
229

230
    for (pad = 0; pad < filt_ctx->nb_inputs; pad++) {
231
        AVFilterInOut *p = *curr_inputs;
232

233
        if (p) {
234
            *curr_inputs = (*curr_inputs)->next;
235 236
            p->next = NULL;
        } else if (!(p = av_mallocz(sizeof(*p))))
237
            return AVERROR(ENOMEM);
238

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

252
    if (*curr_inputs) {
253 254
        av_log(log_ctx, AV_LOG_ERROR,
               "Too many inputs specified for the \"%s\" filter.\n",
255
               filt_ctx->filter->name);
256
        return AVERROR(EINVAL);
257 258
    }

259
    pad = filt_ctx->nb_outputs;
260
    while (pad--) {
261
        AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut));
262 263
        if (!currlinkn)
            return AVERROR(ENOMEM);
264
        currlinkn->filter_ctx  = filt_ctx;
265
        currlinkn->pad_idx = pad;
266
        insert_inout(curr_inputs, currlinkn);
267 268 269 270 271
    }

    return 0;
}

272
static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs,
273
                        AVFilterInOut **open_outputs, void *log_ctx)
274
{
275
    AVFilterInOut *parsed_inputs = NULL;
276 277
    int pad = 0;

278
    while (**buf == '[') {
279
        char *name = parse_link_name(buf, log_ctx);
Vitor Sessak's avatar
Vitor Sessak committed
280
        AVFilterInOut *match;
Vitor Sessak's avatar
Vitor Sessak committed
281

282
        if (!name)
283
            return AVERROR(EINVAL);
Vitor Sessak's avatar
Vitor Sessak committed
284

285 286
        /* First check if the label is not in the open_outputs list */
        match = extract_inout(name, open_outputs);
287

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

300
        append_inout(&parsed_inputs, &match);
301

302
        *buf += strspn(*buf, WHITESPACES);
303
        pad++;
304
    }
305

306 307 308
    append_inout(&parsed_inputs, curr_inputs);
    *curr_inputs = parsed_inputs;

309 310 311
    return pad;
}

312 313
static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs,
                         AVFilterInOut **open_inputs,
314
                         AVFilterInOut **open_outputs, void *log_ctx)
315
{
316
    int ret, pad = 0;
317

318
    while (**buf == '[') {
319
        char *name = parse_link_name(buf, log_ctx);
320 321
        AVFilterInOut *match;

322
        AVFilterInOut *input = *curr_inputs;
323 324 325 326

        if (!name)
            return AVERROR(EINVAL);

327 328
        if (!input) {
            av_log(log_ctx, AV_LOG_ERROR,
329 330
                   "No output pad can be associated to link label '%s'.\n", name);
            av_free(name);
331 332
            return AVERROR(EINVAL);
        }
333
        *curr_inputs = (*curr_inputs)->next;
334

335 336
        /* First check if the label is not in the open_inputs list */
        match = extract_inout(name, open_inputs);
337

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

357 358
    return pad;
}
359

360 361 362 363 364 365 366 367
static int parse_sws_flags(const char **buf, AVFilterGraph *graph)
{
    char *p = strchr(*buf, ';');

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

    if (!p) {
368
        av_log(graph, AV_LOG_ERROR, "sws_flags not terminated with ';'.\n");
369 370 371 372 373 374 375 376 377 378 379 380 381 382
        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;
}

383 384 385
int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters,
                          AVFilterInOut **inputs,
                          AVFilterInOut **outputs)
386
{
387
    int index = 0, ret;
388 389
    char chr = 0;

390
    AVFilterInOut *curr_inputs = NULL, *open_inputs = NULL, *open_outputs = NULL;
391

392 393 394 395 396
    filters += strspn(filters, WHITESPACES);

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

397
    do {
398
        AVFilterContext *filter;
399
        filters += strspn(filters, WHITESPACES);
400

401
        if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, graph)) < 0)
402 403
            goto fail;

404
        if ((ret = parse_filter(&filter, &filters, graph, index, graph)) < 0)
Vitor Sessak's avatar
Vitor Sessak committed
405 406
            goto fail;

407

408
        if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, graph)) < 0)
409
            goto fail;
Vitor Sessak's avatar
Vitor Sessak committed
410

411
        if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs,
412
                                 graph)) < 0)
Vitor Sessak's avatar
Vitor Sessak committed
413 414
            goto fail;

415
        filters += strspn(filters, WHITESPACES);
416 417
        chr = *filters++;

418 419
        if (chr == ';' && curr_inputs)
            append_inout(&open_outputs, &curr_inputs);
420
        index++;
421
    } while (chr == ',' || chr == ';');
422

423
    if (chr) {
424
        av_log(graph, AV_LOG_ERROR,
425 426
               "Unable to parse graph description substring: \"%s\"\n",
               filters - 1);
427
        ret = AVERROR(EINVAL);
428 429 430
        goto fail;
    }

431
    append_inout(&open_outputs, &curr_inputs);
432

433 434
    *inputs  = open_inputs;
    *outputs = open_outputs;
435 436 437
    return 0;

 fail:
438 439
    while (graph->nb_filters)
        avfilter_free(graph->filters[0]);
440
    av_freep(&graph->filters);
441 442 443
    avfilter_inout_free(&open_inputs);
    avfilter_inout_free(&open_outputs);
    avfilter_inout_free(&curr_inputs);
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 471 472 473 474 475

    *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);
476
        avfilter_inout_free(&match);
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
        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);
496
        avfilter_inout_free(&match);
497 498 499 500 501 502
        if (ret < 0)
            goto fail;
    }

 fail:
    if (ret < 0) {
503 504
        while (graph->nb_filters)
            avfilter_free(graph->filters[0]);
505 506
        av_freep(&graph->filters);
    }
507 508 509 510
    avfilter_inout_free(&inputs);
    avfilter_inout_free(&outputs);
    avfilter_inout_free(&open_inputs);
    avfilter_inout_free(&open_outputs);
511
    return ret;
512
}