diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index c2ec7a4b5f..a6134de759 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -1118,6 +1118,242 @@ int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters,
                           AVFilterInOut **inputs,
                           AVFilterInOut **outputs);
 
+/**
+ * Parameters of a filter's input or output pad.
+ *
+ * Created as a child of AVFilterParams by avfilter_graph_segment_parse().
+ * Freed in avfilter_graph_segment_free().
+ */
+typedef struct AVFilterPadParams {
+    /**
+     * An av_malloc()'ed string containing the pad label.
+     *
+     * May be freed and set to NULL by the caller, in which case this pad will
+     * be treated as unlabeled for linking.
+     * May also be replaced by another av_malloc()'ed string.
+     */
+    char *label;
+} AVFilterPadParams;
+
+typedef struct AVFilterParams {
+    AVFilterContext     *filter;
+
+    char                *filter_name;
+    char                *instance_name;
+
+    AVDictionary        *opts;
+
+    AVFilterPadParams  **inputs;
+    unsigned          nb_inputs;
+
+    AVFilterPadParams  **outputs;
+    unsigned          nb_outputs;
+} AVFilterParams;
+
+/**
+ * A filterchain is a list of filter specifications.
+ *
+ * Created as a child of AVFilterGraphSegment by avfilter_graph_segment_parse().
+ * Freed in avfilter_graph_segment_free().
+ */
+typedef struct AVFilterChain {
+    AVFilterParams  **filters;
+    size_t         nb_filters;
+} AVFilterChain;
+
+/**
+ * A parsed representation of a filtergraph segment.
+ *
+ * A filtergraph segment is conceptually a list of filterchains, with some
+ * supplementary information (e.g. format conversion flags).
+ *
+ * Created by avfilter_graph_segment_parse(). Must be freed with
+ * avfilter_graph_segment_free().
+ */
+typedef struct AVFilterGraphSegment {
+    /**
+     * The filtergraph this segment is associated with.
+     * Set by avfilter_graph_segment_parse().
+     */
+    AVFilterGraph *graph;
+
+    /**
+     * A list of filter chain contained in this segment..
+     * Set in avfilter_graph_segment_parse().
+     */
+    AVFilterChain **chains;
+    size_t       nb_chains;
+
+    /**
+     * A string containing a colon-separated list of key=value options applied
+     * to all scale filters in this segment.
+     *
+     * May be set by avfilter_graph_segment_parse().
+     * The caller may free this string with av_free() and replace it with a
+     * different av_malloc()'ed string.
+     */
+    AVDictionary *scale_sws_opts;
+} AVFilterGraphSegment;
+
+/**
+ * Parse a textual filtergraph description into an intermediate form.
+ *
+ * This intermediate representation is intended to be modified by the caller as
+ * described in the documentation of AVFilterGraphSegment and its children, and
+ * then applied to the graph either manually or with other
+ * avfilter_graph_segment_*() functions. See the documentation for
+ * avfilter_graph_segment_apply() for the canonical way to apply
+ * AVFilterGraphSegment.
+ *
+ * @param graph Filter graph the parsed segment is associated with. Will only be
+ *              used for logging and similar auxiliary purposes. The graph will
+ *              not be actually modified by this function - the parsing results
+ *              are instead stored in seg for further processing.
+ * @param graph_str a string describing the filtergraph segment
+ * @param flags reserved for future use, caller must set to 0 for now
+ * @param seg A pointer to the newly-created AVFilterGraphSegment is written
+ *            here on success. The graph segment is owned by the caller and must
+ *            be freed with avfilter_graph_segment_free() before graph itself is
+ *            freed.
+ *
+ * @return a non-negative number on success, a negative error code on failure
+ */
+int avfilter_graph_segment_parse(AVFilterGraph *graph, const char *graph_str,
+                                 int flags, AVFilterGraphSegment **seg);
+
+/**
+ * Create filters specified in a graph segment.
+ *
+ * Walk through the creation-pending AVFilterParams in the segment and create
+ * new filter instances for them.
+ * Creation-pending params are those where AVFilterParams.filter_name is
+ * non-NULL (and hence AVFilterParams.filter is NULL). All other AVFilterParams
+ * instances are ignored.
+ *
+ * For any filter created by this function, the corresponding
+ * AVFilterParams.filter is set to the newly-created filter context,
+ * AVFilterParams.filter_name and AVFilterParams.instance_name are freed and set
+ * to NULL.
+ *
+ * @param seg the filtergraph segment to process
+ * @param flags reserved for future use, caller must set to 0 for now
+ *
+ * @return
+ * - a non-negative number if all creation-pending filters were successfully
+ *   created
+ * - AVERROR_FILTER_NOT_FOUND if some filter's name did not correspond to a
+ *   known filter
+ * - another negative error code on other failures
+ *
+ * @note Calling this function multiple times is safe, as it is idempotent.
+ */
+int avfilter_graph_segment_create_filters(AVFilterGraphSegment *seg, int flags);
+
+/**
+ * Apply parsed options to filter instances in a graph segment.
+ *
+ * Walk through all filter instances in the graph segment that have option
+ * dictionaries associated with them and apply those options with
+ * av_opt_set_dict2(..., AV_OPT_SEARCH_CHILDREN). AVFilterParams.opts is
+ * replaced by the dictionary output by av_opt_set_dict2(), which should be
+ * empty (NULL) if all options were successfully applied.
+ *
+ * If any options could not be found, this function will continue processing all
+ * other filters and finally return AVERROR_OPTION_NOT_FOUND (unless another
+ * error happens). The calling program may then deal with unapplied options as
+ * it wishes.
+ *
+ * Any creation-pending filters (see avfilter_graph_segment_create_filters())
+ * present in the segment will cause this function to fail. AVFilterParams with
+ * no associated filter context are simply skipped.
+ *
+ * @param seg the filtergraph segment to process
+ * @param flags reserved for future use, caller must set to 0 for now
+ *
+ * @return
+ * - a non-negative number if all options were successfully applied
+ * - AVERROR_OPTION_NOT_FOUND if some options were not found in a filter
+ * - another negative error code on other failures
+ *
+ * @note Calling this function multiple times is safe, as it is idempotent.
+ */
+int avfilter_graph_segment_apply_opts(AVFilterGraphSegment *seg, int flags);
+
+/**
+ * Initialize all filter instances in a graph segment.
+ *
+ * Walk through all filter instances in the graph segment and call
+ * avfilter_init_dict(..., NULL) on those that have not been initialized yet.
+ *
+ * Any creation-pending filters (see avfilter_graph_segment_create_filters())
+ * present in the segment will cause this function to fail. AVFilterParams with
+ * no associated filter context are simply skipped.
+ *
+ * @param seg the filtergraph segment to process
+ * @param flags reserved for future use, caller must set to 0 for now
+ *
+ * @return
+ * - a non-negative number if all filter instances were successfully initialized
+ * - a negative error code on other failures
+ *
+ * @note Calling this function multiple times is safe, as it is idempotent.
+ */
+int avfilter_graph_segment_init(AVFilterGraphSegment *seg, int flags);
+
+/**
+ * Link filters in a graph segment.
+ *
+ * Walk through all filter instances in the graph segment and call
+ * avfilter_init_dict(..., NULL) on those that have not been initialized yet.
+ *
+ * Any creation-pending filters (see avfilter_graph_segment_create_filters())
+ * present in the segment will cause this function to fail. AVFilterParams with
+ * no associated filter context are simply skipped.
+ *
+ * @param seg the filtergraph segment to process
+ * @param flags reserved for future use, caller must set to 0 for now
+ * @param[out] inputs  a linked list of all free (unlinked) inputs of the
+ *                     filters in this graph segment will be returned here. It
+ *                     is to be freed by the caller using avfilter_inout_free().
+ * @param[out] outputs a linked list of all free (unlinked) outputs of the
+ *                     filters in this graph segment will be returned here. It
+ *                     is to be freed by the caller using avfilter_inout_free().
+ *
+ * @return
+ * - a non-negative number if all filter instances were successfully initialized
+ * - a negative error code on other failures
+ *
+ * @note Calling this function multiple times is safe, as it is idempotent.
+ */
+int avfilter_graph_segment_link(AVFilterGraphSegment *seg, int flags,
+                                AVFilterInOut **inputs,
+                                AVFilterInOut **outputs);
+
+/**
+ * Apply all filter/link descriptions from a graph segment to the associated filtergraph.
+ *
+ * This functions is currently equivalent to calling the following in sequence:
+ * - avfilter_graph_segment_create_filters()
+ * - avfilter_graph_segment_apply_opts()
+ * - avfilter_graph_segment_init()
+ * - avfilter_graph_segment_link()
+ */
+int avfilter_graph_segment_apply(AVFilterGraphSegment *seg, int flags,
+                                 AVFilterInOut **inputs,
+                                 AVFilterInOut **outputs);
+
+/**
+ * Free the provided AVFilterGraphSegment and everything associated with it.
+ *
+ * @param seg double pointer to the AVFilterGraphSegment to be freed. NULL will
+ * be written to this pointer on exit from this function.
+ *
+ * @note
+ * The filter contexts (AVFilterParams.filter) are owned by AVFilterGraph rather
+ * than AVFilterGraphSegment, so they are not freed.
+ */
+void avfilter_graph_segment_free(AVFilterGraphSegment **seg);
+
 /**
  * Send a command to one or more filter instances.
  *
diff --git a/libavfilter/graphparser.c b/libavfilter/graphparser.c
index 0759c39014..07f56cabab 100644
--- a/libavfilter/graphparser.c
+++ b/libavfilter/graphparser.c
@@ -24,10 +24,12 @@
 #include <stdio.h>
 
 #include "libavutil/avstring.h"
+#include "libavutil/dict.h"
 #include "libavutil/mem.h"
 #include "libavutil/opt.h"
 
 #include "avfilter.h"
+#include "internal.h"
 
 #define WHITESPACES " \n\t\r"
 
@@ -386,7 +388,7 @@ static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs,
     return pad;
 }
 
-static int parse_sws_flags(const char **buf, AVFilterGraph *graph)
+static int parse_sws_flags(const char **buf, char **dst, void *log_ctx)
 {
     char *p = strchr(*buf, ';');
 
@@ -394,16 +396,16 @@ static int parse_sws_flags(const char **buf, AVFilterGraph *graph)
         return 0;
 
     if (!p) {
-        av_log(graph, AV_LOG_ERROR, "sws_flags not terminated with ';'.\n");
+        av_log(log_ctx, AV_LOG_ERROR, "sws_flags not terminated with ';'.\n");
         return AVERROR(EINVAL);
     }
 
     *buf += 4;  // keep the 'flags=' part
 
-    av_freep(&graph->scale_sws_opts);
-    if (!(graph->scale_sws_opts = av_mallocz(p - *buf + 1)))
+    av_freep(dst);
+    if (!(*dst = av_mallocz(p - *buf + 1)))
         return AVERROR(ENOMEM);
-    av_strlcpy(graph->scale_sws_opts, *buf, p - *buf + 1);
+    av_strlcpy(*dst, *buf, p - *buf + 1);
 
     *buf = p + 1;
     return 0;
@@ -420,7 +422,7 @@ int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters,
 
     filters += strspn(filters, WHITESPACES);
 
-    if ((ret = parse_sws_flags(&filters, graph)) < 0)
+    if ((ret = parse_sws_flags(&filters, &graph->scale_sws_opts, graph)) < 0)
         goto end;
 
     do {
@@ -551,7 +553,7 @@ int avfilter_graph_parse_ptr(AVFilterGraph *graph, const char *filters,
     AVFilterInOut *open_inputs  = open_inputs_ptr  ? *open_inputs_ptr  : NULL;
     AVFilterInOut *open_outputs = open_outputs_ptr ? *open_outputs_ptr : NULL;
 
-    if ((ret = parse_sws_flags(&filters, graph)) < 0)
+    if ((ret = parse_sws_flags(&filters, &graph->scale_sws_opts, graph)) < 0)
         goto end;
 
     do {
@@ -623,3 +625,590 @@ end:
     }
     return ret;
 }
+
+static void pad_params_free(AVFilterPadParams **pfpp)
+{
+    AVFilterPadParams *fpp = *pfpp;
+
+    if (!fpp)
+        return;
+
+    av_freep(&fpp->label);
+
+    av_freep(pfpp);
+}
+
+static void filter_params_free(AVFilterParams **pp)
+{
+    AVFilterParams *p = *pp;
+
+    if (!p)
+        return;
+
+    for (unsigned i = 0; i < p->nb_inputs; i++)
+        pad_params_free(&p->inputs[i]);
+    av_freep(&p->inputs);
+
+    for (unsigned i = 0; i < p->nb_outputs; i++)
+        pad_params_free(&p->outputs[i]);
+    av_freep(&p->outputs);
+
+    av_dict_free(&p->opts);
+
+    av_freep(&p->filter_name);
+    av_freep(&p->instance_name);
+
+    av_freep(pp);
+}
+
+static void chain_free(AVFilterChain **pch)
+{
+    AVFilterChain *ch = *pch;
+
+    if (!ch)
+        return;
+
+    for (size_t i = 0; i < ch->nb_filters; i++)
+        filter_params_free(&ch->filters[i]);
+    av_freep(&ch->filters);
+
+    av_freep(pch);
+}
+
+void avfilter_graph_segment_free(AVFilterGraphSegment **pseg)
+{
+    AVFilterGraphSegment *seg = *pseg;
+
+    if (!seg)
+        return;
+
+    for (size_t i = 0; i < seg->nb_chains; i++)
+        chain_free(&seg->chains[i]);
+    av_freep(&seg->chains);
+
+    av_freep(&seg->scale_sws_opts);
+
+    av_freep(pseg);
+}
+
+static int linklabels_parse(void *logctx, const char **linklabels,
+                            AVFilterPadParams ***res, unsigned *nb_res)
+{
+    AVFilterPadParams **pp = NULL;
+    unsigned nb = 0;
+    int ret;
+
+    while (**linklabels == '[') {
+        char *label;
+        AVFilterPadParams *par, **tmp;
+
+        label = parse_link_name(linklabels, logctx);
+        if (!label) {
+            ret = AVERROR(EINVAL);
+            goto fail;
+        }
+
+        par = av_mallocz(sizeof(*par));
+        if (!par) {
+            av_freep(&label);
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        par->label = label;
+
+        tmp = av_realloc_array(pp, nb + 1, sizeof(*pp));
+        if (!tmp) {
+            pad_params_free(&par);
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        pp = tmp;
+        pp[nb++] = par;
+
+        *linklabels += strspn(*linklabels, WHITESPACES);
+    }
+
+    *res    = pp;
+    *nb_res = nb;
+
+    return 0;
+fail:
+    for (unsigned i = 0; i < nb; i++)
+        pad_params_free(&pp[i]);
+    av_freep(&pp);
+    return ret;
+}
+
+static int filter_parse(void *logctx, const char **filter,
+                        AVFilterParams **pp)
+{
+    AVFilterParams *p;
+    char *inst_name;
+    int ret;
+
+    p = av_mallocz(sizeof(*p));
+    if (!p)
+        return AVERROR(ENOMEM);
+
+    ret = linklabels_parse(logctx, filter, &p->inputs, &p->nb_inputs);
+    if (ret < 0)
+        goto fail;
+
+    p->filter_name = av_get_token(filter, "=,;[");
+    if (!p->filter_name) {
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    inst_name = strchr(p->filter_name, '@');
+    if (inst_name) {
+        *inst_name++ = 0;
+        p->instance_name = av_strdup(inst_name);
+        if (!p->instance_name) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+    }
+
+    if (**filter == '=') {
+        const AVFilter *f = avfilter_get_by_name(p->filter_name);
+        char *opts;
+
+        (*filter)++;
+
+        opts = av_get_token(filter, "[],;");
+        if (!opts) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        ret = ff_filter_opt_parse(logctx, f ? f->priv_class : NULL,
+                                  &p->opts, opts);
+        av_freep(&opts);
+        if (ret < 0)
+            goto fail;
+    }
+
+    ret = linklabels_parse(logctx, filter, &p->outputs, &p->nb_outputs);
+    if (ret < 0)
+        goto fail;
+
+    *filter += strspn(*filter, WHITESPACES);
+
+    *pp = p;
+    return 0;
+fail:
+    av_log(logctx, AV_LOG_ERROR,
+           "Error parsing a filter description around: %s\n", *filter);
+    filter_params_free(&p);
+    return ret;
+}
+
+static int chain_parse(void *logctx, const char **pchain,
+                       AVFilterChain **pch)
+{
+    const char *chain = *pchain;
+    AVFilterChain *ch;
+    int ret;
+
+    *pch = NULL;
+
+    ch = av_mallocz(sizeof(*ch));
+    if (!ch)
+        return AVERROR(ENOMEM);
+
+    while (*chain) {
+        AVFilterParams *p, **tmp;
+        char chr;
+
+        ret = filter_parse(logctx, &chain, &p);
+        if (ret < 0)
+            goto fail;
+
+        tmp = av_realloc_array(ch->filters, ch->nb_filters + 1, sizeof(*ch->filters));
+        if (!tmp) {
+            filter_params_free(&p);
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        ch->filters = tmp;
+        ch->filters[ch->nb_filters++] = p;
+
+        // a filter ends with one of: , ; end-of-string
+        chr = *chain;
+        if (chr && chr != ',' && chr != ';') {
+            av_log(logctx, AV_LOG_ERROR,
+                   "Trailing garbage after a filter: %s\n", chain);
+            ret = AVERROR(EINVAL);
+            goto fail;
+        }
+
+        if (chr) {
+            chain++;
+            chain += strspn(chain, WHITESPACES);
+
+            if (chr == ';')
+                break;
+        }
+    }
+
+    *pchain = chain;
+    *pch    = ch;
+
+    return 0;
+fail:
+    av_log(logctx, AV_LOG_ERROR,
+           "Error parsing filterchain '%s' around: %s\n", *pchain, chain);
+    chain_free(&ch);
+    return ret;
+}
+
+int avfilter_graph_segment_parse(AVFilterGraph *graph, const char *graph_str,
+                                 int flags, AVFilterGraphSegment **pseg)
+{
+    AVFilterGraphSegment *seg;
+    char *scale_sws_opts;
+    int ret;
+
+    *pseg = NULL;
+
+    seg = av_mallocz(sizeof(*seg));
+    if (!seg)
+        return AVERROR(ENOMEM);
+
+    seg->graph = graph;
+
+    graph_str += strspn(graph_str, WHITESPACES);
+
+    ret = parse_sws_flags(&graph_str, &scale_sws_opts, &graph);
+    if (ret < 0)
+        goto fail;
+
+    ret = av_dict_parse_string(&seg->scale_sws_opts, scale_sws_opts, "=", ":", 0);
+    av_freep(&scale_sws_opts);
+    if (ret < 0)
+        goto fail;
+
+    graph_str += strspn(graph_str, WHITESPACES);
+
+    while (*graph_str) {
+        AVFilterChain *ch, **tmp;
+
+        ret = chain_parse(graph, &graph_str, &ch);
+        if (ret < 0)
+            goto fail;
+
+        tmp = av_realloc_array(seg->chains, seg->nb_chains + 1, sizeof(*seg->chains));
+        if (!tmp) {
+            chain_free(&ch);
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        seg->chains = tmp;
+        seg->chains[seg->nb_chains++] = ch;
+
+        graph_str += strspn(graph_str, WHITESPACES);
+    }
+
+    *pseg = seg;
+
+    return 0;
+fail:
+    avfilter_graph_segment_free(&seg);
+    return ret;
+}
+
+int avfilter_graph_segment_create_filters(AVFilterGraphSegment *seg, int flags)
+{
+    size_t idx = 0;
+
+    for (size_t i = 0; i < seg->nb_chains; i++) {
+        AVFilterChain *ch = seg->chains[i];
+
+        for (size_t j = 0; j < ch->nb_filters; j++) {
+            AVFilterParams *p = ch->filters[j];
+            const AVFilter *f = avfilter_get_by_name(p->filter_name);
+            char inst_name[30], *name = p->instance_name ? p->instance_name :
+                                                           inst_name;
+
+            // skip already processed filters
+            if (p->filter || !p->filter_name)
+                continue;
+
+            if (!f) {
+                av_log(seg->graph, AV_LOG_ERROR,
+                       "No such filter: '%s'\n", p->filter_name);
+                return AVERROR_FILTER_NOT_FOUND;
+            }
+
+            if (!p->instance_name)
+                snprintf(inst_name, sizeof(inst_name), "Parsed_%s_%zu", f->name, idx);
+
+            p->filter = avfilter_graph_alloc_filter(seg->graph, f, name);
+            if (!p->filter)
+                return AVERROR(ENOMEM);
+
+            if (!strcmp(f->name, "scale") && seg->scale_sws_opts) {
+                int ret = av_opt_set_dict2(p->filter, &seg->scale_sws_opts, AV_OPT_SEARCH_CHILDREN);
+                if (ret < 0) {
+                    avfilter_free(p->filter);
+                    p->filter = NULL;
+                    return ret;
+                }
+            }
+
+            av_freep(&p->filter_name);
+            av_freep(&p->instance_name);
+
+            idx++;
+        }
+    }
+
+    return 0;
+}
+
+int avfilter_graph_segment_apply_opts(AVFilterGraphSegment *seg, int flags)
+{
+    int leftover_opts = 0;
+
+    for (size_t i = 0; i < seg->nb_chains; i++) {
+        AVFilterChain *ch = seg->chains[i];
+
+        for (size_t j = 0; j < ch->nb_filters; j++) {
+            AVFilterParams *p = ch->filters[j];
+            int ret;
+
+            if (p->filter_name) {
+                av_log(seg->graph, AV_LOG_ERROR,
+                       "A creation-pending filter present "
+                       "in the segment. All filters must be created or cleared "
+                       "before calling avfilter_graph_segment_apply_opts().\n");
+                return AVERROR(EINVAL);
+            }
+            if (!p->filter || !p->opts)
+                continue;
+
+            ret = av_opt_set_dict2(p->filter, &p->opts, AV_OPT_SEARCH_CHILDREN);
+            if (ret < 0)
+                return ret;
+
+            if (av_dict_count(p->opts))
+                leftover_opts = 1;
+        }
+    }
+
+    return leftover_opts ? AVERROR_OPTION_NOT_FOUND : 0;
+}
+
+int avfilter_graph_segment_init(AVFilterGraphSegment *seg, int flags)
+{
+    for (size_t i = 0; i < seg->nb_chains; i++) {
+        AVFilterChain *ch = seg->chains[i];
+
+        for (size_t j = 0; j < ch->nb_filters; j++) {
+            AVFilterParams *p = ch->filters[j];
+            int ret;
+
+            if (p->filter_name) {
+                av_log(seg->graph, AV_LOG_ERROR,
+                       "A creation-pending filter present "
+                       "in the segment. All filters must be created or cleared "
+                       "before calling avfilter_graph_segment_init().\n");
+                return AVERROR(EINVAL);
+            }
+            if (!p->filter || p->filter->internal->initialized)
+                continue;
+
+            ret = avfilter_init_dict(p->filter, NULL);
+            if (ret < 0)
+                return ret;
+        }
+    }
+
+    return 0;
+}
+
+static unsigned
+find_linklabel(AVFilterGraphSegment *seg, const char *label,
+               int output, size_t idx_chain, size_t idx_filter,
+               AVFilterParams **pp)
+{
+    for (; idx_chain < seg->nb_chains; idx_chain++) {
+        AVFilterChain *ch = seg->chains[idx_chain];
+
+        for (; idx_filter < ch->nb_filters; idx_filter++) {
+            AVFilterParams *p = ch->filters[idx_filter];
+            AVFilterPadParams **io = output ? p->outputs    : p->inputs;
+            unsigned         nb_io = output ? p->nb_outputs : p->nb_inputs;
+
+            if (!p->filter)
+                continue;
+
+            for (unsigned i = 0; i < nb_io; i++)
+                if (io[i]->label && !strcmp(io[i]->label, label)) {
+                    *pp = p;
+                    return i;
+                }
+        }
+
+        idx_filter = 0;
+    }
+
+    *pp = NULL;
+    return 0;
+}
+
+int avfilter_graph_segment_link(AVFilterGraphSegment *seg, int flags,
+                                AVFilterInOut **inputs,
+                                AVFilterInOut **outputs)
+{
+    *inputs  = NULL;
+    *outputs = NULL;
+
+    for (size_t idx_chain = 0; idx_chain < seg->nb_chains; idx_chain++) {
+        AVFilterChain *ch = seg->chains[idx_chain];
+
+        for (size_t idx_filter = 0; idx_filter < ch->nb_filters; idx_filter++) {
+            AVFilterParams *p = ch->filters[idx_filter];
+            int ret;
+
+            if (p->filter_name) {
+                av_log(seg->graph, AV_LOG_ERROR,
+                       "A creation-pending filter present "
+                       "in the segment. All filters must be created or cleared "
+                       "before calling avfilter_graph_segment_init().\n");
+                return AVERROR(EINVAL);
+            }
+            if (!p->filter)
+                continue;
+
+            if (p->filter->nb_inputs < p->nb_inputs) {
+                av_log(seg->graph, AV_LOG_ERROR,
+                       "More input link labels specified for filter '%s' than "
+                       "it has inputs: %u > %d\n", p->filter->filter->name,
+                       p->nb_inputs, p->filter->nb_inputs);
+                return AVERROR(EINVAL);
+            }
+            for (unsigned in = 0; in < p->filter->nb_inputs; in++) {
+                AVFilterInOut *io;
+
+                // skip already linked inputs
+                if (p->filter->inputs[in])
+                    continue;
+
+                // handle explicit labels
+                if (in < p->nb_inputs && p->inputs[in]->label) {
+                    AVFilterParams *po = NULL;
+                    unsigned idx = find_linklabel(seg, p->inputs[in]->label, 1,
+                                                  idx_chain, idx_filter, &po);
+
+                    if (po) {
+                        ret = avfilter_link(po->filter, idx, p->filter, in);
+                        if (ret < 0)
+                            return ret;
+
+                        continue;
+                    }
+                }
+
+                io = av_mallocz(sizeof(*io));
+                if (!io)
+                    return AVERROR(ENOMEM);
+
+                io->filter_ctx = p->filter;
+                io->pad_idx    = in;
+                append_inout(inputs, &io);
+            }
+
+            if (p->filter->nb_outputs < p->nb_outputs) {
+                av_log(seg->graph, AV_LOG_ERROR,
+                       "More output link labels specified for filter '%s' than "
+                       "it has outputs: %u > %d\n", p->filter->filter->name,
+                       p->nb_outputs, p->filter->nb_outputs);
+                return AVERROR(EINVAL);
+            }
+            for (unsigned out = 0; out < p->filter->nb_outputs; out++) {
+                AVFilterInOut *io;
+
+                // skip already linked outputs
+                if (p->filter->outputs[out])
+                    continue;
+
+                // handle explicit labels
+                if (out < p->nb_outputs && p->outputs[out]->label) {
+                    AVFilterParams *po = NULL;
+                    unsigned idx = find_linklabel(seg, p->outputs[out]->label, 0,
+                                                  idx_chain, idx_filter, &po);
+
+                    if (po) {
+                        ret = avfilter_link(p->filter, out, po->filter, idx);
+                        if (ret < 0)
+                            return ret;
+
+                        continue;
+                    }
+                }
+
+                {
+                    // find next non-disabled filter in the chain
+                    // try linking unlabelled outputs to it
+                    AVFilterParams *p_next = NULL;
+                    for (size_t i = idx_filter + 1; i < ch->nb_filters; i++)
+                        if (ch->filters[i]->filter) {
+                            p_next = ch->filters[i];
+                            break;
+                        }
+
+                    if (p_next) {
+                        for (unsigned in = 0; in < p_next->filter->nb_inputs; in++) {
+                            if (!p_next->filter->inputs[in] &&
+                                (in > p_next->nb_inputs || !p_next->inputs[in]->label)) {
+                                ret = avfilter_link(p->filter, out, p_next->filter, in);
+                                if (ret < 0)
+                                    return ret;
+                            }
+                        }
+                    }
+                }
+
+                io = av_mallocz(sizeof(*io));
+                if (!io)
+                    return AVERROR(ENOMEM);
+
+                io->filter_ctx = p->filter;
+                io->pad_idx    = out;
+                append_inout(outputs, &io);
+            }
+        }
+    }
+    return 0;
+}
+
+int avfilter_graph_segment_apply(AVFilterGraphSegment *seg, int flags,
+                                 AVFilterInOut **inputs,
+                                 AVFilterInOut **outputs)
+{
+    int ret;
+
+    ret = avfilter_graph_segment_create_filters(seg, 0);
+    if (ret < 0)
+        return ret;
+
+    ret = avfilter_graph_segment_apply_opts(seg, 0);
+    if (ret < 0)
+        return ret;
+
+    ret = avfilter_graph_segment_init(seg, 0);
+    if (ret < 0)
+        return ret;
+
+    ret = avfilter_graph_segment_link(seg, 0, inputs, outputs);
+    if (ret < 0)
+        return ret;
+
+    return 0;
+}
