From 80a5d05108cb218e8cd2e25c6621a3bfef0a832e Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sun, 18 Sep 2016 14:55:26 +0100 Subject: [PATCH 1/4] vaapi_encode: Refactor initialisation This allows better checking of capabilities and will make it easier to add more functionality later. It also commonises some duplicated code around rate control setup and adds more comments explaining the internals. --- libavcodec/vaapi_encode.c | 257 +++++++++++++++++++++----------- libavcodec/vaapi_encode.h | 96 ++++++++---- libavcodec/vaapi_encode_h264.c | 257 ++++++++++++-------------------- libavcodec/vaapi_encode_h265.c | 202 +++++++++---------------- libavcodec/vaapi_encode_mjpeg.c | 45 +++--- 5 files changed, 419 insertions(+), 438 deletions(-) diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c index cdda48583f..41d1a6ed17 100644 --- a/libavcodec/vaapi_encode.c +++ b/libavcodec/vaapi_encode.c @@ -922,7 +922,7 @@ fail: return err; } -static av_cold int vaapi_encode_check_config(AVCodecContext *avctx) +static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx) { VAAPIEncodeContext *ctx = avctx->priv_data; VAStatus vas; @@ -930,6 +930,7 @@ static av_cold int vaapi_encode_check_config(AVCodecContext *avctx) VAProfile *profiles = NULL; VAEntrypoint *entrypoints = NULL; VAConfigAttrib attr[] = { + { VAConfigAttribRTFormat }, { VAConfigAttribRateControl }, { VAConfigAttribEncMaxRefFrames }, }; @@ -1001,13 +1002,33 @@ static av_cold int vaapi_encode_check_config(AVCodecContext *avctx) continue; } switch (attr[i].type) { - case VAConfigAttribRateControl: - if (!(ctx->va_rc_mode & attr[i].value)) { - av_log(avctx, AV_LOG_ERROR, "Rate control mode is not " - "supported: %x\n", attr[i].value); + 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: + if (!(ctx->va_rc_mode & attr[i].value)) { + 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; + } + ctx->config_attributes[ctx->nb_config_attributes++] = + (VAConfigAttrib) { + .type = VAConfigAttribRateControl, + .value = ctx->va_rc_mode, + }; break; case VAConfigAttribEncMaxRefFrames: { @@ -1016,18 +1037,20 @@ static av_cold int vaapi_encode_check_config(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); + "supported (%#x).\n", attr[i].value); err = AVERROR(EINVAL); goto fail; } if (avctx->max_b_frames > 0 && ref_l1 < 1) { av_log(avctx, AV_LOG_ERROR, "B frames are not " - "supported (%x).\n", attr[i].value); + "supported (%#x).\n", attr[i].value); err = AVERROR(EINVAL); goto fail; } } break; + default: + av_assert0(0 && "Unexpected config attribute."); } } @@ -1038,6 +1061,48 @@ fail: return err; } +static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + int hrd_buffer_size; + int hrd_initial_buffer_fullness; + + if (avctx->rc_buffer_size) + hrd_buffer_size = avctx->rc_buffer_size; + else + hrd_buffer_size = avctx->bit_rate; + if (avctx->rc_initial_buffer_occupancy) + hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy; + else + hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4; + + ctx->rc_params.misc.type = VAEncMiscParameterTypeRateControl; + ctx->rc_params.rc = (VAEncMiscParameterRateControl) { + .bits_per_second = avctx->bit_rate, + .target_percentage = 66, + .window_size = 1000, + .initial_qp = (avctx->qmax >= 0 ? avctx->qmax : 40), + .min_qp = (avctx->qmin >= 0 ? avctx->qmin : 18), + .basic_unit_size = 0, + }; + ctx->global_params[ctx->nb_global_params] = + &ctx->rc_params.misc; + ctx->global_params_size[ctx->nb_global_params++] = + sizeof(ctx->rc_params); + + ctx->hrd_params.misc.type = VAEncMiscParameterTypeHRD; + ctx->hrd_params.hrd = (VAEncMiscParameterHRD) { + .initial_buffer_fullness = hrd_initial_buffer_fullness, + .buffer_size = hrd_buffer_size, + }; + ctx->global_params[ctx->nb_global_params] = + &ctx->hrd_params.misc; + ctx->global_params_size[ctx->nb_global_params++] = + sizeof(ctx->hrd_params); + + return 0; +} + static void vaapi_encode_free_output_buffer(void *opaque, uint8_t *data) { @@ -1067,7 +1132,7 @@ static AVBufferRef *vaapi_encode_alloc_output_buffer(void *opaque, // bound on that. vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, VAEncCodedBufferType, - 3 * ctx->aligned_width * ctx->aligned_height + + 3 * ctx->surface_width * ctx->surface_height + (1 << 16), 1, 0, &buffer_id); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to create bitstream " @@ -1089,69 +1154,14 @@ static AVBufferRef *vaapi_encode_alloc_output_buffer(void *opaque, return ref; } -av_cold int ff_vaapi_encode_init(AVCodecContext *avctx, - const VAAPIEncodeType *type) +static av_cold int vaapi_encode_create_recon_frames(AVCodecContext *avctx) { VAAPIEncodeContext *ctx = avctx->priv_data; - AVVAAPIFramesContext *recon_hwctx = NULL; AVVAAPIHWConfig *hwconfig = NULL; AVHWFramesConstraints *constraints = NULL; enum AVPixelFormat recon_format; - VAStatus vas; int err, i; - if (!avctx->hw_frames_ctx) { - av_log(avctx, AV_LOG_ERROR, "A hardware frames reference is " - "required to associate the encoding device.\n"); - return AVERROR(EINVAL); - } - - ctx->codec = type; - ctx->codec_options = ctx->codec_options_data; - - ctx->va_config = VA_INVALID_ID; - ctx->va_context = VA_INVALID_ID; - - ctx->priv_data = av_mallocz(type->priv_data_size); - if (!ctx->priv_data) { - err = AVERROR(ENOMEM); - goto fail; - } - - ctx->input_frames_ref = av_buffer_ref(avctx->hw_frames_ctx); - if (!ctx->input_frames_ref) { - err = AVERROR(ENOMEM); - goto fail; - } - ctx->input_frames = (AVHWFramesContext*)ctx->input_frames_ref->data; - - ctx->device_ref = av_buffer_ref(ctx->input_frames->device_ref); - if (!ctx->device_ref) { - err = AVERROR(ENOMEM); - goto fail; - } - ctx->device = (AVHWDeviceContext*)ctx->device_ref->data; - ctx->hwctx = ctx->device->hwctx; - - err = ctx->codec->init(avctx); - if (err < 0) - goto fail; - - err = vaapi_encode_check_config(avctx); - if (err < 0) - goto fail; - - vas = vaCreateConfig(ctx->hwctx->display, - ctx->va_profile, ctx->va_entrypoint, - ctx->config_attributes, ctx->nb_config_attributes, - &ctx->va_config); - if (vas != VA_STATUS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Failed to create encode pipeline " - "configuration: %d (%s).\n", vas, vaErrorStr(vas)); - err = AVERROR(EIO); - goto fail; - } - hwconfig = av_hwdevice_hwconfig_alloc(ctx->device_ref); if (!hwconfig) { err = AVERROR(ENOMEM); @@ -1190,13 +1200,13 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx, av_log(avctx, AV_LOG_DEBUG, "Using %s as format of " "reconstructed frames.\n", av_get_pix_fmt_name(recon_format)); - if (ctx->aligned_width < constraints->min_width || - ctx->aligned_height < constraints->min_height || - ctx->aligned_width > constraints->max_width || - ctx->aligned_height > constraints->max_height) { + if (ctx->surface_width < constraints->min_width || + ctx->surface_height < constraints->min_height || + ctx->surface_width > constraints->max_width || + ctx->surface_height > constraints->max_height) { av_log(avctx, AV_LOG_ERROR, "Hardware does not support encoding at " "size %dx%d (constraints: width %d-%d height %d-%d).\n", - ctx->aligned_width, ctx->aligned_height, + ctx->surface_width, ctx->surface_height, constraints->min_width, constraints->max_width, constraints->min_height, constraints->max_height); err = AVERROR(EINVAL); @@ -1215,9 +1225,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx, ctx->recon_frames->format = AV_PIX_FMT_VAAPI; ctx->recon_frames->sw_format = recon_format; - ctx->recon_frames->width = ctx->aligned_width; - ctx->recon_frames->height = ctx->aligned_height; - ctx->recon_frames->initial_pool_size = ctx->nb_recon_frames; + ctx->recon_frames->width = ctx->surface_width; + ctx->recon_frames->height = ctx->surface_height; + ctx->recon_frames->initial_pool_size = + avctx->max_b_frames + 3; err = av_hwframe_ctx_init(ctx->recon_frames_ref); if (err < 0) { @@ -1225,10 +1236,75 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx, "frame context: %d.\n", err); goto fail; } - recon_hwctx = ctx->recon_frames->hwctx; + err = 0; + fail: + av_freep(&hwconfig); + av_hwframe_constraints_free(&constraints); + return err; +} + +av_cold int ff_vaapi_encode_init(AVCodecContext *avctx) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + AVVAAPIFramesContext *recon_hwctx = NULL; + VAStatus vas; + int err; + + if (!avctx->hw_frames_ctx) { + av_log(avctx, AV_LOG_ERROR, "A hardware frames reference is " + "required to associate the encoding device.\n"); + return AVERROR(EINVAL); + } + + ctx->codec_options = ctx->codec_options_data; + + ctx->va_config = VA_INVALID_ID; + ctx->va_context = VA_INVALID_ID; + + ctx->priv_data = av_mallocz(ctx->codec->priv_data_size); + if (!ctx->priv_data) { + err = AVERROR(ENOMEM); + goto fail; + } + + ctx->input_frames_ref = av_buffer_ref(avctx->hw_frames_ctx); + if (!ctx->input_frames_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + ctx->input_frames = (AVHWFramesContext*)ctx->input_frames_ref->data; + + ctx->device_ref = av_buffer_ref(ctx->input_frames->device_ref); + if (!ctx->device_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + ctx->device = (AVHWDeviceContext*)ctx->device_ref->data; + ctx->hwctx = ctx->device->hwctx; + + err = vaapi_encode_config_attributes(avctx); + if (err < 0) + goto fail; + + vas = vaCreateConfig(ctx->hwctx->display, + ctx->va_profile, ctx->va_entrypoint, + ctx->config_attributes, ctx->nb_config_attributes, + &ctx->va_config); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to create encode pipeline " + "configuration: %d (%s).\n", vas, vaErrorStr(vas)); + err = AVERROR(EIO); + goto fail; + } + + err = vaapi_encode_create_recon_frames(avctx); + if (err < 0) + goto fail; + + recon_hwctx = ctx->recon_frames->hwctx; vas = vaCreateContext(ctx->hwctx->display, ctx->va_config, - ctx->aligned_width, ctx->aligned_height, + ctx->surface_width, ctx->surface_height, VA_PROGRESSIVE, recon_hwctx->surface_ids, recon_hwctx->nb_surfaces, @@ -1240,6 +1316,26 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx, goto fail; } + ctx->output_buffer_pool = + av_buffer_pool_init2(sizeof(VABufferID), avctx, + &vaapi_encode_alloc_output_buffer, NULL); + if (!ctx->output_buffer_pool) { + err = AVERROR(ENOMEM); + goto fail; + } + + if (ctx->va_rc_mode & ~VA_RC_CQP) { + err = vaapi_encode_init_rate_control(avctx); + if (err < 0) + goto fail; + } + + if (ctx->codec->configure) { + err = ctx->codec->configure(avctx); + if (err < 0) + goto fail; + } + ctx->input_order = 0; ctx->output_delay = avctx->max_b_frames; ctx->decode_delay = 1; @@ -1271,14 +1367,6 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx, } } - ctx->output_buffer_pool = - av_buffer_pool_init2(sizeof(VABufferID), avctx, - &vaapi_encode_alloc_output_buffer, NULL); - if (!ctx->output_buffer_pool) { - err = AVERROR(ENOMEM); - goto fail; - } - // All I are IDR for now. ctx->i_per_idr = 0; ctx->p_per_i = ((avctx->gop_size + avctx->max_b_frames) / @@ -1292,8 +1380,6 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx, return 0; fail: - av_freep(&hwconfig); - av_hwframe_constraints_free(&constraints); ff_vaapi_encode_close(avctx); return err; } @@ -1318,9 +1404,6 @@ av_cold int ff_vaapi_encode_close(AVCodecContext *avctx) ctx->va_config = VA_INVALID_ID; } - if (ctx->codec->close) - ctx->codec->close(avctx); - av_buffer_pool_uninit(&ctx->output_buffer_pool); av_freep(&ctx->codec_sequence_params); diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h index eede73ca97..71f608751b 100644 --- a/libavcodec/vaapi_encode.h +++ b/libavcodec/vaapi_encode.h @@ -48,15 +48,6 @@ enum { PICTURE_TYPE_B = 3, }; -enum { - // All encode operations are done independently. - ISSUE_MODE_SERIALISE_EVERYTHING = 0, - // Overlap as many operations as possible. - ISSUE_MODE_MAXIMISE_THROUGHPUT, - // Overlap operations only when satisfying parallel dependencies. - ISSUE_MODE_MINIMISE_LATENCY, -}; - typedef struct VAAPIEncodeSlice { void *priv_data; void *codec_slice_params; @@ -102,43 +93,65 @@ typedef struct VAAPIEncodeContext { // 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; + // Rate control mode. + unsigned int va_rc_mode; + + // The required size of surfaces. This is probably the input + // size (AVCodecContext.width|height) aligned up to whatever + // block size is required by the codec. + int surface_width; + int surface_height; + + // Everything above this point must be set before calling + // ff_vaapi_encode_init(). + // Codec-specific state. void *priv_data; - VAProfile va_profile; - VAEntrypoint va_entrypoint; + // Configuration attributes to use when creating va_config. + VAConfigAttrib config_attributes[MAX_CONFIG_ATTRIBUTES]; + int nb_config_attributes; + VAConfigID va_config; VAContextID va_context; - int va_rc_mode; - AVBufferRef *device_ref; AVHWDeviceContext *device; AVVAAPIDeviceContext *hwctx; + // The hardware frame context containing the input frames. AVBufferRef *input_frames_ref; AVHWFramesContext *input_frames; - // Input size, set from input frames. - int input_width; - int input_height; - // Aligned size, set by codec init, becomes hwframe size. - int aligned_width; - int aligned_height; - - int nb_recon_frames; + // The hardware frame context containing the reconstructed frames. AVBufferRef *recon_frames_ref; AVHWFramesContext *recon_frames; + // Pool of (reusable) bitstream output buffers. AVBufferPool *output_buffer_pool; - VAConfigAttrib config_attributes[MAX_CONFIG_ATTRIBUTES]; - int nb_config_attributes; - + // Global parameters which will be applied at the start of the + // sequence (includes rate control parameters below). VAEncMiscParameterBuffer *global_params[MAX_GLOBAL_PARAMS]; size_t global_params_size[MAX_GLOBAL_PARAMS]; int nb_global_params; + // Rate control parameters. + struct { + VAEncMiscParameterBuffer misc; + VAEncMiscParameterRateControl rc; + } rc_params; + struct { + VAEncMiscParameterBuffer misc; + VAEncMiscParameterHRD hrd; + } hrd_params; + // Per-sequence parameter structure (VAEncSequenceParameterBuffer*). void *codec_sequence_params; @@ -158,7 +171,15 @@ typedef struct VAAPIEncodeContext { // Next output order index (encode order). int64_t output_order; - int issue_mode; + enum { + // All encode operations are done independently (synchronise + // immediately after every operation). + ISSUE_MODE_SERIALISE_EVERYTHING = 0, + // Overlap as many operations as possible. + ISSUE_MODE_MAXIMISE_THROUGHPUT, + // Overlap operations only when satisfying parallel dependencies. + ISSUE_MODE_MINIMISE_LATENCY, + } issue_mode; // Timestamp handling. int64_t first_pts; @@ -183,15 +204,20 @@ typedef struct VAAPIEncodeContext { typedef struct VAAPIEncodeType { - size_t priv_data_size; + size_t priv_data_size; - int (*init)(AVCodecContext *avctx); - int (*close)(AVCodecContext *avctx); + // Perform any extra codec-specific configuration after the + // codec context is initialised (set up the private data and + // add any necessary global parameters). + int (*configure)(AVCodecContext *avctx); + // The size of the parameter structures: + // sizeof(VAEnc{type}ParameterBuffer{codec}). size_t sequence_params_size; size_t picture_params_size; size_t slice_params_size; + // Fill the parameter structures. int (*init_sequence_params)(AVCodecContext *avctx); int (*init_picture_params)(AVCodecContext *avctx, VAAPIEncodePicture *pic); @@ -199,10 +225,13 @@ typedef struct VAAPIEncodeType { VAAPIEncodePicture *pic, VAAPIEncodeSlice *slice); + // The type used by the packed header: this should look like + // VAEncPackedHeader{something}. int sequence_header_type; int picture_header_type; int slice_header_type; + // Write the packed header data to the provided buffer. int (*write_sequence_header)(AVCodecContext *avctx, char *data, size_t *data_len); int (*write_picture_header)(AVCodecContext *avctx, @@ -213,10 +242,18 @@ typedef struct VAAPIEncodeType { VAAPIEncodeSlice *slice, char *data, size_t *data_len); + // Fill an extra parameter structure, which will then be + // passed to vaRenderPicture(). Will be called repeatedly + // with increasing index argument until AVERROR_EOF is + // returned. int (*write_extra_buffer)(AVCodecContext *avctx, VAAPIEncodePicture *pic, int index, int *type, char *data, size_t *data_len); + + // Write an extra packed header. Will be called repeatedly + // with increasing index argument until AVERROR_EOF is + // returned. int (*write_extra_header)(AVCodecContext *avctx, VAAPIEncodePicture *pic, int index, int *type, @@ -227,8 +264,7 @@ typedef struct VAAPIEncodeType { int ff_vaapi_encode2(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *input_image, int *got_packet); -int ff_vaapi_encode_init(AVCodecContext *avctx, - const VAAPIEncodeType *type); +int ff_vaapi_encode_init(AVCodecContext *avctx); int ff_vaapi_encode_close(AVCodecContext *avctx); #endif /* AVCODEC_VAAPI_ENCODE_H */ diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c index 9d6ff27a96..d9b186816a 100644 --- a/libavcodec/vaapi_encode_h264.c +++ b/libavcodec/vaapi_encode_h264.c @@ -148,14 +148,6 @@ typedef struct VAAPIEncodeH264Context { // Rate control configuration. int send_timing_sei; - struct { - VAEncMiscParameterBuffer misc; - VAEncMiscParameterRateControl rc; - } rc_params; - struct { - VAEncMiscParameterBuffer misc; - VAEncMiscParameterHRD hrd; - } hrd_params; #if VA_CHECK_VERSION(0, 36, 0) // Speed-quality tradeoff setting. @@ -797,16 +789,16 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx) vseq->seq_fields.bits.log2_max_frame_num_minus4 = 4; vseq->seq_fields.bits.pic_order_cnt_type = 0; - if (ctx->input_width != ctx->aligned_width || - ctx->input_height != ctx->aligned_height) { + if (avctx->width != ctx->surface_width || + avctx->height != ctx->surface_height) { vseq->frame_cropping_flag = 1; vseq->frame_crop_left_offset = 0; vseq->frame_crop_right_offset = - (ctx->aligned_width - ctx->input_width) / 2; + (ctx->surface_width - avctx->width) / 2; vseq->frame_crop_top_offset = 0; vseq->frame_crop_bottom_offset = - (ctx->aligned_height - ctx->input_height) / 2; + (ctx->surface_height - avctx->height) / 2; } else { vseq->frame_cropping_flag = 0; } @@ -866,9 +858,9 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx) (avctx->bit_rate >> mseq->bit_rate_scale + 6) - 1; mseq->cpb_size_scale = - av_clip_uintp2(av_log2(priv->hrd_params.hrd.buffer_size) - 15 - 4, 4); + av_clip_uintp2(av_log2(ctx->hrd_params.hrd.buffer_size) - 15 - 4, 4); mseq->cpb_size_value_minus1[0] = - (priv->hrd_params.hrd.buffer_size >> mseq->cpb_size_scale + 4) - 1; + (ctx->hrd_params.hrd.buffer_size >> mseq->cpb_size_scale + 4) - 1; // CBR mode isn't actually available here, despite naming. mseq->cbr_flag[0] = 0; @@ -880,8 +872,8 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx) // This calculation can easily overflow 32 bits. mseq->initial_cpb_removal_delay = 90000 * - (uint64_t)priv->hrd_params.hrd.initial_buffer_fullness / - priv->hrd_params.hrd.buffer_size; + (uint64_t)ctx->hrd_params.hrd.initial_buffer_fullness / + ctx->hrd_params.hrd.buffer_size; mseq->initial_cpb_removal_delay_offset = 0; } else { @@ -1083,94 +1075,94 @@ static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx, return 0; } -static av_cold int vaapi_encode_h264_init_constant_bitrate(AVCodecContext *avctx) -{ - VAAPIEncodeContext *ctx = avctx->priv_data; - VAAPIEncodeH264Context *priv = ctx->priv_data; - int hrd_buffer_size; - int hrd_initial_buffer_fullness; - - if (avctx->rc_buffer_size) - hrd_buffer_size = avctx->rc_buffer_size; - else - hrd_buffer_size = avctx->bit_rate; - if (avctx->rc_initial_buffer_occupancy) - hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy; - else - hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4; - - priv->rc_params.misc.type = VAEncMiscParameterTypeRateControl; - priv->rc_params.rc = (VAEncMiscParameterRateControl) { - .bits_per_second = avctx->bit_rate, - .target_percentage = 66, - .window_size = 1000, - .initial_qp = (avctx->qmax >= 0 ? avctx->qmax : 40), - .min_qp = (avctx->qmin >= 0 ? avctx->qmin : 18), - .basic_unit_size = 0, - }; - ctx->global_params[ctx->nb_global_params] = - &priv->rc_params.misc; - ctx->global_params_size[ctx->nb_global_params++] = - sizeof(priv->rc_params); - - priv->hrd_params.misc.type = VAEncMiscParameterTypeHRD; - priv->hrd_params.hrd = (VAEncMiscParameterHRD) { - .initial_buffer_fullness = hrd_initial_buffer_fullness, - .buffer_size = hrd_buffer_size, - }; - ctx->global_params[ctx->nb_global_params] = - &priv->hrd_params.misc; - ctx->global_params_size[ctx->nb_global_params++] = - sizeof(priv->hrd_params); - - // These still need to be set for pic_init_qp/slice_qp_delta. - priv->fixed_qp_idr = 26; - priv->fixed_qp_p = 26; - priv->fixed_qp_b = 26; - - av_log(avctx, AV_LOG_DEBUG, "Using constant-bitrate = %d bps.\n", - avctx->bit_rate); - return 0; -} - -static av_cold int vaapi_encode_h264_init_fixed_qp(AVCodecContext *avctx) +static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx) { VAAPIEncodeContext *ctx = avctx->priv_data; VAAPIEncodeH264Context *priv = ctx->priv_data; VAAPIEncodeH264Options *opt = ctx->codec_options; - priv->fixed_qp_p = opt->qp; - if (avctx->i_quant_factor > 0.0) - priv->fixed_qp_idr = (int)((priv->fixed_qp_p * avctx->i_quant_factor + - avctx->i_quant_offset) + 0.5); - else - priv->fixed_qp_idr = priv->fixed_qp_p; - if (avctx->b_quant_factor > 0.0) - priv->fixed_qp_b = (int)((priv->fixed_qp_p * avctx->b_quant_factor + - avctx->b_quant_offset) + 0.5); - else - priv->fixed_qp_b = priv->fixed_qp_p; + priv->mb_width = FFALIGN(avctx->width, 16) / 16; + priv->mb_height = FFALIGN(avctx->height, 16) / 16; + + if (ctx->va_rc_mode == VA_RC_CQP) { + priv->fixed_qp_p = opt->qp; + if (avctx->i_quant_factor > 0.0) + priv->fixed_qp_idr = (int)((priv->fixed_qp_p * avctx->i_quant_factor + + avctx->i_quant_offset) + 0.5); + else + priv->fixed_qp_idr = priv->fixed_qp_p; + if (avctx->b_quant_factor > 0.0) + priv->fixed_qp_b = (int)((priv->fixed_qp_p * avctx->b_quant_factor + + avctx->b_quant_offset) + 0.5); + else + priv->fixed_qp_b = priv->fixed_qp_p; + + av_log(avctx, AV_LOG_DEBUG, "Using fixed QP = " + "%d / %d / %d for IDR- / P- / B-frames.\n", + priv->fixed_qp_idr, priv->fixed_qp_p, priv->fixed_qp_b); + + } else if (ctx->va_rc_mode == VA_RC_CBR) { + // These still need to be set for pic_init_qp/slice_qp_delta. + priv->fixed_qp_idr = 26; + priv->fixed_qp_p = 26; + priv->fixed_qp_b = 26; + + av_log(avctx, AV_LOG_DEBUG, "Using constant-bitrate = %d bps.\n", + avctx->bit_rate); + + } else { + av_assert0(0 && "Invalid RC mode."); + } + + if (opt->quality > 0) { +#if VA_CHECK_VERSION(0, 36, 0) + priv->quality_params.misc.type = + VAEncMiscParameterTypeQualityLevel; + priv->quality_params.quality.quality_level = opt->quality; + + ctx->global_params[ctx->nb_global_params] = + &priv->quality_params.misc; + ctx->global_params_size[ctx->nb_global_params++] = + sizeof(priv->quality_params); +#else + av_log(avctx, AV_LOG_WARNING, "The encode quality option is not " + "supported with this VAAPI version.\n"); +#endif + } - av_log(avctx, AV_LOG_DEBUG, "Using fixed QP = " - "%d / %d / %d for IDR- / P- / B-frames.\n", - priv->fixed_qp_idr, priv->fixed_qp_p, priv->fixed_qp_b); return 0; } -static av_cold int vaapi_encode_h264_init_internal(AVCodecContext *avctx) -{ - static const VAConfigAttrib default_config_attributes[] = { - { .type = VAConfigAttribRTFormat, - .value = VA_RT_FORMAT_YUV420 }, - { .type = VAConfigAttribEncPackedHeaders, - .value = (VA_ENC_PACKED_HEADER_SEQUENCE | - VA_ENC_PACKED_HEADER_SLICE) }, - }; +static const VAAPIEncodeType vaapi_encode_type_h264 = { + .priv_data_size = sizeof(VAAPIEncodeH264Context), - VAAPIEncodeContext *ctx = avctx->priv_data; - VAAPIEncodeH264Context *priv = ctx->priv_data; - VAAPIEncodeH264Options *opt = ctx->codec_options; - int i, err; + .configure = &vaapi_encode_h264_configure, + + .sequence_params_size = sizeof(VAEncSequenceParameterBufferH264), + .init_sequence_params = &vaapi_encode_h264_init_sequence_params, + + .picture_params_size = sizeof(VAEncPictureParameterBufferH264), + .init_picture_params = &vaapi_encode_h264_init_picture_params, + + .slice_params_size = sizeof(VAEncSliceParameterBufferH264), + .init_slice_params = &vaapi_encode_h264_init_slice_params, + + .sequence_header_type = VAEncPackedHeaderSequence, + .write_sequence_header = &vaapi_encode_h264_write_sequence_header, + + .slice_header_type = VAEncPackedHeaderH264_Slice, + .write_slice_header = &vaapi_encode_h264_write_slice_header, + + .write_extra_header = &vaapi_encode_h264_write_extra_header, +}; + +static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH264Options *opt = + (VAAPIEncodeH264Options*)ctx->codec_options_data; + + ctx->codec = &vaapi_encode_type_h264; switch (avctx->profile) { case FF_PROFILE_H264_CONSTRAINED_BASELINE: @@ -1210,7 +1202,7 @@ static av_cold int vaapi_encode_h264_init_internal(AVCodecContext *avctx) return AVERROR(EINVAL); } if (opt->low_power) { -#if VA_CHECK_VERSION(0, 39, 1) +#if VA_CHECK_VERSION(0, 39, 2) ctx->va_entrypoint = VAEntrypointEncSliceLP; #else av_log(avctx, AV_LOG_ERROR, "Low-power encoding is not " @@ -1221,80 +1213,19 @@ static av_cold int vaapi_encode_h264_init_internal(AVCodecContext *avctx) ctx->va_entrypoint = VAEntrypointEncSlice; } - ctx->input_width = avctx->width; - ctx->input_height = avctx->height; - ctx->aligned_width = FFALIGN(ctx->input_width, 16); - ctx->aligned_height = FFALIGN(ctx->input_height, 16); - priv->mb_width = ctx->aligned_width / 16; - priv->mb_height = ctx->aligned_height / 16; + // Only 8-bit encode is supported. + ctx->va_rt_format = VA_RT_FORMAT_YUV420; - for (i = 0; i < FF_ARRAY_ELEMS(default_config_attributes); i++) { - ctx->config_attributes[ctx->nb_config_attributes++] = - default_config_attributes[i]; - } - - if (avctx->bit_rate > 0) { + if (avctx->bit_rate > 0) ctx->va_rc_mode = VA_RC_CBR; - err = vaapi_encode_h264_init_constant_bitrate(avctx); - } else { + else ctx->va_rc_mode = VA_RC_CQP; - err = vaapi_encode_h264_init_fixed_qp(avctx); - } - if (err < 0) - return err; - ctx->config_attributes[ctx->nb_config_attributes++] = (VAConfigAttrib) { - .type = VAConfigAttribRateControl, - .value = ctx->va_rc_mode, - }; - if (opt->quality > 0) { -#if VA_CHECK_VERSION(0, 36, 0) - priv->quality_params.misc.type = - VAEncMiscParameterTypeQualityLevel; - priv->quality_params.quality.quality_level = opt->quality; + ctx->surface_width = FFALIGN(avctx->width, 16); + ctx->surface_height = FFALIGN(avctx->height, 16); - ctx->global_params[ctx->nb_global_params] = - &priv->quality_params.misc; - ctx->global_params_size[ctx->nb_global_params++] = - sizeof(priv->quality_params); -#else - av_log(avctx, AV_LOG_WARNING, "The encode quality option is not " - "supported with this VAAPI version.\n"); -#endif - } - - ctx->nb_recon_frames = 20; - - return 0; -} - -static VAAPIEncodeType vaapi_encode_type_h264 = { - .priv_data_size = sizeof(VAAPIEncodeH264Context), - - .init = &vaapi_encode_h264_init_internal, - - .sequence_params_size = sizeof(VAEncSequenceParameterBufferH264), - .init_sequence_params = &vaapi_encode_h264_init_sequence_params, - - .picture_params_size = sizeof(VAEncPictureParameterBufferH264), - .init_picture_params = &vaapi_encode_h264_init_picture_params, - - .slice_params_size = sizeof(VAEncSliceParameterBufferH264), - .init_slice_params = &vaapi_encode_h264_init_slice_params, - - .sequence_header_type = VAEncPackedHeaderSequence, - .write_sequence_header = &vaapi_encode_h264_write_sequence_header, - - .slice_header_type = VAEncPackedHeaderH264_Slice, - .write_slice_header = &vaapi_encode_h264_write_slice_header, - - .write_extra_header = &vaapi_encode_h264_write_extra_header, -}; - -static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx) -{ - return ff_vaapi_encode_init(avctx, &vaapi_encode_type_h264); + return ff_vaapi_encode_init(avctx); } #define OFFSET(x) (offsetof(VAAPIEncodeContext, codec_options_data) + \ diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c index 010db9293b..f5e29443ef 100644 --- a/libavcodec/vaapi_encode_h265.c +++ b/libavcodec/vaapi_encode_h265.c @@ -798,8 +798,8 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx) vseq->intra_idr_period = 0; vseq->ip_period = 0; - vseq->pic_width_in_luma_samples = ctx->aligned_width; - vseq->pic_height_in_luma_samples = ctx->aligned_height; + vseq->pic_width_in_luma_samples = ctx->surface_width; + vseq->pic_height_in_luma_samples = ctx->surface_height; vseq->seq_fields.bits.chroma_format_idc = 1; // 4:2:0. vseq->seq_fields.bits.separate_colour_plane_flag = 0; @@ -911,15 +911,15 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx) mseq->vps_poc_proportional_to_timing_flag = 1; mseq->vps_num_ticks_poc_diff_minus1 = 0; - if (ctx->input_width != ctx->aligned_width || - ctx->input_height != ctx->aligned_height) { + if (avctx->width != ctx->surface_width || + avctx->height != ctx->surface_height) { mseq->conformance_window_flag = 1; mseq->conf_win_left_offset = 0; mseq->conf_win_right_offset = - (ctx->aligned_width - ctx->input_width) / 2; + (ctx->surface_width - avctx->width) / 2; mseq->conf_win_top_offset = 0; mseq->conf_win_bottom_offset = - (ctx->aligned_height - ctx->input_height) / 2; + (ctx->surface_height - avctx->height) / 2; } else { mseq->conformance_window_flag = 0; } @@ -1154,150 +1154,56 @@ static int vaapi_encode_h265_init_slice_params(AVCodecContext *avctx, return 0; } -static av_cold int vaapi_encode_h265_init_constant_bitrate(AVCodecContext *avctx) -{ - VAAPIEncodeContext *ctx = avctx->priv_data; - VAAPIEncodeH265Context *priv = ctx->priv_data; - int hrd_buffer_size; - int hrd_initial_buffer_fullness; - - if (avctx->rc_buffer_size) - hrd_buffer_size = avctx->rc_buffer_size; - else - hrd_buffer_size = avctx->bit_rate; - if (avctx->rc_initial_buffer_occupancy) - hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy; - else - hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4; - - priv->rc_params.misc.type = VAEncMiscParameterTypeRateControl; - priv->rc_params.rc = (VAEncMiscParameterRateControl) { - .bits_per_second = avctx->bit_rate, - .target_percentage = 66, - .window_size = 1000, - .initial_qp = (avctx->qmax >= 0 ? avctx->qmax : 40), - .min_qp = (avctx->qmin >= 0 ? avctx->qmin : 20), - .basic_unit_size = 0, - }; - ctx->global_params[ctx->nb_global_params] = - &priv->rc_params.misc; - ctx->global_params_size[ctx->nb_global_params++] = - sizeof(priv->rc_params); - - priv->hrd_params.misc.type = VAEncMiscParameterTypeHRD; - priv->hrd_params.hrd = (VAEncMiscParameterHRD) { - .initial_buffer_fullness = hrd_initial_buffer_fullness, - .buffer_size = hrd_buffer_size, - }; - ctx->global_params[ctx->nb_global_params] = - &priv->hrd_params.misc; - ctx->global_params_size[ctx->nb_global_params++] = - sizeof(priv->hrd_params); - - // These still need to be set for pic_init_qp/slice_qp_delta. - priv->fixed_qp_idr = 30; - priv->fixed_qp_p = 30; - priv->fixed_qp_b = 30; - - av_log(avctx, AV_LOG_DEBUG, "Using constant-bitrate = %d bps.\n", - avctx->bit_rate); - return 0; -} - -static av_cold int vaapi_encode_h265_init_fixed_qp(AVCodecContext *avctx) +static av_cold int vaapi_encode_h265_configure(AVCodecContext *avctx) { VAAPIEncodeContext *ctx = avctx->priv_data; VAAPIEncodeH265Context *priv = ctx->priv_data; VAAPIEncodeH265Options *opt = ctx->codec_options; - priv->fixed_qp_p = opt->qp; - if (avctx->i_quant_factor > 0.0) - priv->fixed_qp_idr = (int)((priv->fixed_qp_p * avctx->i_quant_factor + - avctx->i_quant_offset) + 0.5); - else - priv->fixed_qp_idr = priv->fixed_qp_p; - if (avctx->b_quant_factor > 0.0) - priv->fixed_qp_b = (int)((priv->fixed_qp_p * avctx->b_quant_factor + - avctx->b_quant_offset) + 0.5); - else - priv->fixed_qp_b = priv->fixed_qp_p; + priv->ctu_width = FFALIGN(ctx->surface_width, 32) / 32; + priv->ctu_height = FFALIGN(ctx->surface_height, 32) / 32; - av_log(avctx, AV_LOG_DEBUG, "Using fixed QP = " - "%d / %d / %d for IDR- / P- / B-frames.\n", - priv->fixed_qp_idr, priv->fixed_qp_p, priv->fixed_qp_b); - return 0; -} + av_log(avctx, AV_LOG_VERBOSE, "Input %ux%u -> Surface %ux%u -> CTU %ux%u.\n", + avctx->width, avctx->height, ctx->surface_width, + ctx->surface_height, priv->ctu_width, priv->ctu_height); -static av_cold int vaapi_encode_h265_init_internal(AVCodecContext *avctx) -{ - static const VAConfigAttrib default_config_attributes[] = { - { .type = VAConfigAttribRTFormat, - .value = VA_RT_FORMAT_YUV420 }, - { .type = VAConfigAttribEncPackedHeaders, - .value = (VA_ENC_PACKED_HEADER_SEQUENCE | - VA_ENC_PACKED_HEADER_SLICE) }, - }; + if (ctx->va_rc_mode == VA_RC_CQP) { + priv->fixed_qp_p = opt->qp; + if (avctx->i_quant_factor > 0.0) + priv->fixed_qp_idr = (int)((priv->fixed_qp_p * avctx->i_quant_factor + + avctx->i_quant_offset) + 0.5); + else + priv->fixed_qp_idr = priv->fixed_qp_p; + if (avctx->b_quant_factor > 0.0) + priv->fixed_qp_b = (int)((priv->fixed_qp_p * avctx->b_quant_factor + + avctx->b_quant_offset) + 0.5); + else + priv->fixed_qp_b = priv->fixed_qp_p; - VAAPIEncodeContext *ctx = avctx->priv_data; - VAAPIEncodeH265Context *priv = ctx->priv_data; - int i, err; + av_log(avctx, AV_LOG_DEBUG, "Using fixed QP = " + "%d / %d / %d for IDR- / P- / B-frames.\n", + priv->fixed_qp_idr, priv->fixed_qp_p, priv->fixed_qp_b); - switch (avctx->profile) { - case FF_PROFILE_HEVC_MAIN: - case FF_PROFILE_UNKNOWN: - ctx->va_profile = VAProfileHEVCMain; - break; - case FF_PROFILE_HEVC_MAIN_10: - av_log(avctx, AV_LOG_ERROR, "H.265 main 10-bit profile " - "is not supported.\n"); - return AVERROR_PATCHWELCOME; - default: - av_log(avctx, AV_LOG_ERROR, "Unknown H.265 profile %d.\n", - avctx->profile); - return AVERROR(EINVAL); - } - ctx->va_entrypoint = VAEntrypointEncSlice; + } else if (ctx->va_rc_mode == VA_RC_CBR) { + // These still need to be set for pic_init_qp/slice_qp_delta. + priv->fixed_qp_idr = 30; + priv->fixed_qp_p = 30; + priv->fixed_qp_b = 30; - ctx->input_width = avctx->width; - ctx->input_height = avctx->height; - ctx->aligned_width = FFALIGN(ctx->input_width, 16); - ctx->aligned_height = FFALIGN(ctx->input_height, 16); - priv->ctu_width = FFALIGN(ctx->aligned_width, 32) / 32; - priv->ctu_height = FFALIGN(ctx->aligned_height, 32) / 32; + av_log(avctx, AV_LOG_DEBUG, "Using constant-bitrate = %d bps.\n", + avctx->bit_rate); - av_log(avctx, AV_LOG_VERBOSE, "Input %ux%u -> Aligned %ux%u -> CTU %ux%u.\n", - ctx->input_width, ctx->input_height, ctx->aligned_width, - ctx->aligned_height, priv->ctu_width, priv->ctu_height); - - for (i = 0; i < FF_ARRAY_ELEMS(default_config_attributes); i++) { - ctx->config_attributes[ctx->nb_config_attributes++] = - default_config_attributes[i]; - } - - if (avctx->bit_rate > 0) { - ctx->va_rc_mode = VA_RC_CBR; - err = vaapi_encode_h265_init_constant_bitrate(avctx); } else { - ctx->va_rc_mode = VA_RC_CQP; - err = vaapi_encode_h265_init_fixed_qp(avctx); + av_assert0(0 && "Invalid RC mode."); } - if (err < 0) - return err; - - ctx->config_attributes[ctx->nb_config_attributes++] = (VAConfigAttrib) { - .type = VAConfigAttribRateControl, - .value = ctx->va_rc_mode, - }; - - ctx->nb_recon_frames = 20; return 0; } -static VAAPIEncodeType vaapi_encode_type_h265 = { +static const VAAPIEncodeType vaapi_encode_type_h265 = { .priv_data_size = sizeof(VAAPIEncodeH265Context), - .init = &vaapi_encode_h265_init_internal, + .configure = &vaapi_encode_h265_configure, .sequence_params_size = sizeof(VAEncSequenceParameterBufferHEVC), .init_sequence_params = &vaapi_encode_h265_init_sequence_params, @@ -1317,7 +1223,39 @@ static VAAPIEncodeType vaapi_encode_type_h265 = { static av_cold int vaapi_encode_h265_init(AVCodecContext *avctx) { - return ff_vaapi_encode_init(avctx, &vaapi_encode_type_h265); + VAAPIEncodeContext *ctx = avctx->priv_data; + + ctx->codec = &vaapi_encode_type_h265; + + switch (avctx->profile) { + case FF_PROFILE_HEVC_MAIN: + case FF_PROFILE_UNKNOWN: + ctx->va_profile = VAProfileHEVCMain; + break; + case FF_PROFILE_HEVC_MAIN_10: + av_log(avctx, AV_LOG_ERROR, "H.265 main 10-bit profile " + "is not supported.\n"); + return AVERROR_PATCHWELCOME; + default: + av_log(avctx, AV_LOG_ERROR, "Unknown H.265 profile %d.\n", + avctx->profile); + return AVERROR(EINVAL); + } + ctx->va_entrypoint = VAEntrypointEncSlice; + + // This will be dependent on profile when 10-bit is supported. + ctx->va_rt_format = VA_RT_FORMAT_YUV420; + + if (avctx->bit_rate > 0) + ctx->va_rc_mode = VA_RC_CBR; + else + ctx->va_rc_mode = VA_RC_CQP; + + + ctx->surface_width = FFALIGN(avctx->width, 16); + ctx->surface_height = FFALIGN(avctx->height, 16); + + return ff_vaapi_encode_init(avctx); } #define OFFSET(x) (offsetof(VAAPIEncodeContext, codec_options_data) + \ diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c index e3bf191884..8866dfba62 100644 --- a/libavcodec/vaapi_encode_mjpeg.c +++ b/libavcodec/vaapi_encode_mjpeg.c @@ -277,8 +277,8 @@ static int vaapi_encode_mjpeg_init_picture_params(AVCodecContext *avctx, vpic->reconstructed_picture = pic->recon_surface; vpic->coded_buf = pic->output_buffer; - vpic->picture_width = ctx->input_width; - vpic->picture_height = ctx->input_height; + vpic->picture_width = avctx->width; + vpic->picture_height = avctx->height; vpic->pic_flags.bits.profile = 0; vpic->pic_flags.bits.progressive = 0; @@ -333,31 +333,10 @@ static int vaapi_encode_mjpeg_init_slice_params(AVCodecContext *avctx, return 0; } -static av_cold int vaapi_encode_mjpeg_init_internal(AVCodecContext *avctx) +static av_cold int vaapi_encode_mjpeg_configure(AVCodecContext *avctx) { - static const VAConfigAttrib default_config_attributes[] = { - { .type = VAConfigAttribRTFormat, - .value = VA_RT_FORMAT_YUV420 }, - { .type = VAConfigAttribEncPackedHeaders, - .value = VA_ENC_PACKED_HEADER_SEQUENCE }, - }; - VAAPIEncodeContext *ctx = avctx->priv_data; VAAPIEncodeMJPEGContext *priv = ctx->priv_data; - int i; - - ctx->va_profile = VAProfileJPEGBaseline; - ctx->va_entrypoint = VAEntrypointEncPicture; - - ctx->input_width = avctx->width; - ctx->input_height = avctx->height; - ctx->aligned_width = FFALIGN(ctx->input_width, 8); - ctx->aligned_height = FFALIGN(ctx->input_height, 8); - - for (i = 0; i < FF_ARRAY_ELEMS(default_config_attributes); i++) { - ctx->config_attributes[ctx->nb_config_attributes++] = - default_config_attributes[i]; - } priv->quality = avctx->global_quality; if (priv->quality < 1 || priv->quality > 100) { @@ -374,7 +353,7 @@ static av_cold int vaapi_encode_mjpeg_init_internal(AVCodecContext *avctx) static VAAPIEncodeType vaapi_encode_type_mjpeg = { .priv_data_size = sizeof(VAAPIEncodeMJPEGContext), - .init = &vaapi_encode_mjpeg_init_internal, + .configure = &vaapi_encode_mjpeg_configure, .picture_params_size = sizeof(VAEncPictureParameterBufferJPEG), .init_picture_params = &vaapi_encode_mjpeg_init_picture_params, @@ -390,7 +369,21 @@ static VAAPIEncodeType vaapi_encode_type_mjpeg = { static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx) { - return ff_vaapi_encode_init(avctx, &vaapi_encode_type_mjpeg); + VAAPIEncodeContext *ctx = avctx->priv_data; + + 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; + + ctx->surface_width = FFALIGN(avctx->width, 8); + ctx->surface_height = FFALIGN(avctx->height, 8); + + return ff_vaapi_encode_init(avctx); } static const AVCodecDefault vaapi_encode_mjpeg_defaults[] = { From 892bbbcdc171ff0d08d69636a240ffb95f54243c Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sun, 18 Sep 2016 14:59:59 +0100 Subject: [PATCH 2/4] vaapi_encode: Check packed header capabilities This improves behaviour with drivers which do not support packed headers, such as AMD VCE on mesa/gallium. --- libavcodec/vaapi_encode.c | 36 ++++++++++++++++++++++++++------- libavcodec/vaapi_encode.h | 3 +++ libavcodec/vaapi_encode_h264.c | 4 ++++ libavcodec/vaapi_encode_h265.c | 3 +++ libavcodec/vaapi_encode_mjpeg.c | 15 ++++++++++++++ 5 files changed, 54 insertions(+), 7 deletions(-) diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c index 41d1a6ed17..7ec5340717 100644 --- a/libavcodec/vaapi_encode.c +++ b/libavcodec/vaapi_encode.c @@ -237,7 +237,8 @@ static int vaapi_encode_issue(AVCodecContext *avctx, } if (pic->type == PICTURE_TYPE_IDR) { - if (ctx->codec->write_sequence_header) { + if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE && + ctx->codec->write_sequence_header) { bit_len = 8 * sizeof(data); err = ctx->codec->write_sequence_header(avctx, data, &bit_len); if (err < 0) { @@ -253,7 +254,8 @@ static int vaapi_encode_issue(AVCodecContext *avctx, } } - if (ctx->codec->write_picture_header) { + if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_PICTURE && + ctx->codec->write_picture_header) { bit_len = 8 * sizeof(data); err = ctx->codec->write_picture_header(avctx, pic, data, &bit_len); if (err < 0) { @@ -289,7 +291,8 @@ static int vaapi_encode_issue(AVCodecContext *avctx, } } - if (ctx->codec->write_extra_header) { + if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_MISC && + ctx->codec->write_extra_header) { for (i = 0;; i++) { int type; bit_len = 8 * sizeof(data); @@ -336,7 +339,8 @@ static int vaapi_encode_issue(AVCodecContext *avctx, } } - if (ctx->codec->write_slice_header) { + if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SLICE && + ctx->codec->write_slice_header) { bit_len = 8 * sizeof(data); err = ctx->codec->write_slice_header(avctx, pic, slice, data, &bit_len); @@ -930,9 +934,10 @@ static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx) VAProfile *profiles = NULL; VAEntrypoint *entrypoints = NULL; VAConfigAttrib attr[] = { - { VAConfigAttribRTFormat }, - { VAConfigAttribRateControl }, - { VAConfigAttribEncMaxRefFrames }, + { VAConfigAttribRTFormat }, + { VAConfigAttribRateControl }, + { VAConfigAttribEncMaxRefFrames }, + { VAConfigAttribEncPackedHeaders }, }; n = vaMaxNumProfiles(ctx->hwctx->display); @@ -1049,6 +1054,23 @@ static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx) } } break; + case VAConfigAttribEncPackedHeaders: + if (ctx->va_packed_headers & ~attr[i].value) { + // This isn't fatal, but packed headers are always + // preferable because they are under our control. + // When absent, the driver is generating them and some + // features may not work (e.g. VUI or SEI in H.264). + av_log(avctx, AV_LOG_WARNING, "Warning: some packed " + "headers are not supported (want %#x, got %#x).\n", + ctx->va_packed_headers, attr[i].value); + ctx->va_packed_headers &= attr[i].value; + } + ctx->config_attributes[ctx->nb_config_attributes++] = + (VAConfigAttrib) { + .type = VAConfigAttribEncPackedHeaders, + .value = ctx->va_packed_headers, + }; + break; default: av_assert0(0 && "Unexpected config attribute."); } diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h index 71f608751b..c47d979db2 100644 --- a/libavcodec/vaapi_encode.h +++ b/libavcodec/vaapi_encode.h @@ -101,6 +101,9 @@ typedef struct VAAPIEncodeContext { unsigned int va_rt_format; // Rate control mode. unsigned int va_rc_mode; + // Supported packed headers (initially the desired set, modified + // later to what is actually supported). + unsigned int va_packed_headers; // The required size of surfaces. This is probably the input // size (AVCodecContext.width|height) aligned up to whatever diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c index d9b186816a..0cd966f662 100644 --- a/libavcodec/vaapi_encode_h264.c +++ b/libavcodec/vaapi_encode_h264.c @@ -1221,6 +1221,10 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx) else ctx->va_rc_mode = VA_RC_CQP; + ctx->va_packed_headers = + VA_ENC_PACKED_HEADER_SEQUENCE | // SPS and PPS. + VA_ENC_PACKED_HEADER_SLICE | // Slice headers. + VA_ENC_PACKED_HEADER_MISC; // SEI. ctx->surface_width = FFALIGN(avctx->width, 16); ctx->surface_height = FFALIGN(avctx->height, 16); diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c index f5e29443ef..7cd9782c20 100644 --- a/libavcodec/vaapi_encode_h265.c +++ b/libavcodec/vaapi_encode_h265.c @@ -1251,6 +1251,9 @@ static av_cold int vaapi_encode_h265_init(AVCodecContext *avctx) else ctx->va_rc_mode = VA_RC_CQP; + ctx->va_packed_headers = + VA_ENC_PACKED_HEADER_SEQUENCE | // VPS, SPS and PPS. + VA_ENC_PACKED_HEADER_SLICE; // Slice headers. ctx->surface_width = FFALIGN(avctx->width, 16); ctx->surface_height = FFALIGN(avctx->height, 16); diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c index 8866dfba62..78d5e789f1 100644 --- a/libavcodec/vaapi_encode_mjpeg.c +++ b/libavcodec/vaapi_encode_mjpeg.c @@ -345,6 +345,17 @@ static av_cold int vaapi_encode_mjpeg_configure(AVCodecContext *avctx) return AVERROR(EINVAL); } + // Hack: the implementation calls the JPEG image header (which we + // will use in the same way as a slice header) generic "raw data". + // Therefore, if after the packed header capability check we have + // PACKED_HEADER_RAW_DATA available, rewrite it as + // PACKED_HEADER_SLICE so that the header-writing code can do the + // right thing. + if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_RAW_DATA) { + ctx->va_packed_headers &= ~VA_ENC_PACKED_HEADER_RAW_DATA; + ctx->va_packed_headers |= VA_ENC_PACKED_HEADER_SLICE; + } + vaapi_encode_mjpeg_init_tables(avctx); return 0; @@ -380,6 +391,10 @@ static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx) ctx->va_rc_mode = VA_RC_CQP; + // The JPEG image header - see note above. + ctx->va_packed_headers = + VA_ENC_PACKED_HEADER_RAW_DATA; + ctx->surface_width = FFALIGN(avctx->width, 8); ctx->surface_height = FFALIGN(avctx->height, 8); From 086e4b58b59ea3993107aa24d92bb962ec69667c Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sun, 18 Sep 2016 16:06:55 +0100 Subject: [PATCH 3/4] vaapi_encode: Sync to input surface rather than output While outwardly bizarre, this change makes the behaviour consistent with other VAAPI encoders which sync to the encode /input/ picture in order to wait for /output/ from the encoder. It is not harmful on i965 (because synchronisation already happens in vaRenderPicture(), so it has no effect there), and it allows the encoder to work on mesa/gallium which assumes this behaviour. --- libavcodec/vaapi_encode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c index 7ec5340717..dc6cdfef60 100644 --- a/libavcodec/vaapi_encode.c +++ b/libavcodec/vaapi_encode.c @@ -109,10 +109,10 @@ static int vaapi_encode_wait(AVCodecContext *avctx, } av_log(avctx, AV_LOG_DEBUG, "Sync to pic %"PRId64"/%"PRId64" " - "(recon surface %#x).\n", pic->display_order, - pic->encode_order, pic->recon_surface); + "(input surface %#x).\n", pic->display_order, + pic->encode_order, pic->input_surface); - vas = vaSyncSurface(ctx->hwctx->display, pic->recon_surface); + vas = vaSyncSurface(ctx->hwctx->display, pic->input_surface); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to sync to picture completion: " "%d (%s).\n", vas, vaErrorStr(vas)); From 956a54129db522998a5abae869568dae2c9774cb Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Tue, 27 Sep 2016 19:08:42 +0100 Subject: [PATCH 4/4] vaapi_h264: Set max_num_ref_frames to 1 when not using B frames --- libavcodec/vaapi_encode_h264.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c index 0cd966f662..020f8924e2 100644 --- a/libavcodec/vaapi_encode_h264.c +++ b/libavcodec/vaapi_encode_h264.c @@ -778,7 +778,7 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx) vseq->level_idc = avctx->level; - vseq->max_num_ref_frames = 2; + vseq->max_num_ref_frames = 1 + (avctx->max_b_frames > 0); vseq->picture_width_in_mbs = priv->mb_width; vseq->picture_height_in_mbs = priv->mb_height;