+static av_cold int vaapi_encode_init_gop_structure(AVCodecContext *avctx)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAStatus vas;
+ VAConfigAttrib attr = { VAConfigAttribEncMaxRefFrames };
+ uint32_t ref_l0, ref_l1;
+
+ 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 reference frames "
+ "attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
+
+ if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ ref_l0 = ref_l1 = 0;
+ } else {
+ ref_l0 = attr.value & 0xffff;
+ ref_l1 = attr.value >> 16 & 0xffff;
+ }
+
+ if (ctx->codec->flags & FLAG_INTRA_ONLY ||
+ avctx->gop_size <= 1) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using intra frames only.\n");
+ ctx->gop_size = 1;
+ } else if (ref_l0 < 1) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any "
+ "reference frames.\n");
+ return AVERROR(EINVAL);
+ } else if (!(ctx->codec->flags & FLAG_B_PICTURES) ||
+ ref_l1 < 1 || avctx->max_b_frames < 1) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using intra and P-frames "
+ "(supported references: %d / %d).\n", ref_l0, ref_l1);
+ ctx->gop_size = avctx->gop_size;
+ ctx->p_per_i = INT_MAX;
+ ctx->b_per_p = 0;
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Using intra, P- and B-frames "
+ "(supported references: %d / %d).\n", ref_l0, ref_l1);
+ ctx->gop_size = avctx->gop_size;
+ ctx->p_per_i = INT_MAX;
+ ctx->b_per_p = avctx->max_b_frames;
+ if (ctx->codec->flags & FLAG_B_PICTURE_REFERENCES) {
+ ctx->max_b_depth = FFMIN(ctx->desired_b_depth,
+ av_log2(ctx->b_per_p) + 1);
+ } else {
+ ctx->max_b_depth = 1;
+ }
+ }
+
+ if (ctx->codec->flags & FLAG_NON_IDR_KEY_PICTURES) {
+ ctx->closed_gop = !!(avctx->flags & AV_CODEC_FLAG_CLOSED_GOP);
+ ctx->gop_per_idr = ctx->idr_interval + 1;
+ } else {
+ ctx->closed_gop = 1;
+ ctx->gop_per_idr = 1;
+ }
+
+ return 0;
+}
+
+static av_cold int vaapi_encode_init_row_slice_structure(AVCodecContext *avctx,
+ uint32_t slice_structure)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ int req_slices;
+
+ // For fixed-size slices currently we only support whole rows, making
+ // rectangular slices. This could be extended to arbitrary runs of
+ // blocks, but since slices tend to be a conformance requirement and
+ // most cases (such as broadcast or bluray) want rectangular slices
+ // only it would need to be gated behind another option.
+ if (avctx->slices > ctx->slice_block_rows) {
+ av_log(avctx, AV_LOG_WARNING, "Not enough rows to use "
+ "configured number of slices (%d < %d); using "
+ "maximum.\n", ctx->slice_block_rows, avctx->slices);
+ req_slices = ctx->slice_block_rows;
+ } else {
+ req_slices = avctx->slices;
+ }
+ if (slice_structure & VA_ENC_SLICE_STRUCTURE_ARBITRARY_ROWS ||
+ slice_structure & VA_ENC_SLICE_STRUCTURE_ARBITRARY_MACROBLOCKS) {
+ ctx->nb_slices = req_slices;
+ ctx->slice_size = ctx->slice_block_rows / ctx->nb_slices;
+ } else if (slice_structure & VA_ENC_SLICE_STRUCTURE_POWER_OF_TWO_ROWS) {
+ int k;
+ for (k = 1;; k *= 2) {
+ if (2 * k * (req_slices - 1) + 1 >= ctx->slice_block_rows)
+ break;
+ }
+ ctx->nb_slices = (ctx->slice_block_rows + k - 1) / k;
+ ctx->slice_size = k;
+#if VA_CHECK_VERSION(1, 0, 0)
+ } else if (slice_structure & VA_ENC_SLICE_STRUCTURE_EQUAL_ROWS) {
+ ctx->nb_slices = ctx->slice_block_rows;
+ ctx->slice_size = 1;
+#endif
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any usable "
+ "slice structure modes (%#x).\n", slice_structure);
+ return AVERROR(EINVAL);
+ }
+
+ return 0;
+}
+
+static av_cold int vaapi_encode_init_tile_slice_structure(AVCodecContext *avctx,
+ uint32_t slice_structure)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ int i, req_tiles;
+
+ if (!(slice_structure & VA_ENC_SLICE_STRUCTURE_ARBITRARY_MACROBLOCKS ||
+ (slice_structure & VA_ENC_SLICE_STRUCTURE_ARBITRARY_ROWS &&
+ ctx->tile_cols == 1))) {
+ av_log(avctx, AV_LOG_ERROR, "Supported slice structure (%#x) doesn't work for "
+ "current tile requirement.\n", slice_structure);
+ return AVERROR(EINVAL);
+ }
+
+ if (ctx->tile_rows > ctx->slice_block_rows ||
+ ctx->tile_cols > ctx->slice_block_cols) {
+ av_log(avctx, AV_LOG_WARNING, "Not enough block rows/cols (%d x %d) "
+ "for configured number of tile (%d x %d); ",
+ ctx->slice_block_rows, ctx->slice_block_cols,
+ ctx->tile_rows, ctx->tile_cols);
+ ctx->tile_rows = ctx->tile_rows > ctx->slice_block_rows ?
+ ctx->slice_block_rows : ctx->tile_rows;
+ ctx->tile_cols = ctx->tile_cols > ctx->slice_block_cols ?
+ ctx->slice_block_cols : ctx->tile_cols;
+ av_log(avctx, AV_LOG_WARNING, "using allowed maximum (%d x %d).\n",
+ ctx->tile_rows, ctx->tile_cols);
+ }
+
+ req_tiles = ctx->tile_rows * ctx->tile_cols;
+
+ // Tile slice is not allowed to cross the boundary of a tile due to
+ // the constraints of media-driver. Currently we support one slice
+ // per tile. This could be extended to multiple slices per tile.
+ if (avctx->slices != req_tiles)
+ av_log(avctx, AV_LOG_WARNING, "The number of requested slices "
+ "mismatches with configured number of tile (%d != %d); "
+ "using requested tile number for slice.\n",
+ avctx->slices, req_tiles);
+
+ ctx->nb_slices = req_tiles;
+
+ // Default in uniform spacing
+ // 6-3, 6-5
+ for (i = 0; i < ctx->tile_cols; i++) {
+ ctx->col_width[i] = ( i + 1 ) * ctx->slice_block_cols / ctx->tile_cols -
+ i * ctx->slice_block_cols / ctx->tile_cols;
+ ctx->col_bd[i + 1] = ctx->col_bd[i] + ctx->col_width[i];
+ }
+ // 6-4, 6-6
+ for (i = 0; i < ctx->tile_rows; i++) {
+ ctx->row_height[i] = ( i + 1 ) * ctx->slice_block_rows / ctx->tile_rows -
+ i * ctx->slice_block_rows / ctx->tile_rows;
+ ctx->row_bd[i + 1] = ctx->row_bd[i] + ctx->row_height[i];
+ }
+
+ av_log(avctx, AV_LOG_VERBOSE, "Encoding pictures with %d x %d tile.\n",
+ ctx->tile_rows, ctx->tile_cols);
+
+ return 0;
+}
+
+static av_cold int vaapi_encode_init_slice_structure(AVCodecContext *avctx)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAConfigAttrib attr[3] = { { VAConfigAttribEncMaxSlices },
+ { VAConfigAttribEncSliceStructure },
+#if VA_CHECK_VERSION(1, 1, 0)
+ { VAConfigAttribEncTileSupport },
+#endif
+ };
+ VAStatus vas;
+ uint32_t max_slices, slice_structure;
+ int ret;
+
+ if (!(ctx->codec->flags & FLAG_SLICE_CONTROL)) {
+ if (avctx->slices > 0) {
+ av_log(avctx, AV_LOG_WARNING, "Multiple slices were requested "
+ "but this codec does not support controlling slices.\n");
+ }
+ return 0;
+ }
+
+ ctx->slice_block_rows = (avctx->height + ctx->slice_block_height - 1) /
+ ctx->slice_block_height;
+ ctx->slice_block_cols = (avctx->width + ctx->slice_block_width - 1) /
+ ctx->slice_block_width;
+
+ if (avctx->slices <= 1 && !ctx->tile_rows && !ctx->tile_cols) {
+ ctx->nb_slices = 1;
+ ctx->slice_size = ctx->slice_block_rows;
+ return 0;
+ }
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile,
+ ctx->va_entrypoint,
+ attr, FF_ARRAY_ELEMS(attr));
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query slice "
+ "attributes: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
+ max_slices = attr[0].value;
+ slice_structure = attr[1].value;
+ if (max_slices == VA_ATTRIB_NOT_SUPPORTED ||
+ slice_structure == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support encoding "
+ "pictures as multiple slices.\n.");
+ return AVERROR(EINVAL);
+ }
+
+ if (ctx->tile_rows && ctx->tile_cols) {
+#if VA_CHECK_VERSION(1, 1, 0)
+ uint32_t tile_support = attr[2].value;
+ if (tile_support == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support encoding "
+ "pictures as multiple tiles.\n.");
+ return AVERROR(EINVAL);
+ }
+#else
+ av_log(avctx, AV_LOG_ERROR, "Tile encoding option is "
+ "not supported with this VAAPI version.\n");
+ return AVERROR(EINVAL);
+#endif
+ }
+
+ if (ctx->tile_rows && ctx->tile_cols)
+ ret = vaapi_encode_init_tile_slice_structure(avctx, slice_structure);
+ else
+ ret = vaapi_encode_init_row_slice_structure(avctx, slice_structure);
+ if (ret < 0)
+ return ret;
+
+ if (ctx->nb_slices > avctx->slices) {
+ av_log(avctx, AV_LOG_WARNING, "Slice count rounded up to "
+ "%d (from %d) due to driver constraints on slice "
+ "structure.\n", ctx->nb_slices, avctx->slices);
+ }
+ if (ctx->nb_slices > max_slices) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support "
+ "encoding with %d slices (max %"PRIu32").\n",
+ ctx->nb_slices, max_slices);
+ return AVERROR(EINVAL);
+ }
+
+ av_log(avctx, AV_LOG_VERBOSE, "Encoding pictures with %d slices.\n",
+ ctx->nb_slices);
+ return 0;
+}
+
+static av_cold int vaapi_encode_init_packed_headers(AVCodecContext *avctx)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAStatus vas;
+ VAConfigAttrib attr = { VAConfigAttribEncPackedHeaders };
+
+ 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 packed headers "
+ "attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
+
+ if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ if (ctx->desired_packed_headers) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support any "
+ "packed headers (wanted %#x).\n",
+ ctx->desired_packed_headers);
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Driver does not support any "
+ "packed headers (none wanted).\n");
+ }
+ ctx->va_packed_headers = 0;
+ } else {
+ if (ctx->desired_packed_headers & ~attr.value) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support some "
+ "wanted packed headers (wanted %#x, found %#x).\n",
+ ctx->desired_packed_headers, attr.value);
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "All wanted packed headers "
+ "available (wanted %#x, found %#x).\n",
+ ctx->desired_packed_headers, attr.value);
+ }
+ ctx->va_packed_headers = ctx->desired_packed_headers & attr.value;
+ }
+
+ if (ctx->va_packed_headers) {
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribEncPackedHeaders,
+ .value = ctx->va_packed_headers,
+ };
+ }
+
+ if ( (ctx->desired_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE) &&
+ !(ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE) &&
+ (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support packed "
+ "sequence headers, but a global header is requested.\n");
+ av_log(avctx, AV_LOG_WARNING, "No global header will be written: "
+ "this may result in a stream which is not usable for some "
+ "purposes (e.g. not muxable to some containers).\n");
+ }
+
+ return 0;
+}
+
+static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx)
+{
+#if VA_CHECK_VERSION(0, 36, 0)
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAStatus vas;
+ VAConfigAttrib attr = { VAConfigAttribEncQualityRange };
+ int quality = avctx->compression_level;
+
+ 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 quality "
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
+
+ if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ if (quality != 0) {
+ av_log(avctx, AV_LOG_WARNING, "Quality attribute is not "
+ "supported: will use default quality level.\n");
+ }
+ } else {
+ if (quality > attr.value) {
+ av_log(avctx, AV_LOG_WARNING, "Invalid quality level: "
+ "valid range is 0-%d, using %d.\n",
+ attr.value, attr.value);
+ quality = attr.value;
+ }
+
+ ctx->quality_params = (VAEncMiscParameterBufferQualityLevel) {
+ .quality_level = quality,
+ };
+ vaapi_encode_add_global_param(avctx,
+ VAEncMiscParameterTypeQualityLevel,
+ &ctx->quality_params,
+ sizeof(ctx->quality_params));
+ }
+#else
+ av_log(avctx, AV_LOG_WARNING, "The encode quality option is "
+ "not supported with this VAAPI version.\n");
+#endif
+
+ 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;
+}
+