avfilter: properly reduce YUV colorspace format lists

Doing this with REDUCE_FORMATS() instead of swap_color_*() is not only
shorter, but more importantly comes with the benefit of being done
inside a loop, allowing us to correctly propagate complex graphs
involving multiple conversion filters (e.g. -vf scale,zscale).

The latter family of swapping functions is only used to settle the
best *remaining* entry if no exact match was found, and as such was
never the correct solution to YUV colorspaces, which only care about
exact matches.
This commit is contained in:
Niklas Haas 2024-03-25 16:07:23 +01:00
parent 189c32f536
commit b89ee26539
1 changed files with 4 additions and 80 deletions

View File

@ -794,6 +794,10 @@ static int reduce_formats_on_filter(AVFilterContext *filter)
nb_formats, ff_add_format);
REDUCE_FORMATS(int, AVFilterFormats, samplerates, formats,
nb_formats, ff_add_format);
REDUCE_FORMATS(int, AVFilterFormats, color_spaces, formats,
nb_formats, ff_add_format);
REDUCE_FORMATS(int, AVFilterFormats, color_ranges, formats,
nb_formats, ff_add_format);
/* reduce channel layouts */
for (i = 0; i < filter->nb_inputs; i++) {
@ -906,82 +910,6 @@ static void swap_samplerates(AVFilterGraph *graph)
swap_samplerates_on_filter(graph->filters[i]);
}
static void swap_color_spaces_on_filter(AVFilterContext *filter)
{
AVFilterLink *link = NULL;
enum AVColorSpace csp;
int i;
for (i = 0; i < filter->nb_inputs; i++) {
link = filter->inputs[i];
if (link->type == AVMEDIA_TYPE_VIDEO &&
link->outcfg.color_spaces->nb_formats == 1)
break;
}
if (i == filter->nb_inputs)
return;
csp = link->outcfg.color_spaces->formats[0];
for (i = 0; i < filter->nb_outputs; i++) {
AVFilterLink *outlink = filter->outputs[i];
if (outlink->type != AVMEDIA_TYPE_VIDEO)
continue;
/* there is no meaningful 'score' between different yuv matrices,
* so just prioritize an exact match if it exists */
for (int j = 0; j < outlink->incfg.color_spaces->nb_formats; j++) {
if (csp == outlink->incfg.color_spaces->formats[j]) {
FFSWAP(int, outlink->incfg.color_spaces->formats[0],
outlink->incfg.color_spaces->formats[j]);
break;
}
}
}
}
static void swap_color_spaces(AVFilterGraph *graph)
{
for (int i = 0; i < graph->nb_filters; i++)
swap_color_spaces_on_filter(graph->filters[i]);
}
static void swap_color_ranges_on_filter(AVFilterContext *filter)
{
AVFilterLink *link = NULL;
enum AVColorRange range;
int i;
for (i = 0; i < filter->nb_inputs; i++) {
link = filter->inputs[i];
if (link->type == AVMEDIA_TYPE_VIDEO &&
link->outcfg.color_ranges->nb_formats == 1)
break;
}
if (i == filter->nb_inputs)
return;
range = link->outcfg.color_ranges->formats[0];
for (i = 0; i < filter->nb_outputs; i++) {
AVFilterLink *outlink = filter->outputs[i];
if (outlink->type != AVMEDIA_TYPE_VIDEO)
continue;
for (int j = 0; j < outlink->incfg.color_ranges->nb_formats; j++) {
if (range == outlink->incfg.color_ranges->formats[j]) {
FFSWAP(int, outlink->incfg.color_ranges->formats[0],
outlink->incfg.color_ranges->formats[j]);
break;
}
}
}
}
static void swap_color_ranges(AVFilterGraph *graph)
{
for (int i = 0; i < graph->nb_filters; i++)
swap_color_ranges_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)
@ -1258,10 +1186,6 @@ static int graph_config_formats(AVFilterGraph *graph, void *log_ctx)
if ((ret = reduce_formats(graph)) < 0)
return ret;
/* for video filters, ensure that the best colorspace metadata is selected */
swap_color_spaces(graph);
swap_color_ranges(graph);
/* for audio filters, ensure the best format, sample rate and channel layout
* is selected */
swap_sample_fmts(graph);