[FFmpeg-devel] [PATCH v4 29/38] vaapi_encode: Support configurable slices
Mark Thompson
sw at jkqxz.net
Wed Sep 19 01:31:07 EEST 2018
This adds common code to query driver support and set appropriate
address/size information for each slice. It only supports rectangular
slices for now, since that is the most common use-case.
Also immediately use that to replace the ad-hoc code doing the same
thing for MPEG-2.
---
libavcodec/vaapi_encode.c | 149 +++++++++++++++++++++++++++++++-
libavcodec/vaapi_encode.h | 22 +++++
libavcodec/vaapi_encode_mpeg2.c | 18 ++--
3 files changed, 177 insertions(+), 12 deletions(-)
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 2c34cdce2c..601d74b5c4 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -319,16 +319,60 @@ static int vaapi_encode_issue(AVCodecContext *avctx,
}
}
+ if (pic->nb_slices == 0)
+ pic->nb_slices = ctx->nb_slices;
if (pic->nb_slices > 0) {
+ int rounding;
+
pic->slices = av_mallocz_array(pic->nb_slices, sizeof(*pic->slices));
if (!pic->slices) {
err = AVERROR(ENOMEM);
goto fail;
}
+
+ for (i = 0; i < pic->nb_slices; i++)
+ pic->slices[i].row_size = ctx->slice_size;
+
+ rounding = ctx->slice_block_rows - ctx->nb_slices * ctx->slice_size;
+ if (rounding > 0) {
+ // Place rounding error at top and bottom of frame.
+ av_assert0(rounding < pic->nb_slices);
+ // Some Intel hardware contains a bug where the encoder will fail
+ // if the last slice is smaller than the one before it. Since
+ // that's straightforward to avoid here, just do so.
+ if (rounding <= 2) {
+ for (i = 0; i < rounding; i++)
+ ++pic->slices[i].row_size;
+ } else {
+ for (i = 0; i < (rounding + 1) / 2; i++)
+ ++pic->slices[pic->nb_slices - i - 1].row_size;
+ for (i = 0; i < rounding / 2; i++)
+ ++pic->slices[i].row_size;
+ }
+ } else if (rounding < 0) {
+ // Remove rounding error from last slice only.
+ av_assert0(rounding < ctx->slice_size);
+ pic->slices[pic->nb_slices - 1].row_size += rounding;
+ }
}
for (i = 0; i < pic->nb_slices; i++) {
slice = &pic->slices[i];
slice->index = i;
+ if (i == 0) {
+ slice->row_start = 0;
+ slice->block_start = 0;
+ } else {
+ const VAAPIEncodeSlice *prev = &pic->slices[i - 1];
+ slice->row_start = prev->row_start + prev->row_size;
+ slice->block_start = prev->block_start + prev->block_size;
+ }
+ slice->block_size = slice->row_size * ctx->slice_block_cols;
+
+ av_log(avctx, AV_LOG_DEBUG, "Slice %d: %d-%d (%d rows), "
+ "%d-%d (%d blocks).\n", i, slice->row_start,
+ slice->row_start + slice->row_size - 1, slice->row_size,
+ slice->block_start, slice->block_start + slice->block_size - 1,
+ slice->block_size);
if (ctx->codec->slice_params_size > 0) {
slice->codec_slice_params = av_mallocz(ctx->codec->slice_params_size);
@@ -1014,8 +1058,7 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
VAEntrypoint *va_entrypoints = NULL;
VAStatus vas;
const VAEntrypoint *usable_entrypoints;
- const VAAPIEncodeProfile *profile;
- const AVPixFmtDescriptor *desc;
+ const VAAPIEncodeProfile *profile; const AVPixFmtDescriptor *desc;
VAConfigAttrib rt_format_attr;
const VAAPIEncodeRTFormat *rt_format;
const char *profile_string, *entrypoint_string;
@@ -1444,6 +1487,104 @@ static av_cold int vaapi_encode_init_gop_structure(AVCodecContext *avctx)
return 0;
}
+static av_cold int vaapi_encode_init_slice_structure(AVCodecContext *avctx)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAConfigAttrib attr[2] = { { VAConfigAttribEncMaxSlices },
+ { VAConfigAttribEncSliceStructure } };
+ VAStatus vas;
+ uint32_t max_slices, slice_structure;
+ int req_slices;
+
+ 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->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);
+ }
+
+ // 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;
+ } else if (slice_structure & VA_ENC_SLICE_STRUCTURE_EQUAL_ROWS) {
+ ctx->nb_slices = ctx->slice_block_rows;
+ ctx->slice_size = 1;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any usable "
+ "slice structure modes (%#x).\n", slice_structure);
+ return AVERROR(EINVAL);
+ }
+
+ 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 "
+ "(default size %d block rows).\n",
+ ctx->nb_slices, ctx->slice_size);
+ return 0;
+}
+
static av_cold int vaapi_encode_init_packed_headers(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
@@ -1734,6 +1875,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
if (err < 0)
goto fail;
+ err = vaapi_encode_init_slice_structure(avctx);
+ if (err < 0)
+ goto fail;
+
err = vaapi_encode_init_packed_headers(avctx);
if (err < 0)
goto fail;
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index 091889f9ae..271d4ef518 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -52,6 +52,10 @@ enum {
typedef struct VAAPIEncodeSlice {
int index;
+ int row_start;
+ int row_size;
+ int block_start;
+ int block_size;
void *priv_data;
void *codec_slice_params;
} VAAPIEncodeSlice;
@@ -125,6 +129,10 @@ typedef struct VAAPIEncodeContext {
int surface_width;
int surface_height;
+ // The block size for slice calculations.
+ int slice_block_width;
+ int slice_block_height;
+
// Everything above this point must be set before calling
// ff_vaapi_encode_init().
@@ -224,6 +232,12 @@ typedef struct VAAPIEncodeContext {
int64_t dts_pts_diff;
int64_t ts_ring[MAX_REORDER_DELAY * 3];
+ // Slice structure.
+ int slice_block_rows;
+ int slice_block_cols;
+ int nb_slices;
+ int slice_size;
+
// Frame type decision.
int gop_size;
int p_per_i;
@@ -234,11 +248,19 @@ typedef struct VAAPIEncodeContext {
int end_of_stream;
} VAAPIEncodeContext;
+enum {
+ // Codec supports controlling the subdivision of pictures into slices.
+ FLAG_SLICE_CONTROL = 1 << 0,
+};
+
typedef struct VAAPIEncodeType {
// List of supported profiles and corresponding VAAPI profiles.
// (Must end with FF_PROFILE_UNKNOWN.)
const VAAPIEncodeProfile *profiles;
+ // Codec feature flags.
+ int flags;
+
// 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_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index 1377eeb9bb..99a8b43237 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -35,9 +35,6 @@ typedef struct VAAPIEncodeMPEG2Context {
int level;
// Derived settings.
- int mb_width;
- int mb_height;
-
int quant_i;
int quant_p;
int quant_b;
@@ -477,8 +474,6 @@ static int vaapi_encode_mpeg2_init_picture_params(AVCodecContext *avctx,
vpic->f_code[1][0] = pce->f_code[1][0];
vpic->f_code[1][1] = pce->f_code[1][1];
- pic->nb_slices = priv->mb_height;
-
return 0;
}
@@ -490,8 +485,8 @@ static int vaapi_encode_mpeg2_init_slice_params(AVCodecContext *avctx,
VAEncSliceParameterBufferMPEG2 *vslice = slice->codec_slice_params;
int qp;
- vslice->macroblock_address = priv->mb_width * slice->index;
- vslice->num_macroblocks = priv->mb_width;
+ vslice->macroblock_address = slice->block_start;
+ vslice->num_macroblocks = slice->block_size;
switch (pic->type) {
case PICTURE_TYPE_IDR:
@@ -525,9 +520,6 @@ static av_cold int vaapi_encode_mpeg2_configure(AVCodecContext *avctx)
if (err < 0)
return err;
- 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->quant_p = av_clip(avctx->global_quality, 1, 31);
if (avctx->i_quant_factor > 0.0)
@@ -553,6 +545,12 @@ static av_cold int vaapi_encode_mpeg2_configure(AVCodecContext *avctx)
av_assert0(0 && "Invalid RC mode.");
}
+ ctx->slice_block_rows = FFALIGN(avctx->width, 16) / 16;
+ ctx->slice_block_cols = FFALIGN(avctx->height, 16) / 16;
+
+ ctx->nb_slices = ctx->slice_block_rows;
+ ctx->slice_size = 1;
+
return 0;
}
--
2.18.0
More information about the ffmpeg-devel
mailing list