Separate the process of creating links between filters from that of configuring
[ffmpeg.git] / libavfilter / avfiltergraph.c
1 /*
2  * Filter graphs
3  * copyright (c) 2007 Bobby Bingham
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 #include <string.h>
23 #include <stddef.h>
24
25 #include "avstring.h"
26 #include "avfilter.h"
27 #include "avfiltergraph.h"
28
29 typedef struct AVFilterGraph {
30     unsigned filter_count;
31     AVFilterContext **filters;
32 } GraphContext;
33
34 static void uninit(AVFilterContext *ctx)
35 {
36     GraphContext *graph = ctx->priv;
37
38     for(; graph->filter_count > 0; graph->filter_count --)
39         avfilter_destroy(graph->filters[graph->filter_count - 1]);
40     av_freep(&graph->filters);
41 }
42
43 void avfilter_graph_add_filter(AVFilterContext *graphctx, AVFilterContext *filter)
44 {
45     GraphContext *graph = graphctx->priv;
46
47     graph->filters = av_realloc(graph->filters,
48                                 sizeof(AVFilterContext*) * ++graph->filter_count);
49     graph->filters[graph->filter_count - 1] = filter;
50 }
51
52 int avfilter_graph_config_links(AVFilterContext *graphctx)
53 {
54     GraphContext *graph = graphctx->priv;
55     int i, j;
56
57     for(i = 0; i < graph->filter_count; i ++) {
58         for(j = 0; j < graph->filters[i]->input_count; j ++)
59             if(avfilter_config_link(graph->filters[i]->inputs[j]))
60                 return -1;
61     }
62
63     return 0;
64 }
65
66 static AVFilterContext *create_filter_with_args(const char *filt, void *opaque)
67 {
68     AVFilterContext *ret;
69     char *filter = av_strdup(filt); /* copy - don't mangle the input string */
70     char *name, *args;
71
72     name = filter;
73     if((args = strchr(filter, '='))) {
74         /* ensure we at least have a name */
75         if(args == filter)
76             goto fail;
77
78         *args ++ = 0;
79     }
80
81     av_log(NULL, AV_LOG_INFO, "creating filter \"%s\" with args \"%s\"\n",
82            name, args ? args : "(none)");
83
84     if((ret = avfilter_create_by_name(name, NULL))) {
85         if(avfilter_init_filter(ret, args, opaque)) {
86             av_log(NULL, AV_LOG_ERROR, "error initializing filter!\n");
87             avfilter_destroy(ret);
88             goto fail;
89         }
90     } else av_log(NULL, AV_LOG_ERROR, "error creating filter!\n");
91
92     av_free(filter);
93
94     return ret;
95
96 fail:
97     av_free(filter);
98     return NULL;
99 }
100
101 static int graph_load_chain(AVFilterContext *graphctx,
102                               unsigned count, char **filter_list, void **opaque,
103                               AVFilterContext **first, AVFilterContext **last)
104 {
105     unsigned i;
106     AVFilterContext *filters[2] = {NULL,NULL};
107
108     for(i = 0; i < count; i ++) {
109         void *op;
110
111         if(opaque) op = opaque[i];
112         else       op = NULL;
113
114         if(!(filters[1] = create_filter_with_args(filter_list[i], op)))
115             goto fail;
116         if(i == 0) {
117             if(first) *first = filters[1];
118         } else {
119             if(avfilter_link(filters[0], 0, filters[1], 0)) {
120                 av_log(NULL, AV_LOG_ERROR, "error linking filters!\n");
121                 goto fail;
122             }
123         }
124         avfilter_graph_add_filter(graphctx, filters[1]);
125         filters[0] = filters[1];
126     }
127
128     if(last) *last = filters[1];
129     return 0;
130
131 fail:
132     uninit(graphctx);
133     if(first) *first = NULL;
134     if(last)  *last  = NULL;
135     return -1;
136 }
137
138 static int graph_load_chain_from_string(AVFilterContext *ctx, const char *str,
139                                         AVFilterContext **first,
140                                         AVFilterContext **last)
141 {
142     int count, ret = 0;
143     char **strings;
144     char *filt;
145
146     strings    = av_malloc(sizeof(char *));
147     strings[0] = av_strdup(str);
148
149     filt = strchr(strings[0], ',');
150     for(count = 1; filt; count ++) {
151         if(filt == strings[count-1]) {
152             ret = -1;
153             goto done;
154         }
155
156         strings = av_realloc(strings, sizeof(char *) * (count+1));
157         strings[count] = filt + 1;
158         *filt = '\0';
159         filt = strchr(strings[count], ',');
160     }
161
162     ret = graph_load_chain(ctx, count, strings, NULL, first, last);
163
164 done:
165     av_free(strings[0]);
166     av_free(strings);
167
168     return ret;
169 }
170
171 static int init(AVFilterContext *ctx, const char *args, void *opaque)
172 {
173     AVFilterContext **filters = opaque;
174
175     if(!args)
176         return 0;
177     if(!opaque)
178         return -1;
179
180     return graph_load_chain_from_string(ctx, args, filters, filters + 1);
181 }
182
183 AVFilter vf_graph =
184 {
185     .name      = "graph",
186     .author    = "Bobby Bingham",
187
188     .priv_size = sizeof(GraphContext),
189
190     .init      = init,
191     .uninit    = uninit,
192
193     .inputs    = (AVFilterPad[]) {{ .name = NULL, }},
194     .outputs   = (AVFilterPad[]) {{ .name = NULL, }},
195 };
196