• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

libavfilter/graphparser.c

Go to the documentation of this file.
00001 /*
00002  * filter graph parser
00003  * copyright (c) 2008 Vitor Sessak
00004  * copyright (c) 2007 Bobby Bingham
00005  *
00006  * This file is part of FFmpeg.
00007  *
00008  * FFmpeg is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * FFmpeg is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with FFmpeg; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00021  */
00022 
00023 #include <ctype.h>
00024 #include <string.h>
00025 
00026 #include "graphparser.h"
00027 #include "avfilter.h"
00028 #include "avfiltergraph.h"
00029 #include "parseutils.h"
00030 
00031 #define WHITESPACES " \n\t"
00032 
00033 static int link_filter(AVFilterContext *src, int srcpad,
00034                        AVFilterContext *dst, int dstpad,
00035                        AVClass *log_ctx)
00036 {
00037     if(avfilter_link(src, srcpad, dst, dstpad)) {
00038         av_log(log_ctx, AV_LOG_ERROR,
00039                "cannot create the link %s:%d -> %s:%d\n",
00040                src->filter->name, srcpad, dst->filter->name, dstpad);
00041         return -1;
00042     }
00043 
00044     return 0;
00045 }
00046 
00052 static char *parse_link_name(const char **buf, AVClass *log_ctx)
00053 {
00054     const char *start = *buf;
00055     char *name;
00056     (*buf)++;
00057 
00058     name = av_get_token(buf, "]");
00059 
00060     if(!name[0]) {
00061         av_log(log_ctx, AV_LOG_ERROR,
00062                "Bad (empty?) label found in the following: \"%s\".\n", start);
00063         goto fail;
00064     }
00065 
00066     if(*(*buf)++ != ']') {
00067         av_log(log_ctx, AV_LOG_ERROR,
00068                "Mismatched '[' found in the following: \"%s\".\n", start);
00069     fail:
00070         av_freep(&name);
00071     }
00072 
00073     return name;
00074 }
00075 
00076 static AVFilterContext *create_filter(AVFilterGraph *ctx, int index,
00077                                       const char *filt_name, const char *args,
00078                                       AVClass *log_ctx)
00079 {
00080     AVFilterContext *filt_ctx;
00081 
00082     AVFilter *filt;
00083     char inst_name[30];
00084 
00085     snprintf(inst_name, sizeof(inst_name), "Parsed filter %d", index);
00086 
00087     filt = avfilter_get_by_name(filt_name);
00088 
00089     if(!filt) {
00090         av_log(log_ctx, AV_LOG_ERROR,
00091                "no such filter: '%s'\n", filt_name);
00092         return NULL;
00093     }
00094 
00095     filt_ctx = avfilter_open(filt, inst_name);
00096     if(!filt_ctx) {
00097         av_log(log_ctx, AV_LOG_ERROR,
00098                "error creating filter '%s'\n", filt_name);
00099         return NULL;
00100     }
00101 
00102     if(avfilter_graph_add_filter(ctx, filt_ctx) < 0) {
00103         avfilter_destroy(filt_ctx);
00104         return NULL;
00105     }
00106 
00107     if(avfilter_init_filter(filt_ctx, args, NULL)) {
00108         av_log(log_ctx, AV_LOG_ERROR,
00109                "error initializing filter '%s' with args '%s'\n", filt_name, args);
00110         return NULL;
00111     }
00112 
00113     return filt_ctx;
00114 }
00115 
00119 static AVFilterContext *parse_filter(const char **buf, AVFilterGraph *graph,
00120                                      int index, AVClass *log_ctx)
00121 {
00122     char *opts = NULL;
00123     char *name = av_get_token(buf, "=,;[\n");
00124     AVFilterContext *ret;
00125 
00126     if(**buf == '=') {
00127         (*buf)++;
00128         opts = av_get_token(buf, "[],;\n");
00129     }
00130 
00131     ret = create_filter(graph, index, name, opts, log_ctx);
00132     av_free(name);
00133     av_free(opts);
00134     return ret;
00135 }
00136 
00137 static void free_inout(AVFilterInOut *head)
00138 {
00139     while(head) {
00140         AVFilterInOut *next = head->next;
00141         av_free(head->name);
00142         av_free(head);
00143         head = next;
00144     }
00145 }
00146 
00147 static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links)
00148 {
00149     AVFilterInOut *ret;
00150 
00151     while(*links && strcmp((*links)->name, label))
00152         links = &((*links)->next);
00153 
00154     ret = *links;
00155 
00156     if(ret)
00157         *links = ret->next;
00158 
00159     return ret;
00160 }
00161 
00162 static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element)
00163 {
00164     element->next = *inouts;
00165     *inouts = element;
00166 }
00167 
00168 static int link_filter_inouts(AVFilterContext *filter,
00169                               AVFilterInOut **curr_inputs,
00170                               AVFilterInOut **open_inputs, AVClass *log_ctx)
00171 {
00172     int pad = filter->input_count;
00173 
00174     while(pad--) {
00175         AVFilterInOut *p = *curr_inputs;
00176         if(!p) {
00177             av_log(log_ctx, AV_LOG_ERROR,
00178                    "Not enough inputs specified for the \"%s\" filter.\n",
00179                    filter->filter->name);
00180             return -1;
00181         }
00182 
00183         *curr_inputs = (*curr_inputs)->next;
00184 
00185         if(p->filter) {
00186             if(link_filter(p->filter, p->pad_idx, filter, pad, log_ctx))
00187                 return -1;
00188             av_free(p->name);
00189             av_free(p);
00190         } else {
00191             p->filter = filter;
00192             p->pad_idx = pad;
00193             insert_inout(open_inputs, p);
00194         }
00195     }
00196 
00197     if(*curr_inputs) {
00198         av_log(log_ctx, AV_LOG_ERROR,
00199                "Too many inputs specified for the \"%s\" filter.\n",
00200                filter->filter->name);
00201         return -1;
00202     }
00203 
00204     pad = filter->output_count;
00205     while(pad--) {
00206         AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut));
00207         currlinkn->filter  = filter;
00208         currlinkn->pad_idx = pad;
00209         insert_inout(curr_inputs, currlinkn);
00210     }
00211 
00212     return 0;
00213 }
00214 
00215 static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs,
00216                         AVFilterInOut **open_outputs, AVClass *log_ctx)
00217 {
00218     int pad = 0;
00219 
00220     while(**buf == '[') {
00221         char *name = parse_link_name(buf, log_ctx);
00222         AVFilterInOut *match;
00223 
00224         if(!name)
00225             return -1;
00226 
00227         /* First check if the label is not in the open_outputs list */
00228         match = extract_inout(name, open_outputs);
00229 
00230         if(match) {
00231             av_free(name);
00232         } else {
00233             /* Not in the list, so add it as an input */
00234             match = av_mallocz(sizeof(AVFilterInOut));
00235             match->name    = name;
00236             match->pad_idx = pad;
00237         }
00238 
00239         insert_inout(curr_inputs, match);
00240 
00241         *buf += strspn(*buf, WHITESPACES);
00242         pad++;
00243     }
00244 
00245     return pad;
00246 }
00247 
00248 static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs,
00249                          AVFilterInOut **open_inputs,
00250                          AVFilterInOut **open_outputs, AVClass *log_ctx)
00251 {
00252     int pad = 0;
00253 
00254     while(**buf == '[') {
00255         char *name = parse_link_name(buf, log_ctx);
00256         AVFilterInOut *match;
00257 
00258         AVFilterInOut *input = *curr_inputs;
00259         *curr_inputs = (*curr_inputs)->next;
00260 
00261         if(!name)
00262             return -1;
00263 
00264         /* First check if the label is not in the open_inputs list */
00265         match = extract_inout(name, open_inputs);
00266 
00267         if(match) {
00268             if(link_filter(input->filter, input->pad_idx,
00269                            match->filter, match->pad_idx, log_ctx) < 0)
00270                 return -1;
00271             av_free(match->name);
00272             av_free(name);
00273             av_free(match);
00274             av_free(input);
00275         } else {
00276             /* Not in the list, so add the first input as a open_output */
00277             input->name = name;
00278             insert_inout(open_outputs, input);
00279         }
00280         *buf += strspn(*buf, WHITESPACES);
00281         pad++;
00282     }
00283 
00284     return pad;
00285 }
00286 
00287 int avfilter_graph_parse(AVFilterGraph *graph, const char *filters,
00288                          AVFilterInOut *open_inputs,
00289                          AVFilterInOut *open_outputs, AVClass *log_ctx)
00290 {
00291     int index = 0;
00292     char chr = 0;
00293 
00294     AVFilterInOut *curr_inputs = NULL;
00295 
00296     do {
00297         AVFilterContext *filter;
00298         filters += strspn(filters, WHITESPACES);
00299 
00300         if(parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx) < 0)
00301             goto fail;
00302 
00303         filter = parse_filter(&filters, graph, index, log_ctx);
00304 
00305         if(!filter)
00306             goto fail;
00307 
00308         if(filter->input_count == 1 && !curr_inputs && !index) {
00309             /* First input can be omitted if it is "[in]" */
00310             const char *tmp = "[in]";
00311             if(parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx) < 0)
00312                 goto fail;
00313         }
00314 
00315         if(link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx) < 0)
00316             goto fail;
00317 
00318         if(parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs,
00319                          log_ctx) < 0)
00320             goto fail;
00321 
00322         filters += strspn(filters, WHITESPACES);
00323         chr = *filters++;
00324 
00325         if(chr == ';' && curr_inputs) {
00326             av_log(log_ctx, AV_LOG_ERROR,
00327                    "Could not find a output to link when parsing \"%s\"\n",
00328                    filters - 1);
00329             goto fail;
00330         }
00331         index++;
00332     } while(chr == ',' || chr == ';');
00333 
00334     if (chr) {
00335         av_log(log_ctx, AV_LOG_ERROR,
00336                "Unable to parse graph description substring: \"%s\"\n",
00337                filters - 1);
00338         goto fail;
00339     }
00340 
00341     if(open_inputs && !strcmp(open_inputs->name, "out") && curr_inputs) {
00342         /* Last output can be omitted if it is "[out]" */
00343         const char *tmp = "[out]";
00344         if(parse_outputs(&tmp, &curr_inputs, &open_inputs,
00345                          &open_outputs, log_ctx) < 0)
00346             goto fail;
00347     }
00348 
00349     return 0;
00350 
00351  fail:
00352     avfilter_graph_destroy(graph);
00353     free_inout(open_inputs);
00354     free_inout(open_outputs);
00355     free_inout(curr_inputs);
00356     return -1;
00357 }

Generated on Fri Sep 16 2011 17:17:47 for FFmpeg by  doxygen 1.7.1