static int insert_trim(int64_t start_time, int64_t duration, AVFilterContext **last_filter, int *pad_idx, const char *filter_name) { AVFilterGraph *graph = (*last_filter)->graph; AVFilterContext *ctx; const AVFilter *trim; enum AVMediaType type = avfilter_pad_get_type((*last_filter)->output_pads, *pad_idx); const char *name = (type == AVMEDIA_TYPE_VIDEO) ? "trim" : "atrim"; int ret = 0; if (duration == INT64_MAX && start_time == AV_NOPTS_VALUE) return 0; trim = avfilter_get_by_name(name); if (!trim) { av_log(NULL, AV_LOG_ERROR, "%s filter not present, cannot limit " "recording time.\n", name); return AVERROR_FILTER_NOT_FOUND; } ctx = avfilter_graph_alloc_filter(graph, trim, filter_name); if (!ctx) return AVERROR(ENOMEM); if (duration != INT64_MAX) { ret = av_opt_set_double(ctx, "duration", (double)duration / 1e6, AV_OPT_SEARCH_CHILDREN); } if (ret >= 0 && start_time != AV_NOPTS_VALUE) { ret = av_opt_set_double(ctx, "start", (double)start_time / 1e6, AV_OPT_SEARCH_CHILDREN); } if (ret < 0) { av_log(ctx, AV_LOG_ERROR, "Error configuring the %s filter", name); return ret; } ret = avfilter_init_str(ctx, NULL); if (ret < 0) return ret; ret = avfilter_link(*last_filter, *pad_idx, ctx, 0); if (ret < 0) return ret; *last_filter = ctx; *pad_idx = 0; return 0; }
/** * Create an instance of a filter, initialize and insert it in the * filtergraph in *ctx. * * @param filt_ctx put here a filter context in case of successful creation and configuration, NULL otherwise. * @param ctx the filtergraph context * @param index an index which is supposed to be unique for each filter instance added to the filtergraph * @param filt_name the name of the filter to create * @param args the arguments provided to the filter during its initialization * @param log_ctx the log context to use * @return >= 0 in case of success, a negative AVERROR code otherwise */ static int create_filter(AVFilterContext **filt_ctx, AVFilterGraph *ctx, int index, const char *filt_name, const char *args, void *log_ctx) { AVFilter *filt; char inst_name[30]; char *tmp_args = NULL; int ret; snprintf(inst_name, sizeof(inst_name), "Parsed_%s_%d", filt_name, index); filt = avfilter_get_by_name(filt_name); if (!filt) { av_log(log_ctx, AV_LOG_ERROR, "No such filter: '%s'\n", filt_name); return AVERROR(EINVAL); } *filt_ctx = avfilter_graph_alloc_filter(ctx, filt, inst_name); if (!*filt_ctx) { av_log(log_ctx, AV_LOG_ERROR, "Error creating filter '%s'\n", filt_name); return AVERROR(ENOMEM); } if (!strcmp(filt_name, "scale") && args && !strstr(args, "flags") && ctx->scale_sws_opts) { tmp_args = av_asprintf("%s:%s", args, ctx->scale_sws_opts); if (!tmp_args) return AVERROR(ENOMEM); args = tmp_args; } ret = avfilter_init_str(*filt_ctx, args); if (ret < 0) { av_log(log_ctx, AV_LOG_ERROR, "Error initializing filter '%s'", filt_name); if (args) av_log(log_ctx, AV_LOG_ERROR, " with args '%s'", args); av_log(log_ctx, AV_LOG_ERROR, "\n"); avfilter_free(*filt_ctx); *filt_ctx = NULL; } av_free(tmp_args); return ret; }
int avfilter_init_filter(AVFilterContext *filter, const char *args, void *opaque) { return avfilter_init_str(filter, args); }
int main(int argc, char **argv) { const AVFilter *filter; AVFilterContext *filter_ctx; AVFilterGraph *graph_ctx; const char *filter_name; const char *filter_args = NULL; int i; int ret = 0; av_log_set_level(AV_LOG_DEBUG); if (argc < 2) { fprintf(stderr, "Missing filter name as argument\n"); return 1; } filter_name = argv[1]; if (argc > 2) filter_args = argv[2]; /* allocate graph */ graph_ctx = avfilter_graph_alloc(); if (!graph_ctx) return 1; avfilter_register_all(); /* get a corresponding filter and open it */ if (!(filter = avfilter_get_by_name(filter_name))) { fprintf(stderr, "Unrecognized filter with name '%s'\n", filter_name); return 1; } /* open filter and add it to the graph */ if (!(filter_ctx = avfilter_graph_alloc_filter(graph_ctx, filter, filter_name))) { fprintf(stderr, "Impossible to open filter with name '%s'\n", filter_name); return 1; } if (avfilter_init_str(filter_ctx, filter_args) < 0) { fprintf(stderr, "Impossible to init filter '%s' with arguments '%s'\n", filter_name, filter_args); return 1; } /* create a link for each of the input pads */ for (i = 0; i < filter_ctx->nb_inputs; i++) { AVFilterLink *link = av_mallocz(sizeof(AVFilterLink)); if (!link) { fprintf(stderr, "Unable to allocate memory for filter input link\n"); ret = 1; goto fail; } link->type = avfilter_pad_get_type(filter_ctx->input_pads, i); filter_ctx->inputs[i] = link; } for (i = 0; i < filter_ctx->nb_outputs; i++) { AVFilterLink *link = av_mallocz(sizeof(AVFilterLink)); if (!link) { fprintf(stderr, "Unable to allocate memory for filter output link\n"); ret = 1; goto fail; } link->type = avfilter_pad_get_type(filter_ctx->output_pads, i); filter_ctx->outputs[i] = link; } if (filter->query_formats) filter->query_formats(filter_ctx); else ff_default_query_formats(filter_ctx); print_formats(filter_ctx); fail: avfilter_free(filter_ctx); avfilter_graph_free(&graph_ctx); fflush(stdout); return ret; }
// Attempt to initialize all pads. Return true if all are initialized, or // false if more data is needed (or on error). static bool init_pads(struct lavfi *c) { if (!c->graph) goto error; for (int n = 0; n < c->num_out_pads; n++) { struct lavfi_pad *pad = c->out_pads[n]; if (pad->buffer) continue; const AVFilter *dst_filter = NULL; if (pad->type == MP_FRAME_AUDIO) { dst_filter = avfilter_get_by_name("abuffersink"); } else if (pad->type == MP_FRAME_VIDEO) { dst_filter = avfilter_get_by_name("buffersink"); } else { assert(0); } if (!dst_filter) goto error; char name[256]; snprintf(name, sizeof(name), "mpv_sink_%s", pad->name); if (avfilter_graph_create_filter(&pad->buffer, dst_filter, name, NULL, NULL, c->graph) < 0) goto error; if (avfilter_link(pad->filter, pad->filter_pad, pad->buffer, 0) < 0) goto error; } for (int n = 0; n < c->num_in_pads; n++) { struct lavfi_pad *pad = c->in_pads[n]; if (pad->buffer) continue; mp_frame_unref(&pad->in_fmt); read_pad_input(c, pad); // no input data, format unknown, can't init, wait longer. if (!pad->pending.type) return false; if (mp_frame_is_data(pad->pending)) { assert(pad->pending.type == pad->type); pad->in_fmt = mp_frame_ref(pad->pending); if (!pad->in_fmt.type) goto error; if (pad->in_fmt.type == MP_FRAME_VIDEO) mp_image_unref_data(pad->in_fmt.data); if (pad->in_fmt.type == MP_FRAME_AUDIO) mp_aframe_unref_data(pad->in_fmt.data); } if (pad->pending.type == MP_FRAME_EOF && !pad->in_fmt.type) { // libavfilter makes this painful. Init it with a dummy config, // just so we can tell it the stream is EOF. if (pad->type == MP_FRAME_AUDIO) { struct mp_aframe *fmt = mp_aframe_create(); mp_aframe_set_format(fmt, AF_FORMAT_FLOAT); mp_aframe_set_chmap(fmt, &(struct mp_chmap)MP_CHMAP_INIT_STEREO); mp_aframe_set_rate(fmt, 48000); pad->in_fmt = (struct mp_frame){MP_FRAME_AUDIO, fmt}; } if (pad->type == MP_FRAME_VIDEO) { struct mp_image *fmt = talloc_zero(NULL, struct mp_image); mp_image_setfmt(fmt, IMGFMT_420P); mp_image_set_size(fmt, 64, 64); pad->in_fmt = (struct mp_frame){MP_FRAME_VIDEO, fmt}; } } if (pad->in_fmt.type != pad->type) goto error; AVBufferSrcParameters *params = av_buffersrc_parameters_alloc(); if (!params) goto error; pad->timebase = AV_TIME_BASE_Q; char *filter_name = NULL; if (pad->type == MP_FRAME_AUDIO) { struct mp_aframe *fmt = pad->in_fmt.data; params->format = af_to_avformat(mp_aframe_get_format(fmt)); params->sample_rate = mp_aframe_get_rate(fmt); struct mp_chmap chmap = {0}; mp_aframe_get_chmap(fmt, &chmap); params->channel_layout = mp_chmap_to_lavc(&chmap); pad->timebase = (AVRational){1, mp_aframe_get_rate(fmt)}; filter_name = "abuffer"; } else if (pad->type == MP_FRAME_VIDEO) { struct mp_image *fmt = pad->in_fmt.data; params->format = imgfmt2pixfmt(fmt->imgfmt); params->width = fmt->w; params->height = fmt->h; params->sample_aspect_ratio.num = fmt->params.p_w; params->sample_aspect_ratio.den = fmt->params.p_h; params->hw_frames_ctx = fmt->hwctx; params->frame_rate = av_d2q(fmt->nominal_fps, 1000000); filter_name = "buffer"; } else { assert(0); } params->time_base = pad->timebase; const AVFilter *filter = avfilter_get_by_name(filter_name); if (filter) { char name[256]; snprintf(name, sizeof(name), "mpv_src_%s", pad->name); pad->buffer = avfilter_graph_alloc_filter(c->graph, filter, name); } if (!pad->buffer) { av_free(params); goto error; } int ret = av_buffersrc_parameters_set(pad->buffer, params); av_free(params); if (ret < 0) goto error; if (avfilter_init_str(pad->buffer, NULL) < 0) goto error; if (avfilter_link(pad->buffer, 0, pad->filter, pad->filter_pad) < 0) goto error; } return true; error: MP_FATAL(c, "could not initialize filter pads\n"); c->failed = true; mp_filter_internal_mark_failed(c->f); return false; }
// Parse the user-provided filter graph, and populate the unlinked filter pads. static void precreate_graph(struct lavfi *c, bool first_init) { assert(!c->graph); c->failed = false; c->graph = avfilter_graph_alloc(); if (!c->graph) abort(); if (mp_set_avopts(c->log, c->graph, c->graph_opts) < 0) goto error; if (c->direct_filter) { AVFilterContext *filter = avfilter_graph_alloc_filter(c->graph, avfilter_get_by_name(c->graph_string), "filter"); if (!filter) { MP_FATAL(c, "filter '%s' not found or failed to allocate\n", c->graph_string); goto error; } if (mp_set_avopts_pos(c->log, filter, filter->priv, c->direct_filter_opts) < 0) goto error; if (avfilter_init_str(filter, NULL) < 0) { MP_FATAL(c, "filter failed to initialize\n"); goto error; } add_pads_direct(c, MP_PIN_IN, filter, filter->input_pads, filter->nb_inputs, first_init); add_pads_direct(c, MP_PIN_OUT, filter, filter->output_pads, filter->nb_outputs, first_init); } else { AVFilterInOut *in = NULL, *out = NULL; if (avfilter_graph_parse2(c->graph, c->graph_string, &in, &out) < 0) { MP_FATAL(c, "parsing the filter graph failed\n"); goto error; } add_pads(c, MP_PIN_IN, in, first_init); add_pads(c, MP_PIN_OUT, out, first_init); avfilter_inout_free(&in); avfilter_inout_free(&out); } for (int n = 0; n < c->num_all_pads; n++) c->failed |= !c->all_pads[n]->filter; if (c->failed) goto error; return; error: free_graph(c); c->failed = true; mp_filter_internal_mark_failed(c->f); return; }
static int init_filter_graph(AVFilterGraph **graph, AVFilterContext **src, AVFilterContext **sink) { AVFilterGraph *filter_graph; AVFilterContext *abuffer_ctx; AVFilter *abuffer; AVFilterContext *volume_ctx; AVFilter *volume; AVFilterContext *aformat_ctx; AVFilter *aformat; AVFilterContext *abuffersink_ctx; AVFilter *abuffersink; AVDictionary *options_dict = NULL; uint8_t options_str[1024]; uint8_t ch_layout[64]; int err; /* Create a new filtergraph, which will contain all the filters. */ filter_graph = avfilter_graph_alloc(); if (!filter_graph) { fprintf(stderr, "Unable to create filter graph.\n"); return AVERROR(ENOMEM); } /* Create the abuffer filter; * it will be used for feeding the data into the graph. */ abuffer = avfilter_get_by_name("abuffer"); if (!abuffer) { fprintf(stderr, "Could not find the abuffer filter.\n"); return AVERROR_FILTER_NOT_FOUND; } abuffer_ctx = avfilter_graph_alloc_filter(filter_graph, abuffer, "src"); if (!abuffer_ctx) { fprintf(stderr, "Could not allocate the abuffer instance.\n"); return AVERROR(ENOMEM); } /* Set the filter options through the AVOptions API. */ av_get_channel_layout_string(ch_layout, sizeof(ch_layout), 0, INPUT_CHANNEL_LAYOUT); av_opt_set (abuffer_ctx, "channel_layout", ch_layout, AV_OPT_SEARCH_CHILDREN); av_opt_set (abuffer_ctx, "sample_fmt", av_get_sample_fmt_name(INPUT_FORMAT), AV_OPT_SEARCH_CHILDREN); av_opt_set_q (abuffer_ctx, "time_base", (AVRational){ 1, INPUT_SAMPLERATE }, AV_OPT_SEARCH_CHILDREN); av_opt_set_int(abuffer_ctx, "sample_rate", INPUT_SAMPLERATE, AV_OPT_SEARCH_CHILDREN); /* Now initialize the filter; we pass NULL options, since we have already * set all the options above. */ err = avfilter_init_str(abuffer_ctx, NULL); if (err < 0) { fprintf(stderr, "Could not initialize the abuffer filter.\n"); return err; } /* Create volume filter. */ volume = avfilter_get_by_name("volume"); if (!volume) { fprintf(stderr, "Could not find the volume filter.\n"); return AVERROR_FILTER_NOT_FOUND; } volume_ctx = avfilter_graph_alloc_filter(filter_graph, volume, "volume"); if (!volume_ctx) { fprintf(stderr, "Could not allocate the volume instance.\n"); return AVERROR(ENOMEM); } /* A different way of passing the options is as key/value pairs in a * dictionary. */ av_dict_set(&options_dict, "volume", AV_STRINGIFY(VOLUME_VAL), 0); err = avfilter_init_dict(volume_ctx, &options_dict); av_dict_free(&options_dict); if (err < 0) { fprintf(stderr, "Could not initialize the volume filter.\n"); return err; } /* Create the aformat filter; * it ensures that the output is of the format we want. */ aformat = avfilter_get_by_name("aformat"); if (!aformat) { fprintf(stderr, "Could not find the aformat filter.\n"); return AVERROR_FILTER_NOT_FOUND; } aformat_ctx = avfilter_graph_alloc_filter(filter_graph, aformat, "aformat"); if (!aformat_ctx) { fprintf(stderr, "Could not allocate the aformat instance.\n"); return AVERROR(ENOMEM); } /* A third way of passing the options is in a string of the form * key1=value1:key2=value2.... */ snprintf(options_str, sizeof(options_str), "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%"PRIx64, av_get_sample_fmt_name(AV_SAMPLE_FMT_S16), 44100, (uint64_t)AV_CH_LAYOUT_STEREO); err = avfilter_init_str(aformat_ctx, options_str); if (err < 0) { av_log(NULL, AV_LOG_ERROR, "Could not initialize the aformat filter.\n"); return err; } /* Finally create the abuffersink filter; * it will be used to get the filtered data out of the graph. */ abuffersink = avfilter_get_by_name("abuffersink"); if (!abuffersink) { fprintf(stderr, "Could not find the abuffersink filter.\n"); return AVERROR_FILTER_NOT_FOUND; } abuffersink_ctx = avfilter_graph_alloc_filter(filter_graph, abuffersink, "sink"); if (!abuffersink_ctx) { fprintf(stderr, "Could not allocate the abuffersink instance.\n"); return AVERROR(ENOMEM); } /* This filter takes no options. */ err = avfilter_init_str(abuffersink_ctx, NULL); if (err < 0) { fprintf(stderr, "Could not initialize the abuffersink instance.\n"); return err; } /* Connect the filters; * in this simple case the filters just form a linear chain. */ err = avfilter_link(abuffer_ctx, 0, volume_ctx, 0); if (err >= 0) err = avfilter_link(volume_ctx, 0, aformat_ctx, 0); if (err >= 0) err = avfilter_link(aformat_ctx, 0, abuffersink_ctx, 0); if (err < 0) { fprintf(stderr, "Error connecting filters\n"); return err; } /* Configure the graph. */ err = avfilter_graph_config(filter_graph, NULL); if (err < 0) { av_log(NULL, AV_LOG_ERROR, "Error configuring the filter graph\n"); return err; } *graph = filter_graph; *src = abuffer_ctx; *sink = abuffersink_ctx; return 0; }