|
|
|
@ -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; |
|
|
|
|
} |
|
|
|
|