[FFmpeg-devel] [PATCH v3 1/2] lavfi/vaapi: Improve support for colour properties

Mark Thompson sw at jkqxz.net
Thu Feb 28 02:38:08 EET 2019


Attempts to pick the set of supported colour properties best matching the
input.  Output is then set with the same values, except for the colour
matrix which may change when converting between RGB and YUV.
---
Not much change since the version sent two months ago - rebased, and the transpose filter updated to match the others.


 libavfilter/vaapi_vpp.c            | 273 ++++++++++++++++++++++++++++-
 libavfilter/vaapi_vpp.h            |   5 +-
 libavfilter/vf_deinterlace_vaapi.c |  16 +-
 libavfilter/vf_misc_vaapi.c        |  13 +-
 libavfilter/vf_procamp_vaapi.c     |  13 +-
 libavfilter/vf_scale_vaapi.c       |  12 +-
 libavfilter/vf_transpose_vaapi.c   |  15 +-
 7 files changed, 309 insertions(+), 38 deletions(-)

diff --git a/libavfilter/vaapi_vpp.c b/libavfilter/vaapi_vpp.c
index c5bbc3b85b..f4ee622a2b 100644
--- a/libavfilter/vaapi_vpp.c
+++ b/libavfilter/vaapi_vpp.c
@@ -234,18 +234,273 @@ fail:
     return err;
 }
 
-int ff_vaapi_vpp_colour_standard(enum AVColorSpace av_cs)
+typedef struct VAAPIColourProperties {
+    VAProcColorStandardType va_color_standard;
+
+    enum AVColorPrimaries color_primaries;
+    enum AVColorTransferCharacteristic color_trc;
+    enum AVColorSpace colorspace;
+
+    uint8_t va_chroma_sample_location;
+    uint8_t va_color_range;
+
+    enum AVColorRange color_range;
+    enum AVChromaLocation chroma_sample_location;
+} VAAPIColourProperties;
+
+static const VAAPIColourProperties*
+    vaapi_vpp_find_colour_props(VAProcColorStandardType vacs)
+{
+    static const VAAPIColourProperties cs_map[] = {
+        { VAProcColorStandardBT601,       5,  6,  5 },
+        { VAProcColorStandardBT601,       6,  6,  6 },
+        { VAProcColorStandardBT709,       1,  1,  1 },
+        { VAProcColorStandardBT470M,      4,  4,  4 },
+        { VAProcColorStandardBT470BG,     5,  5,  5 },
+        { VAProcColorStandardSMPTE170M,   6,  6,  6 },
+        { VAProcColorStandardSMPTE240M,   7,  7,  7 },
+        { VAProcColorStandardGenericFilm, 8,  1,  1 },
+#if VA_CHECK_VERSION(1, 1, 0)
+        { VAProcColorStandardSRGB,        1, 13,  0 },
+        { VAProcColorStandardXVYCC601,    1, 11,  5 },
+        { VAProcColorStandardXVYCC709,    1, 11,  1 },
+        { VAProcColorStandardBT2020,      9, 14,  9 },
+#endif
+    };
+    int i;
+    for (i = 0; i < FF_ARRAY_ELEMS(cs_map); i++) {
+        if (vacs == cs_map[i].va_color_standard)
+            return &cs_map[i];
+    }
+    return NULL;
+}
+
+static void vaapi_vpp_fill_colour_standard(VAAPIColourProperties *props,
+                                           VAProcColorStandardType *vacs,
+                                           int nb_vacs)
+{
+    const VAAPIColourProperties *t;
+    int i, k, score, best_score, worst_score;
+
+    // If the driver supports explicit use of the standard values then just
+    // use them and avoid doing any mapping.  (The driver may not support
+    // some particular code point, but it still has enough information to
+    // make a better fallback choice than we do in that case.)
+#if VA_CHECK_VERSION(1, 1, 0)
+    for (i = 0; i < nb_vacs; i++) {
+        if (vacs[i] == VAProcColorStandardExplicit) {
+            props->va_color_standard = VAProcColorStandardExplicit;
+            return;
+        }
+    }
+#endif
+
+    // Give scores to the possible options and choose the lowest one.
+    // An exact match will score zero and therefore always be chosen, as
+    // will a partial match where all unmatched elements are explicitly
+    // unspecified.  (If all elements are unspecified this will use the
+    // first available value.)  If no options match at all then just
+    // pass "none" to the driver and let it make its own choice.
+    best_score = -1;
+    worst_score = 4 * (props->colorspace != AVCOL_SPC_UNSPECIFIED) +
+                  2 * (props->color_trc != AVCOL_TRC_UNSPECIFIED) +
+                      (props->color_primaries != AVCOL_PRI_UNSPECIFIED);
+    for (k = 0; k < 2; k++) {
+        for (i = 0; i < nb_vacs; i++) {
+            t = vaapi_vpp_find_colour_props(vacs[i]);
+            if (!t)
+                continue;
+
+            score = 0;
+            if (props->colorspace != AVCOL_SPC_UNSPECIFIED &&
+                props->colorspace != AVCOL_SPC_RGB)
+                score += 4 * (props->colorspace != t->colorspace);
+            if (props->color_trc != AVCOL_TRC_UNSPECIFIED)
+                score += 2 * (props->color_trc != t->color_trc);
+            if (props->color_primaries != AVCOL_PRI_UNSPECIFIED)
+                score += (props->color_primaries != t->color_primaries);
+
+            if (k == 0)  {
+                // Only include things which matched something.
+                if ((worst_score == 0 || score < worst_score) &&
+                    (best_score == -1 || score < best_score))
+                    best_score = score;
+            } else {
+                if (score == best_score) {
+                    props->va_color_standard = t->va_color_standard;
+                    return;
+                }
+            }
+        }
+
+        if (best_score == -1)
+            break;
+    }
+    props->va_color_standard = VAProcColorStandardNone;
+}
+
+static void vaapi_vpp_fill_chroma_sample_location(VAAPIColourProperties *props)
+{
+#if VA_CHECK_VERSION(1, 1, 0)
+    static const struct {
+        enum AVChromaLocation av;
+        uint8_t va;
+    } csl_map[] = {
+        { AVCHROMA_LOC_UNSPECIFIED, VA_CHROMA_SITING_UNKNOWN },
+        { AVCHROMA_LOC_LEFT,        VA_CHROMA_SITING_VERTICAL_CENTER |
+                                    VA_CHROMA_SITING_HORIZONTAL_LEFT },
+        { AVCHROMA_LOC_CENTER,      VA_CHROMA_SITING_VERTICAL_CENTER |
+                                    VA_CHROMA_SITING_HORIZONTAL_CENTER },
+        { AVCHROMA_LOC_TOPLEFT,     VA_CHROMA_SITING_VERTICAL_TOP |
+                                    VA_CHROMA_SITING_HORIZONTAL_LEFT },
+        { AVCHROMA_LOC_TOP,         VA_CHROMA_SITING_VERTICAL_TOP |
+                                    VA_CHROMA_SITING_HORIZONTAL_CENTER },
+        { AVCHROMA_LOC_BOTTOMLEFT,  VA_CHROMA_SITING_VERTICAL_BOTTOM |
+                                    VA_CHROMA_SITING_HORIZONTAL_LEFT },
+        { AVCHROMA_LOC_BOTTOM,      VA_CHROMA_SITING_VERTICAL_BOTTOM |
+                                    VA_CHROMA_SITING_HORIZONTAL_CENTER },
+    };
+    int i;
+
+    for (i = 0; i < FF_ARRAY_ELEMS(csl_map); i++) {
+        if (props->chroma_sample_location == csl_map[i].av) {
+            props->va_chroma_sample_location = csl_map[i].va;
+            return;
+        }
+    }
+    props->va_chroma_sample_location = VA_CHROMA_SITING_UNKNOWN;
+#else
+    props->va_chroma_sample_location = 0;
+#endif
+}
+
+static void vaapi_vpp_fill_colour_range(VAAPIColourProperties *props)
 {
-    switch(av_cs) {
-#define CS(av, va) case AVCOL_SPC_ ## av: return VAProcColorStandard ## va;
-        CS(BT709,     BT709);
-        CS(BT470BG,   BT601);
-        CS(SMPTE170M, SMPTE170M);
-        CS(SMPTE240M, SMPTE240M);
-#undef CS
+#if VA_CHECK_VERSION(1, 1, 0)
+    switch (props->color_range) {
+    case AVCOL_RANGE_MPEG:
+        props->va_color_range = VA_SOURCE_RANGE_REDUCED;
+        break;
+    case AVCOL_RANGE_JPEG:
+        props->va_color_range = VA_SOURCE_RANGE_FULL;
+        break;
+    case AVCOL_RANGE_UNSPECIFIED:
     default:
-        return VAProcColorStandardNone;
+        props->va_color_range = VA_SOURCE_RANGE_UNKNOWN;
     }
+#else
+    props->va_color_range = 0;
+#endif
+}
+
+static void vaapi_vpp_fill_colour_properties(AVFilterContext *avctx,
+                                             VAAPIColourProperties *props,
+                                             VAProcColorStandardType *vacs,
+                                             int nb_vacs)
+{
+    vaapi_vpp_fill_colour_standard(props, vacs, nb_vacs);
+    vaapi_vpp_fill_chroma_sample_location(props);
+    vaapi_vpp_fill_colour_range(props);
+
+    av_log(avctx, AV_LOG_DEBUG, "Mapped colour properties %s %s/%s/%s %s "
+           "to VA standard %d chroma siting %#x range %#x.\n",
+           av_color_range_name(props->color_range),
+           av_color_space_name(props->colorspace),
+           av_color_primaries_name(props->color_primaries),
+           av_color_transfer_name(props->color_trc),
+           av_chroma_location_name(props->chroma_sample_location),
+           props->va_color_standard,
+           props->va_chroma_sample_location, props->va_color_range);
+}
+
+static int vaapi_vpp_frame_is_rgb(const AVFrame *frame)
+{
+    const AVHWFramesContext *hwfc;
+    const AVPixFmtDescriptor *desc;
+    av_assert0(frame->format == AV_PIX_FMT_VAAPI &&
+               frame->hw_frames_ctx);
+    hwfc = (const AVHWFramesContext*)frame->hw_frames_ctx->data;
+    desc = av_pix_fmt_desc_get(hwfc->sw_format);
+    av_assert0(desc);
+    return !!(desc->flags & AV_PIX_FMT_FLAG_RGB);
+}
+
+int ff_vaapi_vpp_colour_properties(AVFilterContext *avctx,
+                                   VAProcPipelineParameterBuffer *params,
+                                   const AVFrame *input_frame,
+                                   AVFrame *output_frame)
+{
+    VAAPIVPPContext *ctx = avctx->priv;
+    VAAPIColourProperties input_props, output_props;
+    const VAAPIColourProperties *output_standard;
+    VAProcPipelineCaps caps;
+    VAStatus vas;
+
+    vas = vaQueryVideoProcPipelineCaps(ctx->hwctx->display, ctx->va_context,
+                                       ctx->filter_buffers, ctx->nb_filter_buffers,
+                                       &caps);
+    if (vas != VA_STATUS_SUCCESS) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to query capabilities for "
+               "colour standard support: %d (%s).\n", vas, vaErrorStr(vas));
+        return AVERROR_EXTERNAL;
+    }
+
+    input_props = (VAAPIColourProperties) {
+        .colorspace = vaapi_vpp_frame_is_rgb(input_frame)
+                ? AVCOL_SPC_RGB : input_frame->colorspace,
+        .color_primaries        = input_frame->color_primaries,
+        .color_trc              = input_frame->color_trc,
+        .color_range            = input_frame->color_range,
+        .chroma_sample_location = input_frame->chroma_location,
+    };
+
+    vaapi_vpp_fill_colour_properties(avctx, &input_props,
+                                     caps.input_color_standards,
+                                     caps.num_input_color_standards);
+
+    output_props = (VAAPIColourProperties) {
+        .colorspace = vaapi_vpp_frame_is_rgb(output_frame)
+                ? AVCOL_SPC_RGB : output_frame->colorspace,
+        .color_primaries        = output_frame->color_primaries,
+        .color_trc              = output_frame->color_trc,
+        .color_range            = output_frame->color_range,
+        .chroma_sample_location = output_frame->chroma_location,
+    };
+    vaapi_vpp_fill_colour_properties(avctx, &output_props,
+                                     caps.output_color_standards,
+                                     caps.num_output_color_standards);
+
+    // If the properties weren't filled completely in the output frame and
+    // we chose a fixed standard then fill the known values in here.
+    output_standard = vaapi_vpp_find_colour_props(output_props.va_color_standard);
+    if (output_standard) {
+        output_frame->colorspace = vaapi_vpp_frame_is_rgb(output_frame)
+                      ? AVCOL_SPC_RGB : output_standard->color_primaries;
+        output_frame->color_primaries = output_standard->color_primaries;
+        output_frame->color_trc       = output_standard->color_trc;
+    }
+
+    params->surface_color_standard = input_props.va_color_standard;
+    params->output_color_standard = output_props.va_color_standard;
+
+#if VA_CHECK_VERSION(1, 1, 0)
+    params->input_color_properties = (VAProcColorProperties) {
+        .chroma_sample_location   = input_props.va_chroma_sample_location,
+        .color_range              = input_props.va_color_range,
+        .colour_primaries         = input_props.color_primaries,
+        .transfer_characteristics = input_props.color_trc,
+        .matrix_coefficients      = input_props.colorspace,
+    };
+    params->output_color_properties = (VAProcColorProperties) {
+        .chroma_sample_location   = output_props.va_chroma_sample_location,
+        .color_range              = output_props.va_color_range,
+        .colour_primaries         = output_props.color_primaries,
+        .transfer_characteristics = output_props.color_trc,
+        .matrix_coefficients      = output_props.colorspace,
+    };
+#endif
+
+    return 0;
 }
 
 int ff_vaapi_vpp_make_param_buffers(AVFilterContext *avctx,
diff --git a/libavfilter/vaapi_vpp.h b/libavfilter/vaapi_vpp.h
index 96f720f07d..cb95737ccb 100644
--- a/libavfilter/vaapi_vpp.h
+++ b/libavfilter/vaapi_vpp.h
@@ -67,7 +67,10 @@ int ff_vaapi_vpp_config_input(AVFilterLink *inlink);
 
 int ff_vaapi_vpp_config_output(AVFilterLink *outlink);
 
-int ff_vaapi_vpp_colour_standard(enum AVColorSpace av_cs);
+int ff_vaapi_vpp_colour_properties(AVFilterContext *avctx,
+                                   VAProcPipelineParameterBuffer *params,
+                                   const AVFrame *input_frame,
+                                   AVFrame *output_frame);
 
 int ff_vaapi_vpp_make_param_buffers(AVFilterContext *avctx,
                                     int type,
diff --git a/libavfilter/vf_deinterlace_vaapi.c b/libavfilter/vf_deinterlace_vaapi.c
index f67a1c8e79..665e395225 100644
--- a/libavfilter/vf_deinterlace_vaapi.c
+++ b/libavfilter/vf_deinterlace_vaapi.c
@@ -238,6 +238,10 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
             goto fail;
         }
 
+        err = av_frame_copy_props(output_frame, input_frame);
+        if (err < 0)
+            goto fail;
+
         output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];
         av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for "
                "deinterlace output.\n", output_surface);
@@ -253,16 +257,18 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
 
         params.surface = input_surface;
         params.surface_region = &input_region;
-        params.surface_color_standard =
-            ff_vaapi_vpp_colour_standard(input_frame->colorspace);
 
         params.output_region = NULL;
         params.output_background_color = VAAPI_VPP_BACKGROUND_BLACK;
-        params.output_color_standard = params.surface_color_standard;
 
         params.pipeline_flags = 0;
         params.filter_flags   = VA_FRAME_PICTURE;
 
+        err = ff_vaapi_vpp_colour_properties(avctx, &params,
+                                             input_frame, output_frame);
+        if (err < 0)
+            goto fail;
+
         if (!ctx->auto_enable || input_frame->interlaced_frame) {
             vas = vaMapBuffer(vpp_ctx->hwctx->display, vpp_ctx->filter_buffers[0],
                               &filter_params_addr);
@@ -305,10 +311,6 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
         if (err < 0)
             goto fail;
 
-        err = av_frame_copy_props(output_frame, input_frame);
-        if (err < 0)
-            goto fail;
-
         if (ctx->field_rate == 2) {
             if (field == 0)
                 output_frame->pts = 2 * input_frame->pts;
diff --git a/libavfilter/vf_misc_vaapi.c b/libavfilter/vf_misc_vaapi.c
index e227c9ff6b..0962fe7b69 100644
--- a/libavfilter/vf_misc_vaapi.c
+++ b/libavfilter/vf_misc_vaapi.c
@@ -153,6 +153,10 @@ static int misc_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
         goto fail;
     }
 
+    err = av_frame_copy_props(output_frame, input_frame);
+    if (err < 0)
+        goto fail;
+
     output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];
     av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for misc vpp output.\n",
            output_surface);
@@ -170,23 +174,22 @@ static int misc_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
     }
     params.surface = input_surface;
     params.surface_region = &input_region;
-    params.surface_color_standard =
-        ff_vaapi_vpp_colour_standard(input_frame->colorspace);
 
     params.output_region = NULL;
     params.output_background_color = VAAPI_VPP_BACKGROUND_BLACK;
-    params.output_color_standard = params.surface_color_standard;
 
     params.pipeline_flags = 0;
     params.filter_flags = VA_FRAME_PICTURE;
 
-    err = ff_vaapi_vpp_render_picture(avctx, &params, output_surface);
+    err = ff_vaapi_vpp_colour_properties(avctx, &params,
+                                         input_frame, output_frame);
     if (err < 0)
         goto fail;
 
-    err = av_frame_copy_props(output_frame, input_frame);
+    err = ff_vaapi_vpp_render_picture(avctx, &params, output_surface);
     if (err < 0)
         goto fail;
+
     av_frame_free(&input_frame);
 
     av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
diff --git a/libavfilter/vf_procamp_vaapi.c b/libavfilter/vf_procamp_vaapi.c
index 46f3ab6465..8c3ec94038 100644
--- a/libavfilter/vf_procamp_vaapi.c
+++ b/libavfilter/vf_procamp_vaapi.c
@@ -154,6 +154,10 @@ static int procamp_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame
         goto fail;
     }
 
+    err = av_frame_copy_props(output_frame, input_frame);
+    if (err < 0)
+        goto fail;
+
     output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];
     av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for procamp output.\n",
            output_surface);
@@ -167,12 +171,9 @@ static int procamp_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame
 
     params.surface = input_surface;
     params.surface_region = &input_region;
-    params.surface_color_standard =
-        ff_vaapi_vpp_colour_standard(input_frame->colorspace);
 
     params.output_region = NULL;
     params.output_background_color = VAAPI_VPP_BACKGROUND_BLACK;
-    params.output_color_standard = params.surface_color_standard;
 
     params.pipeline_flags = 0;
     params.filter_flags = VA_FRAME_PICTURE;
@@ -180,13 +181,15 @@ static int procamp_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame
     params.filters     = &vpp_ctx->filter_buffers[0];
     params.num_filters = 1;
 
-    err = ff_vaapi_vpp_render_picture(avctx, &params, output_surface);
+    err = ff_vaapi_vpp_colour_properties(avctx, &params,
+                                         input_frame, output_frame);
     if (err < 0)
         goto fail;
 
-    err = av_frame_copy_props(output_frame, input_frame);
+    err = ff_vaapi_vpp_render_picture(avctx, &params, output_surface);
     if (err < 0)
         goto fail;
+
     av_frame_free(&input_frame);
 
     av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
diff --git a/libavfilter/vf_scale_vaapi.c b/libavfilter/vf_scale_vaapi.c
index 3699363140..b56217c56d 100644
--- a/libavfilter/vf_scale_vaapi.c
+++ b/libavfilter/vf_scale_vaapi.c
@@ -112,6 +112,10 @@ static int scale_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
         goto fail;
     }
 
+    err = av_frame_copy_props(output_frame, input_frame);
+    if (err < 0)
+        goto fail;
+
     output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];
     av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for scale output.\n",
            output_surface);
@@ -129,21 +133,19 @@ static int scale_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
 
     params.surface = input_surface;
     params.surface_region = &input_region;
-    params.surface_color_standard =
-        ff_vaapi_vpp_colour_standard(input_frame->colorspace);
 
     params.output_region = 0;
     params.output_background_color = VAAPI_VPP_BACKGROUND_BLACK;
-    params.output_color_standard = params.surface_color_standard;
 
     params.pipeline_flags = 0;
     params.filter_flags = ctx->mode;
 
-    err = ff_vaapi_vpp_render_picture(avctx, &params, output_surface);
+    err = ff_vaapi_vpp_colour_properties(avctx, &params,
+                                         input_frame, output_frame);
     if (err < 0)
         goto fail;
 
-    err = av_frame_copy_props(output_frame, input_frame);
+    err = ff_vaapi_vpp_render_picture(avctx, &params, output_surface);
     if (err < 0)
         goto fail;
 
diff --git a/libavfilter/vf_transpose_vaapi.c b/libavfilter/vf_transpose_vaapi.c
index 0e2acc9983..6536fa8fe0 100644
--- a/libavfilter/vf_transpose_vaapi.c
+++ b/libavfilter/vf_transpose_vaapi.c
@@ -150,6 +150,10 @@ static int transpose_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_fra
         goto fail;
     }
 
+    err = av_frame_copy_props(output_frame, input_frame);
+    if (err < 0)
+        goto fail;
+
     output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];
     av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for transpose vpp output.\n",
            output_surface);
@@ -176,20 +180,19 @@ static int transpose_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_fra
 
     params.surface = input_surface;
     params.surface_region = &input_region;
-    params.surface_color_standard =
-        ff_vaapi_vpp_colour_standard(input_frame->colorspace);
 
     params.output_region = &output_region;
     params.output_background_color = VAAPI_VPP_BACKGROUND_BLACK;
-    params.output_color_standard = params.surface_color_standard;
+
+    err = ff_vaapi_vpp_colour_properties(avctx, &params,
+                                         input_frame, output_frame);
+     if (err < 0)
+         goto fail;
 
     err = ff_vaapi_vpp_render_picture(avctx, &params, output_surface);
     if (err < 0)
         goto fail;
 
-    err = av_frame_copy_props(output_frame, input_frame);
-    if (err < 0)
-        goto fail;
     av_frame_free(&input_frame);
 
     av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
-- 
2.19.2



More information about the ffmpeg-devel mailing list