[FFmpeg-devel,v2,11/36] vaapi_encode: Choose profiles dynamically

Submitted by Mark Thompson on June 7, 2018, 11:43 p.m.

Details

Message ID 20180607234331.32139-12-sw@jkqxz.net
State New
Headers show

Commit Message

Mark Thompson June 7, 2018, 11:43 p.m.
Previously there was one fixed choice for each codec (e.g. H.265 -> Main
profile), and using anything else then required an explicit option from
the user.  This changes to selecting the profile based on the input format
and the set of profiles actually supported by the driver (e.g. P010 input
will choose Main 10 profile for H.265 if the driver supports it).

The entrypoint and render target format are also chosen dynamically in the
same way, removing those explicit selections from the per-codec code.
---
 doc/encoders.texi               |   3 +
 libavcodec/vaapi_encode.c       | 271 ++++++++++++++++++++++++++++++++--------
 libavcodec/vaapi_encode.h       |  43 +++++--
 libavcodec/vaapi_encode_h264.c  |  45 ++-----
 libavcodec/vaapi_encode_h265.c  |  35 ++----
 libavcodec/vaapi_encode_mjpeg.c |  13 +-
 libavcodec/vaapi_encode_mpeg2.c |  36 ++----
 libavcodec/vaapi_encode_vp8.c   |  11 +-
 libavcodec/vaapi_encode_vp9.c   |  34 ++---
 9 files changed, 310 insertions(+), 181 deletions(-)

Comments

Haihao Xiang June 12, 2018, 7:22 a.m.
On Fri, 2018-06-08 at 00:43 +0100, Mark Thompson wrote:
> Previously there was one fixed choice for each codec (e.g. H.265 -> Main

> profile), and using anything else then required an explicit option from

> the user.  This changes to selecting the profile based on the input format

> and the set of profiles actually supported by the driver (e.g. P010 input

> will choose Main 10 profile for H.265 if the driver supports it).

> 

> The entrypoint and render target format are also chosen dynamically in the

> same way, removing those explicit selections from the per-codec code.

> ---

>  doc/encoders.texi               |   3 +

>  libavcodec/vaapi_encode.c       | 271 ++++++++++++++++++++++++++++++++-------

> -

>  libavcodec/vaapi_encode.h       |  43 +++++--

>  libavcodec/vaapi_encode_h264.c  |  45 ++-----

>  libavcodec/vaapi_encode_h265.c  |  35 ++----

>  libavcodec/vaapi_encode_mjpeg.c |  13 +-

>  libavcodec/vaapi_encode_mpeg2.c |  36 ++----

>  libavcodec/vaapi_encode_vp8.c   |  11 +-

>  libavcodec/vaapi_encode_vp9.c   |  34 ++---

>  9 files changed, 310 insertions(+), 181 deletions(-)

> 

> diff --git a/doc/encoders.texi b/doc/encoders.texi

> index 7b095754d1..16be6359b3 100644

> --- a/doc/encoders.texi

> +++ b/doc/encoders.texi

> @@ -2565,6 +2565,9 @@ The following standard libavcodec options are used:

>  @option{bf} / @option{max_b_frames}

>  @item

>  @option{profile}

> +

> +If not set, this will be determined automatically from the format of the

> input

> +frames and the profiles supported by the driver.

>  @item

>  @option{level}

>  @item

> diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c

> index 27ce792fbe..6104470b31 100644

> --- a/libavcodec/vaapi_encode.c

> +++ b/libavcodec/vaapi_encode.c

> @@ -983,70 +983,247 @@ static av_cold void

> vaapi_encode_add_global_param(AVCodecContext *avctx,

>      ++ctx->nb_global_params;

>  }

>  

> -static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)

> +typedef struct VAAPIEncodeRTFormat {

> +    const char *name;

> +    unsigned int value;

> +    int depth;

> +    int components;


How about adding a prefix of 'nb_' to this field? I think nb_components is more
readable. 

> +    int log2_chroma_w;

> +    int log2_chroma_h;

> +} VAAPIEncodeRTFormat;

> +

> +static const VAAPIEncodeRTFormat vaapi_encode_rt_formats[] = {

> +    { "YUV400",    VA_RT_FORMAT_YUV400,        8, 1,      },

> +    { "YUV420",    VA_RT_FORMAT_YUV420,        8, 3, 1, 1 },

> +    { "YUV422",    VA_RT_FORMAT_YUV422,        8, 3, 1, 0 },

> +    { "YUV444",    VA_RT_FORMAT_YUV444,        8, 3, 0, 0 },

> +    { "YUV411",    VA_RT_FORMAT_YUV411,        8, 3, 2, 0 },

> +#if VA_CHECK_VERSION(0, 38, 1)

> +    { "YUV420_10", VA_RT_FORMAT_YUV420_10BPP, 10, 3, 1, 1 },

> +#endif

> +};

> +

> +static const VAEntrypoint vaapi_encode_entrypoints_normal[] = {

> +    VAEntrypointEncSlice,

> +    VAEntrypointEncPicture,

> +#if VA_CHECK_VERSION(0, 39, 2)

> +    VAEntrypointEncSliceLP,

> +#endif

> +    0

> +};

> +#if VA_CHECK_VERSION(0, 39, 2)

> +static const VAEntrypoint vaapi_encode_entrypoints_low_power[] = {

> +    VAEntrypointEncSliceLP,

> +    0

> +};

> +#endif

> +

> +static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)

>  {

> -    VAAPIEncodeContext *ctx = avctx->priv_data;

> +    VAAPIEncodeContext      *ctx = avctx->priv_data;

> +    VAProfile    *va_profiles    = NULL;

> +    VAEntrypoint *va_entrypoints = NULL;

>      VAStatus vas;

> -    int i, n, err;

> -    VAProfile    *profiles    = NULL;

> -    VAEntrypoint *entrypoints = NULL;

> -    VAConfigAttrib attr[] = {

> -        { VAConfigAttribRTFormat         },

> -        { VAConfigAttribRateControl      },

> -        { VAConfigAttribEncMaxRefFrames  },

> -        { VAConfigAttribEncPackedHeaders },

> -    };

> +    const VAEntrypoint *usable_entrypoints;

> +    const VAAPIEncodeProfile *profile;

> +    const AVPixFmtDescriptor *desc;

> +    VAConfigAttrib rt_format_attr;

> +    const VAAPIEncodeRTFormat *rt_format;

> +    int i, j, n, depth, err;

> +

> +

> +    if (ctx->low_power) {

> +#if VA_CHECK_VERSION(0, 39, 2)

> +        usable_entrypoints = vaapi_encode_entrypoints_low_power;

> +#else

> +        av_log(avctx, AV_LOG_ERROR, "Low-power encoding is not "

> +               "supported with this VAAPI version.\n");


Is it possible to report the minimal VAAPI version in the log in case user
doesn't know the requirement on vaapi version 0.39.2?

> +        return AVERROR(EINVAL);

> +#endif

> +    } else {

> +        usable_entrypoints = vaapi_encode_entrypoints_normal;

> +    }

> +

> +    desc = av_pix_fmt_desc_get(ctx->input_frames->sw_format);

> +    if (!desc) {

> +        av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%d).\n",

> +               ctx->input_frames->sw_format);

> +        return AVERROR(EINVAL);

> +    }

> +    depth = desc->comp[0].depth;

> +    for (i = 1; i < desc->nb_components; i++) {

> +        if (desc->comp[i].depth != depth) {

> +            av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%s).\n",

> +                   desc->name);

> +            return AVERROR(EINVAL);

> +        }

> +    }

> +    av_log(avctx, AV_LOG_VERBOSE, "Input surface format is %s.\n",

> +           desc->name);

>  

>      n = vaMaxNumProfiles(ctx->hwctx->display);

> -    profiles = av_malloc_array(n, sizeof(VAProfile));

> -    if (!profiles) {

> +    va_profiles = av_malloc_array(n, sizeof(VAProfile));

> +    if (!va_profiles) {

>          err = AVERROR(ENOMEM);

>          goto fail;

>      }

> -    vas = vaQueryConfigProfiles(ctx->hwctx->display, profiles, &n);

> +    vas = vaQueryConfigProfiles(ctx->hwctx->display, va_profiles, &n);

>      if (vas != VA_STATUS_SUCCESS) {

> -        av_log(ctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",

> +        av_log(avctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",

>                 vas, vaErrorStr(vas));

> -        err = AVERROR(ENOSYS);

> +        err = AVERROR_EXTERNAL;

>          goto fail;

>      }

> -    for (i = 0; i < n; i++) {

> -        if (profiles[i] == ctx->va_profile)

> -            break;

> +

> +    av_assert0(ctx->codec->profiles);

> +    for (i = 0; (ctx->codec->profiles[i].av_profile !=

> +                 FF_PROFILE_UNKNOWN); i++) {

> +        profile = &ctx->codec->profiles[i];

> +        if (depth               != profile->depth ||

> +            desc->nb_components != profile->components)

> +            continue;

> +        if (desc->nb_components > 1 &&

> +            (desc->log2_chroma_w != profile->log2_chroma_w ||

> +             desc->log2_chroma_h != profile->log2_chroma_h))

> +            continue;

> +        if (avctx->profile != profile->av_profile &&

> +            avctx->profile != FF_PROFILE_UNKNOWN)

> +            continue;

> +

> +        for (j = 0; j < n; j++) {

> +            if (va_profiles[j] == profile->va_profile)

> +                break;

> +        }

> +        if (j >= n) {

> +            av_log(avctx, AV_LOG_VERBOSE, "Matching profile %d is "

> +                   "not supported by driver.\n", profile->va_profile);


Is it possible to report the profile string in the log as what you did below?

> +            continue;

> +        }

> +

> +        ctx->profile = profile;

> +        break;

>      }

> -    if (i >= n) {

> -        av_log(ctx, AV_LOG_ERROR, "Encoding profile not found (%d).\n",

> -               ctx->va_profile);

> -        err = AVERROR(ENOSYS);

> -        goto fail;

> +    if (!ctx->profile) {

> +        av_log(avctx, AV_LOG_ERROR, "No usable encoding profile found.\n");

> +        return AVERROR(ENOSYS);


Set err to AVERROR(ENOSYS) then goto fail, otherwise it will result in memory
leak.

>      }

>  

> +    avctx->profile  = profile->av_profile;

> +    ctx->va_profile = profile->va_profile;

> +#if VA_CHECK_VERSION(1, 0, 0)

> +    av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %s (%d).\n",

> +           vaProfileStr(ctx->va_profile), ctx->va_profile);

> +#else

> +    av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %d.\n",

> +           ctx->va_profile);

> +#endif

> +

>      n = vaMaxNumEntrypoints(ctx->hwctx->display);

> -    entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));

> -    if (!entrypoints) {

> +    va_entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));

> +    if (!va_entrypoints) {

>          err = AVERROR(ENOMEM);

>          goto fail;

>      }

>      vas = vaQueryConfigEntrypoints(ctx->hwctx->display, ctx->va_profile,

> -                                   entrypoints, &n);

> +                                   va_entrypoints, &n);

>      if (vas != VA_STATUS_SUCCESS) {

> -        av_log(ctx, AV_LOG_ERROR, "Failed to query entrypoints for "

> +        av_log(avctx, AV_LOG_ERROR, "Failed to query entrypoints for "

>                 "profile %u: %d (%s).\n", ctx->va_profile,


Log profile string?

>                 vas, vaErrorStr(vas));

> -        err = AVERROR(ENOSYS);

> +        err = AVERROR_EXTERNAL;

>          goto fail;

>      }

> +

>      for (i = 0; i < n; i++) {

> -        if (entrypoints[i] == ctx->va_entrypoint)

> +        for (j = 0; usable_entrypoints[j]; j++) {

> +            if (va_entrypoints[i] == usable_entrypoints[j])

> +                break;

> +        }

> +        if (usable_entrypoints[j])

>              break;

>      }

>      if (i >= n) {

> -        av_log(ctx, AV_LOG_ERROR, "Encoding entrypoint not found "

> -               "(%d / %d).\n", ctx->va_profile, ctx->va_entrypoint);

> +        av_log(avctx, AV_LOG_ERROR, "No usable encoding entrypoint found "

> +               "for profile %d.\n", ctx->va_profile);


Log profile string?

> +        err = AVERROR(ENOSYS);

> +        goto fail;

> +    }

> +

> +    ctx->va_entrypoint = va_entrypoints[i];

> +#if VA_CHECK_VERSION(1, 0, 0)

> +    av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %s (%d).\n",

> +           vaEntrypointStr(ctx->va_entrypoint), ctx->va_entrypoint);

> +#else

> +    av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %d.\n",

> +           ctx->va_entrypoint);

> +#endif

> +

> +    for (i = 0; i < FF_ARRAY_ELEMS(vaapi_encode_rt_formats); i++) {

> +        rt_format = &vaapi_encode_rt_formats[i];

> +        if (rt_format->depth         == depth &&

> +            rt_format->components    == profile->components    &&

> +            rt_format->log2_chroma_w == profile->log2_chroma_w &&

> +            rt_format->log2_chroma_h == profile->log2_chroma_h)

> +            break;

> +    }

> +    if (i >= FF_ARRAY_ELEMS(vaapi_encode_rt_formats)) {

> +        av_log(avctx, AV_LOG_ERROR, "No usable render target format "

> +               "found for profile %d entrypoint %d.\n",

> +               ctx->va_profile, ctx->va_entrypoint);


Log profile and entrypoint strings?

>          err = AVERROR(ENOSYS);

>          goto fail;

>      }

>  

> +    rt_format_attr = (VAConfigAttrib) { VAConfigAttribRTFormat };

> +    vas = vaGetConfigAttributes(ctx->hwctx->display,

> +                                ctx->va_profile, ctx->va_entrypoint,

> +                                &rt_format_attr, 1);

> +    if (vas != VA_STATUS_SUCCESS) {

> +        av_log(avctx, AV_LOG_ERROR, "Failed to query RT format "

> +               "config attribute: %d (%s).\n", vas, vaErrorStr(vas));

> +        err = AVERROR_EXTERNAL;

> +        goto fail;

> +    }

> +

> +    if (rt_format_attr.value == VA_ATTRIB_NOT_SUPPORTED) {

> +        av_log(avctx, AV_LOG_VERBOSE, "RT format config attribute not "

> +               "supported by driver: assuming surface RT format %s "

> +               "is valid.\n", rt_format->name);


I think it would be better to log it as a warning. 

> +    } else if (!(rt_format_attr.value & rt_format->value)) {

> +        av_log(avctx, AV_LOG_ERROR, "Surface RT format %s not supported "

> +               "by driver for encoding profile %d entrypoint %d.\n",

> +               rt_format->name, ctx->va_profile, ctx->va_entrypoint);

> +        err = AVERROR(ENOSYS);

> +        goto fail;

> +    } else {

> +        av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI render target "

> +               "format %s (%#x).\n", rt_format->name, rt_format->value);

> +        ctx->config_attributes[ctx->nb_config_attributes++] =

> +            (VAConfigAttrib) {

> +            .type  = VAConfigAttribRTFormat,

> +            .value = rt_format->value,

> +        };

> +    }

> +

> +    err = 0;

> +fail:

> +    av_freep(&va_profiles);

> +    av_freep(&va_entrypoints);

> +    return err;

> +}

> +

> +static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)

> +{

> +    VAAPIEncodeContext *ctx = avctx->priv_data;

> +    VAStatus vas;

> +    int i;

> +

> +    VAConfigAttrib attr[] = {

> +        { VAConfigAttribRateControl      },

> +        { VAConfigAttribEncMaxRefFrames  },

> +        { VAConfigAttribEncPackedHeaders },

> +    };

> +

>      vas = vaGetConfigAttributes(ctx->hwctx->display,

>                                  ctx->va_profile, ctx->va_entrypoint,

>                                  attr, FF_ARRAY_ELEMS(attr));

> @@ -1066,20 +1243,6 @@ static av_cold int

> vaapi_encode_config_attributes(AVCodecContext *avctx)

>              continue;

>          }

>          switch (attr[i].type) {

> -        case VAConfigAttribRTFormat:

> -            if (!(ctx->va_rt_format & attr[i].value)) {

> -                av_log(avctx, AV_LOG_ERROR, "Surface RT format %#x "

> -                       "is not supported (mask %#x).\n",

> -                       ctx->va_rt_format, attr[i].value);

> -                err = AVERROR(EINVAL);

> -                goto fail;

> -            }

> -            ctx->config_attributes[ctx->nb_config_attributes++] =

> -                (VAConfigAttrib) {

> -                .type  = VAConfigAttribRTFormat,

> -                .value = ctx->va_rt_format,

> -            };

> -            break;

>          case VAConfigAttribRateControl:

>              // Hack for backward compatibility: CBR was the only

>              // usable RC mode for a long time, so old drivers will

> @@ -1098,8 +1261,7 @@ static av_cold int

> vaapi_encode_config_attributes(AVCodecContext *avctx)

>                  av_log(avctx, AV_LOG_ERROR, "Rate control mode %#x "

>                         "is not supported (mask: %#x).\n",

>                         ctx->va_rc_mode, attr[i].value);

> -                err = AVERROR(EINVAL);

> -                goto fail;

> +                return AVERROR(EINVAL);

>              }

>              ctx->config_attributes[ctx->nb_config_attributes++] =

>                  (VAConfigAttrib) {

> @@ -1115,8 +1277,7 @@ static av_cold int

> vaapi_encode_config_attributes(AVCodecContext *avctx)

>              if (avctx->gop_size > 1 && ref_l0 < 1) {

>                  av_log(avctx, AV_LOG_ERROR, "P frames are not "

>                         "supported (%#x).\n", attr[i].value);

> -                err = AVERROR(EINVAL);

> -                goto fail;

> +                return AVERROR(EINVAL);

>              }

>              if (avctx->max_b_frames > 0 && ref_l1 < 1) {

>                  av_log(avctx, AV_LOG_WARNING, "B frames are not "

> @@ -1148,11 +1309,7 @@ static av_cold int

> vaapi_encode_config_attributes(AVCodecContext *avctx)

>          }

>      }

>  

> -    err = 0;

> -fail:

> -    av_freep(&profiles);

> -    av_freep(&entrypoints);

> -    return err;

> +    return 0;

>  }

>  

>  static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)

> @@ -1407,6 +1564,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)

>      ctx->device = (AVHWDeviceContext*)ctx->device_ref->data;

>      ctx->hwctx = ctx->device->hwctx;

>  

> +    err = vaapi_encode_profile_entrypoint(avctx);

> +    if (err < 0)

> +        goto fail;

> +

>      err = vaapi_encode_config_attributes(avctx);

>      if (err < 0)

>          goto fail;

> diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h

> index 54dc4a475e..212ab6726d 100644

> --- a/libavcodec/vaapi_encode.h

> +++ b/libavcodec/vaapi_encode.h

> @@ -23,6 +23,10 @@

>  

>  #include <va/va.h>

>  

> +#if VA_CHECK_VERSION(1, 0, 0)

> +#include <va/va_str.h>

> +#endif

> +

>  #include "libavutil/hwcontext.h"

>  #include "libavutil/hwcontext_vaapi.h"

>  

> @@ -86,18 +90,32 @@ typedef struct VAAPIEncodePicture {

>      VAAPIEncodeSlice *slices;

>  } VAAPIEncodePicture;

>  

> +typedef struct VAAPIEncodeProfile {

> +    // lavc profile value (FF_PROFILE_*).

> +    int       av_profile;

> +    // Supported bit depth.

> +    int       depth;

> +    // Number of components.

> +    int       components;

> +    // Chroma subsampling in width dimension.

> +    int       log2_chroma_w;

> +    // Chroma subsampling in height dimension.

> +    int       log2_chroma_h;

> +    // VAAPI profile value.

> +    VAProfile va_profile;

> +} VAAPIEncodeProfile;

> +

>  typedef struct VAAPIEncodeContext {

>      const AVClass *class;

>  

>      // Codec-specific hooks.

>      const struct VAAPIEncodeType *codec;

>  

> -    // Encoding profile (VAProfileXXX).

> -    VAProfile       va_profile;

> -    // Encoding entrypoint (usually VAEntryointEncSlice).

> -    VAEntrypoint    va_entrypoint;

> -    // Surface colour/sampling format (usually VA_RT_FORMAT_YUV420).

> -    unsigned int    va_rt_format;

> +    // Global options.

> +

> +    // Use low power encoding mode.

> +    int             low_power;

> +

>      // Rate control mode.

>      unsigned int    va_rc_mode;

>      // Supported packed headers (initially the desired set, modified

> @@ -113,6 +131,14 @@ typedef struct VAAPIEncodeContext {

>      // Everything above this point must be set before calling

>      // ff_vaapi_encode_init().

>  

> +    // Chosen encoding profile details.

> +    const VAAPIEncodeProfile *profile;

> +

> +    // Encoding profile (VAProfile*).

> +    VAProfile       va_profile;

> +    // Encoding entrypoint (VAEntryoint*).

> +    VAEntrypoint    va_entrypoint;

> +

>      // Configuration attributes to use when creating va_config.

>      VAConfigAttrib  config_attributes[MAX_CONFIG_ATTRIBUTES];

>      int          nb_config_attributes;

> @@ -204,8 +230,11 @@ typedef struct VAAPIEncodeContext {

>      int end_of_stream;

>  } VAAPIEncodeContext;

>  

> -

>  typedef struct VAAPIEncodeType {

> +    // List of supported profiles and corresponding VAAPI profiles.

> +    // (Must end with FF_PROFILE_UNKNOWN.)

> +    const VAAPIEncodeProfile *profiles;

> +

>      // Perform any extra codec-specific configuration after the

>      // codec context is initialised (set up the private data and

>      // add any necessary global parameters).

> diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c

> index 8f999b2311..ab8bf13ef3 100644

> --- a/libavcodec/vaapi_encode_h264.c

> +++ b/libavcodec/vaapi_encode_h264.c

> @@ -866,7 +866,17 @@ static av_cold int

> vaapi_encode_h264_configure(AVCodecContext *avctx)

>      return 0;

>  }

>  

> +static const VAAPIEncodeProfile vaapi_encode_h264_profiles[] = {

> +    { FF_PROFILE_H264_HIGH, 8, 3, 1, 1, VAProfileH264High },

> +    { FF_PROFILE_H264_MAIN, 8, 3, 1, 1, VAProfileH264Main },

> +    { FF_PROFILE_H264_CONSTRAINED_BASELINE,

> +                            8, 3, 1, 1, VAProfileH264ConstrainedBaseline },

> +    { FF_PROFILE_UNKNOWN }

> +};

> +

>  static const VAAPIEncodeType vaapi_encode_type_h264 = {

> +    .profiles              = vaapi_encode_h264_profiles,

> +

>      .configure             = &vaapi_encode_h264_configure,

>  

>      .sequence_params_size  = sizeof(VAEncSequenceParameterBufferH264),

> @@ -899,30 +909,17 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext

> *avctx)

>      if (avctx->level == FF_LEVEL_UNKNOWN)

>          avctx->level = priv->level;

>  

> +    // Reject unsupported profiles.

>      switch (avctx->profile) {

>      case FF_PROFILE_H264_BASELINE:

>          av_log(avctx, AV_LOG_WARNING, "H.264 baseline profile is not "

>                 "supported, using constrained baseline profile instead.\n");

>          avctx->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE;

> -    case FF_PROFILE_H264_CONSTRAINED_BASELINE:

> -        ctx->va_profile = VAProfileH264ConstrainedBaseline;

> -        if (avctx->max_b_frames != 0) {

> -            avctx->max_b_frames = 0;

> -            av_log(avctx, AV_LOG_WARNING, "H.264 constrained baseline profile

> "

> -                   "doesn't support encoding with B frames, disabling

> them.\n");

> -        }

> -        break;

> -    case FF_PROFILE_H264_MAIN:

> -        ctx->va_profile = VAProfileH264Main;

>          break;

>      case FF_PROFILE_H264_EXTENDED:

>          av_log(avctx, AV_LOG_ERROR, "H.264 extended profile "

>                 "is not supported.\n");

>          return AVERROR_PATCHWELCOME;

> -    case FF_PROFILE_UNKNOWN:

> -    case FF_PROFILE_H264_HIGH:

> -        ctx->va_profile = VAProfileH264High;

> -        break;

>      case FF_PROFILE_H264_HIGH_10:

>      case FF_PROFILE_H264_HIGH_10_INTRA:

>          av_log(avctx, AV_LOG_ERROR, "H.264 10-bit profiles "

> @@ -937,25 +934,9 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext

> *avctx)

>          av_log(avctx, AV_LOG_ERROR, "H.264 non-4:2:0 profiles "

>                 "are not supported.\n");

>          return AVERROR_PATCHWELCOME;

> -    default:

> -        av_log(avctx, AV_LOG_ERROR, "Unknown H.264 profile %d.\n",

> -               avctx->profile);

> -        return AVERROR(EINVAL);

> -    }

> -    if (priv->low_power) {

> -#if VA_CHECK_VERSION(0, 39, 2)

> -        ctx->va_entrypoint = VAEntrypointEncSliceLP;

> -#else

> -        av_log(avctx, AV_LOG_ERROR, "Low-power encoding is not "

> -               "supported with this VAAPI version.\n");

> -        return AVERROR(EINVAL);

> -#endif

> -    } else {

> -        ctx->va_entrypoint = VAEntrypointEncSlice;

>      }

>  

> -    // Only 8-bit encode is supported.

> -    ctx->va_rt_format = VA_RT_FORMAT_YUV420;

> +    ctx->low_power = priv->low_power;

>  

>      if (avctx->bit_rate > 0) {

>          if (avctx->rc_max_rate == avctx->bit_rate)

> @@ -1022,7 +1003,7 @@ static const AVOption vaapi_encode_h264_options[] = {

>  

>      { "profile", "Set profile (profile_idc and constraint_set*_flag)",

>        OFFSET(profile), AV_OPT_TYPE_INT,

> -      { .i64 = FF_PROFILE_H264_HIGH }, 0x0000, 0xffff, FLAGS, "profile" },

> +      { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0xffff, FLAGS,

> "profile" },

>  

>  #define PROFILE(name, value)  name, NULL, 0, AV_OPT_TYPE_CONST, \

>        { .i64 = value }, 0, 0, FLAGS, "profile"

> diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c

> index 8f191efc4b..9fa16593d0 100644

> --- a/libavcodec/vaapi_encode_h265.c

> +++ b/libavcodec/vaapi_encode_h265.c

> @@ -1025,7 +1025,17 @@ static av_cold int

> vaapi_encode_h265_configure(AVCodecContext *avctx)

>      return 0;

>  }

>  

> +static const VAAPIEncodeProfile vaapi_encode_h265_profiles[] = {

> +    { FF_PROFILE_HEVC_MAIN,     8, 3, 1, 1, VAProfileHEVCMain       },

> +#if VA_CHECK_VERSION(0, 37, 0)

> +    { FF_PROFILE_HEVC_MAIN_10, 10, 3, 1, 1, VAProfileHEVCMain10     },

> +#endif

> +    { FF_PROFILE_UNKNOWN }

> +};

> +

>  static const VAAPIEncodeType vaapi_encode_type_h265 = {

> +    .profiles              = vaapi_encode_h265_profiles,

> +

>      .configure             = &vaapi_encode_h265_configure,

>  

>      .sequence_params_size  = sizeof(VAEncSequenceParameterBufferHEVC),

> @@ -1058,29 +1068,6 @@ static av_cold int

> vaapi_encode_h265_init(AVCodecContext *avctx)

>      if (avctx->level == FF_LEVEL_UNKNOWN)

>          avctx->level = priv->level;

>  

> -    switch (avctx->profile) {

> -    case FF_PROFILE_HEVC_MAIN:

> -    case FF_PROFILE_UNKNOWN:

> -        ctx->va_profile = VAProfileHEVCMain;

> -        ctx->va_rt_format = VA_RT_FORMAT_YUV420;

> -        break;

> -    case FF_PROFILE_HEVC_MAIN_10:

> -#ifdef VA_RT_FORMAT_YUV420_10BPP

> -        ctx->va_profile = VAProfileHEVCMain10;

> -        ctx->va_rt_format = VA_RT_FORMAT_YUV420_10BPP;

> -        break;

> -#else

> -        av_log(avctx, AV_LOG_ERROR, "10-bit encoding is not "

> -               "supported with this VAAPI version.\n");

> -        return AVERROR(ENOSYS);

> -#endif

> -    default:

> -        av_log(avctx, AV_LOG_ERROR, "Unknown H.265 profile %d.\n",

> -               avctx->profile);

> -        return AVERROR(EINVAL);

> -    }

> -    ctx->va_entrypoint = VAEntrypointEncSlice;

> -

>      if (avctx->bit_rate > 0) {

>          if (avctx->rc_max_rate == avctx->bit_rate)

>              ctx->va_rc_mode = VA_RC_CBR;

> @@ -1120,7 +1107,7 @@ static const AVOption vaapi_encode_h265_options[] = {

>  

>      { "profile", "Set profile (general_profile_idc)",

>        OFFSET(profile), AV_OPT_TYPE_INT,

> -      { .i64 = FF_PROFILE_HEVC_MAIN }, 0x00, 0xff, FLAGS, "profile" },

> +      { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0xff, FLAGS,

> "profile" },

>  

>  #define PROFILE(name, value)  name, NULL, 0, AV_OPT_TYPE_CONST, \

>        { .i64 = value }, 0, 0, FLAGS, "profile"

> diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c

> index 481981a71c..b328beaa09 100644

> --- a/libavcodec/vaapi_encode_mjpeg.c

> +++ b/libavcodec/vaapi_encode_mjpeg.c

> @@ -359,7 +359,15 @@ static av_cold int

> vaapi_encode_mjpeg_configure(AVCodecContext *avctx)

>      return 0;

>  }

>  

> +static const VAAPIEncodeProfile vaapi_encode_mjpeg_profiles[] = {

> +    { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,

> +            8, 3, 1, 1, VAProfileJPEGBaseline },

> +    { FF_PROFILE_UNKNOWN }

> +};

> +

>  static const VAAPIEncodeType vaapi_encode_type_mjpeg = {

> +    .profiles              = vaapi_encode_mjpeg_profiles,

> +

>      .configure             = &vaapi_encode_mjpeg_configure,

>  

>      .picture_params_size   = sizeof(VAEncPictureParameterBufferJPEG),

> @@ -380,11 +388,6 @@ static av_cold int vaapi_encode_mjpeg_init(AVCodecContext

> *avctx)

>  

>      ctx->codec = &vaapi_encode_type_mjpeg;

>  

> -    ctx->va_profile    = VAProfileJPEGBaseline;

> -    ctx->va_entrypoint = VAEntrypointEncPicture;

> -

> -    ctx->va_rt_format = VA_RT_FORMAT_YUV420;

> -

>      ctx->va_rc_mode = VA_RC_CQP;

>  

>      // The JPEG image header - see note above.

> diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c

> index 5577fa9e04..7f6c7833da 100644

> --- a/libavcodec/vaapi_encode_mpeg2.c

> +++ b/libavcodec/vaapi_encode_mpeg2.c

> @@ -552,7 +552,15 @@ static av_cold int

> vaapi_encode_mpeg2_configure(AVCodecContext *avctx)

>      return 0;

>  }

>  

> +static const VAAPIEncodeProfile vaapi_encode_mpeg2_profiles[] = {

> +    { FF_PROFILE_MPEG2_MAIN,   8, 3, 1, 1, VAProfileMPEG2Main   },

> +    { FF_PROFILE_MPEG2_SIMPLE, 8, 3, 1, 1, VAProfileMPEG2Simple },

> +    { FF_PROFILE_UNKNOWN }

> +};

> +

>  static const VAAPIEncodeType vaapi_encode_type_mpeg2 = {

> +    .profiles              = vaapi_encode_mpeg2_profiles,

> +

>      .configure             = &vaapi_encode_mpeg2_configure,

>  

>      .sequence_params_size  = sizeof(VAEncSequenceParameterBufferMPEG2),

> @@ -577,31 +585,6 @@ static av_cold int vaapi_encode_mpeg2_init(AVCodecContext

> *avctx)

>  

>      ctx->codec = &vaapi_encode_type_mpeg2;

>  

> -    switch (avctx->profile) {

> -    case FF_PROFILE_MPEG2_SIMPLE:

> -        ctx->va_profile = VAProfileMPEG2Simple;

> -        break;

> -    case FF_PROFILE_MPEG2_MAIN:

> -        ctx->va_profile = VAProfileMPEG2Main;

> -        break;

> -    case FF_PROFILE_MPEG2_422:

> -        av_log(avctx, AV_LOG_ERROR, "MPEG-2 4:2:2 profile "

> -               "is not supported.\n");

> -        return AVERROR_PATCHWELCOME;

> -    case FF_PROFILE_MPEG2_HIGH:

> -        av_log(avctx, AV_LOG_ERROR, "MPEG-2 high profile "

> -               "is not supported.\n");

> -        return AVERROR_PATCHWELCOME;

> -    case FF_PROFILE_MPEG2_SS:

> -    case FF_PROFILE_MPEG2_SNR_SCALABLE:

> -        av_log(avctx, AV_LOG_ERROR, "MPEG-2 scalable profiles "

> -               "are not supported.\n");

> -        return AVERROR_PATCHWELCOME;

> -    default:

> -        av_log(avctx, AV_LOG_ERROR, "Unknown MPEG-2 profile %d.\n",

> -               avctx->profile);

> -        return AVERROR(EINVAL);

> -    }

>      switch (avctx->level) {

>      case 4: // High

>      case 6: // High 1440

> @@ -620,8 +603,6 @@ static av_cold int vaapi_encode_mpeg2_init(AVCodecContext

> *avctx)

>          return AVERROR(EINVAL);

>      }

>  

> -    ctx->va_entrypoint = VAEntrypointEncSlice;

> -    ctx->va_rt_format  = VA_RT_FORMAT_YUV420;

>      ctx->va_rc_mode    = VA_RC_CQP;

>  

>      ctx->va_packed_headers = VA_ENC_PACKED_HEADER_SEQUENCE |

> @@ -643,7 +624,6 @@ static av_cold int vaapi_encode_mpeg2_close(AVCodecContext

> *avctx)

>  }

>  

>  static const AVCodecDefault vaapi_encode_mpeg2_defaults[] = {

> -    { "profile",        "4"   },

>      { "level",          "4"   },

>      { "bf",             "1"   },

>      { "g",              "120" },

> diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c

> index 6cdd30abda..a502df7885 100644

> --- a/libavcodec/vaapi_encode_vp8.c

> +++ b/libavcodec/vaapi_encode_vp8.c

> @@ -175,7 +175,14 @@ static av_cold int

> vaapi_encode_vp8_configure(AVCodecContext *avctx)

>      return 0;

>  }

>  

> +static const VAAPIEncodeProfile vaapi_encode_vp8_profiles[] = {

> +    { 0 /* VP8 has no profiles */, 8, 3, 1, 1, VAProfileVP8Version0_3 },

> +    { FF_PROFILE_UNKNOWN }

> +};

> +

>  static const VAAPIEncodeType vaapi_encode_type_vp8 = {

> +    .profiles              = vaapi_encode_vp8_profiles,

> +

>      .configure             = &vaapi_encode_vp8_configure,

>  

>      .sequence_params_size  = sizeof(VAEncSequenceParameterBufferVP8),

> @@ -198,10 +205,6 @@ static av_cold int vaapi_encode_vp8_init(AVCodecContext

> *avctx)

>  

>      ctx->codec = &vaapi_encode_type_vp8;

>  

> -    ctx->va_profile    = VAProfileVP8Version0_3;

> -    ctx->va_entrypoint = VAEntrypointEncSlice;

> -    ctx->va_rt_format  = VA_RT_FORMAT_YUV420;

> -

>      if (avctx->flags & AV_CODEC_FLAG_QSCALE) {

>          ctx->va_rc_mode = VA_RC_CQP;

>      } else if (avctx->bit_rate > 0) {

> diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c

> index bf99597e4c..88c0ce3b0a 100644

> --- a/libavcodec/vaapi_encode_vp9.c

> +++ b/libavcodec/vaapi_encode_vp9.c

> @@ -203,7 +203,15 @@ static av_cold int

> vaapi_encode_vp9_configure(AVCodecContext *avctx)

>      return 0;

>  }

>  

> +static const VAAPIEncodeProfile vaapi_encode_vp9_profiles[] = {

> +    { FF_PROFILE_VP9_0,  8, 3, 1, 1, VAProfileVP9Profile0 },

> +    { FF_PROFILE_VP9_2, 10, 3, 1, 1, VAProfileVP9Profile2 },

> +    { FF_PROFILE_UNKNOWN }

> +};

> +

>  static const VAAPIEncodeType vaapi_encode_type_vp9 = {

> +    .profiles              = vaapi_encode_vp9_profiles,

> +

>      .configure             = &vaapi_encode_vp9_configure,

>  

>      .sequence_params_size  = sizeof(VAEncSequenceParameterBufferVP9),

> @@ -219,31 +227,6 @@ static av_cold int vaapi_encode_vp9_init(AVCodecContext

> *avctx)

>  

>      ctx->codec = &vaapi_encode_type_vp9;

>  

> -    switch (avctx->profile) {

> -    case FF_PROFILE_VP9_0:

> -    case FF_PROFILE_UNKNOWN:

> -        ctx->va_profile = VAProfileVP9Profile0;

> -        ctx->va_rt_format = VA_RT_FORMAT_YUV420;

> -        break;

> -    case FF_PROFILE_VP9_1:

> -        av_log(avctx, AV_LOG_ERROR, "VP9 profile 1 is not "

> -               "supported.\n");

> -        return AVERROR_PATCHWELCOME;

> -    case FF_PROFILE_VP9_2:

> -        ctx->va_profile = VAProfileVP9Profile2;

> -        ctx->va_rt_format = VA_RT_FORMAT_YUV420_10BPP;

> -        break;

> -    case FF_PROFILE_VP9_3:

> -        av_log(avctx, AV_LOG_ERROR, "VP9 profile 3 is not "

> -               "supported.\n");

> -        return AVERROR_PATCHWELCOME;

> -    default:

> -        av_log(avctx, AV_LOG_ERROR, "Unknown VP9 profile %d.\n",

> -               avctx->profile);

> -        return AVERROR(EINVAL);

> -    }

> -    ctx->va_entrypoint = VAEntrypointEncSlice;

> -

>      if (avctx->flags & AV_CODEC_FLAG_QSCALE) {

>          ctx->va_rc_mode = VA_RC_CQP;

>      } else if (avctx->bit_rate > 0) {

> @@ -276,7 +259,6 @@ static const AVOption vaapi_encode_vp9_options[] = {

>  };

>  

>  static const AVCodecDefault vaapi_encode_vp9_defaults[] = {

> -    { "profile",        "0"   },

>      { "b",              "0"   },

>      { "bf",             "0"   },

>      { "g",              "250" },

Patch hide | download patch | download mbox

diff --git a/doc/encoders.texi b/doc/encoders.texi
index 7b095754d1..16be6359b3 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -2565,6 +2565,9 @@  The following standard libavcodec options are used:
 @option{bf} / @option{max_b_frames}
 @item
 @option{profile}
+
+If not set, this will be determined automatically from the format of the input
+frames and the profiles supported by the driver.
 @item
 @option{level}
 @item
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 27ce792fbe..6104470b31 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -983,70 +983,247 @@  static av_cold void vaapi_encode_add_global_param(AVCodecContext *avctx,
     ++ctx->nb_global_params;
 }
 
-static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
+typedef struct VAAPIEncodeRTFormat {
+    const char *name;
+    unsigned int value;
+    int depth;
+    int components;
+    int log2_chroma_w;
+    int log2_chroma_h;
+} VAAPIEncodeRTFormat;
+
+static const VAAPIEncodeRTFormat vaapi_encode_rt_formats[] = {
+    { "YUV400",    VA_RT_FORMAT_YUV400,        8, 1,      },
+    { "YUV420",    VA_RT_FORMAT_YUV420,        8, 3, 1, 1 },
+    { "YUV422",    VA_RT_FORMAT_YUV422,        8, 3, 1, 0 },
+    { "YUV444",    VA_RT_FORMAT_YUV444,        8, 3, 0, 0 },
+    { "YUV411",    VA_RT_FORMAT_YUV411,        8, 3, 2, 0 },
+#if VA_CHECK_VERSION(0, 38, 1)
+    { "YUV420_10", VA_RT_FORMAT_YUV420_10BPP, 10, 3, 1, 1 },
+#endif
+};
+
+static const VAEntrypoint vaapi_encode_entrypoints_normal[] = {
+    VAEntrypointEncSlice,
+    VAEntrypointEncPicture,
+#if VA_CHECK_VERSION(0, 39, 2)
+    VAEntrypointEncSliceLP,
+#endif
+    0
+};
+#if VA_CHECK_VERSION(0, 39, 2)
+static const VAEntrypoint vaapi_encode_entrypoints_low_power[] = {
+    VAEntrypointEncSliceLP,
+    0
+};
+#endif
+
+static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
 {
-    VAAPIEncodeContext *ctx = avctx->priv_data;
+    VAAPIEncodeContext      *ctx = avctx->priv_data;
+    VAProfile    *va_profiles    = NULL;
+    VAEntrypoint *va_entrypoints = NULL;
     VAStatus vas;
-    int i, n, err;
-    VAProfile    *profiles    = NULL;
-    VAEntrypoint *entrypoints = NULL;
-    VAConfigAttrib attr[] = {
-        { VAConfigAttribRTFormat         },
-        { VAConfigAttribRateControl      },
-        { VAConfigAttribEncMaxRefFrames  },
-        { VAConfigAttribEncPackedHeaders },
-    };
+    const VAEntrypoint *usable_entrypoints;
+    const VAAPIEncodeProfile *profile;
+    const AVPixFmtDescriptor *desc;
+    VAConfigAttrib rt_format_attr;
+    const VAAPIEncodeRTFormat *rt_format;
+    int i, j, n, depth, err;
+
+
+    if (ctx->low_power) {
+#if VA_CHECK_VERSION(0, 39, 2)
+        usable_entrypoints = vaapi_encode_entrypoints_low_power;
+#else
+        av_log(avctx, AV_LOG_ERROR, "Low-power encoding is not "
+               "supported with this VAAPI version.\n");
+        return AVERROR(EINVAL);
+#endif
+    } else {
+        usable_entrypoints = vaapi_encode_entrypoints_normal;
+    }
+
+    desc = av_pix_fmt_desc_get(ctx->input_frames->sw_format);
+    if (!desc) {
+        av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%d).\n",
+               ctx->input_frames->sw_format);
+        return AVERROR(EINVAL);
+    }
+    depth = desc->comp[0].depth;
+    for (i = 1; i < desc->nb_components; i++) {
+        if (desc->comp[i].depth != depth) {
+            av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%s).\n",
+                   desc->name);
+            return AVERROR(EINVAL);
+        }
+    }
+    av_log(avctx, AV_LOG_VERBOSE, "Input surface format is %s.\n",
+           desc->name);
 
     n = vaMaxNumProfiles(ctx->hwctx->display);
-    profiles = av_malloc_array(n, sizeof(VAProfile));
-    if (!profiles) {
+    va_profiles = av_malloc_array(n, sizeof(VAProfile));
+    if (!va_profiles) {
         err = AVERROR(ENOMEM);
         goto fail;
     }
-    vas = vaQueryConfigProfiles(ctx->hwctx->display, profiles, &n);
+    vas = vaQueryConfigProfiles(ctx->hwctx->display, va_profiles, &n);
     if (vas != VA_STATUS_SUCCESS) {
-        av_log(ctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",
+        av_log(avctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",
                vas, vaErrorStr(vas));
-        err = AVERROR(ENOSYS);
+        err = AVERROR_EXTERNAL;
         goto fail;
     }
-    for (i = 0; i < n; i++) {
-        if (profiles[i] == ctx->va_profile)
-            break;
+
+    av_assert0(ctx->codec->profiles);
+    for (i = 0; (ctx->codec->profiles[i].av_profile !=
+                 FF_PROFILE_UNKNOWN); i++) {
+        profile = &ctx->codec->profiles[i];
+        if (depth               != profile->depth ||
+            desc->nb_components != profile->components)
+            continue;
+        if (desc->nb_components > 1 &&
+            (desc->log2_chroma_w != profile->log2_chroma_w ||
+             desc->log2_chroma_h != profile->log2_chroma_h))
+            continue;
+        if (avctx->profile != profile->av_profile &&
+            avctx->profile != FF_PROFILE_UNKNOWN)
+            continue;
+
+        for (j = 0; j < n; j++) {
+            if (va_profiles[j] == profile->va_profile)
+                break;
+        }
+        if (j >= n) {
+            av_log(avctx, AV_LOG_VERBOSE, "Matching profile %d is "
+                   "not supported by driver.\n", profile->va_profile);
+            continue;
+        }
+
+        ctx->profile = profile;
+        break;
     }
-    if (i >= n) {
-        av_log(ctx, AV_LOG_ERROR, "Encoding profile not found (%d).\n",
-               ctx->va_profile);
-        err = AVERROR(ENOSYS);
-        goto fail;
+    if (!ctx->profile) {
+        av_log(avctx, AV_LOG_ERROR, "No usable encoding profile found.\n");
+        return AVERROR(ENOSYS);
     }
 
+    avctx->profile  = profile->av_profile;
+    ctx->va_profile = profile->va_profile;
+#if VA_CHECK_VERSION(1, 0, 0)
+    av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %s (%d).\n",
+           vaProfileStr(ctx->va_profile), ctx->va_profile);
+#else
+    av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %d.\n",
+           ctx->va_profile);
+#endif
+
     n = vaMaxNumEntrypoints(ctx->hwctx->display);
-    entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
-    if (!entrypoints) {
+    va_entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
+    if (!va_entrypoints) {
         err = AVERROR(ENOMEM);
         goto fail;
     }
     vas = vaQueryConfigEntrypoints(ctx->hwctx->display, ctx->va_profile,
-                                   entrypoints, &n);
+                                   va_entrypoints, &n);
     if (vas != VA_STATUS_SUCCESS) {
-        av_log(ctx, AV_LOG_ERROR, "Failed to query entrypoints for "
+        av_log(avctx, AV_LOG_ERROR, "Failed to query entrypoints for "
                "profile %u: %d (%s).\n", ctx->va_profile,
                vas, vaErrorStr(vas));
-        err = AVERROR(ENOSYS);
+        err = AVERROR_EXTERNAL;
         goto fail;
     }
+
     for (i = 0; i < n; i++) {
-        if (entrypoints[i] == ctx->va_entrypoint)
+        for (j = 0; usable_entrypoints[j]; j++) {
+            if (va_entrypoints[i] == usable_entrypoints[j])
+                break;
+        }
+        if (usable_entrypoints[j])
             break;
     }
     if (i >= n) {
-        av_log(ctx, AV_LOG_ERROR, "Encoding entrypoint not found "
-               "(%d / %d).\n", ctx->va_profile, ctx->va_entrypoint);
+        av_log(avctx, AV_LOG_ERROR, "No usable encoding entrypoint found "
+               "for profile %d.\n", ctx->va_profile);
+        err = AVERROR(ENOSYS);
+        goto fail;
+    }
+
+    ctx->va_entrypoint = va_entrypoints[i];
+#if VA_CHECK_VERSION(1, 0, 0)
+    av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %s (%d).\n",
+           vaEntrypointStr(ctx->va_entrypoint), ctx->va_entrypoint);
+#else
+    av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %d.\n",
+           ctx->va_entrypoint);
+#endif
+
+    for (i = 0; i < FF_ARRAY_ELEMS(vaapi_encode_rt_formats); i++) {
+        rt_format = &vaapi_encode_rt_formats[i];
+        if (rt_format->depth         == depth &&
+            rt_format->components    == profile->components    &&
+            rt_format->log2_chroma_w == profile->log2_chroma_w &&
+            rt_format->log2_chroma_h == profile->log2_chroma_h)
+            break;
+    }
+    if (i >= FF_ARRAY_ELEMS(vaapi_encode_rt_formats)) {
+        av_log(avctx, AV_LOG_ERROR, "No usable render target format "
+               "found for profile %d entrypoint %d.\n",
+               ctx->va_profile, ctx->va_entrypoint);
         err = AVERROR(ENOSYS);
         goto fail;
     }
 
+    rt_format_attr = (VAConfigAttrib) { VAConfigAttribRTFormat };
+    vas = vaGetConfigAttributes(ctx->hwctx->display,
+                                ctx->va_profile, ctx->va_entrypoint,
+                                &rt_format_attr, 1);
+    if (vas != VA_STATUS_SUCCESS) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to query RT format "
+               "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+        err = AVERROR_EXTERNAL;
+        goto fail;
+    }
+
+    if (rt_format_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+        av_log(avctx, AV_LOG_VERBOSE, "RT format config attribute not "
+               "supported by driver: assuming surface RT format %s "
+               "is valid.\n", rt_format->name);
+    } else if (!(rt_format_attr.value & rt_format->value)) {
+        av_log(avctx, AV_LOG_ERROR, "Surface RT format %s not supported "
+               "by driver for encoding profile %d entrypoint %d.\n",
+               rt_format->name, ctx->va_profile, ctx->va_entrypoint);
+        err = AVERROR(ENOSYS);
+        goto fail;
+    } else {
+        av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI render target "
+               "format %s (%#x).\n", rt_format->name, rt_format->value);
+        ctx->config_attributes[ctx->nb_config_attributes++] =
+            (VAConfigAttrib) {
+            .type  = VAConfigAttribRTFormat,
+            .value = rt_format->value,
+        };
+    }
+
+    err = 0;
+fail:
+    av_freep(&va_profiles);
+    av_freep(&va_entrypoints);
+    return err;
+}
+
+static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
+{
+    VAAPIEncodeContext *ctx = avctx->priv_data;
+    VAStatus vas;
+    int i;
+
+    VAConfigAttrib attr[] = {
+        { VAConfigAttribRateControl      },
+        { VAConfigAttribEncMaxRefFrames  },
+        { VAConfigAttribEncPackedHeaders },
+    };
+
     vas = vaGetConfigAttributes(ctx->hwctx->display,
                                 ctx->va_profile, ctx->va_entrypoint,
                                 attr, FF_ARRAY_ELEMS(attr));
@@ -1066,20 +1243,6 @@  static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
             continue;
         }
         switch (attr[i].type) {
-        case VAConfigAttribRTFormat:
-            if (!(ctx->va_rt_format & attr[i].value)) {
-                av_log(avctx, AV_LOG_ERROR, "Surface RT format %#x "
-                       "is not supported (mask %#x).\n",
-                       ctx->va_rt_format, attr[i].value);
-                err = AVERROR(EINVAL);
-                goto fail;
-            }
-            ctx->config_attributes[ctx->nb_config_attributes++] =
-                (VAConfigAttrib) {
-                .type  = VAConfigAttribRTFormat,
-                .value = ctx->va_rt_format,
-            };
-            break;
         case VAConfigAttribRateControl:
             // Hack for backward compatibility: CBR was the only
             // usable RC mode for a long time, so old drivers will
@@ -1098,8 +1261,7 @@  static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
                 av_log(avctx, AV_LOG_ERROR, "Rate control mode %#x "
                        "is not supported (mask: %#x).\n",
                        ctx->va_rc_mode, attr[i].value);
-                err = AVERROR(EINVAL);
-                goto fail;
+                return AVERROR(EINVAL);
             }
             ctx->config_attributes[ctx->nb_config_attributes++] =
                 (VAConfigAttrib) {
@@ -1115,8 +1277,7 @@  static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
             if (avctx->gop_size > 1 && ref_l0 < 1) {
                 av_log(avctx, AV_LOG_ERROR, "P frames are not "
                        "supported (%#x).\n", attr[i].value);
-                err = AVERROR(EINVAL);
-                goto fail;
+                return AVERROR(EINVAL);
             }
             if (avctx->max_b_frames > 0 && ref_l1 < 1) {
                 av_log(avctx, AV_LOG_WARNING, "B frames are not "
@@ -1148,11 +1309,7 @@  static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
         }
     }
 
-    err = 0;
-fail:
-    av_freep(&profiles);
-    av_freep(&entrypoints);
-    return err;
+    return 0;
 }
 
 static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
@@ -1407,6 +1564,10 @@  av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
     ctx->device = (AVHWDeviceContext*)ctx->device_ref->data;
     ctx->hwctx = ctx->device->hwctx;
 
+    err = vaapi_encode_profile_entrypoint(avctx);
+    if (err < 0)
+        goto fail;
+
     err = vaapi_encode_config_attributes(avctx);
     if (err < 0)
         goto fail;
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index 54dc4a475e..212ab6726d 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -23,6 +23,10 @@ 
 
 #include <va/va.h>
 
+#if VA_CHECK_VERSION(1, 0, 0)
+#include <va/va_str.h>
+#endif
+
 #include "libavutil/hwcontext.h"
 #include "libavutil/hwcontext_vaapi.h"
 
@@ -86,18 +90,32 @@  typedef struct VAAPIEncodePicture {
     VAAPIEncodeSlice *slices;
 } VAAPIEncodePicture;
 
+typedef struct VAAPIEncodeProfile {
+    // lavc profile value (FF_PROFILE_*).
+    int       av_profile;
+    // Supported bit depth.
+    int       depth;
+    // Number of components.
+    int       components;
+    // Chroma subsampling in width dimension.
+    int       log2_chroma_w;
+    // Chroma subsampling in height dimension.
+    int       log2_chroma_h;
+    // VAAPI profile value.
+    VAProfile va_profile;
+} VAAPIEncodeProfile;
+
 typedef struct VAAPIEncodeContext {
     const AVClass *class;
 
     // Codec-specific hooks.
     const struct VAAPIEncodeType *codec;
 
-    // Encoding profile (VAProfileXXX).
-    VAProfile       va_profile;
-    // Encoding entrypoint (usually VAEntryointEncSlice).
-    VAEntrypoint    va_entrypoint;
-    // Surface colour/sampling format (usually VA_RT_FORMAT_YUV420).
-    unsigned int    va_rt_format;
+    // Global options.
+
+    // Use low power encoding mode.
+    int             low_power;
+
     // Rate control mode.
     unsigned int    va_rc_mode;
     // Supported packed headers (initially the desired set, modified
@@ -113,6 +131,14 @@  typedef struct VAAPIEncodeContext {
     // Everything above this point must be set before calling
     // ff_vaapi_encode_init().
 
+    // Chosen encoding profile details.
+    const VAAPIEncodeProfile *profile;
+
+    // Encoding profile (VAProfile*).
+    VAProfile       va_profile;
+    // Encoding entrypoint (VAEntryoint*).
+    VAEntrypoint    va_entrypoint;
+
     // Configuration attributes to use when creating va_config.
     VAConfigAttrib  config_attributes[MAX_CONFIG_ATTRIBUTES];
     int          nb_config_attributes;
@@ -204,8 +230,11 @@  typedef struct VAAPIEncodeContext {
     int end_of_stream;
 } VAAPIEncodeContext;
 
-
 typedef struct VAAPIEncodeType {
+    // List of supported profiles and corresponding VAAPI profiles.
+    // (Must end with FF_PROFILE_UNKNOWN.)
+    const VAAPIEncodeProfile *profiles;
+
     // Perform any extra codec-specific configuration after the
     // codec context is initialised (set up the private data and
     // add any necessary global parameters).
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 8f999b2311..ab8bf13ef3 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -866,7 +866,17 @@  static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)
     return 0;
 }
 
+static const VAAPIEncodeProfile vaapi_encode_h264_profiles[] = {
+    { FF_PROFILE_H264_HIGH, 8, 3, 1, 1, VAProfileH264High },
+    { FF_PROFILE_H264_MAIN, 8, 3, 1, 1, VAProfileH264Main },
+    { FF_PROFILE_H264_CONSTRAINED_BASELINE,
+                            8, 3, 1, 1, VAProfileH264ConstrainedBaseline },
+    { FF_PROFILE_UNKNOWN }
+};
+
 static const VAAPIEncodeType vaapi_encode_type_h264 = {
+    .profiles              = vaapi_encode_h264_profiles,
+
     .configure             = &vaapi_encode_h264_configure,
 
     .sequence_params_size  = sizeof(VAEncSequenceParameterBufferH264),
@@ -899,30 +909,17 @@  static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
     if (avctx->level == FF_LEVEL_UNKNOWN)
         avctx->level = priv->level;
 
+    // Reject unsupported profiles.
     switch (avctx->profile) {
     case FF_PROFILE_H264_BASELINE:
         av_log(avctx, AV_LOG_WARNING, "H.264 baseline profile is not "
                "supported, using constrained baseline profile instead.\n");
         avctx->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE;
-    case FF_PROFILE_H264_CONSTRAINED_BASELINE:
-        ctx->va_profile = VAProfileH264ConstrainedBaseline;
-        if (avctx->max_b_frames != 0) {
-            avctx->max_b_frames = 0;
-            av_log(avctx, AV_LOG_WARNING, "H.264 constrained baseline profile "
-                   "doesn't support encoding with B frames, disabling them.\n");
-        }
-        break;
-    case FF_PROFILE_H264_MAIN:
-        ctx->va_profile = VAProfileH264Main;
         break;
     case FF_PROFILE_H264_EXTENDED:
         av_log(avctx, AV_LOG_ERROR, "H.264 extended profile "
                "is not supported.\n");
         return AVERROR_PATCHWELCOME;
-    case FF_PROFILE_UNKNOWN:
-    case FF_PROFILE_H264_HIGH:
-        ctx->va_profile = VAProfileH264High;
-        break;
     case FF_PROFILE_H264_HIGH_10:
     case FF_PROFILE_H264_HIGH_10_INTRA:
         av_log(avctx, AV_LOG_ERROR, "H.264 10-bit profiles "
@@ -937,25 +934,9 @@  static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
         av_log(avctx, AV_LOG_ERROR, "H.264 non-4:2:0 profiles "
                "are not supported.\n");
         return AVERROR_PATCHWELCOME;
-    default:
-        av_log(avctx, AV_LOG_ERROR, "Unknown H.264 profile %d.\n",
-               avctx->profile);
-        return AVERROR(EINVAL);
-    }
-    if (priv->low_power) {
-#if VA_CHECK_VERSION(0, 39, 2)
-        ctx->va_entrypoint = VAEntrypointEncSliceLP;
-#else
-        av_log(avctx, AV_LOG_ERROR, "Low-power encoding is not "
-               "supported with this VAAPI version.\n");
-        return AVERROR(EINVAL);
-#endif
-    } else {
-        ctx->va_entrypoint = VAEntrypointEncSlice;
     }
 
-    // Only 8-bit encode is supported.
-    ctx->va_rt_format = VA_RT_FORMAT_YUV420;
+    ctx->low_power = priv->low_power;
 
     if (avctx->bit_rate > 0) {
         if (avctx->rc_max_rate == avctx->bit_rate)
@@ -1022,7 +1003,7 @@  static const AVOption vaapi_encode_h264_options[] = {
 
     { "profile", "Set profile (profile_idc and constraint_set*_flag)",
       OFFSET(profile), AV_OPT_TYPE_INT,
-      { .i64 = FF_PROFILE_H264_HIGH }, 0x0000, 0xffff, FLAGS, "profile" },
+      { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0xffff, FLAGS, "profile" },
 
 #define PROFILE(name, value)  name, NULL, 0, AV_OPT_TYPE_CONST, \
       { .i64 = value }, 0, 0, FLAGS, "profile"
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 8f191efc4b..9fa16593d0 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -1025,7 +1025,17 @@  static av_cold int vaapi_encode_h265_configure(AVCodecContext *avctx)
     return 0;
 }
 
+static const VAAPIEncodeProfile vaapi_encode_h265_profiles[] = {
+    { FF_PROFILE_HEVC_MAIN,     8, 3, 1, 1, VAProfileHEVCMain       },
+#if VA_CHECK_VERSION(0, 37, 0)
+    { FF_PROFILE_HEVC_MAIN_10, 10, 3, 1, 1, VAProfileHEVCMain10     },
+#endif
+    { FF_PROFILE_UNKNOWN }
+};
+
 static const VAAPIEncodeType vaapi_encode_type_h265 = {
+    .profiles              = vaapi_encode_h265_profiles,
+
     .configure             = &vaapi_encode_h265_configure,
 
     .sequence_params_size  = sizeof(VAEncSequenceParameterBufferHEVC),
@@ -1058,29 +1068,6 @@  static av_cold int vaapi_encode_h265_init(AVCodecContext *avctx)
     if (avctx->level == FF_LEVEL_UNKNOWN)
         avctx->level = priv->level;
 
-    switch (avctx->profile) {
-    case FF_PROFILE_HEVC_MAIN:
-    case FF_PROFILE_UNKNOWN:
-        ctx->va_profile = VAProfileHEVCMain;
-        ctx->va_rt_format = VA_RT_FORMAT_YUV420;
-        break;
-    case FF_PROFILE_HEVC_MAIN_10:
-#ifdef VA_RT_FORMAT_YUV420_10BPP
-        ctx->va_profile = VAProfileHEVCMain10;
-        ctx->va_rt_format = VA_RT_FORMAT_YUV420_10BPP;
-        break;
-#else
-        av_log(avctx, AV_LOG_ERROR, "10-bit encoding is not "
-               "supported with this VAAPI version.\n");
-        return AVERROR(ENOSYS);
-#endif
-    default:
-        av_log(avctx, AV_LOG_ERROR, "Unknown H.265 profile %d.\n",
-               avctx->profile);
-        return AVERROR(EINVAL);
-    }
-    ctx->va_entrypoint = VAEntrypointEncSlice;
-
     if (avctx->bit_rate > 0) {
         if (avctx->rc_max_rate == avctx->bit_rate)
             ctx->va_rc_mode = VA_RC_CBR;
@@ -1120,7 +1107,7 @@  static const AVOption vaapi_encode_h265_options[] = {
 
     { "profile", "Set profile (general_profile_idc)",
       OFFSET(profile), AV_OPT_TYPE_INT,
-      { .i64 = FF_PROFILE_HEVC_MAIN }, 0x00, 0xff, FLAGS, "profile" },
+      { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0xff, FLAGS, "profile" },
 
 #define PROFILE(name, value)  name, NULL, 0, AV_OPT_TYPE_CONST, \
       { .i64 = value }, 0, 0, FLAGS, "profile"
diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index 481981a71c..b328beaa09 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
@@ -359,7 +359,15 @@  static av_cold int vaapi_encode_mjpeg_configure(AVCodecContext *avctx)
     return 0;
 }
 
+static const VAAPIEncodeProfile vaapi_encode_mjpeg_profiles[] = {
+    { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
+            8, 3, 1, 1, VAProfileJPEGBaseline },
+    { FF_PROFILE_UNKNOWN }
+};
+
 static const VAAPIEncodeType vaapi_encode_type_mjpeg = {
+    .profiles              = vaapi_encode_mjpeg_profiles,
+
     .configure             = &vaapi_encode_mjpeg_configure,
 
     .picture_params_size   = sizeof(VAEncPictureParameterBufferJPEG),
@@ -380,11 +388,6 @@  static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx)
 
     ctx->codec = &vaapi_encode_type_mjpeg;
 
-    ctx->va_profile    = VAProfileJPEGBaseline;
-    ctx->va_entrypoint = VAEntrypointEncPicture;
-
-    ctx->va_rt_format = VA_RT_FORMAT_YUV420;
-
     ctx->va_rc_mode = VA_RC_CQP;
 
     // The JPEG image header - see note above.
diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index 5577fa9e04..7f6c7833da 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -552,7 +552,15 @@  static av_cold int vaapi_encode_mpeg2_configure(AVCodecContext *avctx)
     return 0;
 }
 
+static const VAAPIEncodeProfile vaapi_encode_mpeg2_profiles[] = {
+    { FF_PROFILE_MPEG2_MAIN,   8, 3, 1, 1, VAProfileMPEG2Main   },
+    { FF_PROFILE_MPEG2_SIMPLE, 8, 3, 1, 1, VAProfileMPEG2Simple },
+    { FF_PROFILE_UNKNOWN }
+};
+
 static const VAAPIEncodeType vaapi_encode_type_mpeg2 = {
+    .profiles              = vaapi_encode_mpeg2_profiles,
+
     .configure             = &vaapi_encode_mpeg2_configure,
 
     .sequence_params_size  = sizeof(VAEncSequenceParameterBufferMPEG2),
@@ -577,31 +585,6 @@  static av_cold int vaapi_encode_mpeg2_init(AVCodecContext *avctx)
 
     ctx->codec = &vaapi_encode_type_mpeg2;
 
-    switch (avctx->profile) {
-    case FF_PROFILE_MPEG2_SIMPLE:
-        ctx->va_profile = VAProfileMPEG2Simple;
-        break;
-    case FF_PROFILE_MPEG2_MAIN:
-        ctx->va_profile = VAProfileMPEG2Main;
-        break;
-    case FF_PROFILE_MPEG2_422:
-        av_log(avctx, AV_LOG_ERROR, "MPEG-2 4:2:2 profile "
-               "is not supported.\n");
-        return AVERROR_PATCHWELCOME;
-    case FF_PROFILE_MPEG2_HIGH:
-        av_log(avctx, AV_LOG_ERROR, "MPEG-2 high profile "
-               "is not supported.\n");
-        return AVERROR_PATCHWELCOME;
-    case FF_PROFILE_MPEG2_SS:
-    case FF_PROFILE_MPEG2_SNR_SCALABLE:
-        av_log(avctx, AV_LOG_ERROR, "MPEG-2 scalable profiles "
-               "are not supported.\n");
-        return AVERROR_PATCHWELCOME;
-    default:
-        av_log(avctx, AV_LOG_ERROR, "Unknown MPEG-2 profile %d.\n",
-               avctx->profile);
-        return AVERROR(EINVAL);
-    }
     switch (avctx->level) {
     case 4: // High
     case 6: // High 1440
@@ -620,8 +603,6 @@  static av_cold int vaapi_encode_mpeg2_init(AVCodecContext *avctx)
         return AVERROR(EINVAL);
     }
 
-    ctx->va_entrypoint = VAEntrypointEncSlice;
-    ctx->va_rt_format  = VA_RT_FORMAT_YUV420;
     ctx->va_rc_mode    = VA_RC_CQP;
 
     ctx->va_packed_headers = VA_ENC_PACKED_HEADER_SEQUENCE |
@@ -643,7 +624,6 @@  static av_cold int vaapi_encode_mpeg2_close(AVCodecContext *avctx)
 }
 
 static const AVCodecDefault vaapi_encode_mpeg2_defaults[] = {
-    { "profile",        "4"   },
     { "level",          "4"   },
     { "bf",             "1"   },
     { "g",              "120" },
diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c
index 6cdd30abda..a502df7885 100644
--- a/libavcodec/vaapi_encode_vp8.c
+++ b/libavcodec/vaapi_encode_vp8.c
@@ -175,7 +175,14 @@  static av_cold int vaapi_encode_vp8_configure(AVCodecContext *avctx)
     return 0;
 }
 
+static const VAAPIEncodeProfile vaapi_encode_vp8_profiles[] = {
+    { 0 /* VP8 has no profiles */, 8, 3, 1, 1, VAProfileVP8Version0_3 },
+    { FF_PROFILE_UNKNOWN }
+};
+
 static const VAAPIEncodeType vaapi_encode_type_vp8 = {
+    .profiles              = vaapi_encode_vp8_profiles,
+
     .configure             = &vaapi_encode_vp8_configure,
 
     .sequence_params_size  = sizeof(VAEncSequenceParameterBufferVP8),
@@ -198,10 +205,6 @@  static av_cold int vaapi_encode_vp8_init(AVCodecContext *avctx)
 
     ctx->codec = &vaapi_encode_type_vp8;
 
-    ctx->va_profile    = VAProfileVP8Version0_3;
-    ctx->va_entrypoint = VAEntrypointEncSlice;
-    ctx->va_rt_format  = VA_RT_FORMAT_YUV420;
-
     if (avctx->flags & AV_CODEC_FLAG_QSCALE) {
         ctx->va_rc_mode = VA_RC_CQP;
     } else if (avctx->bit_rate > 0) {
diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c
index bf99597e4c..88c0ce3b0a 100644
--- a/libavcodec/vaapi_encode_vp9.c
+++ b/libavcodec/vaapi_encode_vp9.c
@@ -203,7 +203,15 @@  static av_cold int vaapi_encode_vp9_configure(AVCodecContext *avctx)
     return 0;
 }
 
+static const VAAPIEncodeProfile vaapi_encode_vp9_profiles[] = {
+    { FF_PROFILE_VP9_0,  8, 3, 1, 1, VAProfileVP9Profile0 },
+    { FF_PROFILE_VP9_2, 10, 3, 1, 1, VAProfileVP9Profile2 },
+    { FF_PROFILE_UNKNOWN }
+};
+
 static const VAAPIEncodeType vaapi_encode_type_vp9 = {
+    .profiles              = vaapi_encode_vp9_profiles,
+
     .configure             = &vaapi_encode_vp9_configure,
 
     .sequence_params_size  = sizeof(VAEncSequenceParameterBufferVP9),
@@ -219,31 +227,6 @@  static av_cold int vaapi_encode_vp9_init(AVCodecContext *avctx)
 
     ctx->codec = &vaapi_encode_type_vp9;
 
-    switch (avctx->profile) {
-    case FF_PROFILE_VP9_0:
-    case FF_PROFILE_UNKNOWN:
-        ctx->va_profile = VAProfileVP9Profile0;
-        ctx->va_rt_format = VA_RT_FORMAT_YUV420;
-        break;
-    case FF_PROFILE_VP9_1:
-        av_log(avctx, AV_LOG_ERROR, "VP9 profile 1 is not "
-               "supported.\n");
-        return AVERROR_PATCHWELCOME;
-    case FF_PROFILE_VP9_2:
-        ctx->va_profile = VAProfileVP9Profile2;
-        ctx->va_rt_format = VA_RT_FORMAT_YUV420_10BPP;
-        break;
-    case FF_PROFILE_VP9_3:
-        av_log(avctx, AV_LOG_ERROR, "VP9 profile 3 is not "
-               "supported.\n");
-        return AVERROR_PATCHWELCOME;
-    default:
-        av_log(avctx, AV_LOG_ERROR, "Unknown VP9 profile %d.\n",
-               avctx->profile);
-        return AVERROR(EINVAL);
-    }
-    ctx->va_entrypoint = VAEntrypointEncSlice;
-
     if (avctx->flags & AV_CODEC_FLAG_QSCALE) {
         ctx->va_rc_mode = VA_RC_CQP;
     } else if (avctx->bit_rate > 0) {
@@ -276,7 +259,6 @@  static const AVOption vaapi_encode_vp9_options[] = {
 };
 
 static const AVCodecDefault vaapi_encode_vp9_defaults[] = {
-    { "profile",        "0"   },
     { "b",              "0"   },
     { "bf",             "0"   },
     { "g",              "250" },