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 24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <ctype.h>
#include <string.h>
25
#include <stdio.h>
26

27
#include "libavutil/avstring.h"
28
#include "libavutil/mem.h"
29 30 31
#include "avfilter.h"
#include "avfiltergraph.h"

32 33
#define WHITESPACES " \n\t"

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

    return 0;
}

/**
55 56 57 58
 * 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
59
 */
60
static char *parse_link_name(const char **buf, void *log_ctx)
61
{
Vitor Sessak's avatar
Vitor Sessak committed
62
    const char *start = *buf;
63
    char *name;
64 65
    (*buf)++;

66
    name = av_get_token(buf, "]");
67

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

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

    return name;
82 83
}

Stefano Sabatini's avatar
Stefano Sabatini committed
84 85 86 87
/**
 * Create an instance of a filter, initialize and insert it in the
 * filtergraph in *ctx.
 *
88
 * @param filt_ctx put here a filter context in case of successful creation and configuration, NULL otherwise.
Stefano Sabatini's avatar
Stefano Sabatini committed
89 90 91 92 93
 * @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
94
 * @return 0 in case of success, a negative AVERROR code otherwise
Stefano Sabatini's avatar
Stefano Sabatini committed
95
 */
96
static int create_filter(AVFilterContext **filt_ctx, AVFilterGraph *ctx, int index,
97
                         const char *filt_name, const char *args, void *log_ctx)
98
{
99
    AVFilter *filt;
100
    char inst_name[30];
101
    char tmp_args[256];
102
    int ret;
103

104
    snprintf(inst_name, sizeof(inst_name), "Parsed filter %d %s", index, filt_name);
105

106
    filt = avfilter_get_by_name(filt_name);
107

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

114 115
    ret = avfilter_open(filt_ctx, filt, inst_name);
    if (!*filt_ctx) {
116
        av_log(log_ctx, AV_LOG_ERROR,
117
               "Error creating filter '%s'\n", filt_name);
118
        return ret;
119 120
    }

121
    if ((ret = avfilter_graph_add_filter(ctx, *filt_ctx)) < 0) {
122
        avfilter_free(*filt_ctx);
123
        return ret;
Vitor Sessak's avatar
Vitor Sessak committed
124
    }
125

126
    if (!strcmp(filt_name, "scale") && args && !strstr(args, "flags")) {
127 128 129 130 131
        snprintf(tmp_args, sizeof(tmp_args), "%s:%s",
                 args, ctx->scale_sws_opts);
        args = tmp_args;
    }

132
    if ((ret = avfilter_init_filter(*filt_ctx, args, NULL)) < 0) {
133
        av_log(log_ctx, AV_LOG_ERROR,
134
               "Error initializing filter '%s' with args '%s'\n", filt_name, args);
135
        return ret;
136 137
    }

138
    return 0;
139 140
}

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

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

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

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

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

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

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

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

    return ret;
}

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

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

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

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

234
        if (p) {
235
            *curr_inputs = (*curr_inputs)->next;
236 237
            p->next = NULL;
        } else if (!(p = av_mallocz(sizeof(*p))))
238
            return AVERROR(ENOMEM);
Vitor Sessak's avatar
Vitor Sessak committed
239

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

Stefano Sabatini's avatar
Stefano Sabatini committed
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;
Stefano Sabatini's avatar
Stefano Sabatini committed
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;

Stefano Sabatini's avatar
Stefano Sabatini committed
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

Stefano Sabatini's avatar
Stefano Sabatini committed
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

Stefano Sabatini's avatar
Stefano Sabatini committed
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))))
                return AVERROR(ENOMEM);
Vitor Sessak's avatar
Vitor Sessak committed
294 295
            match->name    = name;
            match->pad_idx = pad;
296
        }
297

298
        append_inout(&parsed_inputs, &match);
299

300
        *buf += strspn(*buf, WHITESPACES);
301
        pad++;
302
    }
303

304 305 306
    append_inout(&parsed_inputs, curr_inputs);
    *curr_inputs = parsed_inputs;

307 308 309
    return pad;
}

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

Stefano Sabatini's avatar
Stefano Sabatini committed
316
    while (**buf == '[') {
317
        char *name = parse_link_name(buf, log_ctx);
318 319
        AVFilterInOut *match;

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

Stefano Sabatini's avatar
Stefano Sabatini committed
329
        if (!name)
330
            return AVERROR(EINVAL);
331

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

Stefano Sabatini's avatar
Stefano Sabatini committed
335
        if (match) {
336 337
            if ((ret = link_filter(input->filter_ctx, input->pad_idx,
                                   match->filter_ctx, match->pad_idx, log_ctx)) < 0)
338
                return ret;
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 435
    for (; graph->filter_count > 0; graph->filter_count--)
        avfilter_free(graph->filters[graph->filter_count - 1]);
    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 498 499 500 501
        if (ret < 0)
            goto fail;
    }

 fail:
    if (ret < 0) {
        for (; graph->filter_count > 0; graph->filter_count--)
            avfilter_free(graph->filters[graph->filter_count - 1]);
        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
}