LCOV - code coverage report
Current view: top level - libavfilter - graphparser.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 190 323 58.8 %
Date: 2017-12-14 01:15:32 Functions: 14 16 87.5 %

          Line data    Source code
       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 <string.h>
      24             : #include <stdio.h>
      25             : 
      26             : #include "libavutil/avstring.h"
      27             : #include "libavutil/mem.h"
      28             : #include "avfilter.h"
      29             : 
      30             : #define WHITESPACES " \n\t\r"
      31             : 
      32             : /**
      33             :  * Link two filters together.
      34             :  *
      35             :  * @see avfilter_link()
      36             :  */
      37        3142 : static int link_filter(AVFilterContext *src, int srcpad,
      38             :                        AVFilterContext *dst, int dstpad,
      39             :                        void *log_ctx)
      40             : {
      41             :     int ret;
      42        3142 :     if ((ret = avfilter_link(src, srcpad, dst, dstpad))) {
      43           0 :         av_log(log_ctx, AV_LOG_ERROR,
      44             :                "Cannot create the link %s:%d -> %s:%d\n",
      45           0 :                src->filter->name, srcpad, dst->filter->name, dstpad);
      46           0 :         return ret;
      47             :     }
      48             : 
      49        3142 :     return 0;
      50             : }
      51             : 
      52             : /**
      53             :  * Parse the name of a link, which has the format "[linkname]".
      54             :  *
      55             :  * @return a pointer (that need to be freed after use) to the name
      56             :  * between parenthesis
      57             :  */
      58         291 : static char *parse_link_name(const char **buf, void *log_ctx)
      59             : {
      60         291 :     const char *start = *buf;
      61             :     char *name;
      62         291 :     (*buf)++;
      63             : 
      64         291 :     name = av_get_token(buf, "]");
      65         291 :     if (!name)
      66           0 :         goto fail;
      67             : 
      68         291 :     if (!name[0]) {
      69           0 :         av_log(log_ctx, AV_LOG_ERROR,
      70             :                "Bad (empty?) label found in the following: \"%s\".\n", start);
      71           0 :         goto fail;
      72             :     }
      73             : 
      74         291 :     if (*(*buf)++ != ']') {
      75           0 :         av_log(log_ctx, AV_LOG_ERROR,
      76             :                "Mismatched '[' found in the following: \"%s\".\n", start);
      77           0 :     fail:
      78           0 :         av_freep(&name);
      79             :     }
      80             : 
      81         291 :     return name;
      82             : }
      83             : 
      84             : /**
      85             :  * Create an instance of a filter, initialize and insert it in the
      86             :  * filtergraph in *ctx.
      87             :  *
      88             :  * @param filt_ctx put here a filter context in case of successful creation and configuration, NULL otherwise.
      89             :  * @param ctx the filtergraph context
      90             :  * @param index an index which is supposed to be unique for each filter instance added to the filtergraph
      91             :  * @param name the name of the filter to create, can be filter name or filter_name\@id as instance name
      92             :  * @param args the arguments provided to the filter during its initialization
      93             :  * @param log_ctx the log context to use
      94             :  * @return >= 0 in case of success, a negative AVERROR code otherwise
      95             :  */
      96        8527 : static int create_filter(AVFilterContext **filt_ctx, AVFilterGraph *ctx, int index,
      97             :                          const char *name, const char *args, void *log_ctx)
      98             : {
      99             :     const AVFilter *filt;
     100             :     char name2[30];
     101        8527 :     const char *inst_name = NULL, *filt_name = NULL;
     102        8527 :     char *tmp_args = NULL;
     103             :     int ret, k;
     104             : 
     105        8527 :     av_strlcpy(name2, name, sizeof(name2));
     106             : 
     107       56349 :     for (k = 0; name2[k]; k++) {
     108       47822 :         if (name2[k] == '@' && name[k+1]) {
     109           0 :             name2[k] = 0;
     110           0 :             inst_name = name;
     111           0 :             filt_name = name2;
     112           0 :             break;
     113             :         }
     114             :     }
     115             : 
     116        8527 :     if (!inst_name) {
     117        8527 :         snprintf(name2, sizeof(name2), "Parsed_%s_%d", name, index);
     118        8527 :         inst_name = name2;
     119        8527 :         filt_name = name;
     120             :     }
     121             : 
     122        8527 :     filt = avfilter_get_by_name(filt_name);
     123             : 
     124        8527 :     if (!filt) {
     125           0 :         av_log(log_ctx, AV_LOG_ERROR,
     126             :                "No such filter: '%s'\n", filt_name);
     127           0 :         return AVERROR(EINVAL);
     128             :     }
     129             : 
     130        8527 :     *filt_ctx = avfilter_graph_alloc_filter(ctx, filt, inst_name);
     131        8527 :     if (!*filt_ctx) {
     132           0 :         av_log(log_ctx, AV_LOG_ERROR,
     133             :                "Error creating filter '%s'\n", filt_name);
     134           0 :         return AVERROR(ENOMEM);
     135             :     }
     136             : 
     137        8705 :     if (!strcmp(filt_name, "scale") && (!args || !strstr(args, "flags")) &&
     138         178 :         ctx->scale_sws_opts) {
     139         178 :         if (args) {
     140         178 :             tmp_args = av_asprintf("%s:%s",
     141             :                     args, ctx->scale_sws_opts);
     142         178 :             if (!tmp_args)
     143           0 :                 return AVERROR(ENOMEM);
     144         178 :             args = tmp_args;
     145             :         } else
     146           0 :             args = ctx->scale_sws_opts;
     147             :     }
     148             : 
     149        8527 :     ret = avfilter_init_str(*filt_ctx, args);
     150        8527 :     if (ret < 0) {
     151           0 :         av_log(log_ctx, AV_LOG_ERROR,
     152             :                "Error initializing filter '%s'", filt_name);
     153           0 :         if (args)
     154           0 :             av_log(log_ctx, AV_LOG_ERROR, " with args '%s'", args);
     155           0 :         av_log(log_ctx, AV_LOG_ERROR, "\n");
     156           0 :         avfilter_free(*filt_ctx);
     157           0 :         *filt_ctx = NULL;
     158             :     }
     159             : 
     160        8527 :     av_free(tmp_args);
     161        8527 :     return ret;
     162             : }
     163             : 
     164             : /**
     165             :  * Parse a string of the form FILTER_NAME[=PARAMS], and create a
     166             :  * corresponding filter instance which is added to graph with
     167             :  * create_filter().
     168             :  *
     169             :  * @param filt_ctx Pointer that is set to the created and configured filter
     170             :  *                 context on success, set to NULL on failure.
     171             :  * @param filt_ctx put here a pointer to the created filter context on
     172             :  * success, NULL otherwise
     173             :  * @param buf pointer to the buffer to parse, *buf will be updated to
     174             :  * point to the char next after the parsed string
     175             :  * @param index an index which is assigned to the created filter
     176             :  * instance, and which is supposed to be unique for each filter
     177             :  * instance added to the filtergraph
     178             :  * @return >= 0 in case of success, a negative AVERROR code otherwise
     179             :  */
     180        8527 : static int parse_filter(AVFilterContext **filt_ctx, const char **buf, AVFilterGraph *graph,
     181             :                         int index, void *log_ctx)
     182             : {
     183        8527 :     char *opts = NULL;
     184        8527 :     char *name = av_get_token(buf, "=,;[");
     185             :     int ret;
     186             : 
     187        8527 :     if (**buf == '=') {
     188        5267 :         (*buf)++;
     189        5267 :         opts = av_get_token(buf, "[],;");
     190             :     }
     191             : 
     192        8527 :     ret = create_filter(filt_ctx, graph, index, name, opts, log_ctx);
     193        8527 :     av_free(name);
     194        8527 :     av_free(opts);
     195        8527 :     return ret;
     196             : }
     197             : 
     198           0 : AVFilterInOut *avfilter_inout_alloc(void)
     199             : {
     200           0 :     return av_mallocz(sizeof(AVFilterInOut));
     201             : }
     202             : 
     203       10843 : void avfilter_inout_free(AVFilterInOut **inout)
     204             : {
     205       32491 :     while (*inout) {
     206       10805 :         AVFilterInOut *next = (*inout)->next;
     207       10805 :         av_freep(&(*inout)->name);
     208       10805 :         av_freep(inout);
     209       10805 :         *inout = next;
     210             :     }
     211       10843 : }
     212             : 
     213         291 : static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links)
     214             : {
     215             :     AVFilterInOut *ret;
     216             : 
     217         721 :     while (*links && (!(*links)->name || strcmp((*links)->name, label)))
     218         139 :         links = &((*links)->next);
     219             : 
     220         291 :     ret = *links;
     221             : 
     222         291 :     if (ret) {
     223         122 :         *links = ret->next;
     224         122 :         ret->next = NULL;
     225             :     }
     226             : 
     227         291 :     return ret;
     228             : }
     229             : 
     230        8708 : static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element)
     231             : {
     232        8708 :     element->next = *inouts;
     233        8708 :     *inouts = element;
     234        8708 : }
     235             : 
     236       19449 : static void append_inout(AVFilterInOut **inouts, AVFilterInOut **element)
     237             : {
     238       38977 :     while (*inouts && (*inouts)->next)
     239          79 :         inouts = &((*inouts)->next);
     240             : 
     241       19449 :     if (!*inouts)
     242       19259 :         *inouts = *element;
     243             :     else
     244         190 :         (*inouts)->next = *element;
     245       19449 :     *element = NULL;
     246       19449 : }
     247             : 
     248        8527 : static int link_filter_inouts(AVFilterContext *filt_ctx,
     249             :                               AVFilterInOut **curr_inputs,
     250             :                               AVFilterInOut **open_inputs, void *log_ctx)
     251             : {
     252             :     int pad, ret;
     253             : 
     254       34114 :     for (pad = 0; pad < filt_ctx->nb_inputs; pad++) {
     255        8530 :         AVFilterInOut *p = *curr_inputs;
     256             : 
     257        8530 :         if (p) {
     258        3162 :             *curr_inputs = (*curr_inputs)->next;
     259        3162 :             p->next = NULL;
     260        5368 :         } else if (!(p = av_mallocz(sizeof(*p))))
     261           0 :             return AVERROR(ENOMEM);
     262             : 
     263        8530 :         if (p->filter_ctx) {
     264        3142 :             ret = link_filter(p->filter_ctx, p->pad_idx, filt_ctx, pad, log_ctx);
     265        3142 :             av_freep(&p->name);
     266        3142 :             av_freep(&p);
     267        3142 :             if (ret < 0)
     268           0 :                 return ret;
     269             :         } else {
     270        5388 :             p->filter_ctx = filt_ctx;
     271        5388 :             p->pad_idx = pad;
     272        5388 :             append_inout(open_inputs, &p);
     273             :         }
     274             :     }
     275             : 
     276        8527 :     if (*curr_inputs) {
     277           0 :         av_log(log_ctx, AV_LOG_ERROR,
     278             :                "Too many inputs specified for the \"%s\" filter.\n",
     279           0 :                filt_ctx->filter->name);
     280           0 :         return AVERROR(EINVAL);
     281             :     }
     282             : 
     283        8527 :     pad = filt_ctx->nb_outputs;
     284       25613 :     while (pad--) {
     285        8559 :         AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut));
     286        8559 :         if (!currlinkn)
     287           0 :             return AVERROR(ENOMEM);
     288        8559 :         currlinkn->filter_ctx  = filt_ctx;
     289        8559 :         currlinkn->pad_idx = pad;
     290        8559 :         insert_inout(curr_inputs, currlinkn);
     291             :     }
     292             : 
     293        8527 :     return 0;
     294             : }
     295             : 
     296        8527 : static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs,
     297             :                         AVFilterInOut **open_outputs, void *log_ctx)
     298             : {
     299        8527 :     AVFilterInOut *parsed_inputs = NULL;
     300        8527 :     int pad = 0;
     301             : 
     302       17196 :     while (**buf == '[') {
     303         142 :         char *name = parse_link_name(buf, log_ctx);
     304             :         AVFilterInOut *match;
     305             : 
     306         142 :         if (!name)
     307           0 :             return AVERROR(EINVAL);
     308             : 
     309             :         /* First check if the label is not in the open_outputs list */
     310         142 :         match = extract_inout(name, open_outputs);
     311             : 
     312         142 :         if (match) {
     313         122 :             av_free(name);
     314             :         } else {
     315             :             /* Not in the list, so add it as an input */
     316          20 :             if (!(match = av_mallocz(sizeof(AVFilterInOut)))) {
     317           0 :                 av_free(name);
     318           0 :                 return AVERROR(ENOMEM);
     319             :             }
     320          20 :             match->name    = name;
     321          20 :             match->pad_idx = pad;
     322             :         }
     323             : 
     324         142 :         append_inout(&parsed_inputs, &match);
     325             : 
     326         142 :         *buf += strspn(*buf, WHITESPACES);
     327         142 :         pad++;
     328             :     }
     329             : 
     330        8527 :     append_inout(&parsed_inputs, curr_inputs);
     331        8527 :     *curr_inputs = parsed_inputs;
     332             : 
     333        8527 :     return pad;
     334             : }
     335             : 
     336        8542 : static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs,
     337             :                          AVFilterInOut **open_inputs,
     338             :                          AVFilterInOut **open_outputs, void *log_ctx)
     339             : {
     340        8542 :     int ret, pad = 0;
     341             : 
     342       17233 :     while (**buf == '[') {
     343         149 :         char *name = parse_link_name(buf, log_ctx);
     344             :         AVFilterInOut *match;
     345             : 
     346         149 :         AVFilterInOut *input = *curr_inputs;
     347             : 
     348         149 :         if (!name)
     349           0 :             return AVERROR(EINVAL);
     350             : 
     351         149 :         if (!input) {
     352           0 :             av_log(log_ctx, AV_LOG_ERROR,
     353             :                    "No output pad can be associated to link label '%s'.\n", name);
     354           0 :             av_free(name);
     355           0 :             return AVERROR(EINVAL);
     356             :         }
     357         149 :         *curr_inputs = (*curr_inputs)->next;
     358             : 
     359             :         /* First check if the label is not in the open_inputs list */
     360         149 :         match = extract_inout(name, open_inputs);
     361             : 
     362         149 :         if (match) {
     363           0 :             if ((ret = link_filter(input->filter_ctx, input->pad_idx,
     364           0 :                                    match->filter_ctx, match->pad_idx, log_ctx)) < 0) {
     365           0 :                 av_free(name);
     366           0 :                 return ret;
     367             :             }
     368           0 :             av_freep(&match->name);
     369           0 :             av_freep(&name);
     370           0 :             av_freep(&match);
     371           0 :             av_freep(&input);
     372             :         } else {
     373             :             /* Not in the list, so add the first input as an open_output */
     374         149 :             input->name = name;
     375         149 :             insert_inout(open_outputs, input);
     376             :         }
     377         149 :         *buf += strspn(*buf, WHITESPACES);
     378         149 :         pad++;
     379             :     }
     380             : 
     381        8542 :     return pad;
     382             : }
     383             : 
     384        5411 : static int parse_sws_flags(const char **buf, AVFilterGraph *graph)
     385             : {
     386        5411 :     char *p = strchr(*buf, ';');
     387             : 
     388        5411 :     if (strncmp(*buf, "sws_flags=", 10))
     389        5378 :         return 0;
     390             : 
     391          33 :     if (!p) {
     392           0 :         av_log(graph, AV_LOG_ERROR, "sws_flags not terminated with ';'.\n");
     393           0 :         return AVERROR(EINVAL);
     394             :     }
     395             : 
     396          33 :     *buf += 4;  // keep the 'flags=' part
     397             : 
     398          33 :     av_freep(&graph->scale_sws_opts);
     399          33 :     if (!(graph->scale_sws_opts = av_mallocz(p - *buf + 1)))
     400           0 :         return AVERROR(ENOMEM);
     401          33 :     av_strlcpy(graph->scale_sws_opts, *buf, p - *buf + 1);
     402             : 
     403          33 :     *buf = p + 1;
     404          33 :     return 0;
     405             : }
     406             : 
     407        5392 : int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters,
     408             :                           AVFilterInOut **inputs,
     409             :                           AVFilterInOut **outputs)
     410             : {
     411        5392 :     int index = 0, ret = 0;
     412        5392 :     char chr = 0;
     413             : 
     414        5392 :     AVFilterInOut *curr_inputs = NULL, *open_inputs = NULL, *open_outputs = NULL;
     415             : 
     416        5392 :     filters += strspn(filters, WHITESPACES);
     417             : 
     418        5392 :     if ((ret = parse_sws_flags(&filters, graph)) < 0)
     419           0 :         goto fail;
     420             : 
     421             :     do {
     422             :         AVFilterContext *filter;
     423        8494 :         filters += strspn(filters, WHITESPACES);
     424             : 
     425        8494 :         if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, graph)) < 0)
     426           0 :             goto end;
     427        8494 :         if ((ret = parse_filter(&filter, &filters, graph, index, graph)) < 0)
     428           0 :             goto end;
     429             : 
     430             : 
     431        8494 :         if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, graph)) < 0)
     432           0 :             goto end;
     433             : 
     434        8494 :         if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs,
     435             :                                  graph)) < 0)
     436           0 :             goto end;
     437             : 
     438        8494 :         filters += strspn(filters, WHITESPACES);
     439        8494 :         chr = *filters++;
     440             : 
     441        8494 :         if (chr == ';' && curr_inputs)
     442           0 :             append_inout(&open_outputs, &curr_inputs);
     443        8494 :         index++;
     444       13980 :     } while (chr == ',' || chr == ';');
     445             : 
     446        5392 :     if (chr) {
     447           0 :         av_log(graph, AV_LOG_ERROR,
     448             :                "Unable to parse graph description substring: \"%s\"\n",
     449             :                filters - 1);
     450           0 :         ret = AVERROR(EINVAL);
     451           0 :         goto end;
     452             :     }
     453             : 
     454        5392 :     append_inout(&open_outputs, &curr_inputs);
     455             : 
     456             : 
     457        5392 :     *inputs  = open_inputs;
     458        5392 :     *outputs = open_outputs;
     459        5392 :     return 0;
     460             : 
     461           0 :  fail:end:
     462           0 :     while (graph->nb_filters)
     463           0 :         avfilter_free(graph->filters[0]);
     464           0 :     av_freep(&graph->filters);
     465           0 :     avfilter_inout_free(&open_inputs);
     466           0 :     avfilter_inout_free(&open_outputs);
     467           0 :     avfilter_inout_free(&curr_inputs);
     468             : 
     469           0 :     *inputs  = NULL;
     470           0 :     *outputs = NULL;
     471             : 
     472           0 :     return ret;
     473             : }
     474             : 
     475           0 : int avfilter_graph_parse(AVFilterGraph *graph, const char *filters,
     476             :                          AVFilterInOut *open_inputs,
     477             :                          AVFilterInOut *open_outputs, void *log_ctx)
     478             : {
     479             :     int ret;
     480           0 :     AVFilterInOut *cur, *match, *inputs = NULL, *outputs = NULL;
     481             : 
     482           0 :     if ((ret = avfilter_graph_parse2(graph, filters, &inputs, &outputs)) < 0)
     483           0 :         goto fail;
     484             : 
     485             :     /* First input can be omitted if it is "[in]" */
     486           0 :     if (inputs && !inputs->name)
     487           0 :         inputs->name = av_strdup("in");
     488           0 :     for (cur = inputs; cur; cur = cur->next) {
     489           0 :         if (!cur->name) {
     490           0 :               av_log(log_ctx, AV_LOG_ERROR,
     491             :                      "Not enough inputs specified for the \"%s\" filter.\n",
     492           0 :                      cur->filter_ctx->filter->name);
     493           0 :               ret = AVERROR(EINVAL);
     494           0 :               goto fail;
     495             :         }
     496           0 :         if (!(match = extract_inout(cur->name, &open_outputs)))
     497           0 :             continue;
     498           0 :         ret = avfilter_link(match->filter_ctx, match->pad_idx,
     499           0 :                             cur->filter_ctx,   cur->pad_idx);
     500           0 :         avfilter_inout_free(&match);
     501           0 :         if (ret < 0)
     502           0 :             goto fail;
     503             :     }
     504             : 
     505             :     /* Last output can be omitted if it is "[out]" */
     506           0 :     if (outputs && !outputs->name)
     507           0 :         outputs->name = av_strdup("out");
     508           0 :     for (cur = outputs; cur; cur = cur->next) {
     509           0 :         if (!cur->name) {
     510           0 :             av_log(log_ctx, AV_LOG_ERROR,
     511             :                    "Invalid filterchain containing an unlabelled output pad: \"%s\"\n",
     512             :                    filters);
     513           0 :             ret = AVERROR(EINVAL);
     514           0 :             goto fail;
     515             :         }
     516           0 :         if (!(match = extract_inout(cur->name, &open_inputs)))
     517           0 :             continue;
     518           0 :         ret = avfilter_link(cur->filter_ctx,   cur->pad_idx,
     519           0 :                             match->filter_ctx, match->pad_idx);
     520           0 :         avfilter_inout_free(&match);
     521           0 :         if (ret < 0)
     522           0 :             goto fail;
     523             :     }
     524             : 
     525           0 :  fail:
     526           0 :     if (ret < 0) {
     527           0 :         while (graph->nb_filters)
     528           0 :             avfilter_free(graph->filters[0]);
     529           0 :         av_freep(&graph->filters);
     530             :     }
     531           0 :     avfilter_inout_free(&inputs);
     532           0 :     avfilter_inout_free(&outputs);
     533           0 :     avfilter_inout_free(&open_inputs);
     534           0 :     avfilter_inout_free(&open_outputs);
     535           0 :     return ret;
     536             : }
     537             : 
     538          19 : int avfilter_graph_parse_ptr(AVFilterGraph *graph, const char *filters,
     539             :                          AVFilterInOut **open_inputs_ptr, AVFilterInOut **open_outputs_ptr,
     540             :                          void *log_ctx)
     541             : {
     542          19 :     int index = 0, ret = 0;
     543          19 :     char chr = 0;
     544             : 
     545          19 :     AVFilterInOut *curr_inputs = NULL;
     546          19 :     AVFilterInOut *open_inputs  = open_inputs_ptr  ? *open_inputs_ptr  : NULL;
     547          19 :     AVFilterInOut *open_outputs = open_outputs_ptr ? *open_outputs_ptr : NULL;
     548             : 
     549          19 :     if ((ret = parse_sws_flags(&filters, graph)) < 0)
     550           0 :         goto end;
     551             : 
     552             :     do {
     553             :         AVFilterContext *filter;
     554          33 :         const char *filterchain = filters;
     555          33 :         filters += strspn(filters, WHITESPACES);
     556             : 
     557          33 :         if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx)) < 0)
     558           0 :             goto end;
     559             : 
     560          33 :         if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0)
     561           0 :             goto end;
     562             : 
     563          33 :         if (filter->nb_inputs == 1 && !curr_inputs && !index) {
     564             :             /* First input pad, assume it is "[in]" if not specified */
     565           0 :             const char *tmp = "[in]";
     566           0 :             if ((ret = parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx)) < 0)
     567           0 :                 goto end;
     568             :         }
     569             : 
     570          33 :         if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx)) < 0)
     571           0 :             goto end;
     572             : 
     573          33 :         if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs,
     574             :                                  log_ctx)) < 0)
     575           0 :             goto end;
     576             : 
     577          33 :         filters += strspn(filters, WHITESPACES);
     578          33 :         chr = *filters++;
     579             : 
     580          33 :         if (chr == ';' && curr_inputs) {
     581           0 :             av_log(log_ctx, AV_LOG_ERROR,
     582             :                    "Invalid filterchain containing an unlabelled output pad: \"%s\"\n",
     583             :                    filterchain);
     584           0 :             ret = AVERROR(EINVAL);
     585           0 :             goto end;
     586             :         }
     587          33 :         index++;
     588          56 :     } while (chr == ',' || chr == ';');
     589             : 
     590          19 :     if (chr) {
     591           0 :         av_log(log_ctx, AV_LOG_ERROR,
     592             :                "Unable to parse graph description substring: \"%s\"\n",
     593             :                filters - 1);
     594           0 :         ret = AVERROR(EINVAL);
     595           0 :         goto end;
     596             :     }
     597             : 
     598          19 :     if (curr_inputs) {
     599             :         /* Last output pad, assume it is "[out]" if not specified */
     600          15 :         const char *tmp = "[out]";
     601          15 :         if ((ret = parse_outputs(&tmp, &curr_inputs, &open_inputs, &open_outputs,
     602             :                                  log_ctx)) < 0)
     603           0 :             goto end;
     604             :     }
     605             : 
     606          23 : end:
     607             :     /* clear open_in/outputs only if not passed as parameters */
     608          19 :     if (open_inputs_ptr) *open_inputs_ptr = open_inputs;
     609           0 :     else avfilter_inout_free(&open_inputs);
     610          19 :     if (open_outputs_ptr) *open_outputs_ptr = open_outputs;
     611           0 :     else avfilter_inout_free(&open_outputs);
     612          19 :     avfilter_inout_free(&curr_inputs);
     613             : 
     614          19 :     if (ret < 0) {
     615           0 :         while (graph->nb_filters)
     616           0 :             avfilter_free(graph->filters[0]);
     617           0 :         av_freep(&graph->filters);
     618             :     }
     619          19 :     return ret;
     620             : }

Generated by: LCOV version 1.13