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

#include <ctype.h>
#include <string.h>

#include "avfilter.h"
#include "avfiltergraph.h"

29
static AVFilterContext *create_filter(AVFilterGraph *ctx, int index,
30 31
                                      const char *name, const char *args,
                                      AVClass *log_ctx)
32 33 34 35
{
    AVFilterContext *filt;

    AVFilter *filterdef;
36
    char inst_name[30];
37

38
    snprintf(inst_name, sizeof(inst_name), "Parsed filter %d", index);
Vitor Sessak's avatar
Vitor Sessak committed
39 40

    if(!(filterdef = avfilter_get_by_name(name))) {
41
        av_log(log_ctx, AV_LOG_ERROR,
Vitor Sessak's avatar
Vitor Sessak committed
42
               "no such filter: '%s'\n", name);
43
        return NULL;
Vitor Sessak's avatar
Vitor Sessak committed
44 45
    }

46
    if(!(filt = avfilter_open(filterdef, inst_name))) {
47
        av_log(log_ctx, AV_LOG_ERROR,
48
               "error creating filter '%s'\n", name);
49
        return NULL;
50
    }
Vitor Sessak's avatar
Vitor Sessak committed
51

Vitor Sessak's avatar
Vitor Sessak committed
52
    if(avfilter_graph_add_filter(ctx, filt) < 0)
53
        return NULL;
Vitor Sessak's avatar
Vitor Sessak committed
54

55
    if(avfilter_init_filter(filt, args, NULL)) {
56
        av_log(log_ctx, AV_LOG_ERROR,
Vitor Sessak's avatar
Vitor Sessak committed
57
               "error initializing filter '%s' with args '%s'\n", name, args);
58
        return NULL;
59 60
    }

61
    return filt;
62 63
}

64
static int link_filter(AVFilterContext *src, int srcpad,
65 66
                       AVFilterContext *dst, int dstpad,
                       AVClass *log_ctx)
67
{
68
    if(avfilter_link(src, srcpad, dst, dstpad)) {
69
        av_log(log_ctx, AV_LOG_ERROR,
70 71
               "cannot create the link %s:%d -> %s:%d\n",
               src->filter->name, srcpad, dst->filter->name, dstpad);
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
        return -1;
    }

    return 0;
}

static void consume_whitespace(const char **buf)
{
    *buf += strspn(*buf, " \n\t");
}

/**
 * Consumes a string from *buf.
 * @return a copy of the consumed string, which should be free'd after use
 */
static char *consume_string(const char **buf)
{
89
    char *out = av_malloc(strlen(*buf) + 1);
90
    char *ret = out;
91 92 93

    consume_whitespace(buf);

94
    do{
Vitor Sessak's avatar
Vitor Sessak committed
95
        char c = *(*buf)++;
96 97
        switch (c) {
        case '\\':
98
            *out++ = *(*buf)++;
99
            break;
100
        case '\'':
Vitor Sessak's avatar
Vitor Sessak committed
101
            while(**buf && **buf != '\'')
102
                *out++ = *(*buf)++;
Vitor Sessak's avatar
Vitor Sessak committed
103
            if(**buf) (*buf)++;
104 105
            break;
        case 0:
106 107
        case ']':
        case '[':
108 109
        case '=':
        case ',':
110
        case ';':
111 112
        case ' ':
        case '\n':
113
            *out++ = 0;
114 115
            break;
        default:
116
            *out++ = c;
117 118
        }
    } while(out[-1]);
119

Vitor Sessak's avatar
Vitor Sessak committed
120
    (*buf)--;
121 122
    consume_whitespace(buf);

123 124 125 126
    return ret;
}

/**
Vitor Sessak's avatar
Vitor Sessak committed
127
 * Parse "[linkname]"
128 129 130
 * @arg name a pointer (that need to be free'd after use) to the name between
 *           parenthesis
 */
131
static void parse_link_name(const char **buf, char **name, AVClass *log_ctx)
132
{
Vitor Sessak's avatar
Vitor Sessak committed
133
    const char *start = *buf;
134 135 136 137
    (*buf)++;

    *name = consume_string(buf);

Vitor Sessak's avatar
Vitor Sessak committed
138
    if(!*name[0]) {
139
        av_log(log_ctx, AV_LOG_ERROR,
Vitor Sessak's avatar
Vitor Sessak committed
140
               "Bad (empty?) label found in the following: \"%s\".\n", start);
141
        goto fail;
Vitor Sessak's avatar
Vitor Sessak committed
142
    }
143

Vitor Sessak's avatar
Vitor Sessak committed
144
    if(*(*buf)++ != ']') {
145
        av_log(log_ctx, AV_LOG_ERROR,
Vitor Sessak's avatar
Vitor Sessak committed
146
               "Mismatched '[' found in the following: \"%s\".\n", start);
Vitor Sessak's avatar
Vitor Sessak committed
147 148
    fail:
        av_freep(name);
Vitor Sessak's avatar
Vitor Sessak committed
149
    }
150 151 152 153 154 155 156 157 158 159 160 161 162
}


enum LinkType {
    LinkTypeIn,
    LinkTypeOut,
};

/**
 * A linked-list of the inputs/outputs of the filter chain.
 */
typedef struct AVFilterInOut {
    enum LinkType type;
163
    const char *name;
164
    AVFilterContext *filter;
165 166 167 168 169 170 171
    int pad_idx;

    struct AVFilterInOut *next;
} AVFilterInOut;

static void free_inout(AVFilterInOut *head)
{
172
    while(head) {
173
        AVFilterInOut *next = head->next;
174 175 176 177 178
        av_free(head);
        head = next;
    }
}

179 180 181 182
static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links)
{
    AVFilterInOut *ret;

183
    while(*links && strcmp((*links)->name, label))
Vitor Sessak's avatar
Vitor Sessak committed
184
        links = &((*links)->next);
185

Vitor Sessak's avatar
Vitor Sessak committed
186
    ret = *links;
187

188
    if(ret)
Vitor Sessak's avatar
Vitor Sessak committed
189
        *links = ret->next;
190 191 192 193 194 195 196 197 198 199 200 201

    return ret;
}


static int link_filter_inouts(AVFilterContext *filter,
                              AVFilterInOut **currInputs,
                              AVFilterInOut **openLinks, AVClass *log_ctx)
{
    int pad = 0;

    pad = filter->input_count;
202
    while(pad--) {
203
        AVFilterInOut *p = *currInputs;
Vitor Sessak's avatar
Vitor Sessak committed
204
        *currInputs = (*currInputs)->next;
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
        if(!p) {
            av_log(log_ctx, AV_LOG_ERROR,
                   "Not enough inputs specified for the \"%s\" filter.\n",
                   filter->name);
            return -1;
        }

        if(p->filter) {
            if(link_filter(p->filter, p->pad_idx, filter, pad, log_ctx))
                return -1;
            av_free(p);
        } else {
            p->filter = filter;
            p->pad_idx = pad;
            p->next = *openLinks;
            *openLinks = p;
        }
    }


    if(*currInputs) {
        av_log(log_ctx, AV_LOG_ERROR,
               "Too many inputs specified for the \"%s\" filter.\n",
               filter->name);
        return -1;
    }

    pad = filter->output_count;
233
    while(pad--) {
234 235 236 237 238 239 240 241 242 243 244 245
        AVFilterInOut *currlinkn = av_malloc(sizeof(AVFilterInOut));
        currlinkn->name    = NULL;
        currlinkn->type    = LinkTypeOut;
        currlinkn->filter  = filter;
        currlinkn->pad_idx = pad;
        currlinkn->next    = *currInputs;
        *currInputs = currlinkn;
    }

    return 0;
}

246
/**
247 248 249 250 251
 * Parse "filter=params"
 * @arg name a pointer (that need to be free'd after use) to the name of the
 *           filter
 * @arg ars  a pointer (that need to be free'd after use) to the args of the
 *           filter
252
 */
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
static AVFilterContext *parse_filter(const char **buf, AVFilterGraph *graph,
                                     int index, AVClass *log_ctx)
{
    char *opts;
    char *name = consume_string(buf);

    if(**buf == '=') {
        (*buf)++;
        opts = consume_string(buf);
    } else {
        opts = NULL;
    }

    return create_filter(graph, index, name, opts, log_ctx);
}

static int parse_inputs(const char **buf, AVFilterInOut **currInputs,
                        AVFilterInOut **openLinks, AVClass *log_ctx)
271
{
272 273
    int pad = 0;

274
    while(**buf == '[') {
275
        char *name;
Vitor Sessak's avatar
Vitor Sessak committed
276
        AVFilterInOut *link_to_add;
Vitor Sessak's avatar
Vitor Sessak committed
277
        AVFilterInOut *match;
Vitor Sessak's avatar
Vitor Sessak committed
278

279
        parse_link_name(buf, &name, log_ctx);
280

Vitor Sessak's avatar
Vitor Sessak committed
281
        if(!name)
Vitor Sessak's avatar
Vitor Sessak committed
282 283
            return -1;

284
        /* First check if the label is not in the openLinks list */
Vitor Sessak's avatar
Vitor Sessak committed
285
        match = extract_inout(name, openLinks);
286

Vitor Sessak's avatar
Vitor Sessak committed
287
        if(match) {
288 289
            /* A label of a open link. Make it one of the inputs of the next
               filter */
Vitor Sessak's avatar
Vitor Sessak committed
290
            if(match->type != LinkTypeOut) {
291
                av_log(log_ctx, AV_LOG_ERROR,
Vitor Sessak's avatar
Vitor Sessak committed
292
                       "Label \"%s\" appears twice as input!\n", match->name);
293 294
                return -1;
            }
Vitor Sessak's avatar
Vitor Sessak committed
295 296

            link_to_add = match;
297 298
        } else {
            /* Not in the list, so add it as an input */
Vitor Sessak's avatar
Vitor Sessak committed
299 300 301 302 303 304
            link_to_add = av_malloc(sizeof(AVFilterInOut));

            link_to_add->name    = name;
            link_to_add->type    = LinkTypeIn;
            link_to_add->filter  = NULL;
            link_to_add->pad_idx = pad;
305
        }
Vitor Sessak's avatar
Vitor Sessak committed
306 307
        link_to_add->next = *currInputs;
        *currInputs = link_to_add;
308
        consume_whitespace(buf);
309
        pad++;
310
    }
311

312 313 314
    return pad;
}

315 316
static int parse_outputs(const char **buf, AVFilterInOut **currInputs,
                         AVFilterInOut **openLinks, AVClass *log_ctx)
317
{
318 319
    int pad = 0;

320
    while(**buf == '[') {
321 322 323
        char *name;
        AVFilterInOut *match;

Vitor Sessak's avatar
Vitor Sessak committed
324 325 326
        AVFilterInOut *input = *currInputs;
        *currInputs = (*currInputs)->next;

327 328 329 330 331 332 333 334
        parse_link_name(buf, &name, log_ctx);

        if(!name)
            return -1;

        /* First check if the label is not in the openLinks list */
        match = extract_inout(name, openLinks);

335
        if(match) {
336
            /* A label of a open link. Link it. */
Vitor Sessak's avatar
Vitor Sessak committed
337
            if(match->type != LinkTypeIn) {
338 339 340 341 342
                av_log(log_ctx, AV_LOG_ERROR,
                       "Label \"%s\" appears twice as output!\n", match->name);
                return -1;
            }

Vitor Sessak's avatar
Vitor Sessak committed
343
            if(link_filter(input->filter, input->pad_idx,
344 345 346
                           match->filter, match->pad_idx, log_ctx) < 0)
                return -1;
            av_free(match);
Vitor Sessak's avatar
Vitor Sessak committed
347
            av_free(input);
348 349
        } else {
            /* Not in the list, so add the first input as a openLink */
Vitor Sessak's avatar
Vitor Sessak committed
350 351 352 353
            input->next = *openLinks;
            input->type = LinkTypeOut;
            input->name = name;
            *openLinks = input;
354 355 356
        }
        consume_whitespace(buf);
        pad++;
357
    }
358

359 360
    return pad;
}
361

362 363 364
/**
 * Parse a string describing a filter graph.
 */
365 366
int avfilter_parse_graph(AVFilterGraph *graph, const char *filters,
                         AVFilterContext *in, int inpad,
367 368
                         AVFilterContext *out, int outpad,
                         AVClass *log_ctx)
369 370 371 372 373
{
    int index = 0;
    char chr = 0;
    int pad = 0;

374
    AVFilterInOut *currInputs = NULL;
375 376
    AVFilterInOut *openLinks  = av_malloc(sizeof(AVFilterInOut));

Vitor Sessak's avatar
Vitor Sessak committed
377 378 379
    openLinks->name    = "in";
    openLinks->filter  = in;
    openLinks->type    = LinkTypeOut;
380
    openLinks->pad_idx = inpad;
Vitor Sessak's avatar
Vitor Sessak committed
381
    openLinks->next    = av_malloc(sizeof(AVFilterInOut));
382

Vitor Sessak's avatar
Vitor Sessak committed
383 384 385
    openLinks->next->name    = "out";
    openLinks->next->filter  = out;
    openLinks->next->type    = LinkTypeIn;
386
    openLinks->next->pad_idx = outpad;
Vitor Sessak's avatar
Vitor Sessak committed
387
    openLinks->next->next    = NULL;
388 389

    do {
390
        AVFilterContext *filter;
391
        consume_whitespace(&filters);
392

393
        pad = parse_inputs(&filters, &currInputs, &openLinks, log_ctx);
394

395
        if(pad < 0)
396 397
            goto fail;

398
        if(!(filter = parse_filter(&filters, graph, index, log_ctx)))
Vitor Sessak's avatar
Vitor Sessak committed
399 400
            goto fail;

401 402 403 404
        if(filter->input_count == 1 && !currInputs && !index) {
            // First input can be ommitted if it is "[in]"
            const char *tmp = "[in]";
            pad = parse_inputs(&tmp, &currInputs, &openLinks, log_ctx);
Vitor Sessak's avatar
Vitor Sessak committed
405
            if(pad < 0)
406 407 408
                goto fail;
        }

409 410
        if(link_filter_inouts(filter, &currInputs, &openLinks, log_ctx) < 0)
            goto fail;
Vitor Sessak's avatar
Vitor Sessak committed
411

412
        pad = parse_outputs(&filters, &currInputs, &openLinks, log_ctx);
413

414
        if(pad < 0)
Vitor Sessak's avatar
Vitor Sessak committed
415 416
            goto fail;

417
        consume_whitespace(&filters);
418 419
        chr = *filters++;

Vitor Sessak's avatar
Vitor Sessak committed
420
        if(chr == ';' && currInputs) {
421 422 423 424
            av_log(log_ctx, AV_LOG_ERROR,
                   "Could not find a output to link when parsing \"%s\"\n",
                   filters - 1);
            goto fail;
425
        }
426
        index++;
427
    } while(chr == ',' || chr == ';');
428

429 430 431 432
    if(openLinks && !strcmp(openLinks->name, "out") && currInputs) {
        // Last output can be ommitted if it is "[out]"
        const char *tmp = "[out]";
        if(parse_outputs(&tmp, &currInputs, &openLinks, log_ctx) < 0)
433 434 435 436 437 438 439
            goto fail;
    }

    return 0;

 fail:
    avfilter_destroy_graph(graph);
440 441
    free_inout(openLinks);
    free_inout(currInputs);
442 443
    return -1;
}