static int create_sink(Stream *st, AVFilterGraph *graph, AVFilterContext *f, int idx) { enum AVMediaType type = avfilter_pad_get_type(f->output_pads, idx); const char *sink_name; int ret; switch (type) { case AVMEDIA_TYPE_VIDEO: sink_name = "buffersink"; break; case AVMEDIA_TYPE_AUDIO: sink_name = "abuffersink"; break; default: av_log(NULL, AV_LOG_ERROR, "Stream type not supported\n"); return AVERROR(EINVAL); } ret = avfilter_graph_create_filter(&st->sink, avfilter_get_by_name(sink_name), NULL, NULL, NULL, graph); if (ret < 0) return ret; ret = avfilter_link(f, idx, st->sink, 0); if (ret < 0) return ret; st->link = st->sink->inputs[0]; return 0; }
int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) { av_freep(&ofilter->name); DESCRIBE_FILTER_LINK(ofilter, out, 0); switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) { case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out); case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out); default: av_assert0(0); } }
static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter, AVFilterInOut *in) { av_freep(&ifilter->name); DESCRIBE_FILTER_LINK(ifilter, in, 1); switch (avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx)) { case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in); case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in); default: av_assert0(0); } }
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; }
int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) { av_freep(&ofilter->name); DESCRIBE_FILTER_LINK(ofilter, out, 0); if (!ofilter->ost) { av_log(NULL, AV_LOG_FATAL, "Filter %s has a unconnected output\n", ofilter->name); exit_program(1); } switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) { case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out); case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out); default: av_assert0(0); } }
int init_complex_filtergraph(FilterGraph *fg) { AVFilterInOut *inputs, *outputs, *cur; AVFilterGraph *graph; int ret = 0; /* this graph is only used for determining the kinds of inputs * and outputs we have, and is discarded on exit from this function */ graph = avfilter_graph_alloc(); if(!graph) { return AVERROR(ENOMEM); } ret = avfilter_graph_parse2(graph, fg->graph_desc, &inputs, &outputs); if(ret < 0) { goto fail; } for(cur = inputs; cur; cur = cur->next) { init_input_filter(fg, cur); } for(cur = outputs; cur;) { GROW_ARRAY(fg->outputs, fg->nb_outputs); fg->outputs[fg->nb_outputs - 1] = av_mallocz(sizeof(*fg->outputs[0])); if(!fg->outputs[fg->nb_outputs - 1]) { exit_program(1); } fg->outputs[fg->nb_outputs - 1]->graph = fg; fg->outputs[fg->nb_outputs - 1]->out_tmp = cur; fg->outputs[fg->nb_outputs - 1]->type = avfilter_pad_get_type(cur->filter_ctx->output_pads, cur->pad_idx); cur = cur->next; fg->outputs[fg->nb_outputs - 1]->out_tmp->next = NULL; } fail: avfilter_inout_free(&inputs); avfilter_graph_free(&graph); return ret; }
static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter, AVFilterInOut *in) { av_freep(&ifilter->name); DESCRIBE_FILTER_LINK(ifilter, in, 1); if (!ifilter->ist->dec) { av_log(NULL, AV_LOG_ERROR, "No decoder for stream #%d:%d, filtering impossible\n", ifilter->ist->file_index, ifilter->ist->st->index); return AVERROR_DECODER_NOT_FOUND; } switch (avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx)) { case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in); case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in); default: av_assert0(0); } }
static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) { InputStream *ist = NULL; enum AVMediaType type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx); int i; // TODO: support other filter types if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) { av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported " "currently.\n"); exit(1); } if (in->name) { AVFormatContext *s; AVStream *st = NULL; char *p; int file_idx = strtol(in->name, &p, 0); if (file_idx < 0 || file_idx >= nb_input_files) { av_log(NULL, AV_LOG_FATAL, "Invalid file index %d in filtegraph description %s.\n", file_idx, fg->graph_desc); exit(1); } s = input_files[file_idx]->ctx; for (i = 0; i < s->nb_streams; i++) { if (s->streams[i]->codec->codec_type != type) continue; if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) { st = s->streams[i]; break; } } if (!st) { av_log(NULL, AV_LOG_FATAL, "Stream specifier '%s' in filtergraph description %s " "matches no streams.\n", p, fg->graph_desc); exit(1); } ist = input_streams[input_files[file_idx]->ist_index + st->index]; } else { /* find the first unused stream of corresponding type */ for (i = 0; i < nb_input_streams; i++) { ist = input_streams[i]; if (ist->dec_ctx->codec_type == type && ist->discard) break; } if (i == nb_input_streams) { av_log(NULL, AV_LOG_FATAL, "Cannot find a matching stream for " "unlabeled input pad %d on filter %s\n", in->pad_idx, in->filter_ctx->name); exit(1); } } av_assert0(ist); ist->discard = 0; ist->decoding_needed = 1; ist->st->discard = AVDISCARD_NONE; GROW_ARRAY(fg->inputs, fg->nb_inputs); if (!(fg->inputs[fg->nb_inputs - 1] = av_mallocz(sizeof(*fg->inputs[0])))) exit(1); fg->inputs[fg->nb_inputs - 1]->ist = ist; fg->inputs[fg->nb_inputs - 1]->graph = fg; GROW_ARRAY(ist->filters, ist->nb_filters); ist->filters[ist->nb_filters - 1] = fg->inputs[fg->nb_inputs - 1]; }
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; }
static void add_pad(struct lavfi *c, int dir, int index, AVFilterContext *filter, int filter_pad, const char *name, bool first_init) { if (c->failed) return; enum AVMediaType avmt; if (dir == MP_PIN_IN) { avmt = avfilter_pad_get_type(filter->input_pads, filter_pad); } else { avmt = avfilter_pad_get_type(filter->output_pads, filter_pad); } int type; switch (avmt) { case AVMEDIA_TYPE_VIDEO: type = MP_FRAME_VIDEO; break; case AVMEDIA_TYPE_AUDIO: type = MP_FRAME_AUDIO; break; default: MP_FATAL(c, "unknown media type\n"); c->failed = true; return; } // For anonymous pads, just make something up. libavfilter allows duplicate // pad names (while we don't), so we check for collisions along with normal // duplicate pads below. char tmp[80]; const char *dir_string = dir == MP_PIN_IN ? "in" : "out"; if (name) { if (c->direct_filter) { // libavfilter has this very unpleasant thing that filter labels // don't have to be unique - in particular, both input and output // are usually named "default". With direct filters, the user has // no chance to provide better names, so do something to resolve it. snprintf(tmp, sizeof(tmp), "%s_%s", name, dir_string); name = tmp; } } else { snprintf(tmp, sizeof(tmp), "%s%d", dir_string, index); name = tmp; } struct lavfi_pad *p = NULL; for (int n = 0; n < c->num_all_pads; n++) { if (strcmp(c->all_pads[n]->name, name) == 0) { p = c->all_pads[n]; break; } } if (p) { // Graph recreation case: reassociate an existing pad. if (p->filter) { // Collision due to duplicate names. MP_FATAL(c, "more than one pad with label '%s'\n", name); c->failed = true; return; } if (p->dir != dir || p->type != type) { // libavfilter graph parser behavior not deterministic. MP_FATAL(c, "pad '%s' changed type or direction\n", name); c->failed = true; return; } } else { if (!first_init) { MP_FATAL(c, "filter pad '%s' got added later?\n", name); c->failed = true; return; } p = talloc_zero(c, struct lavfi_pad); p->main = c; p->dir = dir; p->name = talloc_strdup(p, name); p->type = type; p->pin_index = -1; p->metadata = talloc_zero(p, struct mp_tags); if (p->dir == MP_PIN_IN) MP_TARRAY_APPEND(c, c->in_pads, c->num_in_pads, p); if (p->dir == MP_PIN_OUT) MP_TARRAY_APPEND(c, c->out_pads, c->num_out_pads, p); MP_TARRAY_APPEND(c, c->all_pads, c->num_all_pads, p); } p->filter = filter; p->filter_pad = filter_pad; }