graphparser.c 11.6 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's avatar
vitor committed
39 40

    if(!(filterdef = avfilter_get_by_name(name))) {
41
        av_log(log_ctx, AV_LOG_ERROR,
vitor's avatar
vitor committed
42
               "no such filter: '%s'\n", name);
43
        return NULL;
vitor's avatar
vitor 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's avatar
vitor committed
51

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

55
    if(avfilter_init_filter(filt, args, NULL)) {
56
        av_log(log_ctx, AV_LOG_ERROR,
vitor's avatar
vitor 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's avatar
vitor committed
95
        char c = *(*buf)++;
96 97
        switch (c) {
        case '\\':
vitor's avatar
vitor committed
98
            *out++= *(*buf)++;
99
            break;
100
        case '\'':
vitor's avatar
vitor committed
101 102 103
            while(**buf && **buf != '\'')
                *out++= *(*buf)++;
            if(**buf) (*buf)++;
104 105
            break;
        case 0:
vitor's avatar
vitor committed
106 107
        case ']':
        case '[':
108 109
        case '=':
        case ',':
vitor's avatar
vitor committed
110
        case ';':
111 112
        case ' ':
        case '\n':
113 114 115 116 117 118
            *out++= 0;
            break;
        default:
            *out++= c;
        }
    } while(out[-1]);
119

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

123 124 125 126
    return ret;
}

/**
vitor's avatar
vitor 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's avatar
vitor committed
133
    const char *start = *buf;
134 135 136 137
    (*buf)++;

    *name = consume_string(buf);

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

vitor's avatar
vitor committed
144
    if(*(*buf)++ != ']') {
145
        av_log(log_ctx, AV_LOG_ERROR,
vitor's avatar
vitor committed
146
               "Mismatched '[' found in the following: \"%s\".\n", start);
vitor's avatar
vitor committed
147 148
    fail:
        av_freep(name);
vitor's avatar
vitor 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 172
    int pad_idx;

    struct AVFilterInOut *next;
} AVFilterInOut;

static void free_inout(AVFilterInOut *head)
{
    while (head) {
vitor's avatar
vitor committed
173
        AVFilterInOut *next = head->next;
174 175 176 177 178
        av_free(head);
        head = next;
    }
}

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


184 185
    while(*links && strcmp((*links)->name, label))
        links= &((*links)->next);
186

187
    ret= *links;
188

189 190
    if(ret)
        *links= ret->next;
191 192 193 194 195 196 197 198 199 200 201 202 203

    return ret;
}


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

    pad = filter->input_count;
204
    while(pad--) {
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 233 234 235
        p = *currInputs;
        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;
            *currInputs = (*currInputs)->next;
            av_free(p);
        } else {
            *currInputs = (*currInputs)->next;
            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;
236
    while(pad--) {
237 238 239 240 241 242 243 244 245 246 247 248
        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;
}

249
/**
250 251 252 253 254
 * 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
255
 */
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
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)
274
{
275 276
    int pad = 0;

vitor's avatar
vitor committed
277
    while (**buf == '[') {
278
        char *name;
vitor's avatar
vitor committed
279
        AVFilterInOut *match;
vitor's avatar
vitor committed
280

281
        parse_link_name(buf, &name, log_ctx);
282

vitor's avatar
vitor committed
283
        if(!name)
vitor's avatar
vitor committed
284 285
            return -1;

286
        /* First check if the label is not in the openLinks list */
vitor's avatar
vitor committed
287
        match = extract_inout(name, openLinks);
288

vitor's avatar
vitor committed
289
        if(match) {
290 291
            /* A label of a open link. Make it one of the inputs of the next
               filter */
vitor's avatar
vitor committed
292 293
            AVFilterInOut *currlinkn = match;
            if (match->type != LinkTypeOut) {
294
                av_log(log_ctx, AV_LOG_ERROR,
vitor's avatar
vitor committed
295
                       "Label \"%s\" appears twice as input!\n", match->name);
296 297
                return -1;
            }
298 299
            currlinkn->next = *currInputs;
            *currInputs = currlinkn;
300 301 302 303 304 305 306 307 308 309
        } else {
            /* Not in the list, so add it as an input */
            AVFilterInOut *currlinkn = av_malloc(sizeof(AVFilterInOut));

            currlinkn->name    = name;
            currlinkn->type    = LinkTypeIn;
            currlinkn->filter  = NULL;
            currlinkn->pad_idx = pad;
            currlinkn->next    = *currInputs;
            *currInputs = currlinkn;
310
        }
311
        consume_whitespace(buf);
312
        pad++;
313
    }
314

315 316 317
    return pad;
}

318 319
static int parse_outputs(const char **buf, AVFilterInOut **currInputs,
                         AVFilterInOut **openLinks, AVClass *log_ctx)
320
{
321 322 323 324 325 326 327 328 329 330 331 332 333 334
    int pad = 0;

    while (**buf == '[') {
        char *name;
        AVFilterInOut *match;

        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 337 338 339 340 341 342 343 344 345 346 347 348 349
            /* A label of a open link. Link it. */
            AVFilterInOut *p = *currInputs;
            if (match->type != LinkTypeIn) {
                av_log(log_ctx, AV_LOG_ERROR,
                       "Label \"%s\" appears twice as output!\n", match->name);
                return -1;
            }

            *currInputs = (*currInputs)->next;
            if(link_filter(p->filter, p->pad_idx,
                           match->filter, match->pad_idx, log_ctx) < 0)
                return -1;
            av_free(match);
            av_free(p);
350 351 352 353 354 355 356 357
        } else {
            /* Not in the list, so add the first input as a openLink */
            AVFilterInOut *p = *currInputs;
            *currInputs = (*currInputs)->next;
            p->next = *openLinks;
            p->type = LinkTypeOut;
            p->name = name;
            *openLinks = p;
358 359 360
        }
        consume_whitespace(buf);
        pad++;
361
    }
362

363 364
    return pad;
}
365

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

378 379 380 381 382 383 384 385 386 387 388 389 390 391
    AVFilterInOut *currInputs=NULL;
    AVFilterInOut *openLinks  = av_malloc(sizeof(AVFilterInOut));

    openLinks->name = "in";
    openLinks->filter = in;
    openLinks->type = LinkTypeOut;
    openLinks->pad_idx = inpad;
    openLinks->next = av_malloc(sizeof(AVFilterInOut));

    openLinks->next->name = "out";
    openLinks->next->filter = out;
    openLinks->next->type = LinkTypeIn;
    openLinks->next->pad_idx = outpad;
    openLinks->next->next = NULL;
392 393

    do {
394
        AVFilterContext *filter;
395
        consume_whitespace(&filters);
396

397
        pad = parse_inputs(&filters, &currInputs, &openLinks, log_ctx);
398

399
        if(pad < 0)
400 401
            goto fail;

402
        if(!(filter = parse_filter(&filters, graph, index, log_ctx)))
vitor's avatar
vitor committed
403 404
            goto fail;

405 406 407 408 409
        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);
            if (pad < 0)
410 411 412
                goto fail;
        }

413 414
        if(link_filter_inouts(filter, &currInputs, &openLinks, log_ctx) < 0)
            goto fail;
vitor's avatar
vitor committed
415

416
        pad = parse_outputs(&filters, &currInputs, &openLinks, log_ctx);
417

418
        if(pad < 0)
vitor's avatar
vitor committed
419 420
            goto fail;

421
        consume_whitespace(&filters);
422 423
        chr = *filters++;

424 425 426 427 428
        if (chr == ';' && currInputs) {
            av_log(log_ctx, AV_LOG_ERROR,
                   "Could not find a output to link when parsing \"%s\"\n",
                   filters - 1);
            goto fail;
429
        }
430 431
        index++;
    } while (chr == ',' || chr == ';');
432

433 434 435 436
    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)
437 438 439 440 441 442 443
            goto fail;
    }

    return 0;

 fail:
    avfilter_destroy_graph(graph);
444 445
    free_inout(openLinks);
    free_inout(currInputs);
446 447
    return -1;
}