avfilter: add link negotiation for AVChromaLocation

Chroma sample location is the last of the video colorimetry properties
without a negotiated link value, so nothing downstream can ask a filter for
a particular siting and no encoder can constrain the graph. Add it alongside
the existing color space, color range and alpha mode negotiation.

Merge conflicts are resolved by inserting a scale filter, the same way as
for color space and color range.

Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
This commit is contained in:
Kacper Michajłow committed 2026-10-01 14:31:15 +00:00
1 parent a74f493bae
commit 08a654a564
8 files changed
+167 -4

No files matched your search

+3
View File
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2026-06-23.
API changes, most recent first:
2026-09-xx - xxxxxxxxxx - lavfi 12.4.101 - avfilter.h
Add AVFilterLink.chroma_location.
2026-08-xx - xxxxxxxxxx - lavf 63.7.100 - avformat.h
Add AV_STREAM_GROUP_PARAMS_GAIN_MAP.
+5
View File
@@ -315,6 +315,9 @@ int avfilter_insert_filter(AVFilterLink *link, AVFilterContext *filt,
if (link->outcfg.alpha_modes)
ff_formats_changeref(&link->outcfg.alpha_modes,
&filt->outputs[filt_dstpad_idx]->outcfg.alpha_modes);
if (link->outcfg.chroma_locations)
ff_formats_changeref(&link->outcfg.chroma_locations,
&filt->outputs[filt_dstpad_idx]->outcfg.chroma_locations);
if (link->outcfg.samplerates)
ff_formats_changeref(&link->outcfg.samplerates,
&filt->outputs[filt_dstpad_idx]->outcfg.samplerates);
@@ -790,6 +793,8 @@ static void free_link(AVFilterLink *link)
ff_formats_unref(&link->outcfg.color_ranges);
ff_formats_unref(&link->incfg.alpha_modes);
ff_formats_unref(&link->outcfg.alpha_modes);
ff_formats_unref(&link->incfg.chroma_locations);
ff_formats_unref(&link->outcfg.chroma_locations);
ff_formats_unref(&link->incfg.samplerates);
ff_formats_unref(&link->outcfg.samplerates);
ff_channel_layouts_unref(&link->incfg.channel_layouts);
+11
View File
@@ -145,6 +145,12 @@ typedef struct AVFilterFormatsConfig {
*/
AVFilterFormats *alpha_modes; ///< AVAlphaMode
/**
* List of supported chroma sample locations, only for video with
* subsampled chroma.
*/
AVFilterFormats *chroma_locations; ///< AVChromaLocation
} AVFilterFormatsConfig;
/**
@@ -407,6 +413,11 @@ struct AVFilterLink {
enum AVAlphaMode alpha_mode; ///< alpha mode (for videos with an alpha channel)
/**
* Agreed upon chroma sample location, for video with subsampled chroma.
*/
enum AVChromaLocation chroma_location;
/*****************************************************************
* All fields below this line are not part of the public API. They
* may not be used outside of libavfilter and can be changed and
+28 -3
View File
@@ -304,7 +304,8 @@ static int filter_link_check_formats(void *log, AVFilterLink *link, AVFilterForm
if ((ret = ff_formats_check_pixel_formats(log, cfg->formats)) < 0 ||
(ret = ff_formats_check_color_spaces(log, cfg->color_spaces)) < 0 ||
(ret = ff_formats_check_color_ranges(log, cfg->color_ranges)) < 0 ||
(ret = ff_formats_check_alpha_modes(log, cfg->alpha_modes)) < 0)
(ret = ff_formats_check_alpha_modes(log, cfg->alpha_modes)) < 0 ||
(ret = ff_formats_check_chroma_locations(log, cfg->chroma_locations)) < 0)
return ret;
break;
@@ -420,7 +421,8 @@ static int formats_declared(AVFilterContext *f)
if (f->inputs[i]->type == AVMEDIA_TYPE_VIDEO &&
!(f->inputs[i]->outcfg.color_ranges &&
f->inputs[i]->outcfg.color_spaces &&
f->inputs[i]->outcfg.alpha_modes))
f->inputs[i]->outcfg.alpha_modes &&
f->inputs[i]->outcfg.chroma_locations))
return 0;
if (f->inputs[i]->type == AVMEDIA_TYPE_AUDIO &&
!(f->inputs[i]->outcfg.samplerates &&
@@ -433,7 +435,8 @@ static int formats_declared(AVFilterContext *f)
if (f->outputs[i]->type == AVMEDIA_TYPE_VIDEO &&
!(f->outputs[i]->incfg.color_ranges &&
f->outputs[i]->incfg.color_spaces &&
f->outputs[i]->incfg.alpha_modes))
f->outputs[i]->incfg.alpha_modes &&
f->outputs[i]->incfg.chroma_locations))
return 0;
if (f->outputs[i]->type == AVMEDIA_TYPE_AUDIO &&
!(f->outputs[i]->incfg.samplerates &&
@@ -666,6 +669,10 @@ retry:
av_assert0( inlink->outcfg.alpha_modes->refcount > 0);
av_assert0(outlink-> incfg.alpha_modes->refcount > 0);
av_assert0(outlink->outcfg.alpha_modes->refcount > 0);
av_assert0( inlink-> incfg.chroma_locations->refcount > 0);
av_assert0( inlink->outcfg.chroma_locations->refcount > 0);
av_assert0(outlink-> incfg.chroma_locations->refcount > 0);
av_assert0(outlink->outcfg.chroma_locations->refcount > 0);
} else if (outlink->type == AVMEDIA_TYPE_AUDIO) {
av_assert0( inlink-> incfg.samplerates->refcount > 0);
av_assert0( inlink->outcfg.samplerates->refcount > 0);
@@ -896,6 +903,20 @@ static int pick_format(AVFilterLink *link, AVFilterLink *ref)
} else {
link->alpha_mode = AVALPHA_MODE_UNSPECIFIED;
}
if (desc->log2_chroma_w || desc->log2_chroma_h) {
if (!link->incfg.chroma_locations->nb_formats) {
av_log(link->src, AV_LOG_ERROR, "Cannot select chroma location"
" for the link between filters %s and %s.\n", link->src->name,
link->dst->name);
return AVERROR(EINVAL);
}
link->incfg.chroma_locations->nb_formats = 1;
link->chroma_location = link->incfg.chroma_locations->formats[0];
} else {
/* Chroma location is meaningless without subsampled chroma. */
link->chroma_location = AVCHROMA_LOC_UNSPECIFIED;
}
} else if (link->type == AVMEDIA_TYPE_AUDIO) {
int ret;
@@ -936,6 +957,8 @@ static int pick_format(AVFilterLink *link, AVFilterLink *ref)
ff_formats_unref(&link->outcfg.color_ranges);
ff_formats_unref(&link->incfg.alpha_modes);
ff_formats_unref(&link->outcfg.alpha_modes);
ff_formats_unref(&link->incfg.chroma_locations);
ff_formats_unref(&link->outcfg.chroma_locations);
return 0;
}
@@ -991,6 +1014,8 @@ static int reduce_formats_on_filter(AVFilterContext *filter)
nb_formats, ff_add_format);
REDUCE_FORMATS(int, AVFilterFormats, alpha_modes, formats,
nb_formats, ff_add_format);
REDUCE_FORMATS(int, AVFilterFormats, chroma_locations, formats,
nb_formats, ff_add_format);
/* reduce channel layouts */
for (i = 0; i < filter->nb_inputs; i++) {
+71
View File
@@ -363,6 +363,7 @@ PRINT_NAME(enum AVPixelFormat, get_pix_fmt_name)
PRINT_NAME(enum AVColorSpace, color_space_name)
PRINT_NAME(enum AVColorRange, color_range_name)
PRINT_NAME(enum AVAlphaMode, alpha_mode_name)
PRINT_NAME(enum AVChromaLocation, chroma_location_name)
static void print_channel_layout_desc(AVBPrint *bp, const void *layoutsp)
{
@@ -421,6 +422,14 @@ static const AVFilterFormatsMerger mergers_video[] = {
.print_list = print_alpha_mode_name,
.conversion_filter = "premultiply_dynamic",
},
{
.name = "Chroma locations",
.offset = offsetof(AVFilterFormatsConfig, chroma_locations),
.merge = merge_generic,
.can_merge = can_merge_generic,
.print_list = print_chroma_location_name,
CONVERSION_FILTER_SWSCALE
},
};
static const AVFilterFormatsMerger mergers_audio[] = {
@@ -733,6 +742,17 @@ AVFilterFormats *ff_all_alpha_modes(void)
return ret;
}
AVFilterFormats *ff_all_chroma_locations(void)
{
AVFilterFormats *ret = NULL;
for (int loc = 0; loc < AVCHROMA_LOC_NB; loc++) {
if (ff_add_format(&ret, loc) < 0)
return NULL;
}
return ret;
}
#define FORMATS_REF(f, ref, unref_fn) \
void *tmp; \
\
@@ -956,6 +976,24 @@ int ff_set_common_all_alpha_modes(AVFilterContext *ctx)
return ff_set_common_alpha_modes(ctx, ff_all_alpha_modes());
}
int ff_set_common_chroma_locations(AVFilterContext *ctx,
AVFilterFormats *chroma_locations)
{
SET_COMMON_FORMATS(ctx, chroma_locations, AVMEDIA_TYPE_VIDEO,
ff_formats_ref, ff_formats_unref);
}
int ff_set_common_chroma_locations_from_list(AVFilterContext *ctx,
const int *chroma_locations)
{
return ff_set_common_chroma_locations(ctx, ff_make_format_list(chroma_locations));
}
int ff_set_common_all_chroma_locations(AVFilterContext *ctx)
{
return ff_set_common_chroma_locations(ctx, ff_all_chroma_locations());
}
/**
* A helper for query_formats() which sets all links to the same list of
* formats. If there are no links hooked to this filter, the list of formats is
@@ -1135,6 +1173,31 @@ int ff_set_common_all_alpha_modes2(const AVFilterContext *ctx,
return ff_set_common_alpha_modes2(ctx, cfg_in, cfg_out, ff_all_alpha_modes());
}
int ff_set_common_chroma_locations2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
AVFilterFormats *chroma_locations)
{
SET_COMMON_FORMATS2(ctx, cfg_in, cfg_out, chroma_locations, AVMEDIA_TYPE_VIDEO,
ff_formats_ref, ff_formats_unref);
}
int ff_set_common_chroma_locations_from_list2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
const int *chroma_locations)
{
return ff_set_common_chroma_locations2(ctx, cfg_in, cfg_out,
ff_make_format_list(chroma_locations));
}
int ff_set_common_all_chroma_locations2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out)
{
return ff_set_common_chroma_locations2(ctx, cfg_in, cfg_out, ff_all_chroma_locations());
}
int ff_set_common_formats2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
@@ -1218,6 +1281,9 @@ int ff_default_query_formats(AVFilterContext *ctx)
ret = ff_set_common_all_alpha_modes(ctx);
if (ret < 0)
return ret;
ret = ff_set_common_all_chroma_locations(ctx);
if (ret < 0)
return ret;
}
if (type != AVMEDIA_TYPE_VIDEO) {
ret = ff_set_common_all_channel_counts(ctx);
@@ -1290,6 +1356,11 @@ int ff_formats_check_alpha_modes(void *log, const AVFilterFormats *fmts)
return check_list(log, "alpha mode", fmts);
}
int ff_formats_check_chroma_locations(void *log, const AVFilterFormats *fmts)
{
return check_list(log, "chroma location", fmts);
}
static int layouts_compatible(const AVChannelLayout *a, const AVChannelLayout *b)
{
return !av_channel_layout_compare(a, b) ||
+47
View File
@@ -150,6 +150,12 @@ AVFilterFormats *ff_all_color_ranges(void);
av_warn_unused_result
AVFilterFormats *ff_all_alpha_modes(void);
/**
* Construct an AVFilterFormats representing all possible chroma locations.
*/
av_warn_unused_result
AVFilterFormats *ff_all_chroma_locations(void);
/**
* Helpers for query_formats() which set all free audio links to the same list
* of channel layouts/sample rates. If there are no links hooked to this list,
@@ -233,6 +239,23 @@ int ff_set_common_alpha_modes_from_list(AVFilterContext *ctx,
av_warn_unused_result
int ff_set_common_all_alpha_modes(AVFilterContext *ctx);
av_warn_unused_result
int ff_set_common_chroma_locations(AVFilterContext *ctx,
AVFilterFormats *chroma_locations);
/**
* Equivalent to ff_set_common_chroma_locations(ctx, ff_make_format_list(chroma_locations))
*/
av_warn_unused_result
int ff_set_common_chroma_locations_from_list(AVFilterContext *ctx,
const int *chroma_locations);
/**
* Equivalent to ff_set_common_chroma_locations(ctx, ff_all_chroma_locations())
*/
av_warn_unused_result
int ff_set_common_all_chroma_locations(AVFilterContext *ctx);
/**
* A helper for query_formats() which sets all links to the same list of
@@ -349,6 +372,23 @@ int ff_set_common_all_alpha_modes2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out);
av_warn_unused_result
int ff_set_common_chroma_locations2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
AVFilterFormats *chroma_locations);
av_warn_unused_result
int ff_set_common_chroma_locations_from_list2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
const int *chroma_locations);
av_warn_unused_result
int ff_set_common_all_chroma_locations2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out);
av_warn_unused_result
int ff_set_common_formats2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
@@ -557,6 +597,13 @@ int ff_formats_check_color_ranges(void *log, const AVFilterFormats *fmts);
*/
int ff_formats_check_alpha_modes(void *log, const AVFilterFormats *fmts);
/**
* Check that fmts is a valid formats list for chroma locations.
*
* In particular, check for duplicates.
*/
int ff_formats_check_chroma_locations(void *log, const AVFilterFormats *fmts);
struct AVBPrint;
typedef struct AVFilterFormatMerger {
+1 -1
View File
@@ -32,7 +32,7 @@
#include "version_major.h"
#define LIBAVFILTER_VERSION_MINOR 4
#define LIBAVFILTER_VERSION_MICRO 100
#define LIBAVFILTER_VERSION_MICRO 101
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
+1
View File
@@ -77,6 +77,7 @@ AVFrame *ff_default_get_video_buffer2(AVFilterLink *link, int w, int h, int alig
frame->colorspace = link->colorspace;
frame->color_range = link->color_range;
frame->alpha_mode = link->alpha_mode;
frame->chroma_location = link->chroma_location;
return frame;
}