Statistics
| Branch: | Revision:

ffmpeg / libavfilter / graphparser.c @ 24de0edb

History | View | Annotate | Download (11.2 KB)

1
/*
2
 * filter graph parser
3
 * copyright (c) 2008 Vitor Sessak
4
 * copyright (c) 2007 Bobby Bingham
5
 *
6
 * This file is part of FFmpeg.
7
 *
8
 * FFmpeg is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * FFmpeg is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with FFmpeg; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 */
22

    
23
#include <ctype.h>
24
#include <string.h>
25

    
26
#include "libavutil/avstring.h"
27
#include "avfilter.h"
28
#include "avfiltergraph.h"
29
#include "parseutils.h"
30

    
31
#define WHITESPACES " \n\t"
32

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

    
50
    return 0;
51
}
52

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

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

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

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

    
80
    return name;
81
}
82

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

    
103
    snprintf(inst_name, sizeof(inst_name), "Filter %d %s", index, filt_name);
104

    
105
    filt = avfilter_get_by_name(filt_name);
106

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

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

    
120
    if ((ret = avfilter_graph_add_filter(ctx, *filt_ctx)) < 0) {
121
        avfilter_free(*filt_ctx);
122
        return ret;
123
    }
124

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

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

    
137
    return 0;
138
}
139

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

    
161
    if (**buf == '=') {
162
        (*buf)++;
163
        opts = av_get_token(buf, "[],;\n");
164
    }
165

    
166
    ret = create_filter(filt_ctx, graph, index, name, opts, log_ctx);
167
    av_free(name);
168
    av_free(opts);
169
    return ret;
170
}
171

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

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

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

    
189
    ret = *links;
190

    
191
    if (ret)
192
        *links = ret->next;
193

    
194
    return ret;
195
}
196

    
197
static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element)
198
{
199
    element->next = *inouts;
200
    *inouts = element;
201
}
202

    
203
static int link_filter_inouts(AVFilterContext *filt_ctx,
204
                              AVFilterInOut **curr_inputs,
205
                              AVFilterInOut **open_inputs, AVClass *log_ctx)
206
{
207
    int pad = filt_ctx->input_count, ret;
208

    
209
    while (pad--) {
210
        AVFilterInOut *p = *curr_inputs;
211
        if (!p) {
212
            av_log(log_ctx, AV_LOG_ERROR,
213
                   "Not enough inputs specified for the \"%s\" filter.\n",
214
                   filt_ctx->filter->name);
215
            return AVERROR(EINVAL);
216
        }
217

    
218
        *curr_inputs = (*curr_inputs)->next;
219

    
220
        if (p->filter_ctx) {
221
            if ((ret = link_filter(p->filter_ctx, p->pad_idx, filt_ctx, pad, log_ctx)) < 0)
222
                return ret;
223
            av_free(p->name);
224
            av_free(p);
225
        } else {
226
            p->filter_ctx = filt_ctx;
227
            p->pad_idx = pad;
228
            insert_inout(open_inputs, p);
229
        }
230
    }
231

    
232
    if (*curr_inputs) {
233
        av_log(log_ctx, AV_LOG_ERROR,
234
               "Too many inputs specified for the \"%s\" filter.\n",
235
               filt_ctx->filter->name);
236
        return AVERROR(EINVAL);
237
    }
238

    
239
    pad = filt_ctx->output_count;
240
    while (pad--) {
241
        AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut));
242
        if (!currlinkn)
243
            return AVERROR(ENOMEM);
244
        currlinkn->filter_ctx  = filt_ctx;
245
        currlinkn->pad_idx = pad;
246
        insert_inout(curr_inputs, currlinkn);
247
    }
248

    
249
    return 0;
250
}
251

    
252
static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs,
253
                        AVFilterInOut **open_outputs, AVClass *log_ctx)
254
{
255
    int pad = 0;
256

    
257
    while (**buf == '[') {
258
        char *name = parse_link_name(buf, log_ctx);
259
        AVFilterInOut *match;
260

    
261
        if (!name)
262
            return AVERROR(EINVAL);
263

    
264
        /* First check if the label is not in the open_outputs list */
265
        match = extract_inout(name, open_outputs);
266

    
267
        if (match) {
268
            av_free(name);
269
        } else {
270
            /* Not in the list, so add it as an input */
271
            if (!(match = av_mallocz(sizeof(AVFilterInOut))))
272
                return AVERROR(ENOMEM);
273
            match->name    = name;
274
            match->pad_idx = pad;
275
        }
276

    
277
        insert_inout(curr_inputs, match);
278

    
279
        *buf += strspn(*buf, WHITESPACES);
280
        pad++;
281
    }
282

    
283
    return pad;
284
}
285

    
286
static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs,
287
                         AVFilterInOut **open_inputs,
288
                         AVFilterInOut **open_outputs, AVClass *log_ctx)
289
{
290
    int ret, pad = 0;
291

    
292
    while (**buf == '[') {
293
        char *name = parse_link_name(buf, log_ctx);
294
        AVFilterInOut *match;
295

    
296
        AVFilterInOut *input = *curr_inputs;
297
        *curr_inputs = (*curr_inputs)->next;
298

    
299
        if (!name)
300
            return AVERROR(EINVAL);
301

    
302
        /* First check if the label is not in the open_inputs list */
303
        match = extract_inout(name, open_inputs);
304

    
305
        if (match) {
306
            if ((ret = link_filter(input->filter_ctx, input->pad_idx,
307
                                   match->filter_ctx, match->pad_idx, log_ctx)) < 0)
308
                return ret;
309
            av_free(match->name);
310
            av_free(name);
311
            av_free(match);
312
            av_free(input);
313
        } else {
314
            /* Not in the list, so add the first input as a open_output */
315
            input->name = name;
316
            insert_inout(open_outputs, input);
317
        }
318
        *buf += strspn(*buf, WHITESPACES);
319
        pad++;
320
    }
321

    
322
    return pad;
323
}
324

    
325
int avfilter_graph_parse(AVFilterGraph *graph, const char *filters,
326
                         AVFilterInOut *open_inputs,
327
                         AVFilterInOut *open_outputs, AVClass *log_ctx)
328
{
329
    int index = 0, ret;
330
    char chr = 0;
331

    
332
    AVFilterInOut *curr_inputs = NULL;
333

    
334
    do {
335
        AVFilterContext *filter;
336
        filters += strspn(filters, WHITESPACES);
337

    
338
        if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx)) < 0)
339
            goto fail;
340

    
341
        if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0)
342
            goto fail;
343

    
344
        if (filter->input_count == 1 && !curr_inputs && !index) {
345
            /* First input can be omitted if it is "[in]" */
346
            const char *tmp = "[in]";
347
            if ((ret = parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx)) < 0)
348
                goto fail;
349
        }
350

    
351
        if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx)) < 0)
352
            goto fail;
353

    
354
        if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs,
355
                                 log_ctx)) < 0)
356
            goto fail;
357

    
358
        filters += strspn(filters, WHITESPACES);
359
        chr = *filters++;
360

    
361
        if (chr == ';' && curr_inputs) {
362
            av_log(log_ctx, AV_LOG_ERROR,
363
                   "Could not find a output to link when parsing \"%s\"\n",
364
                   filters - 1);
365
            ret = AVERROR(EINVAL);
366
            goto fail;
367
        }
368
        index++;
369
    } while (chr == ',' || chr == ';');
370

    
371
    if (chr) {
372
        av_log(log_ctx, AV_LOG_ERROR,
373
               "Unable to parse graph description substring: \"%s\"\n",
374
               filters - 1);
375
        ret = AVERROR(EINVAL);
376
        goto fail;
377
    }
378

    
379
    if (open_inputs && !strcmp(open_inputs->name, "out") && curr_inputs) {
380
        /* Last output can be omitted if it is "[out]" */
381
        const char *tmp = "[out]";
382
        if ((ret = parse_outputs(&tmp, &curr_inputs, &open_inputs, &open_outputs,
383
                                 log_ctx)) < 0)
384
            goto fail;
385
    }
386

    
387
    return 0;
388

    
389
 fail:
390
    avfilter_graph_free(graph);
391
    free_inout(open_inputs);
392
    free_inout(open_outputs);
393
    free_inout(curr_inputs);
394
    return ret;
395
}