[FFmpeg-devel] [PATCH v2 03/15] avfilter: add negotiation API for color space/range
Xiang, Haihao
haihao.xiang at intel.com
Wed Jan 3 05:04:06 EET 2024
On Wo, 2023-12-13 at 14:12 +0100, Niklas Haas wrote:
> From: Niklas Haas <git at haasn.dev>
>
> Motivated by YUVJ removal. This change will allow full negotiation
> between color ranges and matrices as needed. By default, all ranges and
> matrices are marked as supported.
>
> Because grayscale formats are currently handled very inconsistently (and
> in particular, assumed as forced full-range by swscale), we exclude them
> from negotiation altogether for the time being, to get this API merged.
>
> After filter negotiation is available, we can relax the
> grayscale-is-forced-jpeg restriction again, when it will be more
> feasible to do so without breaking a million test cases.
>
> Note that this commit updates one FATE test as a consequence of the
> sanity fallback for non-YUV formats. In particular, the test case now
> writes rgb24(pc, gbr/unspecified/unspecified) to the matroska file,
> instead of rgb24(unspecified/unspecified/unspecified) as before.
> ---
> doc/APIchanges | 3 +
> libavfilter/avfilter.c | 17 +++-
> libavfilter/avfilter.h | 28 ++++++
> libavfilter/avfiltergraph.c | 173 +++++++++++++++++++++++++++++++++++-
> libavfilter/formats.c | 122 ++++++++++++++++++++++++-
> libavfilter/formats.h | 54 +++++++++++
> libavfilter/internal.h | 6 ++
> libavfilter/vaapi_vpp.c | 4 +
> libavfilter/video.c | 2 +
> tests/ref/fate/shortest-sub | 4 +-
> 10 files changed, 404 insertions(+), 9 deletions(-)
It caused segfault when using hw accelerations, such as vulkan, vaapi, qsv.
$ffmpeg -init_hw_device qsv -f lavfi -i yuvtestsrc=duration=1,format=nv12 -vf 'hwupload=extra_hw_frames=8' -f null -
$ffmpeg -init_hw_device vaapi -f lavfi -i yuvtestsrc=duration=1,format=nv12 -vf 'hwupload=extra_hw_frames=8' -f null -
$ffmpeg -init_hw_device vulkan -f lavfi -i yuvtestsrc=duration=1,format=nv12 -vf 'hwupload=extra_hw_frames=8' -f null -
Thread 36 "vf#0:0" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffc4fe1640 (LWP 3795664)]
pick_format (link=0x7fffb8004100, ref=0x0) at libavfilter/avfiltergraph.c:671
671 swfmt = ((AVHWFramesContext *) link->hw_frames_ctx->data)->sw_format;
(gdb) bt
#0 pick_format (link=0x7fffb8004100, ref=0x0) at libavfilter/avfiltergraph.c:671
#1 0x00007ffff75d5b57 in pick_formats (graph=0x7fffb8000ff0) at libavfilter/avfiltergraph.c:1213
#2 0x00007ffff75d5ded in graph_config_formats (graph=0x7fffb8000ff0, log_ctx=0x0) at libavfilter/avfiltergraph.c:1273
#3 0x00007ffff75d60c3 in avfilter_graph_config (graphctx=0x7fffb8000ff0, log_ctx=0x0) at libavfilter/avfiltergraph.c:1326
#4 0x00005555555786c8 in configure_filtergraph (fg=0x555555666e60, fgt=0x7fffc4fe0b30) at fftools/ffmpeg_filter.c:1758
#5 0x000055555557b4f5 in send_frame (fg=0x555555666e60, fgt=0x7fffc4fe0b30, ifilter=0x555555663b80, frame=0x7fffb8000b70) at fftools/ffmpeg_filter.c:2652
#6 0x000055555557bdce in filter_thread (arg=0x555555666e60) at fftools/ffmpeg_filter.c:2813
#7 0x000055555559d8cd in task_wrapper (arg=0x555555663df8) at fftools/ffmpeg_sched.c:2200
#8 0x00007ffff4094b43 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#9 0x00007ffff4126a00 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
(gdb) p link->hw_frames_ctx
$1 = (AVBufferRef *) 0x0
link->hw_frames_ctx is set after graph_config_formats()
Thanks
Haihao
>
> diff --git a/doc/APIchanges b/doc/APIchanges
> index 4a2dc1c44f..2f6ea50f63 100644
> --- a/doc/APIchanges
> +++ b/doc/APIchanges
> @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2023-02-09
>
> API changes, most recent first:
>
> +2023-11-xx - xxxxxxxxxx - lavf 58.14.100 - avfilter.h
> + Add AVFilterLink.colorspace, AVFilterLink.color_range
> +
> 2023-11-08 - b82957a66a7 - lavu 58.32.100 - channel_layout.h
> Add AV_CH_LAYOUT_7POINT2POINT3 and AV_CHANNEL_LAYOUT_7POINT2POINT3.
> Add AV_CH_LAYOUT_9POINT1POINT4_BACK and
> AV_CHANNEL_LAYOUT_9POINT1POINT4_BACK.
> diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
> index bde1c33d07..31300bb515 100644
> --- a/libavfilter/avfilter.c
> +++ b/libavfilter/avfilter.c
> @@ -185,6 +185,7 @@ int avfilter_link(AVFilterContext *src, unsigned srcpad,
> link->type = src->output_pads[srcpad].type;
> av_assert0(AV_PIX_FMT_NONE == -1 && AV_SAMPLE_FMT_NONE == -1);
> link->format = -1;
> + link->colorspace = AVCOL_SPC_UNSPECIFIED;
> ff_framequeue_init(&link->fifo, &src->graph->internal->frame_queues);
>
> return 0;
> @@ -286,6 +287,12 @@ int avfilter_insert_filter(AVFilterLink *link,
> AVFilterContext *filt,
> if (link->outcfg.formats)
> ff_formats_changeref(&link->outcfg.formats,
> &filt->outputs[filt_dstpad_idx]-
> >outcfg.formats);
> + if (link->outcfg.color_spaces)
> + ff_formats_changeref(&link->outcfg.color_spaces,
> + &filt->outputs[filt_dstpad_idx]-
> >outcfg.color_spaces);
> + if (link->outcfg.color_ranges)
> + ff_formats_changeref(&link->outcfg.color_ranges,
> + &filt->outputs[filt_dstpad_idx]-
> >outcfg.color_ranges);
> if (link->outcfg.samplerates)
> ff_formats_changeref(&link->outcfg.samplerates,
> &filt->outputs[filt_dstpad_idx]-
> >outcfg.samplerates);
> @@ -730,6 +737,10 @@ static void free_link(AVFilterLink *link)
>
> ff_formats_unref(&link->incfg.formats);
> ff_formats_unref(&link->outcfg.formats);
> + ff_formats_unref(&link->incfg.color_spaces);
> + ff_formats_unref(&link->outcfg.color_spaces);
> + ff_formats_unref(&link->incfg.color_ranges);
> + ff_formats_unref(&link->outcfg.color_ranges);
> ff_formats_unref(&link->incfg.samplerates);
> ff_formats_unref(&link->outcfg.samplerates);
> ff_channel_layouts_unref(&link->incfg.channel_layouts);
> @@ -987,9 +998,9 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
> strcmp(link->dst->filter->name, "idet") &&
> strcmp(link->dst->filter->name, "null") &&
> strcmp(link->dst->filter->name, "scale")) {
> - av_assert1(frame->format == link->format);
> - av_assert1(frame->width == link->w);
> - av_assert1(frame->height == link->h);
> + av_assert1(frame->format == link->format);
> + av_assert1(frame->width == link->w);
> + av_assert1(frame->height == link->h);
> }
>
> frame->sample_aspect_ratio = link->sample_aspect_ratio;
> diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
> index d69381aed4..246d000251 100644
> --- a/libavfilter/avfilter.h
> +++ b/libavfilter/avfilter.h
> @@ -301,6 +301,14 @@ typedef struct AVFilter {
> * @ref AVFilterFormatsConfig.formats "incfg.formats"
> * on every output link to a list of pixel/sample formats that the
> filter
> * supports on that link.
> + * For video links, this filter may also set
> + * @ref AVFilterFormatsConfig.color_spaces "incfg.color_spaces"
> + * /
> + * @ref AVFilterFormatsConfig.color_spaces "outcfg.color_spaces"
> + * and @ref AVFilterFormatsConfig.color_ranges "incfg.color_ranges"
> + * /
> + * @ref AVFilterFormatsConfig.color_ranges "outcfg.color_ranges"
> + * analogously.
> * For audio links, this filter must also set
> * @ref AVFilterFormatsConfig.samplerates "incfg.samplerates"
> * /
> @@ -322,6 +330,10 @@ typedef struct AVFilter {
> * to indicate that this filter supports each of these pixel formats,
> * provided that all inputs and outputs use the same pixel format.
> *
> + * In addition to that the generic code will mark all inputs
> + * and all outputs as supporting all color spaces and ranges, as
> + * long as all inputs and outputs use the same color space/range.
> + *
> * This list must never be NULL if the union is in this state.
> * The type of all inputs and outputs of filters using this must
> * be AVMEDIA_TYPE_VIDEO.
> @@ -514,6 +526,12 @@ typedef struct AVFilterFormatsConfig {
> */
> AVFilterChannelLayouts *channel_layouts;
>
> + /**
> + * Lists of supported YUV color metadata, only for YUV video.
> + */
> + AVFilterFormats *color_spaces; ///< AVColorSpace
> + AVFilterFormats *color_ranges; ///< AVColorRange
> +
> } AVFilterFormatsConfig;
>
> /**
> @@ -565,6 +583,16 @@ struct AVFilterLink {
>
> AVChannelLayout ch_layout; ///< channel layout of current buffer (see
> libavutil/channel_layout.h)
>
> + /**
> + * For non-YUV links, these are respectively set to fallback values (as
> + * appropriate for that colorspace).
> + *
> + * Note: This includes grayscale formats, as these are currently treated
> + * as forced full range always.
> + */
> + enum AVColorSpace colorspace; ///< agreed upon YUV color space
> + enum AVColorRange color_range; ///< agreed upon YUV color range
> +
> /*****************************************************************
> * 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 625cbc022e..ef739735bd 100644
> --- a/libavfilter/avfiltergraph.c
> +++ b/libavfilter/avfiltergraph.c
> @@ -27,6 +27,7 @@
> #include "libavutil/avassert.h"
> #include "libavutil/bprint.h"
> #include "libavutil/channel_layout.h"
> +#include "libavutil/hwcontext.h"
> #include "libavutil/imgutils.h"
> #include "libavutil/opt.h"
> #include "libavutil/pixdesc.h"
> @@ -298,7 +299,9 @@ static int filter_link_check_formats(void *log,
> AVFilterLink *link, AVFilterForm
> switch (link->type) {
>
> case AVMEDIA_TYPE_VIDEO:
> - if ((ret = ff_formats_check_pixel_formats(log, cfg->formats)) < 0)
> + 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)
> return ret;
> break;
>
> @@ -365,6 +368,10 @@ static int formats_declared(AVFilterContext *f)
> for (i = 0; i < f->nb_inputs; i++) {
> if (!f->inputs[i]->outcfg.formats)
> return 0;
> + if (f->inputs[i]->type == AVMEDIA_TYPE_VIDEO &&
> + !(f->inputs[i]->outcfg.color_ranges &&
> + f->inputs[i]->outcfg.color_spaces))
> + return 0;
> if (f->inputs[i]->type == AVMEDIA_TYPE_AUDIO &&
> !(f->inputs[i]->outcfg.samplerates &&
> f->inputs[i]->outcfg.channel_layouts))
> @@ -373,6 +380,10 @@ static int formats_declared(AVFilterContext *f)
> for (i = 0; i < f->nb_outputs; i++) {
> if (!f->outputs[i]->incfg.formats)
> return 0;
> + if (f->outputs[i]->type == AVMEDIA_TYPE_VIDEO &&
> + !(f->outputs[i]->incfg.color_ranges &&
> + f->outputs[i]->incfg.color_spaces))
> + return 0;
> if (f->outputs[i]->type == AVMEDIA_TYPE_AUDIO &&
> !(f->outputs[i]->incfg.samplerates &&
> f->outputs[i]->incfg.channel_layouts))
> @@ -493,7 +504,16 @@ static int query_formats(AVFilterGraph *graph, void
> *log_ctx)
> av_assert0( inlink->outcfg.formats->refcount > 0);
> av_assert0(outlink->incfg.formats->refcount > 0);
> av_assert0(outlink->outcfg.formats->refcount > 0);
> - if (outlink->type == AVMEDIA_TYPE_AUDIO) {
> + if (outlink->type == AVMEDIA_TYPE_VIDEO) {
> + av_assert0( inlink-> incfg.color_spaces->refcount > 0);
> + av_assert0( inlink->outcfg.color_spaces->refcount > 0);
> + av_assert0(outlink-> incfg.color_spaces->refcount > 0);
> + av_assert0(outlink->outcfg.color_spaces->refcount > 0);
> + av_assert0( inlink-> incfg.color_ranges->refcount > 0);
> + av_assert0( inlink->outcfg.color_ranges->refcount > 0);
> + av_assert0(outlink-> incfg.color_ranges->refcount > 0);
> + av_assert0(outlink->outcfg.color_ranges->refcount > 0);
> + } else if (outlink->type == AVMEDIA_TYPE_AUDIO) {
> av_assert0( inlink-> incfg.samplerates->refcount > 0);
> av_assert0( inlink->outcfg.samplerates->refcount > 0);
> av_assert0(outlink-> incfg.samplerates->refcount > 0);
> @@ -583,6 +603,30 @@ static enum AVSampleFormat find_best_sample_fmt_of_2(enum
> AVSampleFormat dst_fmt
> return score1 < score2 ? dst_fmt1 : dst_fmt2;
> }
>
> +int ff_fmt_is_regular_yuv(enum AVPixelFormat fmt)
> +{
> + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
> + if (!desc)
> + return 0;
> + if (desc->nb_components < 3)
> + return 0; /* Grayscale is explicitly full-range in swscale */
> + av_assert1(!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL));
> + if (desc->flags & (AV_PIX_FMT_FLAG_RGB | AV_PIX_FMT_FLAG_PAL |
> + AV_PIX_FMT_FLAG_XYZ | AV_PIX_FMT_FLAG_FLOAT))
> + return 0;
> +
> + switch (fmt) {
> + case AV_PIX_FMT_YUVJ420P:
> + case AV_PIX_FMT_YUVJ422P:
> + case AV_PIX_FMT_YUVJ444P:
> + case AV_PIX_FMT_YUVJ440P:
> + case AV_PIX_FMT_YUVJ411P:
> + return 0;
> + default:
> + return 1;
> + }
> +}
> +
> static int pick_format(AVFilterLink *link, AVFilterLink *ref)
> {
> if (!link || !link->incfg.formats)
> @@ -621,7 +665,46 @@ static int pick_format(AVFilterLink *link, AVFilterLink
> *ref)
> link->incfg.formats->nb_formats = 1;
> link->format = link->incfg.formats->formats[0];
>
> - if (link->type == AVMEDIA_TYPE_AUDIO) {
> + if (link->type == AVMEDIA_TYPE_VIDEO) {
> + enum AVPixelFormat swfmt = link->format;
> + if (av_pix_fmt_desc_get(swfmt)->flags & AV_PIX_FMT_FLAG_HWACCEL) {
> + av_assert1(link->hw_frames_ctx);
> + swfmt = ((AVHWFramesContext *) link->hw_frames_ctx->data)-
> >sw_format;
> + }
> +
> + if (!ff_fmt_is_regular_yuv(swfmt)) {
> + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(swfmt);
> + /* These fields are explicitly documented as affecting YUV only,
> + * so set them to sane values for other formats. */
> + if (desc->flags & AV_PIX_FMT_FLAG_FLOAT)
> + link->color_range = AVCOL_RANGE_UNSPECIFIED;
> + else
> + link->color_range = AVCOL_RANGE_JPEG;
> + if (desc->flags & (AV_PIX_FMT_FLAG_RGB | AV_PIX_FMT_FLAG_XYZ)) {
> + link->colorspace = AVCOL_SPC_RGB;
> + } else {
> + link->colorspace = AVCOL_SPC_UNSPECIFIED;
> + }
> + } else {
> + if (!link->incfg.color_spaces->nb_formats) {
> + av_log(link->src, AV_LOG_ERROR, "Cannot select color space
> for"
> + " the link between filters %s and %s.\n", link->src-
> >name,
> + link->dst->name);
> + return AVERROR(EINVAL);
> + }
> + link->incfg.color_spaces->nb_formats = 1;
> + link->colorspace = link->incfg.color_spaces->formats[0];
> +
> + if (!link->incfg.color_ranges->nb_formats) {
> + av_log(link->src, AV_LOG_ERROR, "Cannot select color range
> for"
> + " the link between filters %s and %s.\n", link->src-
> >name,
> + link->dst->name);
> + return AVERROR(EINVAL);
> + }
> + link->incfg.color_ranges->nb_formats = 1;
> + link->color_range = link->incfg.color_ranges->formats[0];
> + }
> + } else if (link->type == AVMEDIA_TYPE_AUDIO) {
> int ret;
>
> if (!link->incfg.samplerates->nb_formats) {
> @@ -661,6 +744,10 @@ FF_ENABLE_DEPRECATION_WARNINGS
> ff_formats_unref(&link->outcfg.samplerates);
> ff_channel_layouts_unref(&link->incfg.channel_layouts);
> ff_channel_layouts_unref(&link->outcfg.channel_layouts);
> + ff_formats_unref(&link->incfg.color_spaces);
> + ff_formats_unref(&link->outcfg.color_spaces);
> + ff_formats_unref(&link->incfg.color_ranges);
> + ff_formats_unref(&link->outcfg.color_ranges);
>
> return 0;
> }
> @@ -822,6 +909,82 @@ 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)
> @@ -1098,6 +1261,10 @@ 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);
> diff --git a/libavfilter/formats.c b/libavfilter/formats.c
> index d1c97daf64..20f00d2db4 100644
> --- a/libavfilter/formats.c
> +++ b/libavfilter/formats.c
> @@ -321,12 +321,46 @@ static int merge_channel_layouts(void *a, void *b)
> return merge_channel_layouts_internal(a, b, 0);
> }
>
> +static int merge_generic_internal(AVFilterFormats *a,
> + AVFilterFormats *b, int check)
> +{
> + av_assert2(check || (a->refcount && b->refcount));
> +
> + if (a == b)
> + return 1;
> +
> + MERGE_FORMATS(a, b, formats, nb_formats, AVFilterFormats, check, 0);
> +
> + return 1;
> +}
> +
> +static int can_merge_generic(const void *a, const void *b)
> +{
> + return merge_generic_internal((AVFilterFormats *)a,
> + (AVFilterFormats *)b, 1);
> +}
> +
> +static int merge_generic(void *a, void *b)
> +{
> + return merge_generic_internal(a, b, 0);
> +}
> +
> static const AVFilterFormatsMerger mergers_video[] = {
> {
> .offset = offsetof(AVFilterFormatsConfig, formats),
> .merge = merge_pix_fmts,
> .can_merge = can_merge_pix_fmts,
> },
> + {
> + .offset = offsetof(AVFilterFormatsConfig, color_spaces),
> + .merge = merge_generic,
> + .can_merge = can_merge_generic,
> + },
> + {
> + .offset = offsetof(AVFilterFormatsConfig, color_ranges),
> + .merge = merge_generic,
> + .can_merge = can_merge_generic,
> + },
> };
>
> static const AVFilterFormatsMerger mergers_audio[] = {
> @@ -594,6 +628,33 @@ AVFilterChannelLayouts *ff_all_channel_counts(void)
> return ret;
> }
>
> +AVFilterFormats *ff_all_color_spaces(void)
> +{
> + AVFilterFormats *ret = NULL;
> + if (ff_add_format(&ret, AVCOL_SPC_UNSPECIFIED) < 0)
> + return NULL;
> + for (int csp = 0; csp < AVCOL_SPC_NB; csp++) {
> + if (csp == AVCOL_SPC_RESERVED ||
> + csp == AVCOL_SPC_UNSPECIFIED)
> + continue;
> + if (ff_add_format(&ret, csp) < 0)
> + return NULL;
> + }
> +
> + return ret;
> +}
> +
> +AVFilterFormats *ff_all_color_ranges(void)
> +{
> + AVFilterFormats *ret = NULL;
> + for (int range = 0; range < AVCOL_RANGE_NB; range++) {
> + if (ff_add_format(&ret, range) < 0)
> + return NULL;
> + }
> +
> + return ret;
> +}
> +
> #define FORMATS_REF(f, ref,
> unref_fn) \
> void
> *tmp; \
>
> \
> @@ -763,6 +824,42 @@ int ff_set_common_all_samplerates(AVFilterContext *ctx)
> return ff_set_common_samplerates(ctx, ff_all_samplerates());
> }
>
> +int ff_set_common_color_spaces(AVFilterContext *ctx,
> + AVFilterFormats *color_spaces)
> +{
> + SET_COMMON_FORMATS(ctx, color_spaces, AVMEDIA_TYPE_VIDEO,
> + ff_formats_ref, ff_formats_unref);
> +}
> +
> +int ff_set_common_color_spaces_from_list(AVFilterContext *ctx,
> + const int *color_ranges)
> +{
> + return ff_set_common_color_spaces(ctx,
> ff_make_format_list(color_ranges));
> +}
> +
> +int ff_set_common_all_color_spaces(AVFilterContext *ctx)
> +{
> + return ff_set_common_color_spaces(ctx, ff_all_color_spaces());
> +}
> +
> +int ff_set_common_color_ranges(AVFilterContext *ctx,
> + AVFilterFormats *color_ranges)
> +{
> + SET_COMMON_FORMATS(ctx, color_ranges, AVMEDIA_TYPE_VIDEO,
> + ff_formats_ref, ff_formats_unref);
> +}
> +
> +int ff_set_common_color_ranges_from_list(AVFilterContext *ctx,
> + const int *color_ranges)
> +{
> + return ff_set_common_color_ranges(ctx,
> ff_make_format_list(color_ranges));
> +}
> +
> +int ff_set_common_all_color_ranges(AVFilterContext *ctx)
> +{
> + return ff_set_common_color_ranges(ctx, ff_all_color_ranges());
> +}
> +
> /**
> * 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
> @@ -817,7 +914,14 @@ int ff_default_query_formats(AVFilterContext *ctx)
> ret = ff_set_common_formats(ctx, formats);
> if (ret < 0)
> return ret;
> - if (type == AVMEDIA_TYPE_AUDIO) {
> + if (type == AVMEDIA_TYPE_VIDEO) {
> + ret = ff_set_common_all_color_spaces(ctx);
> + if (ret < 0)
> + return ret;
> + ret = ff_set_common_all_color_ranges(ctx);
> + if (ret < 0)
> + return ret;
> + } else if (type == AVMEDIA_TYPE_AUDIO) {
> ret = ff_set_common_all_channel_counts(ctx);
> if (ret < 0)
> return ret;
> @@ -935,6 +1039,22 @@ int ff_formats_check_sample_rates(void *log, const
> AVFilterFormats *fmts)
> return check_list(log, "sample rate", fmts);
> }
>
> +int ff_formats_check_color_spaces(void *log, const AVFilterFormats *fmts)
> +{
> + for (int i = 0; fmts && i < fmts->nb_formats; i++) {
> + if (fmts->formats[i] == AVCOL_SPC_RESERVED) {
> + av_log(log, AV_LOG_ERROR, "Invalid color range\n");
> + return AVERROR(EINVAL);
> + }
> + }
> + return check_list(log, "color space", fmts);
> +}
> +
> +int ff_formats_check_color_ranges(void *log, const AVFilterFormats *fmts)
> +{
> + return check_list(log, "color range", 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 d44890109e..82b3af4be1 100644
> --- a/libavfilter/formats.h
> +++ b/libavfilter/formats.h
> @@ -130,6 +130,20 @@ AVFilterChannelLayouts *ff_all_channel_counts(void);
> av_warn_unused_result
> AVFilterChannelLayouts *ff_make_channel_layout_list(const AVChannelLayout
> *fmts);
>
> +/**
> + * Construct an AVFilterFormats representing all possible color spaces.
> + *
> + * Note: This list does not include AVCOL_SPC_RESERVED.
> + */
> +av_warn_unused_result
> +AVFilterFormats *ff_all_color_spaces(void);
> +
> +/**
> + * Construct an AVFilterFormats representing all possible color ranges.
> + */
> +av_warn_unused_result
> +AVFilterFormats *ff_all_color_ranges(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,
> @@ -165,6 +179,38 @@ int ff_set_common_samplerates_from_list(AVFilterContext
> *ctx,
> av_warn_unused_result
> int ff_set_common_all_samplerates(AVFilterContext *ctx);
>
> +av_warn_unused_result
> +int ff_set_common_color_spaces(AVFilterContext *ctx,
> + AVFilterFormats *color_spaces);
> +/**
> + * Equivalent to ff_set_common_color_spaces(ctx,
> ff_make_format_list(color_spaces))
> + */
> +av_warn_unused_result
> +int ff_set_common_color_spaces_from_list(AVFilterContext *ctx,
> + const int *color_spaces);
> +
> +/**
> + * Equivalent to ff_set_common_color_spaces(ctx, ff_all_color_spaces())
> + */
> +av_warn_unused_result
> +int ff_set_common_all_color_spaces(AVFilterContext *ctx);
> +
> +av_warn_unused_result
> +int ff_set_common_color_ranges(AVFilterContext *ctx,
> + AVFilterFormats *color_ranges);
> +/**
> + * Equivalent to ff_set_common_color_ranges(ctx,
> ff_make_format_list(color_ranges))
> + */
> +av_warn_unused_result
> +int ff_set_common_color_ranges_from_list(AVFilterContext *ctx,
> + const int *color_ranges);
> +
> +/**
> + * Equivalent to ff_set_common_color_ranges(ctx, ff_all_color_ranges())
> + */
> +av_warn_unused_result
> +int ff_set_common_all_color_ranges(AVFilterContext *ctx);
> +
> /**
> * 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
> @@ -328,6 +374,14 @@ int ff_formats_check_sample_rates(void *log, const
> AVFilterFormats *fmts);
> */
> int ff_formats_check_channel_layouts(void *log, const AVFilterChannelLayouts
> *fmts);
>
> +/**
> + * Check that fmts is a valid formats list for YUV colorspace metadata.
> + *
> + * In particular, check for duplicates.
> + */
> +int ff_formats_check_color_spaces(void *log, const AVFilterFormats *fmts);
> +int ff_formats_check_color_ranges(void *log, const AVFilterFormats *fmts);
> +
> typedef struct AVFilterFormatMerger {
> unsigned offset;
> int (*merge)(void *a, void *b);
> diff --git a/libavfilter/internal.h b/libavfilter/internal.h
> index 2dbc5def0a..a6cdf9994c 100644
> --- a/libavfilter/internal.h
> +++ b/libavfilter/internal.h
> @@ -203,6 +203,12 @@ enum FilterFormatsState {
> */
> int ff_fmt_is_in(int fmt, const int *fmts);
>
> +/**
> + * Returns true if a pixel format is "regular YUV", which includes all pixel
> + * formats that are affected by YUV colorspace negotiation.
> + */
> +int ff_fmt_is_regular_yuv(enum AVPixelFormat fmt);
> +
> /* Functions to parse audio format arguments */
>
> /**
> diff --git a/libavfilter/vaapi_vpp.c b/libavfilter/vaapi_vpp.c
> index cf2592e068..59961bfa4a 100644
> --- a/libavfilter/vaapi_vpp.c
> +++ b/libavfilter/vaapi_vpp.c
> @@ -38,6 +38,10 @@ int ff_vaapi_vpp_query_formats(AVFilterContext *avctx)
> &avctx->outputs[0]->incfg.formats)) < 0)
> return err;
>
> + if ((err = ff_set_common_all_color_spaces(avctx)) < 0 ||
> + (err = ff_set_common_all_color_ranges(avctx)) < 0)
> + return err;
> +
> return 0;
> }
>
> diff --git a/libavfilter/video.c b/libavfilter/video.c
> index 42eeb98c28..243762c8fd 100644
> --- a/libavfilter/video.c
> +++ b/libavfilter/video.c
> @@ -96,6 +96,8 @@ AVFrame *ff_default_get_video_buffer2(AVFilterLink *link,
> int w, int h, int alig
> return NULL;
>
> frame->sample_aspect_ratio = link->sample_aspect_ratio;
> + frame->colorspace = link->colorspace;
> + frame->color_range = link->color_range;
>
> return frame;
> }
> diff --git a/tests/ref/fate/shortest-sub b/tests/ref/fate/shortest-sub
> index 9caee587ce..16b1324e83 100644
> --- a/tests/ref/fate/shortest-sub
> +++ b/tests/ref/fate/shortest-sub
> @@ -1,5 +1,5 @@
> -73d142a80965f9e0884a5863abde0dab *tests/data/fate/shortest-sub.matroska
> -139249 tests/data/fate/shortest-sub.matroska
> +d6608277c93097383e62388196dc62f0 *tests/data/fate/shortest-sub.matroska
> +139260 tests/data/fate/shortest-sub.matroska
> #extradata 1: 167, 0xf7272d5f
> #tb 0: 1/1000
> #media_type 0: video
More information about the ffmpeg-devel
mailing list