diff options
author | Anton Khirnov <anton@khirnov.net> | 2012-05-06 07:00:22 +0200 |
---|---|---|
committer | Anton Khirnov <anton@khirnov.net> | 2012-05-14 21:36:10 +0200 |
commit | ff1f51a8aa624753aaec76d8f836cd3c92d91fed (patch) | |
tree | 72db802d5ba0d0bc5a80a20668e729eadb3f97d0 /libavfilter/avfiltergraph.c | |
parent | 5775a1832c4165e6acc1d307004b38701bb463f4 (diff) | |
download | ffmpeg-ff1f51a8aa624753aaec76d8f836cd3c92d91fed.tar.gz |
lavfi: add channel layout/sample rate negotiation.
Diffstat (limited to 'libavfilter/avfiltergraph.c')
-rw-r--r-- | libavfilter/avfiltergraph.c | 257 |
1 files changed, 222 insertions, 35 deletions
diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index e576cca471..01f7411b97 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -25,8 +25,10 @@ #include "avfilter.h" #include "avfiltergraph.h" +#include "formats.h" #include "internal.h" +#include "libavutil/audioconvert.h" #include "libavutil/log.h" static const AVClass filtergraph_class = { @@ -168,9 +170,27 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) for (j = 0; j < filter->input_count; j++) { AVFilterLink *link = filter->inputs[j]; - if (link && link->in_formats != link->out_formats) { - if (!avfilter_merge_formats(link->in_formats, - link->out_formats)) { + int convert_needed = 0; + + if (!link) + continue; + + if (link->in_formats != link->out_formats && + !avfilter_merge_formats(link->in_formats, + link->out_formats)) + convert_needed = 1; + if (link->type == AVMEDIA_TYPE_AUDIO) { + if (link->in_channel_layouts != link->out_channel_layouts && + !ff_merge_channel_layouts(link->in_channel_layouts, + link->out_channel_layouts)) + convert_needed = 1; + if (link->in_samplerates != link->out_samplerates && + !ff_merge_samplerates(link->in_samplerates, + link->out_samplerates)) + convert_needed = 1; + } + + if (convert_needed) { AVFilterContext *convert; AVFilter *filter; AVFilterLink *inlink, *outlink; @@ -214,13 +234,27 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) inlink = convert->inputs[0]; outlink = convert->outputs[0]; if (!avfilter_merge_formats( inlink->in_formats, inlink->out_formats) || - !avfilter_merge_formats(outlink->in_formats, outlink->out_formats)) { + !avfilter_merge_formats(outlink->in_formats, outlink->out_formats)) + ret |= AVERROR(ENOSYS); + if (inlink->type == AVMEDIA_TYPE_AUDIO && + (!ff_merge_samplerates(inlink->in_samplerates, + inlink->out_samplerates) || + !ff_merge_channel_layouts(inlink->in_channel_layouts, + inlink->out_channel_layouts))) + ret |= AVERROR(ENOSYS); + if (outlink->type == AVMEDIA_TYPE_AUDIO && + (!ff_merge_samplerates(outlink->in_samplerates, + outlink->out_samplerates) || + !ff_merge_channel_layouts(outlink->in_channel_layouts, + outlink->out_channel_layouts))) + ret |= AVERROR(ENOSYS); + + if (ret < 0) { av_log(log_ctx, AV_LOG_ERROR, "Impossible to convert between the formats supported by the filter " "'%s' and the filter '%s'\n", link->src->name, link->dst->name); - return AVERROR(EINVAL); + return ret; } - } } } } @@ -228,46 +262,90 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) return 0; } -static void pick_format(AVFilterLink *link) +static int pick_format(AVFilterLink *link) { if (!link || !link->in_formats) - return; + return 0; link->in_formats->format_count = 1; link->format = link->in_formats->formats[0]; + if (link->type == AVMEDIA_TYPE_AUDIO) { + if (!link->in_samplerates->format_count) { + 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->sample_rate = link->in_samplerates->formats[0]; + + if (!link->in_channel_layouts->nb_channel_layouts) { + av_log(link->src, AV_LOG_ERROR, "Cannot select channel layout for" + "the link between filters %s and %s.\n", link->src->name, + link->dst->name); + return AVERROR(EINVAL); + } + link->in_channel_layouts->nb_channel_layouts = 1; + link->channel_layout = link->in_channel_layouts->channel_layouts[0]; + } + avfilter_formats_unref(&link->in_formats); avfilter_formats_unref(&link->out_formats); + avfilter_formats_unref(&link->in_samplerates); + avfilter_formats_unref(&link->out_samplerates); + ff_channel_layouts_unref(&link->in_channel_layouts); + ff_channel_layouts_unref(&link->out_channel_layouts); + + return 0; } +#define REDUCE_FORMATS(fmt_type, list_type, list, var, nb, add_format) \ +do { \ + for (i = 0; i < filter->input_count; i++) { \ + AVFilterLink *link = filter->inputs[i]; \ + fmt_type fmt; \ + \ + if (!link->out_ ## list || link->out_ ## list->nb != 1) \ + continue; \ + fmt = link->out_ ## list->var[0]; \ + \ + for (j = 0; j < filter->output_count; j++) { \ + AVFilterLink *out_link = filter->outputs[j]; \ + list_type *fmts; \ + \ + if (link->type != out_link->type || \ + out_link->in_ ## list->nb == 1) \ + continue; \ + fmts = out_link->in_ ## list; \ + \ + if (!out_link->in_ ## list->nb) { \ + add_format(&out_link->in_ ##list, fmt); \ + break; \ + } \ + \ + for (k = 0; k < out_link->in_ ## list->nb; k++) \ + if (fmts->var[k] == fmt) { \ + fmts->var[0] = fmt; \ + fmts->nb = 1; \ + ret = 1; \ + break; \ + } \ + } \ + } \ +} while (0) + static int reduce_formats_on_filter(AVFilterContext *filter) { int i, j, k, ret = 0; - for (i = 0; i < filter->input_count; i++) { - AVFilterLink *link = filter->inputs[i]; - int format = link->out_formats->formats[0]; - - if (link->out_formats->format_count != 1) - continue; - - for (j = 0; j < filter->output_count; j++) { - AVFilterLink *out_link = filter->outputs[j]; - AVFilterFormats *fmts = out_link->in_formats; + REDUCE_FORMATS(int, AVFilterFormats, formats, formats, + format_count, avfilter_add_format); + REDUCE_FORMATS(int, AVFilterFormats, samplerates, formats, + format_count, avfilter_add_format); + REDUCE_FORMATS(uint64_t, AVFilterChannelLayouts, channel_layouts, + channel_layouts, nb_channel_layouts, ff_add_channel_layout); - if (link->type != out_link->type || - out_link->in_formats->format_count == 1) - continue; - - for (k = 0; k < out_link->in_formats->format_count; k++) - if (fmts->formats[k] == format) { - fmts->formats[0] = format; - fmts->format_count = 1; - ret = 1; - break; - } - } - } return ret; } @@ -283,18 +361,121 @@ static void reduce_formats(AVFilterGraph *graph) } while (reduced); } -static void pick_formats(AVFilterGraph *graph) +static void swap_samplerates_on_filter(AVFilterContext *filter) +{ + AVFilterLink *link = NULL; + int sample_rate; + int i, j; + + for (i = 0; i < filter->input_count; i++) { + link = filter->inputs[i]; + + if (link->type == AVMEDIA_TYPE_AUDIO && + link->out_samplerates->format_count == 1) + break; + } + if (i == filter->input_count) + return; + + sample_rate = link->out_samplerates->formats[0]; + + for (i = 0; i < filter->output_count; i++) { + AVFilterLink *outlink = filter->outputs[i]; + int best_idx, best_diff = INT_MAX; + + if (outlink->type != AVMEDIA_TYPE_AUDIO || + outlink->in_samplerates->format_count < 2) + continue; + + for (j = 0; j < outlink->in_samplerates->format_count; j++) { + int diff = abs(sample_rate - outlink->in_samplerates->formats[j]); + + if (diff < best_diff) { + best_diff = diff; + best_idx = j; + } + } + FFSWAP(int, outlink->in_samplerates->formats[0], + outlink->in_samplerates->formats[best_idx]); + } +} + +static void swap_samplerates(AVFilterGraph *graph) +{ + int i; + + for (i = 0; i < graph->filter_count; i++) + swap_samplerates_on_filter(graph->filters[i]); +} + +static void swap_channel_layouts_on_filter(AVFilterContext *filter) { + AVFilterLink *link = NULL; + uint64_t chlayout; int i, j; + for (i = 0; i < filter->input_count; i++) { + link = filter->inputs[i]; + + if (link->type == AVMEDIA_TYPE_AUDIO && + link->out_channel_layouts->nb_channel_layouts == 1) + break; + } + if (i == filter->input_count) + return; + + chlayout = link->out_channel_layouts->channel_layouts[0]; + + for (i = 0; i < filter->output_count; i++) { + AVFilterLink *outlink = filter->outputs[i]; + int best_idx, best_score = INT_MIN; + + 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 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; + + if (score > best_score) { + best_score = score; + best_idx = j; + } + } + FFSWAP(uint64_t, outlink->in_channel_layouts->channel_layouts[0], + outlink->in_channel_layouts->channel_layouts[best_idx]); + } + +} + +static void swap_channel_layouts(AVFilterGraph *graph) +{ + int i; + + for (i = 0; i < graph->filter_count; i++) + swap_channel_layouts_on_filter(graph->filters[i]); +} + +static int pick_formats(AVFilterGraph *graph) +{ + int i, j, ret; + for (i = 0; i < graph->filter_count; i++) { AVFilterContext *filter = graph->filters[i]; for (j = 0; j < filter->input_count; j++) - pick_format(filter->inputs[j]); + if ((ret = pick_format(filter->inputs[j])) < 0) + return ret; for (j = 0; j < filter->output_count; j++) - pick_format(filter->outputs[j]); + if ((ret = pick_format(filter->outputs[j])) < 0) + return ret; } + return 0; } int ff_avfilter_graph_config_formats(AVFilterGraph *graph, AVClass *log_ctx) @@ -310,7 +491,13 @@ int ff_avfilter_graph_config_formats(AVFilterGraph *graph, AVClass *log_ctx) * of format conversion inside filters */ reduce_formats(graph); - pick_formats(graph); + /* for audio filters, ensure the best sample rate and channel layout + * is selected */ + swap_samplerates(graph); + swap_channel_layouts(graph); + + if ((ret = pick_formats(graph)) < 0) + return ret; return 0; } |