X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fvaapi_encode.c;h=cb05ebd774a1a259b4f2659c0eeba992f4d53957;hb=b7d89963f00bd82f5cf230d7a7c865126fffc5fb;hp=2dda451882614c77262cc29ff440545cb5a805e8;hpb=ac4b5d86222006fa71ffe5922e1a34f1422507d8;p=ffmpeg diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c index 2dda4518826..cb05ebd774a 100644 --- a/libavcodec/vaapi_encode.c +++ b/libavcodec/vaapi_encode.c @@ -27,6 +27,11 @@ #include "vaapi_encode.h" #include "avcodec.h" +const AVCodecHWConfigInternal *ff_vaapi_encode_hw_configs[] = { + HW_CONFIG_ENCODER_FRAMES(VAAPI, VAAPI), + NULL, +}; + static const char * const picture_type_name[] = { "IDR", "I", "P", "B" }; static int vaapi_encode_make_packed_header(AVCodecContext *avctx, @@ -103,6 +108,29 @@ static int vaapi_encode_make_param_buffer(AVCodecContext *avctx, return 0; } +static int vaapi_encode_make_misc_param_buffer(AVCodecContext *avctx, + VAAPIEncodePicture *pic, + int type, + const void *data, size_t len) +{ + // Construct the buffer on the stack - 1KB is much larger than any + // current misc parameter buffer type (the largest is EncQuality at + // 224 bytes). + uint8_t buffer[1024]; + VAEncMiscParameterBuffer header = { + .type = type, + }; + size_t buffer_size = sizeof(header) + len; + av_assert0(buffer_size <= sizeof(buffer)); + + memcpy(buffer, &header, sizeof(header)); + memcpy(buffer + sizeof(header), data, len); + + return vaapi_encode_make_param_buffer(avctx, pic, + VAEncMiscParameterBufferType, + buffer, buffer_size); +} + static int vaapi_encode_wait(AVCodecContext *avctx, VAAPIEncodePicture *pic) { @@ -143,6 +171,7 @@ static int vaapi_encode_issue(AVCodecContext *avctx, int err, i; char data[MAX_PARAM_BUFFER_SIZE]; size_t bit_len; + av_unused AVFrameSideData *sd; av_log(avctx, AV_LOG_DEBUG, "Issuing encode for pic %"PRId64"/%"PRId64" " "as type %s.\n", pic->display_order, pic->encode_order, @@ -212,10 +241,10 @@ static int vaapi_encode_issue(AVCodecContext *avctx, if (pic->type == PICTURE_TYPE_IDR) { for (i = 0; i < ctx->nb_global_params; i++) { - err = vaapi_encode_make_param_buffer(avctx, pic, - VAEncMiscParameterBufferType, - (char*)ctx->global_params[i], - ctx->global_params_size[i]); + err = vaapi_encode_make_misc_param_buffer(avctx, pic, + ctx->global_params_type[i], + ctx->global_params[i], + ctx->global_params_size[i]); if (err < 0) goto fail; } @@ -412,6 +441,71 @@ static int vaapi_encode_issue(AVCodecContext *avctx, } } +#if VA_CHECK_VERSION(1, 0, 0) + sd = av_frame_get_side_data(pic->input_image, + AV_FRAME_DATA_REGIONS_OF_INTEREST); + if (sd && ctx->roi_allowed) { + const AVRegionOfInterest *roi; + uint32_t roi_size; + VAEncMiscParameterBufferROI param_roi; + int nb_roi, i, v; + + roi = (const AVRegionOfInterest*)sd->data; + roi_size = roi->self_size; + av_assert0(roi_size && sd->size % roi_size == 0); + nb_roi = sd->size / roi_size; + if (nb_roi > ctx->roi_max_regions) { + if (!ctx->roi_warned) { + av_log(avctx, AV_LOG_WARNING, "More ROIs set than " + "supported by driver (%d > %d).\n", + nb_roi, ctx->roi_max_regions); + ctx->roi_warned = 1; + } + nb_roi = ctx->roi_max_regions; + } + + pic->roi = av_mallocz_array(nb_roi, sizeof(*pic->roi)); + if (!pic->roi) { + err = AVERROR(ENOMEM); + goto fail; + } + // For overlapping regions, the first in the array takes priority. + for (i = 0; i < nb_roi; i++) { + roi = (const AVRegionOfInterest*)(sd->data + roi_size * i); + + av_assert0(roi->qoffset.den != 0); + v = roi->qoffset.num * ctx->roi_quant_range / roi->qoffset.den; + av_log(avctx, AV_LOG_DEBUG, "ROI: (%d,%d)-(%d,%d) -> %+d.\n", + roi->top, roi->left, roi->bottom, roi->right, v); + + pic->roi[i] = (VAEncROI) { + .roi_rectangle = { + .x = roi->left, + .y = roi->top, + .width = roi->right - roi->left, + .height = roi->bottom - roi->top, + }, + .roi_value = av_clip_int8(v), + }; + } + + param_roi = (VAEncMiscParameterBufferROI) { + .num_roi = nb_roi, + .max_delta_qp = INT8_MAX, + .min_delta_qp = INT8_MIN, + .roi = pic->roi, + .roi_flags.bits.roi_value_is_qp_delta = 1, + }; + + err = vaapi_encode_make_misc_param_buffer(avctx, pic, + VAEncMiscParameterTypeROI, + ¶m_roi, + sizeof(param_roi)); + if (err < 0) + goto fail; + } +#endif + vas = vaBeginPicture(ctx->hwctx->display, ctx->va_context, pic->input_surface); if (vas != VA_STATUS_SUCCESS) { @@ -477,6 +571,7 @@ fail_at_end: av_freep(&pic->codec_picture_params); av_freep(&pic->param_buffers); av_freep(&pic->slices); + av_freep(&pic->roi); av_frame_free(&pic->recon_image); av_buffer_unref(&pic->output_buffer_ref); pic->output_buffer = VA_INVALID_ID; @@ -489,6 +584,8 @@ static int vaapi_encode_output(AVCodecContext *avctx, VAAPIEncodeContext *ctx = avctx->priv_data; VACodedBufferSegment *buf_list, *buf; VAStatus vas; + int total_size = 0; + uint8_t *ptr; int err; err = vaapi_encode_wait(avctx, pic); @@ -505,15 +602,21 @@ static int vaapi_encode_output(AVCodecContext *avctx, goto fail; } + for (buf = buf_list; buf; buf = buf->next) + total_size += buf->size; + + err = av_new_packet(pkt, total_size); + ptr = pkt->data; + + if (err < 0) + goto fail_mapped; + for (buf = buf_list; buf; buf = buf->next) { av_log(avctx, AV_LOG_DEBUG, "Output buffer: %u bytes " "(status %08x).\n", buf->size, buf->status); - err = av_new_packet(pkt, buf->size); - if (err < 0) - goto fail_mapped; - - memcpy(pkt->data, buf->buf, buf->size); + memcpy(ptr, buf->buf, buf->size); + ptr += buf->size; } if (pic->type == PICTURE_TYPE_IDR) @@ -611,6 +714,7 @@ static int vaapi_encode_free(AVCodecContext *avctx, av_freep(&pic->priv_data); av_freep(&pic->codec_picture_params); + av_freep(&pic->roi); av_free(pic); @@ -913,6 +1017,32 @@ static int vaapi_encode_clear_old(AVCodecContext *avctx) return 0; } +static int vaapi_encode_check_frame(AVCodecContext *avctx, + const AVFrame *frame) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + + if ((frame->crop_top || frame->crop_bottom || + frame->crop_left || frame->crop_right) && !ctx->crop_warned) { + av_log(avctx, AV_LOG_WARNING, "Cropping information on input " + "frames ignored due to lack of API support.\n"); + ctx->crop_warned = 1; + } + + if (!ctx->roi_allowed) { + AVFrameSideData *sd = + av_frame_get_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST); + + if (sd && !ctx->roi_warned) { + av_log(avctx, AV_LOG_WARNING, "ROI side data on input " + "frames ignored due to lack of driver support.\n"); + ctx->roi_warned = 1; + } + } + + return 0; +} + int ff_vaapi_encode_send_frame(AVCodecContext *avctx, const AVFrame *frame) { VAAPIEncodeContext *ctx = avctx->priv_data; @@ -923,6 +1053,10 @@ int ff_vaapi_encode_send_frame(AVCodecContext *avctx, const AVFrame *frame) av_log(avctx, AV_LOG_DEBUG, "Input frame: %ux%u (%"PRId64").\n", frame->width, frame->height, frame->pts); + err = vaapi_encode_check_frame(avctx, frame); + if (err < 0) + return err; + pic = vaapi_encode_alloc(avctx); if (!pic) return AVERROR(ENOMEM); @@ -936,7 +1070,7 @@ int ff_vaapi_encode_send_frame(AVCodecContext *avctx, const AVFrame *frame) if (err < 0) goto fail; - if (ctx->input_order == 0) + if (ctx->input_order == 0 || frame->pict_type == AV_PICTURE_TYPE_I) pic->force_idr = 1; pic->input_surface = (VASurfaceID)(uintptr_t)frame->data[3]; @@ -972,6 +1106,7 @@ int ff_vaapi_encode_send_frame(AVCodecContext *avctx, const AVFrame *frame) return 0; fail: + vaapi_encode_free(avctx, pic); return err; } @@ -1028,20 +1163,15 @@ int ff_vaapi_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt) return 0; } -int ff_vaapi_encode2(AVCodecContext *avctx, AVPacket *pkt, - const AVFrame *input_image, int *got_packet) -{ - return AVERROR(ENOSYS); -} -static av_cold void vaapi_encode_add_global_param(AVCodecContext *avctx, - VAEncMiscParameterBuffer *buffer, - size_t size) +static av_cold void vaapi_encode_add_global_param(AVCodecContext *avctx, int type, + void *buffer, size_t size) { VAAPIEncodeContext *ctx = avctx->priv_data; av_assert0(ctx->nb_global_params < MAX_GLOBAL_PARAMS); + ctx->global_params_type[ctx->nb_global_params] = type; ctx->global_params [ctx->nb_global_params] = buffer; ctx->global_params_size[ctx->nb_global_params] = size; @@ -1575,8 +1705,7 @@ rc_mode_found: rc_bits_per_second, rc_window_size); } - ctx->rc_params.misc.type = VAEncMiscParameterTypeRateControl; - ctx->rc_params.rc = (VAEncMiscParameterRateControl) { + ctx->rc_params = (VAEncMiscParameterRateControl) { .bits_per_second = rc_bits_per_second, .target_percentage = rc_target_percentage, .window_size = rc_window_size, @@ -1591,7 +1720,9 @@ rc_mode_found: .quality_factor = rc_quality, #endif }; - vaapi_encode_add_global_param(avctx, &ctx->rc_params.misc, + vaapi_encode_add_global_param(avctx, + VAEncMiscParameterTypeRateControl, + &ctx->rc_params, sizeof(ctx->rc_params)); } @@ -1600,12 +1731,13 @@ rc_mode_found: "initial fullness %"PRId64" bits.\n", hrd_buffer_size, hrd_initial_buffer_fullness); - ctx->hrd_params.misc.type = VAEncMiscParameterTypeHRD; - ctx->hrd_params.hrd = (VAEncMiscParameterHRD) { + ctx->hrd_params = (VAEncMiscParameterHRD) { .initial_buffer_fullness = hrd_initial_buffer_fullness, .buffer_size = hrd_buffer_size, }; - vaapi_encode_add_global_param(avctx, &ctx->hrd_params.misc, + vaapi_encode_add_global_param(avctx, + VAEncMiscParameterTypeHRD, + &ctx->hrd_params, sizeof(ctx->hrd_params)); } @@ -1619,11 +1751,13 @@ rc_mode_found: av_log(avctx, AV_LOG_VERBOSE, "RC framerate: %d/%d (%.2f fps).\n", fr_num, fr_den, (double)fr_num / fr_den); - ctx->fr_params.misc.type = VAEncMiscParameterTypeFrameRate; - ctx->fr_params.fr.framerate = (unsigned int)fr_den << 16 | fr_num; - + ctx->fr_params = (VAEncMiscParameterFrameRate) { + .framerate = (unsigned int)fr_den << 16 | fr_num, + }; #if VA_CHECK_VERSION(0, 40, 0) - vaapi_encode_add_global_param(avctx, &ctx->fr_params.misc, + vaapi_encode_add_global_param(avctx, + VAEncMiscParameterTypeFrameRate, + &ctx->fr_params, sizeof(ctx->fr_params)); #endif @@ -1885,10 +2019,12 @@ static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx) quality = attr.value; } - ctx->quality_params.misc.type = VAEncMiscParameterTypeQualityLevel; - ctx->quality_params.quality.quality_level = quality; - - vaapi_encode_add_global_param(avctx, &ctx->quality_params.misc, + ctx->quality_params = (VAEncMiscParameterBufferQualityLevel) { + .quality_level = quality, + }; + vaapi_encode_add_global_param(avctx, + VAEncMiscParameterTypeQualityLevel, + &ctx->quality_params, sizeof(ctx->quality_params)); } #else @@ -1899,6 +2035,39 @@ static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx) return 0; } +static av_cold int vaapi_encode_init_roi(AVCodecContext *avctx) +{ +#if VA_CHECK_VERSION(1, 0, 0) + VAAPIEncodeContext *ctx = avctx->priv_data; + VAStatus vas; + VAConfigAttrib attr = { VAConfigAttribEncROI }; + + vas = vaGetConfigAttributes(ctx->hwctx->display, + ctx->va_profile, + ctx->va_entrypoint, + &attr, 1); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to query ROI " + "config attribute: %d (%s).\n", vas, vaErrorStr(vas)); + return AVERROR_EXTERNAL; + } + + if (attr.value == VA_ATTRIB_NOT_SUPPORTED) { + ctx->roi_allowed = 0; + } else { + VAConfigAttribValEncROI roi = { + .value = attr.value, + }; + + ctx->roi_max_regions = roi.bits.num_roi_regions; + ctx->roi_allowed = ctx->roi_max_regions > 0 && + (ctx->va_rc_mode == VA_RC_CQP || + roi.bits.roi_rc_qp_delta_support); + } +#endif + return 0; +} + static void vaapi_encode_free_output_buffer(void *opaque, uint8_t *data) { @@ -2089,6 +2258,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx) if (err < 0) goto fail; + err = vaapi_encode_init_roi(avctx); + if (err < 0) + goto fail; + if (avctx->compression_level >= 0) { err = vaapi_encode_init_quality(avctx); if (err < 0) @@ -2193,7 +2366,6 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx) return 0; fail: - ff_vaapi_encode_close(avctx); return err; }