00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 #include <ctype.h>
00024 #include <string.h>
00025 #include <stdio.h>
00026 
00027 #include "libavutil/avstring.h"
00028 #include "libavutil/mem.h"
00029 #include "avfilter.h"
00030 #include "avfiltergraph.h"
00031 
00032 #define WHITESPACES " \n\t"
00033 
00039 static int link_filter(AVFilterContext *src, int srcpad,
00040                        AVFilterContext *dst, int dstpad,
00041                        void *log_ctx)
00042 {
00043     int ret;
00044     if ((ret = avfilter_link(src, srcpad, dst, dstpad))) {
00045         av_log(log_ctx, AV_LOG_ERROR,
00046                "Cannot create the link %s:%d -> %s:%d\n",
00047                src->filter->name, srcpad, dst->filter->name, dstpad);
00048         return ret;
00049     }
00050 
00051     return 0;
00052 }
00053 
00060 static char *parse_link_name(const char **buf, void *log_ctx)
00061 {
00062     const char *start = *buf;
00063     char *name;
00064     (*buf)++;
00065 
00066     name = av_get_token(buf, "]");
00067 
00068     if (!name[0]) {
00069         av_log(log_ctx, AV_LOG_ERROR,
00070                "Bad (empty?) label found in the following: \"%s\".\n", start);
00071         goto fail;
00072     }
00073 
00074     if (*(*buf)++ != ']') {
00075         av_log(log_ctx, AV_LOG_ERROR,
00076                "Mismatched '[' found in the following: \"%s\".\n", start);
00077     fail:
00078         av_freep(&name);
00079     }
00080 
00081     return name;
00082 }
00083 
00096 static int create_filter(AVFilterContext **filt_ctx, AVFilterGraph *ctx, int index,
00097                          const char *filt_name, const char *args, void *log_ctx)
00098 {
00099     AVFilter *filt;
00100     char inst_name[30];
00101     char tmp_args[256];
00102     int ret;
00103 
00104     snprintf(inst_name, sizeof(inst_name), "Parsed_%s_%d", filt_name, index);
00105 
00106     filt = avfilter_get_by_name(filt_name);
00107 
00108     if (!filt) {
00109         av_log(log_ctx, AV_LOG_ERROR,
00110                "No such filter: '%s'\n", filt_name);
00111         return AVERROR(EINVAL);
00112     }
00113 
00114     ret = avfilter_open(filt_ctx, filt, inst_name);
00115     if (!*filt_ctx) {
00116         av_log(log_ctx, AV_LOG_ERROR,
00117                "Error creating filter '%s'\n", filt_name);
00118         return ret;
00119     }
00120 
00121     if ((ret = avfilter_graph_add_filter(ctx, *filt_ctx)) < 0) {
00122         avfilter_free(*filt_ctx);
00123         return ret;
00124     }
00125 
00126     if (!strcmp(filt_name, "scale") && args && !strstr(args, "flags")) {
00127         snprintf(tmp_args, sizeof(tmp_args), "%s:%s",
00128                  args, ctx->scale_sws_opts);
00129         args = tmp_args;
00130     }
00131 
00132     if ((ret = avfilter_init_filter(*filt_ctx, args, NULL)) < 0) {
00133         av_log(log_ctx, AV_LOG_ERROR,
00134                "Error initializing filter '%s' with args '%s'\n", filt_name, args);
00135         return ret;
00136     }
00137 
00138     return 0;
00139 }
00140 
00157 static int parse_filter(AVFilterContext **filt_ctx, const char **buf, AVFilterGraph *graph,
00158                         int index, void *log_ctx)
00159 {
00160     char *opts = NULL;
00161     char *name = av_get_token(buf, "=,;[\n");
00162     int ret;
00163 
00164     if (**buf == '=') {
00165         (*buf)++;
00166         opts = av_get_token(buf, "[],;\n");
00167     }
00168 
00169     ret = create_filter(filt_ctx, graph, index, name, opts, log_ctx);
00170     av_free(name);
00171     av_free(opts);
00172     return ret;
00173 }
00174 
00175 AVFilterInOut *avfilter_inout_alloc(void)
00176 {
00177     return av_mallocz(sizeof(AVFilterInOut));
00178 }
00179 
00180 void avfilter_inout_free(AVFilterInOut **inout)
00181 {
00182     while (*inout) {
00183         AVFilterInOut *next = (*inout)->next;
00184         av_freep(&(*inout)->name);
00185         av_freep(inout);
00186         *inout = next;
00187     }
00188 }
00189 
00190 static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links)
00191 {
00192     AVFilterInOut *ret;
00193 
00194     while (*links && (!(*links)->name || strcmp((*links)->name, label)))
00195         links = &((*links)->next);
00196 
00197     ret = *links;
00198 
00199     if (ret) {
00200         *links = ret->next;
00201         ret->next = NULL;
00202     }
00203 
00204     return ret;
00205 }
00206 
00207 static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element)
00208 {
00209     element->next = *inouts;
00210     *inouts = element;
00211 }
00212 
00213 static void append_inout(AVFilterInOut **inouts, AVFilterInOut **element)
00214 {
00215     while (*inouts && (*inouts)->next)
00216         inouts = &((*inouts)->next);
00217 
00218     if (!*inouts)
00219         *inouts = *element;
00220     else
00221         (*inouts)->next = *element;
00222     *element = NULL;
00223 }
00224 
00225 static int link_filter_inouts(AVFilterContext *filt_ctx,
00226                               AVFilterInOut **curr_inputs,
00227                               AVFilterInOut **open_inputs, void *log_ctx)
00228 {
00229     int pad, ret;
00230 
00231     for (pad = 0; pad < filt_ctx->nb_inputs; pad++) {
00232         AVFilterInOut *p = *curr_inputs;
00233 
00234         if (p) {
00235             *curr_inputs = (*curr_inputs)->next;
00236             p->next = NULL;
00237         } else if (!(p = av_mallocz(sizeof(*p))))
00238             return AVERROR(ENOMEM);
00239 
00240         if (p->filter_ctx) {
00241             if ((ret = link_filter(p->filter_ctx, p->pad_idx, filt_ctx, pad, log_ctx)) < 0)
00242                 return ret;
00243             av_free(p->name);
00244             av_free(p);
00245         } else {
00246             p->filter_ctx = filt_ctx;
00247             p->pad_idx = pad;
00248             append_inout(open_inputs, &p);
00249         }
00250     }
00251 
00252     if (*curr_inputs) {
00253         av_log(log_ctx, AV_LOG_ERROR,
00254                "Too many inputs specified for the \"%s\" filter.\n",
00255                filt_ctx->filter->name);
00256         return AVERROR(EINVAL);
00257     }
00258 
00259     pad = filt_ctx->nb_outputs;
00260     while (pad--) {
00261         AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut));
00262         if (!currlinkn)
00263             return AVERROR(ENOMEM);
00264         currlinkn->filter_ctx  = filt_ctx;
00265         currlinkn->pad_idx = pad;
00266         insert_inout(curr_inputs, currlinkn);
00267     }
00268 
00269     return 0;
00270 }
00271 
00272 static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs,
00273                         AVFilterInOut **open_outputs, void *log_ctx)
00274 {
00275     AVFilterInOut *parsed_inputs = NULL;
00276     int pad = 0;
00277 
00278     while (**buf == '[') {
00279         char *name = parse_link_name(buf, log_ctx);
00280         AVFilterInOut *match;
00281 
00282         if (!name)
00283             return AVERROR(EINVAL);
00284 
00285         
00286         match = extract_inout(name, open_outputs);
00287 
00288         if (match) {
00289             av_free(name);
00290         } else {
00291             
00292             if (!(match = av_mallocz(sizeof(AVFilterInOut))))
00293                 return AVERROR(ENOMEM);
00294             match->name    = name;
00295             match->pad_idx = pad;
00296         }
00297 
00298         append_inout(&parsed_inputs, &match);
00299 
00300         *buf += strspn(*buf, WHITESPACES);
00301         pad++;
00302     }
00303 
00304     append_inout(&parsed_inputs, curr_inputs);
00305     *curr_inputs = parsed_inputs;
00306 
00307     return pad;
00308 }
00309 
00310 static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs,
00311                          AVFilterInOut **open_inputs,
00312                          AVFilterInOut **open_outputs, void *log_ctx)
00313 {
00314     int ret, pad = 0;
00315 
00316     while (**buf == '[') {
00317         char *name = parse_link_name(buf, log_ctx);
00318         AVFilterInOut *match;
00319 
00320         AVFilterInOut *input = *curr_inputs;
00321         if (!input) {
00322             av_log(log_ctx, AV_LOG_ERROR,
00323                    "No output pad can be associated to link label '%s'.\n",
00324                    name);
00325             return AVERROR(EINVAL);
00326         }
00327         *curr_inputs = (*curr_inputs)->next;
00328 
00329         if (!name)
00330             return AVERROR(EINVAL);
00331 
00332         
00333         match = extract_inout(name, open_inputs);
00334 
00335         if (match) {
00336             if ((ret = link_filter(input->filter_ctx, input->pad_idx,
00337                                    match->filter_ctx, match->pad_idx, log_ctx)) < 0)
00338                 return ret;
00339             av_free(match->name);
00340             av_free(name);
00341             av_free(match);
00342             av_free(input);
00343         } else {
00344             
00345             input->name = name;
00346             insert_inout(open_outputs, input);
00347         }
00348         *buf += strspn(*buf, WHITESPACES);
00349         pad++;
00350     }
00351 
00352     return pad;
00353 }
00354 
00355 static int parse_sws_flags(const char **buf, AVFilterGraph *graph)
00356 {
00357     char *p = strchr(*buf, ';');
00358 
00359     if (strncmp(*buf, "sws_flags=", 10))
00360         return 0;
00361 
00362     if (!p) {
00363         av_log(graph, AV_LOG_ERROR, "sws_flags not terminated with ';'.\n");
00364         return AVERROR(EINVAL);
00365     }
00366 
00367     *buf += 4;  
00368 
00369     av_freep(&graph->scale_sws_opts);
00370     if (!(graph->scale_sws_opts = av_mallocz(p - *buf + 1)))
00371         return AVERROR(ENOMEM);
00372     av_strlcpy(graph->scale_sws_opts, *buf, p - *buf + 1);
00373 
00374     *buf = p + 1;
00375     return 0;
00376 }
00377 
00378 int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters,
00379                           AVFilterInOut **inputs,
00380                           AVFilterInOut **outputs)
00381 {
00382     int index = 0, ret = 0;
00383     char chr = 0;
00384 
00385     AVFilterInOut *curr_inputs = NULL, *open_inputs = NULL, *open_outputs = NULL;
00386 
00387     filters += strspn(filters, WHITESPACES);
00388 
00389     if ((ret = parse_sws_flags(&filters, graph)) < 0)
00390         goto fail;
00391 
00392     do {
00393         AVFilterContext *filter;
00394         filters += strspn(filters, WHITESPACES);
00395 
00396         if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, graph)) < 0)
00397             goto end;
00398         if ((ret = parse_filter(&filter, &filters, graph, index, graph)) < 0)
00399             goto end;
00400 
00401 
00402         if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, graph)) < 0)
00403             goto end;
00404 
00405         if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs,
00406                                  graph)) < 0)
00407             goto end;
00408 
00409         filters += strspn(filters, WHITESPACES);
00410         chr = *filters++;
00411 
00412         if (chr == ';' && curr_inputs)
00413             append_inout(&open_outputs, &curr_inputs);
00414         index++;
00415     } while (chr == ',' || chr == ';');
00416 
00417     if (chr) {
00418         av_log(graph, AV_LOG_ERROR,
00419                "Unable to parse graph description substring: \"%s\"\n",
00420                filters - 1);
00421         ret = AVERROR(EINVAL);
00422         goto end;
00423     }
00424 
00425     append_inout(&open_outputs, &curr_inputs);
00426 
00427 
00428     *inputs  = open_inputs;
00429     *outputs = open_outputs;
00430     return 0;
00431 
00432  fail:end:
00433     for (; graph->filter_count > 0; graph->filter_count--)
00434         avfilter_free(graph->filters[graph->filter_count - 1]);
00435     av_freep(&graph->filters);
00436     avfilter_inout_free(&open_inputs);
00437     avfilter_inout_free(&open_outputs);
00438     avfilter_inout_free(&curr_inputs);
00439 
00440     *inputs  = NULL;
00441     *outputs = NULL;
00442 
00443     return ret;
00444 }
00445 
00446 int avfilter_graph_parse(AVFilterGraph *graph, const char *filters,
00447                          AVFilterInOut **open_inputs_ptr, AVFilterInOut **open_outputs_ptr,
00448                          void *log_ctx)
00449 {
00450 #if 0
00451     int ret;
00452     AVFilterInOut *open_inputs  = open_inputs_ptr  ? *open_inputs_ptr  : NULL;
00453     AVFilterInOut *open_outputs = open_outputs_ptr ? *open_outputs_ptr : NULL;
00454     AVFilterInOut *cur, *match, *inputs = NULL, *outputs = NULL;
00455 
00456     if ((ret = avfilter_graph_parse2(graph, filters, &inputs, &outputs)) < 0)
00457         goto fail;
00458 
00459     
00460     if (inputs && !inputs->name)
00461         inputs->name = av_strdup("in");
00462     for (cur = inputs; cur; cur = cur->next) {
00463         if (!cur->name) {
00464               av_log(log_ctx, AV_LOG_ERROR,
00465                      "Not enough inputs specified for the \"%s\" filter.\n",
00466                      cur->filter_ctx->filter->name);
00467               ret = AVERROR(EINVAL);
00468               goto fail;
00469         }
00470         if (!(match = extract_inout(cur->name, &open_outputs)))
00471             continue;
00472         ret = avfilter_link(match->filter_ctx, match->pad_idx,
00473                             cur->filter_ctx,   cur->pad_idx);
00474         avfilter_inout_free(&match);
00475         if (ret < 0)
00476             goto fail;
00477     }
00478 
00479     
00480     if (outputs && !outputs->name)
00481         outputs->name = av_strdup("out");
00482     for (cur = outputs; cur; cur = cur->next) {
00483         if (!cur->name) {
00484             av_log(log_ctx, AV_LOG_ERROR,
00485                    "Invalid filterchain containing an unlabelled output pad: \"%s\"\n",
00486                    filters);
00487             ret = AVERROR(EINVAL);
00488             goto fail;
00489         }
00490         if (!(match = extract_inout(cur->name, &open_inputs)))
00491             continue;
00492         ret = avfilter_link(cur->filter_ctx,   cur->pad_idx,
00493                             match->filter_ctx, match->pad_idx);
00494         avfilter_inout_free(&match);
00495         if (ret < 0)
00496             goto fail;
00497     }
00498 
00499  fail:
00500     if (ret < 0) {
00501         for (; graph->filter_count > 0; graph->filter_count--)
00502             avfilter_free(graph->filters[graph->filter_count - 1]);
00503         av_freep(&graph->filters);
00504     }
00505     avfilter_inout_free(&inputs);
00506     avfilter_inout_free(&outputs);
00507     
00508     if (open_inputs_ptr) *open_inputs_ptr = open_inputs;
00509     else avfilter_inout_free(&open_inputs);
00510     if (open_outputs_ptr) *open_outputs_ptr = open_outputs;
00511     else avfilter_inout_free(&open_outputs);
00512     return ret;
00513 }
00514 #else
00515     int index = 0, ret = 0;
00516     char chr = 0;
00517 
00518     AVFilterInOut *curr_inputs = NULL;
00519     AVFilterInOut *open_inputs  = open_inputs_ptr  ? *open_inputs_ptr  : NULL;
00520     AVFilterInOut *open_outputs = open_outputs_ptr ? *open_outputs_ptr : NULL;
00521 
00522     do {
00523         AVFilterContext *filter;
00524         const char *filterchain = filters;
00525         filters += strspn(filters, WHITESPACES);
00526 
00527         if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx)) < 0)
00528             goto end;
00529 
00530         if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0)
00531             goto end;
00532 
00533         if (filter->input_count == 1 && !curr_inputs && !index) {
00534             
00535             const char *tmp = "[in]";
00536             if ((ret = parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx)) < 0)
00537                 goto end;
00538         }
00539 
00540         if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx)) < 0)
00541             goto end;
00542 
00543         if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs,
00544                                  log_ctx)) < 0)
00545             goto end;
00546 
00547         filters += strspn(filters, WHITESPACES);
00548         chr = *filters++;
00549 
00550         if (chr == ';' && curr_inputs) {
00551             av_log(log_ctx, AV_LOG_ERROR,
00552                    "Invalid filterchain containing an unlabelled output pad: \"%s\"\n",
00553                    filterchain);
00554             ret = AVERROR(EINVAL);
00555             goto end;
00556         }
00557         index++;
00558     } while (chr == ',' || chr == ';');
00559 
00560     if (chr) {
00561         av_log(log_ctx, AV_LOG_ERROR,
00562                "Unable to parse graph description substring: \"%s\"\n",
00563                filters - 1);
00564         ret = AVERROR(EINVAL);
00565         goto end;
00566     }
00567 
00568     if (curr_inputs) {
00569         
00570         const char *tmp = "[out]";
00571         if ((ret = parse_outputs(&tmp, &curr_inputs, &open_inputs, &open_outputs,
00572                                  log_ctx)) < 0)
00573             goto end;
00574     }
00575 
00576 end:
00577     
00578     if (open_inputs_ptr) *open_inputs_ptr = open_inputs;
00579     else avfilter_inout_free(&open_inputs);
00580     if (open_outputs_ptr) *open_outputs_ptr = open_outputs;
00581     else avfilter_inout_free(&open_outputs);
00582     avfilter_inout_free(&curr_inputs);
00583 
00584     if (ret < 0) {
00585         for (; graph->filter_count > 0; graph->filter_count--)
00586             avfilter_free(graph->filters[graph->filter_count - 1]);
00587         av_freep(&graph->filters);
00588     }
00589     return ret;
00590 }
00591 
00592 #endif