This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 08a654a5648b4b3332f608a9da94e6a019ab2cdb Author: Kacper Michajłow <[email protected]> AuthorDate: Wed Sep 9 07:12:40 2026 +0200 Commit: Kacper Michajłow <[email protected]> CommitDate: Thu Oct 1 14:31:15 2026 +0000 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 <[email protected]> --- doc/APIchanges | 3 ++ libavfilter/avfilter.c | 5 ++++ libavfilter/avfilter.h | 11 +++++++ libavfilter/avfiltergraph.c | 31 ++++++++++++++++++-- libavfilter/formats.c | 71 +++++++++++++++++++++++++++++++++++++++++++++ libavfilter/formats.h | 47 ++++++++++++++++++++++++++++++ libavfilter/version.h | 2 +- libavfilter/video.c | 1 + 8 files changed, 167 insertions(+), 4 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index a4b11e2efc..eb2c0c458b 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -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. diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index d10836df3b..46068719b1 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -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); diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index 1363a861a1..a6b875c37a 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -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 diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index ad81d91bbc..7b58883c65 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -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++) { diff --git a/libavfilter/formats.c b/libavfilter/formats.c index 7644fc4c75..2994cdaa89 100644 --- a/libavfilter/formats.c +++ b/libavfilter/formats.c @@ -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) || diff --git a/libavfilter/formats.h b/libavfilter/formats.h index 45864a32db..488a09b50b 100644 --- a/libavfilter/formats.h +++ b/libavfilter/formats.h @@ -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 { diff --git a/libavfilter/version.h b/libavfilter/version.h index 4d8f28e512..cd37ff7921 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -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, \ diff --git a/libavfilter/video.c b/libavfilter/video.c index 9be2f65448..0f4539869e 100644 --- a/libavfilter/video.c +++ b/libavfilter/video.c @@ -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; } -- To stop receiving notification emails like this one, please contact [email protected]. _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
