int avfilter_insert_filter(AVFilterLink *link, AVFilterContext *filt, unsigned filt_srcpad_idx, unsigned filt_dstpad_idx) { int ret; unsigned dstpad_idx = link->dstpad - link->dst->input_pads; av_log(link->dst, AV_LOG_VERBOSE, "auto-inserting filter '%s' " "between the filter '%s' and the filter '%s'\n", filt->name, link->src->name, link->dst->name); link->dst->inputs[dstpad_idx] = NULL; if ((ret = avfilter_link(filt, filt_dstpad_idx, link->dst, dstpad_idx)) < 0) { /* failed to link output filter to new filter */ link->dst->inputs[dstpad_idx] = link; return ret; } /* re-hookup the link to the new destination filter we inserted */ link->dst = filt; link->dstpad = &filt->input_pads[filt_srcpad_idx]; filt->inputs[filt_srcpad_idx] = link; /* if any information on supported media formats already exists on the * link, we need to preserve that */ if (link->out_formats) ff_formats_changeref(&link->out_formats, &filt->outputs[filt_dstpad_idx]->out_formats); if (link->out_samplerates) ff_formats_changeref(&link->out_samplerates, &filt->outputs[filt_dstpad_idx]->out_samplerates); if (link->out_channel_layouts) ff_channel_layouts_changeref(&link->out_channel_layouts, &filt->outputs[filt_dstpad_idx]->out_channel_layouts); return 0; }
int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, AVFilterInOut *open_inputs, AVFilterInOut *open_outputs, void *log_ctx) { int ret; AVFilterInOut *cur, *match, *inputs = NULL, *outputs = NULL; if ((ret = avfilter_graph_parse2(graph, filters, &inputs, &outputs)) < 0) goto fail; /* First input can be omitted if it is "[in]" */ if (inputs && !inputs->name) inputs->name = av_strdup("in"); for (cur = inputs; cur; cur = cur->next) { if (!cur->name) { av_log(log_ctx, AV_LOG_ERROR, "Not enough inputs specified for the \"%s\" filter.\n", cur->filter_ctx->filter->name); ret = AVERROR(EINVAL); goto fail; } if (!(match = extract_inout(cur->name, &open_outputs))) continue; ret = avfilter_link(match->filter_ctx, match->pad_idx, cur->filter_ctx, cur->pad_idx); avfilter_inout_free(&match); if (ret < 0) goto fail; } /* Last output can be omitted if it is "[out]" */ if (outputs && !outputs->name) outputs->name = av_strdup("out"); for (cur = outputs; cur; cur = cur->next) { if (!cur->name) { av_log(log_ctx, AV_LOG_ERROR, "Invalid filterchain containing an unlabelled output pad: \"%s\"\n", filters); ret = AVERROR(EINVAL); goto fail; } if (!(match = extract_inout(cur->name, &open_inputs))) continue; ret = avfilter_link(cur->filter_ctx, cur->pad_idx, match->filter_ctx, match->pad_idx); avfilter_inout_free(&match); if (ret < 0) goto fail; } fail: if (ret < 0) { for (; graph->filter_count > 0; graph->filter_count--) avfilter_free(graph->filters[graph->filter_count - 1]); av_freep(&graph->filters); } avfilter_inout_free(&inputs); avfilter_inout_free(&outputs); avfilter_inout_free(&open_inputs); avfilter_inout_free(&open_outputs); return ret; }
void MovieDecoder::initializeFilterGraph(const AVRational& timeBase, int size, bool maintainAspectRatio) { static const AVPixelFormat pixelFormats[] = { AV_PIX_FMT_BGRA, AV_PIX_FMT_NONE }; auto del = [] (AVBufferSinkParams* p) { av_freep(p); }; std::unique_ptr<AVBufferSinkParams, decltype(del)> buffersinkParams(av_buffersink_params_alloc(), del); m_pFilterGraph = avfilter_graph_alloc(); assert(m_pFilterGraph); std::stringstream ss; ss << "video_size=" << GetVideoWidth() << "x" << GetVideoHeight() << ":pix_fmt=" << m_pVideoCodecContext->pix_fmt << ":time_base=" << timeBase.num << "/" << timeBase.den << ":pixel_aspect=" << m_pVideoCodecContext->sample_aspect_ratio.num << "/" << FFMAX(m_pVideoCodecContext->sample_aspect_ratio.den, 1); checkRc(avfilter_graph_create_filter(&m_pFilterSource, avfilter_get_by_name("buffer"), "thumb_buffer", ss.str().c_str(), nullptr, m_pFilterGraph), "Failed to create filter source"); buffersinkParams->pixel_fmts = pixelFormats; checkRc(avfilter_graph_create_filter(&m_pFilterSink, avfilter_get_by_name("buffersink"), "thumb_buffersink", nullptr, buffersinkParams.get(), m_pFilterGraph), "Failed to create filter sink"); buffersinkParams.release(); AVFilterContext* yadifFilter = nullptr; if (m_pFrame->interlaced_frame != 0) { checkRc(avfilter_graph_create_filter(&yadifFilter, avfilter_get_by_name("yadif"), "thumb_deint", "deint=1", nullptr, m_pFilterGraph), "Failed to create deinterlace filter"); } AVFilterContext* scaleFilter = nullptr; checkRc(avfilter_graph_create_filter(&scaleFilter, avfilter_get_by_name("scale"), "thumb_scale", createScaleString(size, maintainAspectRatio).c_str(), nullptr, m_pFilterGraph), "Failed to create scale filter"); AVFilterContext* formatFilter = nullptr; checkRc(avfilter_graph_create_filter(&formatFilter, avfilter_get_by_name("format"), "thumb_format", "pix_fmts=bgra", nullptr, m_pFilterGraph), "Failed to create format filter"); AVFilterContext* rotateFilter = nullptr; auto rotation = getStreamRotation(); if (rotation != -1) { checkRc(avfilter_graph_create_filter(&rotateFilter, avfilter_get_by_name("transpose"), "thumb_rotate", to_string(rotation).c_str(), nullptr, m_pFilterGraph), "Failed to create rotate filter"); } checkRc(avfilter_link(rotateFilter ? rotateFilter : formatFilter, 0, m_pFilterSink, 0), "Failed to link final filter"); if (rotateFilter) { checkRc(avfilter_link(formatFilter, 0, rotateFilter, 0), "Failed to link format filter"); } checkRc(avfilter_link(scaleFilter, 0, formatFilter, 0), "Failed to link scale filter"); if (yadifFilter) { checkRc(avfilter_link(yadifFilter, 0, scaleFilter, 0), "Failed to link yadif filter"); } checkRc(avfilter_link(m_pFilterSource, 0, yadifFilter ? yadifFilter : scaleFilter, 0), "Failed to link source filter"); checkRc(avfilter_graph_config(m_pFilterGraph, nullptr), "Failed to configure filter graph"); }
// 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; }
/* setup filter chain */ static bool openFilter (player_t * const player) { /* filter setup */ char strbuf[256]; int ret = 0; AVCodecContext * const cctx = player->st->codec; if ((player->fgraph = avfilter_graph_alloc ()) == NULL) { softfail ("graph_alloc"); } /* abuffer */ AVRational time_base = player->st->time_base; /* Workaround for a bug in libav-11, which reports an invalid channel * layout mp3 files */ if (cctx->channel_layout == 0) { cctx->channel_layout = av_get_default_channel_layout (cctx->channels); } snprintf (strbuf, sizeof (strbuf), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%"PRIx64, time_base.num, time_base.den, cctx->sample_rate, av_get_sample_fmt_name (cctx->sample_fmt), cctx->channel_layout); if ((ret = avfilter_graph_create_filter (&player->fabuf, avfilter_get_by_name ("abuffer"), NULL, strbuf, NULL, player->fgraph)) < 0) { softfail ("create_filter abuffer"); } /* volume */ if ((ret = avfilter_graph_create_filter (&player->fvolume, avfilter_get_by_name ("volume"), NULL, "0dB", NULL, player->fgraph)) < 0) { softfail ("create_filter volume"); } /* aformat: convert float samples into something more usable */ AVFilterContext *fafmt = NULL; snprintf (strbuf, sizeof (strbuf), "sample_fmts=%s", av_get_sample_fmt_name (avformat)); if ((ret = avfilter_graph_create_filter (&fafmt, avfilter_get_by_name ("aformat"), NULL, strbuf, NULL, player->fgraph)) < 0) { softfail ("create_filter aformat"); } /* abuffersink */ if ((ret = avfilter_graph_create_filter (&player->fbufsink, avfilter_get_by_name ("abuffersink"), NULL, NULL, NULL, player->fgraph)) < 0) { softfail ("create_filter abuffersink"); } /* connect filter: abuffer -> volume -> aformat -> abuffersink */ if (avfilter_link (player->fabuf, 0, player->fvolume, 0) != 0 || avfilter_link (player->fvolume, 0, fafmt, 0) != 0 || avfilter_link (fafmt, 0, player->fbufsink, 0) != 0) { softfail ("filter_link"); } if ((ret = avfilter_graph_config (player->fgraph, NULL)) < 0) { softfail ("graph_config"); } return true; }
static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) { char *pix_fmts; OutputStream *ost = ofilter->ost; OutputFile *of = output_files[ost->file_index]; AVCodecContext *codec = ost->enc_ctx; AVFilterContext *last_filter = out->filter_ctx; int pad_idx = out->pad_idx; int ret; char name[255]; snprintf(name, sizeof(name), "output stream %d:%d", ost->file_index, ost->index); ret = avfilter_graph_create_filter(&ofilter->filter, avfilter_get_by_name("buffersink"), name, NULL, NULL, fg->graph); if(ret < 0) { return ret; } if(!hw_device_ctx && (codec->width || codec->height)) { char args[255]; AVFilterContext *filter; AVDictionaryEntry *e = NULL; snprintf(args, sizeof(args), "%d:%d", codec->width, codec->height); while((e = av_dict_get(ost->sws_dict, "", e, AV_DICT_IGNORE_SUFFIX))) { av_strlcatf(args, sizeof(args), ":%s=%s", e->key, e->value); } snprintf(name, sizeof(name), "scaler for output stream %d:%d", ost->file_index, ost->index); if((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"), name, args, NULL, fg->graph)) < 0) { return ret; } if((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) { return ret; } last_filter = filter; pad_idx = 0; } if((pix_fmts = choose_pix_fmts(ost))) { AVFilterContext *filter; snprintf(name, sizeof(name), "pixel format for output stream %d:%d", ost->file_index, ost->index); ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("format"), "format", pix_fmts, NULL, fg->graph); av_freep(&pix_fmts); if(ret < 0) { return ret; } if((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) { return ret; } last_filter = filter; pad_idx = 0; } if(ost->frame_rate.num && 0) { AVFilterContext *fps; char args[255]; snprintf(args, sizeof(args), "fps=%d/%d", ost->frame_rate.num, ost->frame_rate.den); snprintf(name, sizeof(name), "fps for output stream %d:%d", ost->file_index, ost->index); ret = avfilter_graph_create_filter(&fps, avfilter_get_by_name("fps"), name, args, NULL, fg->graph); if(ret < 0) { return ret; } ret = avfilter_link(last_filter, pad_idx, fps, 0); if(ret < 0) { return ret; } last_filter = fps; pad_idx = 0; } snprintf(name, sizeof(name), "trim for output stream %d:%d", ost->file_index, ost->index); ret = insert_trim(of->start_time, of->recording_time, &last_filter, &pad_idx, name); if(ret < 0) { return ret; } if((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0) { return ret; } return 0; }
static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, AVFilterInOut *in) { AVFilterContext *last_filter; const AVFilter *buffer_filt = avfilter_get_by_name("buffer"); InputStream *ist = ifilter->ist; InputFile *f = input_files[ist->file_index]; AVRational tb = ist->framerate.num ? av_inv_q(ist->framerate) : ist->st->time_base; AVRational sar; char args[255], name[255]; int ret, pad_idx = 0; sar = ist->st->sample_aspect_ratio.num ? ist->st->sample_aspect_ratio : ist->dec_ctx->sample_aspect_ratio; snprintf(args, sizeof(args), "width=%d:height=%d:pix_fmt=%d:time_base=%d/%d:sar=%d/%d", ist->dec_ctx->width, ist->dec_ctx->height, ist->hwaccel_retrieve_data ? ist->hwaccel_retrieved_pix_fmt : ist->dec_ctx->pix_fmt, tb.num, tb.den, sar.num, sar.den); snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index, ist->file_index, ist->st->index); if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name, args, NULL, fg->graph)) < 0) return ret; last_filter = ifilter->filter; if (ist->autorotate) { uint8_t* displaymatrix = av_stream_get_side_data(ist->st, AV_PKT_DATA_DISPLAYMATRIX, NULL); if (displaymatrix) { double rot = av_display_rotation_get((int32_t*) displaymatrix); if (rot < -135 || rot > 135) { ret = insert_filter(&last_filter, &pad_idx, "vflip", NULL); if (ret < 0) return ret; ret = insert_filter(&last_filter, &pad_idx, "hflip", NULL); } else if (rot < -45) { ret = insert_filter(&last_filter, &pad_idx, "transpose", "dir=clock"); } else if (rot > 45) { ret = insert_filter(&last_filter, &pad_idx, "transpose", "dir=cclock"); } if (ret < 0) return ret; } } if (ist->framerate.num) { AVFilterContext *setpts; snprintf(name, sizeof(name), "force CFR for input from stream %d:%d", ist->file_index, ist->st->index); if ((ret = avfilter_graph_create_filter(&setpts, avfilter_get_by_name("setpts"), name, "N", NULL, fg->graph)) < 0) return ret; if ((ret = avfilter_link(last_filter, 0, setpts, 0)) < 0) return ret; last_filter = setpts; } snprintf(name, sizeof(name), "trim for input stream %d:%d", ist->file_index, ist->st->index); ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ? AV_NOPTS_VALUE : 0, f->recording_time, &last_filter, &pad_idx, name); if (ret < 0) return ret; if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0) return ret; return 0; }
static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) { char *pix_fmts; OutputStream *ost = ofilter->ost; OutputFile *of = output_files[ost->file_index]; AVCodecContext *codec = ost->enc_ctx; AVFilterContext *last_filter = out->filter_ctx; int pad_idx = out->pad_idx; int ret; char name[255]; snprintf(name, sizeof(name), "output stream %d:%d", ost->file_index, ost->index); ret = avfilter_graph_create_filter(&ofilter->filter, avfilter_get_by_name("buffersink"), name, NULL, NULL, fg->graph); if (ret < 0) return ret; if (codec->width || codec->height) { char args[255]; AVFilterContext *filter; snprintf(args, sizeof(args), "%d:%d:0x%X", codec->width, codec->height, (unsigned)ost->sws_flags); snprintf(name, sizeof(name), "scaler for output stream %d:%d", ost->file_index, ost->index); if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"), name, args, NULL, fg->graph)) < 0) return ret; if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) return ret; last_filter = filter; pad_idx = 0; } if ((pix_fmts = choose_pix_fmts(ost))) { AVFilterContext *filter; snprintf(name, sizeof(name), "pixel format for output stream %d:%d", ost->file_index, ost->index); ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("format"), "format", pix_fmts, NULL, fg->graph); av_freep(&pix_fmts); if (ret < 0) return ret; if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) return ret; last_filter = filter; pad_idx = 0; } if (ost->frame_rate.num) { AVFilterContext *fps; char args[255]; snprintf(args, sizeof(args), "fps=%d/%d", ost->frame_rate.num, ost->frame_rate.den); snprintf(name, sizeof(name), "fps for output stream %d:%d", ost->file_index, ost->index); ret = avfilter_graph_create_filter(&fps, avfilter_get_by_name("fps"), name, args, NULL, fg->graph); if (ret < 0) return ret; ret = avfilter_link(last_filter, pad_idx, fps, 0); if (ret < 0) return ret; last_filter = fps; pad_idx = 0; } snprintf(name, sizeof(name), "trim for output stream %d:%d", ost->file_index, ost->index); ret = insert_trim(of->start_time, of->recording_time, &last_filter, &pad_idx, name); if (ret < 0) return ret; if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0) return ret; return 0; }
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; }
static int init_filters(const char *filters_descr) { char args[512]; int ret = 0; AVFilter *buffersrc = avfilter_get_by_name("buffer"); AVFilter *buffersink = avfilter_get_by_name("buffersink"); AVFilterInOut *outputs = avfilter_inout_alloc(); AVFilterInOut *inputs = avfilter_inout_alloc(); AVRational time_base = fmt_ctx->streams[video_stream_index]->time_base; enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE }; AVFilter *vdpau = avfilter_get_by_name("vdpau"); filter_graph = avfilter_graph_alloc(); if (!outputs || !inputs || !filter_graph) { ret = AVERROR(ENOMEM); goto end; } /* buffer video source: the decoded frames from the decoder will be inserted here. */ snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, time_base.num, time_base.den, dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den); ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, NULL, filter_graph); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n"); goto end; } /* buffer video sink: to terminate the filter chain. */ ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, NULL, filter_graph); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n"); goto end; } ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n"); goto end; } ret = avfilter_graph_create_filter(&vdpau_ctx, vdpau, "vdpau", NULL, NULL, filter_graph); vdpau_ctx->hw_device_ctx = device_ref; if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot create vdpau filter.\n"); goto end; } /* Connect the filters; * in this simple case the filters just form a linear chain. */ ret = avfilter_link(buffersrc_ctx, 0, vdpau_ctx, 0); if (ret >= 0) ret = avfilter_link(vdpau_ctx, 0, buffersink_ctx, 0); if (ret < 0) { fprintf(stderr, "Error connecting filters\n"); return ret; } if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) goto end; end: avfilter_inout_free(&inputs); avfilter_inout_free(&outputs); return ret; }
static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, AVFilterInOut *in) { AVFilterContext *last_filter; const AVFilter *abuffer_filt = avfilter_get_by_name("abuffer"); InputStream *ist = ifilter->ist; InputFile *f = input_files[ist->file_index]; AVBPrint args; char name[255]; int ret, pad_idx = 0; int64_t tsoffset = 0; if (ist->dec_ctx->codec_type != AVMEDIA_TYPE_AUDIO) { av_log(NULL, AV_LOG_ERROR, "Cannot connect audio filter to non audio input\n"); return AVERROR(EINVAL); } av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC); av_bprintf(&args, "time_base=%d/%d:sample_rate=%d:sample_fmt=%s", 1, ist->dec_ctx->sample_rate, ist->dec_ctx->sample_rate, av_get_sample_fmt_name(ist->dec_ctx->sample_fmt)); if (ist->dec_ctx->channel_layout) av_bprintf(&args, ":channel_layout=0x%"PRIx64, ist->dec_ctx->channel_layout); else av_bprintf(&args, ":channels=%d", ist->dec_ctx->channels); snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index, ist->file_index, ist->st->index); if ((ret = avfilter_graph_create_filter(&ifilter->filter, abuffer_filt, name, args.str, NULL, fg->graph)) < 0) return ret; last_filter = ifilter->filter; #define AUTO_INSERT_FILTER_INPUT(opt_name, filter_name, arg) do { \ AVFilterContext *filt_ctx; \ \ av_log(NULL, AV_LOG_INFO, opt_name " is forwarded to lavfi " \ "similarly to -af " filter_name "=%s.\n", arg); \ \ snprintf(name, sizeof(name), "graph %d %s for input stream %d:%d", \ fg->index, filter_name, ist->file_index, ist->st->index); \ ret = avfilter_graph_create_filter(&filt_ctx, \ avfilter_get_by_name(filter_name), \ name, arg, NULL, fg->graph); \ if (ret < 0) \ return ret; \ \ ret = avfilter_link(last_filter, 0, filt_ctx, 0); \ if (ret < 0) \ return ret; \ \ last_filter = filt_ctx; \ } while (0) if (audio_sync_method > 0) { char args[256] = {0}; av_strlcatf(args, sizeof(args), "async=%d", audio_sync_method); if (audio_drift_threshold != 0.1) av_strlcatf(args, sizeof(args), ":min_hard_comp=%f", audio_drift_threshold); if (!fg->reconfiguration) av_strlcatf(args, sizeof(args), ":first_pts=0"); AUTO_INSERT_FILTER_INPUT("-async", "aresample", args); } // if (ost->audio_channels_mapped) { // int i; // AVBPrint pan_buf; // av_bprint_init(&pan_buf, 256, 8192); // av_bprintf(&pan_buf, "0x%"PRIx64, // av_get_default_channel_layout(ost->audio_channels_mapped)); // for (i = 0; i < ost->audio_channels_mapped; i++) // if (ost->audio_channels_map[i] != -1) // av_bprintf(&pan_buf, ":c%d=c%d", i, ost->audio_channels_map[i]); // AUTO_INSERT_FILTER_INPUT("-map_channel", "pan", pan_buf.str); // av_bprint_finalize(&pan_buf, NULL); // } if (audio_volume != 256) { char args[256]; av_log(NULL, AV_LOG_WARNING, "-vol has been deprecated. Use the volume " "audio filter instead.\n"); snprintf(args, sizeof(args), "%f", audio_volume / 256.); AUTO_INSERT_FILTER_INPUT("-vol", "volume", args); } snprintf(name, sizeof(name), "trim for input stream %d:%d", ist->file_index, ist->st->index); if (copy_ts) { tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time; if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE) tsoffset += f->ctx->start_time; } ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ? AV_NOPTS_VALUE : tsoffset, f->recording_time, &last_filter, &pad_idx, name); if (ret < 0) return ret; if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0) return ret; return 0; }
static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, AVFilterInOut *in) { AVFilterContext *last_filter; const AVFilter *buffer_filt = avfilter_get_by_name("buffer"); InputStream *ist = ifilter->ist; InputFile *f = input_files[ist->file_index]; AVRational tb = ist->framerate.num ? av_inv_q(ist->framerate) : ist->st->time_base; AVRational fr = ist->framerate; AVRational sar; AVBPrint args; char name[255]; int ret, pad_idx = 0; int64_t tsoffset = 0; if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { av_log(NULL, AV_LOG_ERROR, "Cannot connect video filter to audio input\n"); return AVERROR(EINVAL); } if (!fr.num) fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL); if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { ret = sub2video_prepare(ist); if (ret < 0) return ret; } sar = ist->st->sample_aspect_ratio.num ? ist->st->sample_aspect_ratio : ist->dec_ctx->sample_aspect_ratio; if(!sar.den) sar = (AVRational){0,1}; av_bprint_init(&args, 0, 1); av_bprintf(&args, "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:" "pixel_aspect=%d/%d:sws_param=flags=%d", ist->resample_width, ist->resample_height, ist->hwaccel_retrieve_data ? ist->hwaccel_retrieved_pix_fmt : ist->resample_pix_fmt, tb.num, tb.den, sar.num, sar.den, SWS_BILINEAR + ((ist->dec_ctx->flags&AV_CODEC_FLAG_BITEXACT) ? SWS_BITEXACT:0)); if (fr.num && fr.den) av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den); snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index, ist->file_index, ist->st->index); if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name, args.str, NULL, fg->graph)) < 0) return ret; last_filter = ifilter->filter; if (ist->autorotate) { double theta = get_rotation(ist->st); if (fabs(theta - 90) < 1.0) { ret = insert_filter(&last_filter, &pad_idx, "transpose", "clock"); } else if (fabs(theta - 180) < 1.0) { ret = insert_filter(&last_filter, &pad_idx, "hflip", NULL); if (ret < 0) return ret; ret = insert_filter(&last_filter, &pad_idx, "vflip", NULL); } else if (fabs(theta - 270) < 1.0) { ret = insert_filter(&last_filter, &pad_idx, "transpose", "cclock"); } else if (fabs(theta) > 1.0) { char rotate_buf[64]; snprintf(rotate_buf, sizeof(rotate_buf), "%f*PI/180", theta); ret = insert_filter(&last_filter, &pad_idx, "rotate", rotate_buf); } if (ret < 0) return ret; } if (ist->framerate.num) { AVFilterContext *setpts; snprintf(name, sizeof(name), "force CFR for input from stream %d:%d", ist->file_index, ist->st->index); if ((ret = avfilter_graph_create_filter(&setpts, avfilter_get_by_name("setpts"), name, "N", NULL, fg->graph)) < 0) return ret; if ((ret = avfilter_link(last_filter, 0, setpts, 0)) < 0) return ret; last_filter = setpts; } if (do_deinterlace) { AVFilterContext *yadif; snprintf(name, sizeof(name), "deinterlace input from stream %d:%d", ist->file_index, ist->st->index); if ((ret = avfilter_graph_create_filter(&yadif, avfilter_get_by_name("yadif"), name, "", NULL, fg->graph)) < 0) return ret; if ((ret = avfilter_link(last_filter, 0, yadif, 0)) < 0) return ret; last_filter = yadif; } snprintf(name, sizeof(name), "trim for input stream %d:%d", ist->file_index, ist->st->index); if (copy_ts) { tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time; if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE) tsoffset += f->ctx->start_time; } ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ? AV_NOPTS_VALUE : tsoffset, f->recording_time, &last_filter, &pad_idx, name); if (ret < 0) return ret; if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0) return ret; return 0; }
static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) { OutputStream *ost = ofilter->ost; OutputFile *of = output_files[ost->file_index]; AVCodecContext *codec = ost->enc_ctx; AVFilterContext *last_filter = out->filter_ctx; int pad_idx = out->pad_idx; char *sample_fmts, *sample_rates, *channel_layouts; char name[255]; int ret; snprintf(name, sizeof(name), "output stream %d:%d", ost->file_index, ost->index); ret = avfilter_graph_create_filter(&ofilter->filter, avfilter_get_by_name("abuffersink"), name, NULL, NULL, fg->graph); if (ret < 0) return ret; if ((ret = av_opt_set_int(ofilter->filter, "all_channel_counts", 1, AV_OPT_SEARCH_CHILDREN)) < 0) return ret; #define AUTO_INSERT_FILTER(opt_name, filter_name, arg) do { \ AVFilterContext *filt_ctx; \ \ av_log(NULL, AV_LOG_INFO, opt_name " is forwarded to lavfi " \ "similarly to -af " filter_name "=%s.\n", arg); \ \ ret = avfilter_graph_create_filter(&filt_ctx, \ avfilter_get_by_name(filter_name), \ filter_name, arg, NULL, fg->graph); \ if (ret < 0) \ return ret; \ \ ret = avfilter_link(last_filter, pad_idx, filt_ctx, 0); \ if (ret < 0) \ return ret; \ \ last_filter = filt_ctx; \ pad_idx = 0; \ } while (0) if (ost->audio_channels_mapped) { int i; AVBPrint pan_buf; av_bprint_init(&pan_buf, 256, 8192); av_bprintf(&pan_buf, "0x%"PRIx64, av_get_default_channel_layout(ost->audio_channels_mapped)); for (i = 0; i < ost->audio_channels_mapped; i++) if (ost->audio_channels_map[i] != -1) av_bprintf(&pan_buf, "|c%d=c%d", i, ost->audio_channels_map[i]); AUTO_INSERT_FILTER("-map_channel", "pan", pan_buf.str); av_bprint_finalize(&pan_buf, NULL); } if (codec->channels && !codec->channel_layout) codec->channel_layout = av_get_default_channel_layout(codec->channels); sample_fmts = choose_sample_fmts(ost); sample_rates = choose_sample_rates(ost); channel_layouts = choose_channel_layouts(ost); if (sample_fmts || sample_rates || channel_layouts) { AVFilterContext *format; char args[256]; args[0] = 0; if (sample_fmts) av_strlcatf(args, sizeof(args), "sample_fmts=%s:", sample_fmts); if (sample_rates) av_strlcatf(args, sizeof(args), "sample_rates=%s:", sample_rates); if (channel_layouts) av_strlcatf(args, sizeof(args), "channel_layouts=%s:", channel_layouts); av_freep(&sample_fmts); av_freep(&sample_rates); av_freep(&channel_layouts); snprintf(name, sizeof(name), "audio format for output stream %d:%d", ost->file_index, ost->index); ret = avfilter_graph_create_filter(&format, avfilter_get_by_name("aformat"), name, args, NULL, fg->graph); if (ret < 0) return ret; ret = avfilter_link(last_filter, pad_idx, format, 0); if (ret < 0) return ret; last_filter = format; pad_idx = 0; } if (audio_volume != 256 && 0) { char args[256]; snprintf(args, sizeof(args), "%f", audio_volume / 256.); AUTO_INSERT_FILTER("-vol", "volume", args); } if (ost->apad && of->shortest) { char args[256]; int i; for (i=0; i<of->ctx->nb_streams; i++) if (of->ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) break; if (i<of->ctx->nb_streams) { snprintf(args, sizeof(args), "%s", ost->apad); AUTO_INSERT_FILTER("-apad", "apad", args); } } snprintf(name, sizeof(name), "trim for output stream %d:%d", ost->file_index, ost->index); ret = insert_trim(of->start_time, of->recording_time, &last_filter, &pad_idx, name); if (ret < 0) return ret; if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0) return ret; return 0; }
int CDVDVideoCodecFFmpeg::FilterOpen(const std::string& filters, bool scale) { int result; if (m_pFilterGraph) FilterClose(); if (filters.empty() && !scale) return 0; if (m_pHardware) { CLog::Log(LOGWARNING, "CDVDVideoCodecFFmpeg::FilterOpen - skipped opening filters on hardware decode"); return 0; } if (!(m_pFilterGraph = avfilter_graph_alloc())) { CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - unable to alloc filter graph"); return -1; } AVFilter* srcFilter = avfilter_get_by_name("buffer"); AVFilter* outFilter = avfilter_get_by_name("buffersink"); // should be last filter in the graph for now std::string args = StringUtils::Format("%d:%d:%d:%d:%d:%d:%d", m_pCodecContext->width, m_pCodecContext->height, m_pCodecContext->pix_fmt, m_pCodecContext->time_base.num ? m_pCodecContext->time_base.num : 1, m_pCodecContext->time_base.num ? m_pCodecContext->time_base.den : 1, m_pCodecContext->sample_aspect_ratio.num != 0 ? m_pCodecContext->sample_aspect_ratio.num : 1, m_pCodecContext->sample_aspect_ratio.num != 0 ? m_pCodecContext->sample_aspect_ratio.den : 1); if ((result = avfilter_graph_create_filter(&m_pFilterIn, srcFilter, "src", args.c_str(), NULL, m_pFilterGraph)) < 0) { CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - avfilter_graph_create_filter: src"); return result; } if ((result = avfilter_graph_create_filter(&m_pFilterOut, outFilter, "out", NULL, NULL, m_pFilterGraph)) < 0) { CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - avfilter_graph_create_filter: out"); return result; } if ((result = av_opt_set_int_list(m_pFilterOut, "pix_fmts", &m_formats[0], AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) < 0) { CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - failed settings pix formats"); return result; } if (!filters.empty()) { AVFilterInOut* outputs = avfilter_inout_alloc(); AVFilterInOut* inputs = avfilter_inout_alloc(); outputs->name = av_strdup("in"); outputs->filter_ctx = m_pFilterIn; outputs->pad_idx = 0; outputs->next = nullptr; inputs->name = av_strdup("out"); inputs->filter_ctx = m_pFilterOut; inputs->pad_idx = 0; inputs->next = nullptr; if ((result = avfilter_graph_parse_ptr(m_pFilterGraph, (const char*)m_filters.c_str(), &inputs, &outputs, NULL)) < 0) { CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - avfilter_graph_parse"); return result; } avfilter_inout_free(&outputs); avfilter_inout_free(&inputs); } else { if ((result = avfilter_link(m_pFilterIn, 0, m_pFilterOut, 0)) < 0) { CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - avfilter_link"); return result; } } if ((result = avfilter_graph_config(m_pFilterGraph, nullptr)) < 0) { CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - avfilter_graph_config"); return result; } m_filterEof = false; return result; }
// abuffer -> volume -> asplit for each audio format // -> aformat -> abuffersink // if the volume gain is > 1.0, we use a compand filter instead // for soft limiting. static int init_filter_graph(struct GroovePlaylist *playlist, struct GrooveFile *file) { struct GroovePlaylistPrivate *p = (struct GroovePlaylistPrivate *) playlist; struct GrooveFilePrivate *f = (struct GrooveFilePrivate *) file; // destruct old graph avfilter_graph_free(&p->filter_graph); // create new graph p->filter_graph = avfilter_graph_alloc(); if (!p->filter_graph) { av_log(NULL, AV_LOG_ERROR, "unable to create filter graph: out of memory\n"); return -1; } AVFilter *abuffer = avfilter_get_by_name("abuffer"); AVFilter *volume = avfilter_get_by_name("volume"); AVFilter *compand = avfilter_get_by_name("compand"); AVFilter *asplit = avfilter_get_by_name("asplit"); AVFilter *aformat = avfilter_get_by_name("aformat"); AVFilter *abuffersink = avfilter_get_by_name("abuffersink"); int err; // create abuffer filter AVCodecContext *avctx = f->audio_st->codec; AVRational time_base = f->audio_st->time_base; snprintf(p->strbuf, sizeof(p->strbuf), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%"PRIx64, time_base.num, time_base.den, avctx->sample_rate, av_get_sample_fmt_name(avctx->sample_fmt), avctx->channel_layout); av_log(NULL, AV_LOG_INFO, "abuffer: %s\n", p->strbuf); // save these values so we can compare later and check // whether we have to reconstruct the graph p->in_sample_rate = avctx->sample_rate; p->in_channel_layout = avctx->channel_layout; p->in_sample_fmt = avctx->sample_fmt; p->in_time_base = time_base; err = avfilter_graph_create_filter(&p->abuffer_ctx, abuffer, NULL, p->strbuf, NULL, p->filter_graph); if (err < 0) { av_log(NULL, AV_LOG_ERROR, "error initializing abuffer filter\n"); return err; } // as we create filters, this points the next source to link to AVFilterContext *audio_src_ctx = p->abuffer_ctx; // save the volume value so we can compare later and check // whether we have to reconstruct the graph p->filter_volume = p->volume; // if volume is < 1.0, create volume filter // == 1.0, do not create a filter // > 1.0, create a compand filter (for soft limiting) double vol = p->volume; if (vol < 0.0) vol = 0.0; if (vol < 1.0) { snprintf(p->strbuf, sizeof(p->strbuf), "volume=%f", vol); av_log(NULL, AV_LOG_INFO, "volume: %s\n", p->strbuf); err = avfilter_graph_create_filter(&p->volume_ctx, volume, NULL, p->strbuf, NULL, p->filter_graph); if (err < 0) { av_log(NULL, AV_LOG_ERROR, "error initializing volume filter\n"); return err; } err = avfilter_link(audio_src_ctx, 0, p->volume_ctx, 0); if (err < 0) { av_log(NULL, AV_LOG_ERROR, "unable to link filters\n"); return err; } audio_src_ctx = p->volume_ctx; } else if (vol > 1.0) { double attack = 0.1; double decay = 0.2; const char *points = "-2/-2"; double soft_knee = 0.02; double gain = gain_to_dB(vol); double volume_param = 0.0; double delay = 0.2; snprintf(p->strbuf, sizeof(p->strbuf), "%f:%f:%s:%f:%f:%f:%f", attack, decay, points, soft_knee, gain, volume_param, delay); av_log(NULL, AV_LOG_INFO, "compand: %s\n", p->strbuf); err = avfilter_graph_create_filter(&p->compand_ctx, compand, NULL, p->strbuf, NULL, p->filter_graph); if (err < 0) { av_log(NULL, AV_LOG_ERROR, "error initializing compand filter\n"); return err; } err = avfilter_link(audio_src_ctx, 0, p->compand_ctx, 0); if (err < 0) { av_log(NULL, AV_LOG_ERROR, "unable to link filters\n"); return err; } audio_src_ctx = p->compand_ctx; } else { p->volume_ctx = NULL; } // if only one sink, no need for asplit if (p->sink_map_count < 2) { p->asplit_ctx = NULL; } else { snprintf(p->strbuf, sizeof(p->strbuf), "%d", p->sink_map_count); av_log(NULL, AV_LOG_INFO, "asplit: %s\n", p->strbuf); err = avfilter_graph_create_filter(&p->asplit_ctx, asplit, NULL, p->strbuf, NULL, p->filter_graph); if (err < 0) { av_log(NULL, AV_LOG_ERROR, "unable to create asplit filter\n"); return err; } err = avfilter_link(audio_src_ctx, 0, p->asplit_ctx, 0); if (err < 0) { av_log(NULL, AV_LOG_ERROR, "unable to link to asplit\n"); return err; } audio_src_ctx = p->asplit_ctx; } // for each audio format, create aformat and abuffersink filters struct SinkMap *map_item = p->sink_map; int pad_index = 0; while (map_item) { struct GrooveSink *example_sink = map_item->stack_head->sink; struct GrooveAudioFormat *audio_format = &example_sink->audio_format; AVFilterContext *inner_audio_src_ctx = audio_src_ctx; if (example_sink->disable_resample) { map_item->aformat_ctx = NULL; } else { // create aformat filter snprintf(p->strbuf, sizeof(p->strbuf), "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%"PRIx64, av_get_sample_fmt_name((enum AVSampleFormat)audio_format->sample_fmt), audio_format->sample_rate, audio_format->channel_layout); av_log(NULL, AV_LOG_INFO, "aformat: %s\n", p->strbuf); err = avfilter_graph_create_filter(&map_item->aformat_ctx, aformat, NULL, p->strbuf, NULL, p->filter_graph); if (err < 0) { av_strerror(err, p->strbuf, sizeof(p->strbuf)); av_log(NULL, AV_LOG_ERROR, "unable to create aformat filter: %s\n", p->strbuf); return err; } err = avfilter_link(audio_src_ctx, pad_index, map_item->aformat_ctx, 0); if (err < 0) { av_log(NULL, AV_LOG_ERROR, "unable to link filters\n"); return err; } inner_audio_src_ctx = map_item->aformat_ctx; } // create abuffersink filter err = avfilter_graph_create_filter(&map_item->abuffersink_ctx, abuffersink, NULL, NULL, NULL, p->filter_graph); if (err < 0) { av_log(NULL, AV_LOG_ERROR, "unable to create abuffersink filter\n"); return err; } err = avfilter_link(inner_audio_src_ctx, 0, map_item->abuffersink_ctx, 0); if (err < 0) { av_log(NULL, AV_LOG_ERROR, "unable to link filters\n"); return err; } pad_index += 1; map_item = map_item->next; } err = avfilter_graph_config(p->filter_graph, NULL); if (err < 0) { av_strerror(err, p->strbuf, sizeof(p->strbuf)); av_log(NULL, AV_LOG_ERROR, "error configuring the filter graph: %s\n", p->strbuf); return err; } p->rebuild_filter_graph_flag = 0; return 0; }
int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, AVFilterInOut *open_inputs, AVFilterInOut *open_outputs, void *log_ctx) { int ret; AVFilterInOut *cur, *match, *inputs = NULL, *outputs = NULL; if ((ret = avfilter_graph_parse2(graph, filters, &inputs, &outputs)) < 0) goto fail; /* First input can be omitted if it is "[in]" */ if (inputs && !inputs->name) inputs->name = av_strdup("in"); for (cur = inputs; cur; cur = cur->next) { if (!cur->name) { av_log(log_ctx, AV_LOG_ERROR, "Not enough inputs specified for the \"%s\" filter.\n", cur->filter_ctx->filter->name); ret = AVERROR(EINVAL); goto fail; } if (!(match = extract_inout(cur->name, &open_outputs))) continue; ret = avfilter_link(match->filter_ctx, match->pad_idx, cur->filter_ctx, cur->pad_idx); avfilter_inout_free(&match); if (ret < 0) goto fail; } /* Last output can be omitted if it is "[out]" */ if (outputs && !outputs->name) outputs->name = av_strdup("out"); for (cur = outputs; cur; cur = cur->next) { if (!cur->name) { av_log(log_ctx, AV_LOG_ERROR, "Invalid filterchain containing an unlabelled output pad: \"%s\"\n", filters); ret = AVERROR(EINVAL); goto fail; } if (!(match = extract_inout(cur->name, &open_inputs))) continue; ret = avfilter_link(cur->filter_ctx, cur->pad_idx, match->filter_ctx, match->pad_idx); avfilter_inout_free(&match); if (ret < 0) goto fail; } fail: if (ret < 0) { while (graph->nb_filters) avfilter_free(graph->filters[0]); av_freep(&graph->filters); } avfilter_inout_free(&inputs); avfilter_inout_free(&outputs); avfilter_inout_free(&open_inputs); avfilter_inout_free(&open_outputs); return ret; #else int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, AVFilterInOut **inputs, AVFilterInOut **outputs, void *log_ctx) { return avfilter_graph_parse_ptr(graph, filters, inputs, outputs, log_ctx); #endif } int avfilter_graph_parse_ptr(AVFilterGraph *graph, const char *filters, AVFilterInOut **open_inputs_ptr, AVFilterInOut **open_outputs_ptr, void *log_ctx) { int index = 0, ret = 0; char chr = 0; AVFilterInOut *curr_inputs = NULL; 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) goto end; do { AVFilterContext *filter; const char *filterchain = filters; filters += strspn(filters, WHITESPACES); if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx)) < 0) goto end; if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0) goto end; if (filter->nb_inputs == 1 && !curr_inputs && !index) { /* First input pad, assume it is "[in]" if not specified */ const char *tmp = "[in]"; if ((ret = parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx)) < 0) goto end; } if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx)) < 0) goto end; if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs, log_ctx)) < 0) goto end; filters += strspn(filters, WHITESPACES); chr = *filters++; if (chr == ';' && curr_inputs) { av_log(log_ctx, AV_LOG_ERROR, "Invalid filterchain containing an unlabelled output pad: \"%s\"\n", filterchain); ret = AVERROR(EINVAL); goto end; } index++; } while (chr == ',' || chr == ';'); if (chr) { av_log(log_ctx, AV_LOG_ERROR, "Unable to parse graph description substring: \"%s\"\n", filters - 1); ret = AVERROR(EINVAL); goto end; } if (curr_inputs) { /* Last output pad, assume it is "[out]" if not specified */ const char *tmp = "[out]"; if ((ret = parse_outputs(&tmp, &curr_inputs, &open_inputs, &open_outputs, log_ctx)) < 0) goto end; } end: /* clear open_in/outputs only if not passed as parameters */ if (open_inputs_ptr) *open_inputs_ptr = open_inputs; else avfilter_inout_free(&open_inputs); if (open_outputs_ptr) *open_outputs_ptr = open_outputs; else avfilter_inout_free(&open_outputs); avfilter_inout_free(&curr_inputs); if (ret < 0) { while (graph->nb_filters) avfilter_free(graph->filters[0]); av_freep(&graph->filters); } return ret; }
int mix_config(mix_t *mix, const format_t *format) { const char *err; char args[512]; if (format_eq(format, &mix->format)) return 0; mix_shutdown(mix); mix->format = *format; // filter graph err = "failed to alloc filter graph"; mix->graph = avfilter_graph_alloc(); if (!mix->graph) goto err; // amix err = "no amix filter available"; const AVFilter *flt = avfilter_get_by_name("amix"); if (!flt) goto err; snprintf(args, sizeof(args), "inputs=%lu", (unsigned long) NUM_INPUTS); err = "failed to create amix filter context"; if (avfilter_graph_create_filter(&mix->amix_ctx, flt, NULL, args, NULL, mix->graph)) goto err; // inputs err = "no abuffer filter available"; flt = avfilter_get_by_name("abuffer"); if (!flt) goto err; for (int i = 0; i < NUM_INPUTS; i++) { dbg("init input ctx %i", i); snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:" "channel_layout=0x%" PRIx64, 1, mix->format.clockrate, mix->format.clockrate, av_get_sample_fmt_name(mix->format.format), av_get_default_channel_layout(mix->format.channels)); err = "failed to create abuffer filter context"; if (avfilter_graph_create_filter(&mix->src_ctxs[i], flt, NULL, args, NULL, mix->graph)) goto err; err = "failed to link abuffer to amix"; if (avfilter_link(mix->src_ctxs[i], 0, mix->amix_ctx, i)) goto err; } // sink err = "no abuffersink filter available"; flt = avfilter_get_by_name("abuffersink"); if (!flt) goto err; err = "failed to create abuffersink filter context"; if (avfilter_graph_create_filter(&mix->sink_ctx, flt, NULL, NULL, NULL, mix->graph)) goto err; err = "failed to link amix to abuffersink"; if (avfilter_link(mix->amix_ctx, 0, mix->sink_ctx, 0)) goto err; // finish up err = "failed to configure filter chain"; if (avfilter_graph_config(mix->graph, NULL)) goto err; return 0; err: mix_shutdown(mix); ilog(LOG_ERR, "Failed to initialize mixer: %s", err); return -1; }
static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) { OutputStream *ost = ofilter->ost; OutputFile *of = output_files[ost->file_index]; AVCodecContext *codec = ost->enc_ctx; AVFilterContext *last_filter = out->filter_ctx; int pad_idx = out->pad_idx; char *sample_fmts, *sample_rates, *channel_layouts; char name[255]; int ret; snprintf(name, sizeof(name), "output stream %d:%d", ost->file_index, ost->index); ret = avfilter_graph_create_filter(&ofilter->filter, avfilter_get_by_name("abuffersink"), name, NULL, NULL, fg->graph); if (ret < 0) return ret; if (codec->channels && !codec->channel_layout) codec->channel_layout = av_get_default_channel_layout(codec->channels); sample_fmts = choose_sample_fmts(ost); sample_rates = choose_sample_rates(ost); channel_layouts = choose_channel_layouts(ost); if (sample_fmts || sample_rates || channel_layouts) { AVFilterContext *format; char args[256]; int len = 0; if (sample_fmts) len += snprintf(args + len, sizeof(args) - len, "sample_fmts=%s:", sample_fmts); if (sample_rates) len += snprintf(args + len, sizeof(args) - len, "sample_rates=%s:", sample_rates); if (channel_layouts) len += snprintf(args + len, sizeof(args) - len, "channel_layouts=%s:", channel_layouts); args[len - 1] = 0; av_freep(&sample_fmts); av_freep(&sample_rates); av_freep(&channel_layouts); snprintf(name, sizeof(name), "audio format for output stream %d:%d", ost->file_index, ost->index); ret = avfilter_graph_create_filter(&format, avfilter_get_by_name("aformat"), name, args, NULL, fg->graph); if (ret < 0) return ret; ret = avfilter_link(last_filter, pad_idx, format, 0); if (ret < 0) return ret; last_filter = format; pad_idx = 0; } snprintf(name, sizeof(name), "trim for output stream %d:%d", ost->file_index, ost->index); ret = insert_trim(of->start_time, of->recording_time, &last_filter, &pad_idx, name); if (ret < 0) return ret; if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0) return ret; return 0; }
static bool recreate_graph(struct af_instance *af, struct mp_audio *config) { void *tmp = talloc_new(NULL); struct priv *p = af->priv; AVFilterContext *in = NULL, *out = NULL, *f_format = NULL; if (bstr0(p->cfg_graph).len == 0) { MP_FATAL(af, "lavfi: no filter graph set\n"); return false; } destroy_graph(af); MP_VERBOSE(af, "lavfi: create graph: '%s'\n", p->cfg_graph); AVFilterGraph *graph = avfilter_graph_alloc(); if (!graph) goto error; if (mp_set_avopts(af->log, graph, p->cfg_avopts) < 0) goto error; AVFilterInOut *outputs = avfilter_inout_alloc(); AVFilterInOut *inputs = avfilter_inout_alloc(); if (!outputs || !inputs) goto error; // Build list of acceptable output sample formats. libavfilter will insert // conversion filters if needed. static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_U8P, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE }; char *fmtstr = talloc_strdup(tmp, ""); for (int n = 0; sample_fmts[n] != AV_SAMPLE_FMT_NONE; n++) { const char *name = av_get_sample_fmt_name(sample_fmts[n]); if (name) { const char *s = fmtstr[0] ? "|" : ""; fmtstr = talloc_asprintf_append_buffer(fmtstr, "%s%s", s, name); } } char *src_args = talloc_asprintf(tmp, "sample_rate=%d:sample_fmt=%s:time_base=%d/%d:" "channel_layout=0x%"PRIx64, config->rate, av_get_sample_fmt_name(af_to_avformat(config->format)), 1, config->rate, mp_chmap_to_lavc(&config->channels)); if (avfilter_graph_create_filter(&in, avfilter_get_by_name("abuffer"), "src", src_args, NULL, graph) < 0) goto error; if (avfilter_graph_create_filter(&out, avfilter_get_by_name("abuffersink"), "out", NULL, NULL, graph) < 0) goto error; if (avfilter_graph_create_filter(&f_format, avfilter_get_by_name("aformat"), "format", fmtstr, NULL, graph) < 0) goto error; if (avfilter_link(f_format, 0, out, 0) < 0) goto error; outputs->name = av_strdup("in"); outputs->filter_ctx = in; inputs->name = av_strdup("out"); inputs->filter_ctx = f_format; if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0) goto error; if (avfilter_graph_config(graph, NULL) < 0) goto error; p->in = in; p->out = out; p->graph = graph; assert(out->nb_inputs == 1); assert(in->nb_outputs == 1); talloc_free(tmp); return true; error: MP_FATAL(af, "Can't configure libavfilter graph.\n"); avfilter_graph_free(&graph); talloc_free(tmp); return false; }
static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, AVFilterInOut *in) { AVFilterContext *last_filter; const AVFilter *abuffer_filt = avfilter_get_by_name("abuffer"); InputStream *ist = ifilter->ist; InputFile *f = input_files[ist->file_index]; char args[255], name[255]; int ret, pad_idx = 0; snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s" ":channel_layout=0x%"PRIx64, 1, ist->dec_ctx->sample_rate, ist->dec_ctx->sample_rate, av_get_sample_fmt_name(ist->dec_ctx->sample_fmt), ist->dec_ctx->channel_layout); snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index, ist->file_index, ist->st->index); if ((ret = avfilter_graph_create_filter(&ifilter->filter, abuffer_filt, name, args, NULL, fg->graph)) < 0) return ret; last_filter = ifilter->filter; if (audio_sync_method > 0) { AVFilterContext *async; int len = 0; av_log(NULL, AV_LOG_WARNING, "-async has been deprecated. Used the " "asyncts audio filter instead.\n"); if (audio_sync_method > 1) len += snprintf(args + len, sizeof(args) - len, "compensate=1:" "max_comp=%d:", audio_sync_method); snprintf(args + len, sizeof(args) - len, "min_delta=%f", audio_drift_threshold); snprintf(name, sizeof(name), "graph %d audio sync for input stream %d:%d", fg->index, ist->file_index, ist->st->index); ret = avfilter_graph_create_filter(&async, avfilter_get_by_name("asyncts"), name, args, NULL, fg->graph); if (ret < 0) return ret; ret = avfilter_link(last_filter, 0, async, 0); if (ret < 0) return ret; last_filter = async; } if (audio_volume != 256) { AVFilterContext *volume; av_log(NULL, AV_LOG_WARNING, "-vol has been deprecated. Use the volume " "audio filter instead.\n"); snprintf(args, sizeof(args), "volume=%f", audio_volume / 256.0); snprintf(name, sizeof(name), "graph %d volume for input stream %d:%d", fg->index, ist->file_index, ist->st->index); ret = avfilter_graph_create_filter(&volume, avfilter_get_by_name("volume"), name, args, NULL, fg->graph); if (ret < 0) return ret; ret = avfilter_link(last_filter, 0, volume, 0); if (ret < 0) return ret; last_filter = volume; } snprintf(name, sizeof(name), "trim for input stream %d:%d", ist->file_index, ist->st->index); ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ? AV_NOPTS_VALUE : 0, f->recording_time, &last_filter, &pad_idx, name); if (ret < 0) return ret; if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0) return ret; return 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_int(ctx, "durationi", duration, AV_OPT_SEARCH_CHILDREN); } if(ret >= 0 && start_time != AV_NOPTS_VALUE) { ret = av_opt_set_int(ctx, "starti", start_time, 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; }