X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavfilter%2Favfiltergraph.c;h=0fc385c3a663a76aed96172b7390b5c096f6dc3c;hb=eac77fcd56fc2a3391f0d86faf54302afb368ff7;hp=95e0a219af53e3c5465144bb60ed3712dd9ee696;hpb=58b049f2fa4f192b00baadb5f1f32ca366f936ea;p=ffmpeg diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index 95e0a219af5..0fc385c3a66 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -20,70 +20,139 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include "config.h" + #include +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/channel_layout.h" +#include "libavutil/common.h" +#include "libavutil/internal.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" + #include "avfilter.h" -#include "avfiltergraph.h" #include "formats.h" #include "internal.h" - -#include "libavutil/audioconvert.h" -#include "libavutil/log.h" +#include "thread.h" + +#define OFFSET(x) offsetof(AVFilterGraph, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM +static const AVOption filtergraph_options[] = { + { "thread_type", "Allowed thread types", OFFSET(thread_type), AV_OPT_TYPE_FLAGS, + { .i64 = AVFILTER_THREAD_SLICE }, 0, INT_MAX, FLAGS, "thread_type" }, + { "slice", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AVFILTER_THREAD_SLICE }, .flags = FLAGS, .unit = "thread_type" }, + { "threads", "Maximum number of threads", OFFSET(nb_threads), + AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, + { NULL }, +}; static const AVClass filtergraph_class = { .class_name = "AVFilterGraph", .item_name = av_default_item_name, .version = LIBAVUTIL_VERSION_INT, + .option = filtergraph_options, }; +#if !HAVE_THREADS +void ff_graph_thread_free(AVFilterGraph *graph) +{ +} + +int ff_graph_thread_init(AVFilterGraph *graph) +{ + graph->thread_type = 0; + graph->nb_threads = 1; + return 0; +} +#endif + AVFilterGraph *avfilter_graph_alloc(void) { - AVFilterGraph *ret = av_mallocz(sizeof(AVFilterGraph)); + AVFilterGraph *ret = av_mallocz(sizeof(*ret)); if (!ret) return NULL; -#if FF_API_GRAPH_AVCLASS + + ret->internal = av_mallocz(sizeof(*ret->internal)); + if (!ret->internal) { + av_freep(&ret); + return NULL; + } + ret->av_class = &filtergraph_class; -#endif + av_opt_set_defaults(ret); + return ret; } +void ff_filter_graph_remove_filter(AVFilterGraph *graph, AVFilterContext *filter) +{ + int i; + for (i = 0; i < graph->nb_filters; i++) { + if (graph->filters[i] == filter) { + FFSWAP(AVFilterContext*, graph->filters[i], + graph->filters[graph->nb_filters - 1]); + graph->nb_filters--; + return; + } + } +} + void avfilter_graph_free(AVFilterGraph **graph) { if (!*graph) return; - for (; (*graph)->filter_count > 0; (*graph)->filter_count--) - avfilter_free((*graph)->filters[(*graph)->filter_count - 1]); + + while ((*graph)->nb_filters) + avfilter_free((*graph)->filters[0]); + + ff_graph_thread_free(*graph); + av_freep(&(*graph)->scale_sws_opts); + av_freep(&(*graph)->resample_lavr_opts); av_freep(&(*graph)->filters); + av_freep(&(*graph)->internal); av_freep(graph); } +#if FF_API_AVFILTER_OPEN int avfilter_graph_add_filter(AVFilterGraph *graph, AVFilterContext *filter) { AVFilterContext **filters = av_realloc(graph->filters, - sizeof(AVFilterContext*) * (graph->filter_count+1)); + sizeof(*filters) * (graph->nb_filters + 1)); if (!filters) return AVERROR(ENOMEM); graph->filters = filters; - graph->filters[graph->filter_count++] = filter; + graph->filters[graph->nb_filters++] = filter; + +#if FF_API_FOO_COUNT +FF_DISABLE_DEPRECATION_WARNINGS + graph->filter_count = graph->nb_filters; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + filter->graph = graph; return 0; } +#endif -int avfilter_graph_create_filter(AVFilterContext **filt_ctx, AVFilter *filt, +int avfilter_graph_create_filter(AVFilterContext **filt_ctx, const AVFilter *filt, const char *name, const char *args, void *opaque, AVFilterGraph *graph_ctx) { int ret; - if ((ret = avfilter_open(filt_ctx, filt, name)) < 0) - goto fail; - if ((ret = avfilter_init_filter(*filt_ctx, args, opaque)) < 0) - goto fail; - if ((ret = avfilter_graph_add_filter(graph_ctx, *filt_ctx)) < 0) + *filt_ctx = avfilter_graph_alloc_filter(graph_ctx, filt, name); + if (!*filt_ctx) + return AVERROR(ENOMEM); + + ret = avfilter_init_str(*filt_ctx, args); + if (ret < 0) goto fail; + return 0; fail: @@ -93,6 +162,48 @@ fail: return ret; } +AVFilterContext *avfilter_graph_alloc_filter(AVFilterGraph *graph, + const AVFilter *filter, + const char *name) +{ + AVFilterContext **filters, *s; + + if (graph->thread_type && !graph->internal->thread_execute) { + if (graph->execute) { + graph->internal->thread_execute = graph->execute; + } else { + int ret = ff_graph_thread_init(graph); + if (ret < 0) { + av_log(graph, AV_LOG_ERROR, "Error initializing threading.\n"); + return NULL; + } + } + } + + s = ff_filter_alloc(filter, name); + if (!s) + return NULL; + + filters = av_realloc(graph->filters, sizeof(*filters) * (graph->nb_filters + 1)); + if (!filters) { + avfilter_free(s); + return NULL; + } + + graph->filters = filters; + graph->filters[graph->nb_filters++] = s; + +#if FF_API_FOO_COUNT +FF_DISABLE_DEPRECATION_WARNINGS + graph->filter_count = graph->nb_filters; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + s->graph = graph; + + return s; +} + /** * Check for the validity of graph. * @@ -106,7 +217,7 @@ static int graph_check_validity(AVFilterGraph *graph, AVClass *log_ctx) AVFilterContext *filt; int i, j; - for (i = 0; i < graph->filter_count; i++) { + for (i = 0; i < graph->nb_filters; i++) { filt = graph->filters[i]; for (j = 0; j < filt->nb_inputs; j++) { @@ -141,7 +252,7 @@ static int graph_config_links(AVFilterGraph *graph, AVClass *log_ctx) AVFilterContext *filt; int i, ret; - for (i=0; i < graph->filter_count; i++) { + for (i = 0; i < graph->nb_filters; i++) { filt = graph->filters[i]; if (!filt->nb_outputs) { @@ -157,7 +268,7 @@ AVFilterContext *avfilter_graph_get_filter(AVFilterGraph *graph, char *name) { int i; - for (i = 0; i < graph->filter_count; i++) + for (i = 0; i < graph->nb_filters; i++) if (graph->filters[i]->name && !strcmp(name, graph->filters[i]->name)) return graph->filters[i]; @@ -170,7 +281,7 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) int scaler_count = 0, resampler_count = 0; /* ask all the sub-filters for their supported media formats */ - for (i = 0; i < graph->filter_count; i++) { + for (i = 0; i < graph->nb_filters; i++) { if (graph->filters[i]->filter->query_formats) graph->filters[i]->filter->query_formats(graph->filters[i]); else @@ -178,7 +289,7 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) } /* go through and merge as many format lists as possible */ - for (i = 0; i < graph->filter_count; i++) { + for (i = 0; i < graph->nb_filters; i++) { AVFilterContext *filter = graph->filters[i]; for (j = 0; j < filter->nb_inputs; j++) { @@ -221,9 +332,9 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) snprintf(inst_name, sizeof(inst_name), "auto-inserted scaler %d", scaler_count++); - snprintf(scale_args, sizeof(scale_args), "0:0:%s", graph->scale_sws_opts); + if ((ret = avfilter_graph_create_filter(&convert, filter, - inst_name, scale_args, NULL, + inst_name, graph->scale_sws_opts, NULL, graph)) < 0) return ret; break; @@ -236,8 +347,13 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) snprintf(inst_name, sizeof(inst_name), "auto-inserted resampler %d", resampler_count++); + scale_args[0] = '\0'; + if (graph->resample_lavr_opts) + snprintf(scale_args, sizeof(scale_args), "%s", + graph->resample_lavr_opts); if ((ret = avfilter_graph_create_filter(&convert, filter, - inst_name, NULL, NULL, graph)) < 0) + inst_name, scale_args, + NULL, graph)) < 0) return ret; break; default: @@ -284,17 +400,17 @@ static int pick_format(AVFilterLink *link) if (!link || !link->in_formats) return 0; - link->in_formats->format_count = 1; + link->in_formats->nb_formats = 1; link->format = link->in_formats->formats[0]; if (link->type == AVMEDIA_TYPE_AUDIO) { - if (!link->in_samplerates->format_count) { + if (!link->in_samplerates->nb_formats) { av_log(link->src, AV_LOG_ERROR, "Cannot select sample rate for" " the link between filters %s and %s.\n", link->src->name, link->dst->name); return AVERROR(EINVAL); } - link->in_samplerates->format_count = 1; + link->in_samplerates->nb_formats = 1; link->sample_rate = link->in_samplerates->formats[0]; if (!link->in_channel_layouts->nb_channel_layouts) { @@ -357,9 +473,9 @@ static int reduce_formats_on_filter(AVFilterContext *filter) int i, j, k, ret = 0; REDUCE_FORMATS(int, AVFilterFormats, formats, formats, - format_count, ff_add_format); + nb_formats, ff_add_format); REDUCE_FORMATS(int, AVFilterFormats, samplerates, formats, - format_count, ff_add_format); + nb_formats, ff_add_format); REDUCE_FORMATS(uint64_t, AVFilterChannelLayouts, channel_layouts, channel_layouts, nb_channel_layouts, ff_add_channel_layout); @@ -373,7 +489,7 @@ static void reduce_formats(AVFilterGraph *graph) do { reduced = 0; - for (i = 0; i < graph->filter_count; i++) + for (i = 0; i < graph->nb_filters; i++) reduced |= reduce_formats_on_filter(graph->filters[i]); } while (reduced); } @@ -388,7 +504,7 @@ static void swap_samplerates_on_filter(AVFilterContext *filter) link = filter->inputs[i]; if (link->type == AVMEDIA_TYPE_AUDIO && - link->out_samplerates->format_count == 1) + link->out_samplerates->nb_formats== 1) break; } if (i == filter->nb_inputs) @@ -401,10 +517,10 @@ static void swap_samplerates_on_filter(AVFilterContext *filter) int best_idx, best_diff = INT_MAX; if (outlink->type != AVMEDIA_TYPE_AUDIO || - outlink->in_samplerates->format_count < 2) + outlink->in_samplerates->nb_formats < 2) continue; - for (j = 0; j < outlink->in_samplerates->format_count; j++) { + for (j = 0; j < outlink->in_samplerates->nb_formats; j++) { int diff = abs(sample_rate - outlink->in_samplerates->formats[j]); if (diff < best_diff) { @@ -421,15 +537,48 @@ static void swap_samplerates(AVFilterGraph *graph) { int i; - for (i = 0; i < graph->filter_count; i++) + for (i = 0; i < graph->nb_filters; i++) swap_samplerates_on_filter(graph->filters[i]); } +#define CH_CENTER_PAIR (AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER) +#define CH_FRONT_PAIR (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT) +#define CH_STEREO_PAIR (AV_CH_STEREO_LEFT | AV_CH_STEREO_RIGHT) +#define CH_WIDE_PAIR (AV_CH_WIDE_LEFT | AV_CH_WIDE_RIGHT) +#define CH_SIDE_PAIR (AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT) +#define CH_DIRECT_PAIR (AV_CH_SURROUND_DIRECT_LEFT | AV_CH_SURROUND_DIRECT_RIGHT) +#define CH_BACK_PAIR (AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT) + +/* allowable substitutions for channel pairs when comparing layouts, + * ordered by priority for both values */ +static const uint64_t ch_subst[][2] = { + { CH_FRONT_PAIR, CH_CENTER_PAIR }, + { CH_FRONT_PAIR, CH_WIDE_PAIR }, + { CH_FRONT_PAIR, AV_CH_FRONT_CENTER }, + { CH_CENTER_PAIR, CH_FRONT_PAIR }, + { CH_CENTER_PAIR, CH_WIDE_PAIR }, + { CH_CENTER_PAIR, AV_CH_FRONT_CENTER }, + { CH_WIDE_PAIR, CH_FRONT_PAIR }, + { CH_WIDE_PAIR, CH_CENTER_PAIR }, + { CH_WIDE_PAIR, AV_CH_FRONT_CENTER }, + { AV_CH_FRONT_CENTER, CH_FRONT_PAIR }, + { AV_CH_FRONT_CENTER, CH_CENTER_PAIR }, + { AV_CH_FRONT_CENTER, CH_WIDE_PAIR }, + { CH_SIDE_PAIR, CH_DIRECT_PAIR }, + { CH_SIDE_PAIR, CH_BACK_PAIR }, + { CH_SIDE_PAIR, AV_CH_BACK_CENTER }, + { CH_BACK_PAIR, CH_DIRECT_PAIR }, + { CH_BACK_PAIR, CH_SIDE_PAIR }, + { CH_BACK_PAIR, AV_CH_BACK_CENTER }, + { AV_CH_BACK_CENTER, CH_BACK_PAIR }, + { AV_CH_BACK_CENTER, CH_DIRECT_PAIR }, + { AV_CH_BACK_CENTER, CH_SIDE_PAIR }, +}; + static void swap_channel_layouts_on_filter(AVFilterContext *filter) { AVFilterLink *link = NULL; - uint64_t chlayout; - int i, j; + int i, j, k; for (i = 0; i < filter->nb_inputs; i++) { link = filter->inputs[i]; @@ -441,29 +590,58 @@ static void swap_channel_layouts_on_filter(AVFilterContext *filter) if (i == filter->nb_inputs) return; - chlayout = link->out_channel_layouts->channel_layouts[0]; - for (i = 0; i < filter->nb_outputs; i++) { AVFilterLink *outlink = filter->outputs[i]; - int best_idx, best_score = INT_MIN; + int best_idx = -1, best_score = INT_MIN, best_count_diff = INT_MAX; if (outlink->type != AVMEDIA_TYPE_AUDIO || outlink->in_channel_layouts->nb_channel_layouts < 2) continue; for (j = 0; j < outlink->in_channel_layouts->nb_channel_layouts; j++) { + uint64_t in_chlayout = link->out_channel_layouts->channel_layouts[0]; uint64_t out_chlayout = outlink->in_channel_layouts->channel_layouts[j]; - int matched_channels = av_get_channel_layout_nb_channels(chlayout & - out_chlayout); - int extra_channels = av_get_channel_layout_nb_channels(out_chlayout & - (~chlayout)); - int score = matched_channels - extra_channels; + int in_channels = av_get_channel_layout_nb_channels(in_chlayout); + int out_channels = av_get_channel_layout_nb_channels(out_chlayout); + int count_diff = out_channels - in_channels; + int matched_channels, extra_channels; + int score = 0; + + /* channel substitution */ + for (k = 0; k < FF_ARRAY_ELEMS(ch_subst); k++) { + uint64_t cmp0 = ch_subst[k][0]; + uint64_t cmp1 = ch_subst[k][1]; + if (( in_chlayout & cmp0) && (!(out_chlayout & cmp0)) && + (out_chlayout & cmp1) && (!( in_chlayout & cmp1))) { + in_chlayout &= ~cmp0; + out_chlayout &= ~cmp1; + /* add score for channel match, minus a deduction for + having to do the substitution */ + score += 10 * av_get_channel_layout_nb_channels(cmp1) - 2; + } + } - if (score > best_score) { + /* no penalty for LFE channel mismatch */ + if ( (in_chlayout & AV_CH_LOW_FREQUENCY) && + (out_chlayout & AV_CH_LOW_FREQUENCY)) + score += 10; + in_chlayout &= ~AV_CH_LOW_FREQUENCY; + out_chlayout &= ~AV_CH_LOW_FREQUENCY; + + matched_channels = av_get_channel_layout_nb_channels(in_chlayout & + out_chlayout); + extra_channels = av_get_channel_layout_nb_channels(out_chlayout & + (~in_chlayout)); + score += 10 * matched_channels - 5 * extra_channels; + + if (score > best_score || + (count_diff < best_count_diff && score == best_score)) { best_score = score; best_idx = j; + best_count_diff = count_diff; } } + av_assert0(best_idx >= 0); FFSWAP(uint64_t, outlink->in_channel_layouts->channel_layouts[0], outlink->in_channel_layouts->channel_layouts[best_idx]); } @@ -474,7 +652,7 @@ static void swap_channel_layouts(AVFilterGraph *graph) { int i; - for (i = 0; i < graph->filter_count; i++) + for (i = 0; i < graph->nb_filters; i++) swap_channel_layouts_on_filter(graph->filters[i]); } @@ -488,7 +666,7 @@ static void swap_sample_fmts_on_filter(AVFilterContext *filter) link = filter->inputs[i]; if (link->type == AVMEDIA_TYPE_AUDIO && - link->out_formats->format_count == 1) + link->out_formats->nb_formats == 1) break; } if (i == filter->nb_inputs) @@ -499,13 +677,13 @@ static void swap_sample_fmts_on_filter(AVFilterContext *filter) for (i = 0; i < filter->nb_outputs; i++) { AVFilterLink *outlink = filter->outputs[i]; - int best_idx, best_score = INT_MIN; + int best_idx = -1, best_score = INT_MIN; if (outlink->type != AVMEDIA_TYPE_AUDIO || - outlink->in_formats->format_count < 2) + outlink->in_formats->nb_formats < 2) continue; - for (j = 0; j < outlink->in_formats->format_count; j++) { + for (j = 0; j < outlink->in_formats->nb_formats; j++) { int out_format = outlink->in_formats->formats[j]; int out_bps = av_get_bytes_per_sample(out_format); int score; @@ -532,6 +710,7 @@ static void swap_sample_fmts_on_filter(AVFilterContext *filter) best_idx = j; } } + av_assert0(best_idx >= 0); FFSWAP(int, outlink->in_formats->formats[0], outlink->in_formats->formats[best_idx]); } @@ -541,7 +720,7 @@ static void swap_sample_fmts(AVFilterGraph *graph) { int i; - for (i = 0; i < graph->filter_count; i++) + for (i = 0; i < graph->nb_filters; i++) swap_sample_fmts_on_filter(graph->filters[i]); } @@ -550,7 +729,7 @@ static int pick_formats(AVFilterGraph *graph) { int i, j, ret; - for (i = 0; i < graph->filter_count; i++) { + for (i = 0; i < graph->nb_filters; i++) { AVFilterContext *filter = graph->filters[i]; for (j = 0; j < filter->nb_inputs; j++) @@ -597,7 +776,7 @@ static int graph_insert_fifos(AVFilterGraph *graph, AVClass *log_ctx) int i, j, ret; int fifo_count = 0; - for (i = 0; i < graph->filter_count; i++) { + for (i = 0; i < graph->nb_filters; i++) { f = graph->filters[i]; for (j = 0; j < f->nb_inputs; j++) {