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)
{
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,
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;
}
}
}
+#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) {
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;
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);
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)
av_freep(&pic->priv_data);
av_freep(&pic->codec_picture_params);
+ av_freep(&pic->roi);
av_free(pic);
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;
}
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];
return 0;
fail:
+ vaapi_encode_free(avctx, pic);
return err;
}
return 0;
}
-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;
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,
.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));
}
"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));
}
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
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
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)
{
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)