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.
Showing the changes to libavcodec (2 of 3 files). Show all
Unified · Split
| @@ -319,16 +319,60 @@ static int vaapi_encode_issue(AVCodecContext *avctx, · 318 unmodified lines | |||
| 319 | } | 319 | } |
| 320 | } | 320 | } |
| 321 | 321 | ||
| 322 | if (pic->nb_slices == 0) | ||
| 323 | pic->nb_slices = ctx->nb_slices; | ||
| 322 | if (pic->nb_slices > 0) { | 324 | if (pic->nb_slices > 0) { |
| 325 | int rounding; | ||
| 326 | |||
| 323 | pic->slices = av_mallocz_array(pic->nb_slices, sizeof(*pic->slices)); | 327 | pic->slices = av_mallocz_array(pic->nb_slices, sizeof(*pic->slices)); |
| 324 | if (!pic->slices) { | 328 | if (!pic->slices) { |
| 325 | err = AVERROR(ENOMEM); | 329 | err = AVERROR(ENOMEM); |
| 326 | goto fail; | 330 | goto fail; |
| 327 | } | 331 | } |
| 332 | |||
| 333 | for (i = 0; i < pic->nb_slices; i++) | ||
| 334 | pic->slices[i].row_size = ctx->slice_size; | ||
| 335 | |||
| 336 | rounding = ctx->slice_block_rows - ctx->nb_slices * ctx->slice_size; | ||
| 337 | if (rounding > 0) { | ||
| 338 | // Place rounding error at top and bottom of frame. | ||
| 339 | av_assert0(rounding < pic->nb_slices); | ||
| 340 | // Some Intel drivers contain a bug where the encoder will fail | ||
| 341 | // if the last slice is smaller than the one before it. Since | ||
| 342 | // that's straightforward to avoid here, just do so. | ||
| 343 | if (rounding <= 2) { | ||
| 344 | for (i = 0; i < rounding; i++) | ||
| 345 | ++pic->slices[i].row_size; | ||
| 346 | } else { | ||
| 347 | for (i = 0; i < (rounding + 1) / 2; i++) | ||
| 348 | ++pic->slices[pic->nb_slices - i - 1].row_size; | ||
| 349 | for (i = 0; i < rounding / 2; i++) | ||
| 350 | ++pic->slices[i].row_size; | ||
| 351 | } | ||
| 352 | } else if (rounding < 0) { | ||
| 353 | // Remove rounding error from last slice only. | ||
| 354 | av_assert0(rounding < ctx->slice_size); | ||
| 355 | pic->slices[pic->nb_slices - 1].row_size += rounding; | ||
| 356 | } | ||
| 328 | } | 357 | } |
| 329 | for (i = 0; i < pic->nb_slices; i++) { | 358 | for (i = 0; i < pic->nb_slices; i++) { |
| 330 | slice = &pic->slices[i]; | 359 | slice = &pic->slices[i]; |
| 331 | slice->index = i; | 360 | slice->index = i; |
| 361 | if (i == 0) { | ||
| 362 | slice->row_start = 0; | ||
| 363 | slice->block_start = 0; | ||
| 364 | } else { | ||
| 365 | const VAAPIEncodeSlice *prev = &pic->slices[i - 1]; | ||
| 366 | slice->row_start = prev->row_start + prev->row_size; | ||
| 367 | slice->block_start = prev->block_start + prev->block_size; | ||
| 368 | } | ||
| 369 | slice->block_size = slice->row_size * ctx->slice_block_cols; | ||
| 370 | |||
| 371 | av_log(avctx, AV_LOG_DEBUG, "Slice %d: %d-%d (%d rows), " | ||
| 372 | "%d-%d (%d blocks).\n", i, slice->row_start, | ||
| 373 | slice->row_start + slice->row_size - 1, slice->row_size, | ||
| 374 | slice->block_start, slice->block_start + slice->block_size - 1, | ||
| 375 | slice->block_size); | ||
| 332 | 376 | ||
| 333 | if (ctx->codec->slice_params_size > 0) { | 377 | if (ctx->codec->slice_params_size > 0) { |
| 334 | slice->codec_slice_params = av_mallocz(ctx->codec->slice_params_size); | 378 | slice->codec_slice_params = av_mallocz(ctx->codec->slice_params_size); |
| @@ -1444,6 +1488,106 @@ static av_cold int vaapi_encode_init_gop_structure(AVCodecContext *avctx) · 1109 unmodified lines | |||
| 1444 | return 0; | 1488 | return 0; |
| 1445 | } | 1489 | } |
| 1446 | 1490 | ||
| 1491 | static av_cold int vaapi_encode_init_slice_structure(AVCodecContext *avctx) | ||
| 1492 | { | ||
| 1493 | VAAPIEncodeContext *ctx = avctx->priv_data; | ||
| 1494 | VAConfigAttrib attr[2] = { { VAConfigAttribEncMaxSlices }, | ||
| 1495 | { VAConfigAttribEncSliceStructure } }; | ||
| 1496 | VAStatus vas; | ||
| 1497 | uint32_t max_slices, slice_structure; | ||
| 1498 | int req_slices; | ||
| 1499 | |||
| 1500 | if (!(ctx->codec->flags & FLAG_SLICE_CONTROL)) { | ||
| 1501 | if (avctx->slices > 0) { | ||
| 1502 | av_log(avctx, AV_LOG_WARNING, "Multiple slices were requested " | ||
| 1503 | "but this codec does not support controlling slices.\n"); | ||
| 1504 | } | ||
| 1505 | return 0; | ||
| 1506 | } | ||
| 1507 | |||
| 1508 | ctx->slice_block_rows = (avctx->height + ctx->slice_block_height - 1) / | ||
| 1509 | ctx->slice_block_height; | ||
| 1510 | ctx->slice_block_cols = (avctx->width + ctx->slice_block_width - 1) / | ||
| 1511 | ctx->slice_block_width; | ||
| 1512 | |||
| 1513 | if (avctx->slices <= 1) { | ||
| 1514 | ctx->nb_slices = 1; | ||
| 1515 | ctx->slice_size = ctx->slice_block_rows; | ||
| 1516 | return 0; | ||
| 1517 | } | ||
| 1518 | |||
| 1519 | vas = vaGetConfigAttributes(ctx->hwctx->display, | ||
| 1520 | ctx->va_profile, | ||
| 1521 | ctx->va_entrypoint, | ||
| 1522 | attr, FF_ARRAY_ELEMS(attr)); | ||
| 1523 | if (vas != VA_STATUS_SUCCESS) { | ||
| 1524 | av_log(avctx, AV_LOG_ERROR, "Failed to query slice " | ||
| 1525 | "attributes: %d (%s).\n", vas, vaErrorStr(vas)); | ||
| 1526 | return AVERROR_EXTERNAL; | ||
| 1527 | } | ||
| 1528 | max_slices = attr[0].value; | ||
| 1529 | slice_structure = attr[1].value; | ||
| 1530 | if (max_slices == VA_ATTRIB_NOT_SUPPORTED || | ||
| 1531 | slice_structure == VA_ATTRIB_NOT_SUPPORTED) { | ||
| 1532 | av_log(avctx, AV_LOG_ERROR, "Driver does not support encoding " | ||
| 1533 | "pictures as multiple slices.\n."); | ||
| 1534 | return AVERROR(EINVAL); | ||
| 1535 | } | ||
| 1536 | |||
| 1537 | // For fixed-size slices currently we only support whole rows, making | ||
| 1538 | // rectangular slices. This could be extended to arbitrary runs of | ||
| 1539 | // blocks, but since slices tend to be a conformance requirement and | ||
| 1540 | // most cases (such as broadcast or bluray) want rectangular slices | ||
| 1541 | // only it would need to be gated behind another option. | ||
| 1542 | if (avctx->slices > ctx->slice_block_rows) { | ||
| 1543 | av_log(avctx, AV_LOG_WARNING, "Not enough rows to use " | ||
| 1544 | "configured number of slices (%d < %d); using " | ||
| 1545 | "maximum.\n", ctx->slice_block_rows, avctx->slices); | ||
| 1546 | req_slices = ctx->slice_block_rows; | ||
| 1547 | } else { | ||
| 1548 | req_slices = avctx->slices; | ||
| 1549 | } | ||
| 1550 | if (slice_structure & VA_ENC_SLICE_STRUCTURE_ARBITRARY_ROWS || | ||
| 1551 | slice_structure & VA_ENC_SLICE_STRUCTURE_ARBITRARY_MACROBLOCKS) { | ||
| 1552 | ctx->nb_slices = req_slices; | ||
| 1553 | ctx->slice_size = ctx->slice_block_rows / ctx->nb_slices; | ||
| 1554 | } else if (slice_structure & VA_ENC_SLICE_STRUCTURE_POWER_OF_TWO_ROWS) { | ||
| 1555 | int k; | ||
| 1556 | for (k = 1;; k *= 2) { | ||
| 1557 | if (2 * k * (req_slices - 1) + 1 >= ctx->slice_block_rows) | ||
| 1558 | break; | ||
| 1559 | } | ||
| 1560 | ctx->nb_slices = (ctx->slice_block_rows + k - 1) / k; | ||
| 1561 | ctx->slice_size = k; | ||
| 1562 | #if VA_CHECK_VERSION(1, 0, 0) | ||
| 1563 | } else if (slice_structure & VA_ENC_SLICE_STRUCTURE_EQUAL_ROWS) { | ||
| 1564 | ctx->nb_slices = ctx->slice_block_rows; | ||
| 1565 | ctx->slice_size = 1; | ||
| 1566 | #endif | ||
| 1567 | } else { | ||
| 1568 | av_log(avctx, AV_LOG_ERROR, "Driver does not support any usable " | ||
| 1569 | "slice structure modes (%#x).\n", slice_structure); | ||
| 1570 | return AVERROR(EINVAL); | ||
| 1571 | } | ||
| 1572 | |||
| 1573 | if (ctx->nb_slices > avctx->slices) { | ||
| 1574 | av_log(avctx, AV_LOG_WARNING, "Slice count rounded up to " | ||
| 1575 | "%d (from %d) due to driver constraints on slice " | ||
| 1576 | "structure.\n", ctx->nb_slices, avctx->slices); | ||
| 1577 | } | ||
| 1578 | if (ctx->nb_slices > max_slices) { | ||
| 1579 | av_log(avctx, AV_LOG_ERROR, "Driver does not support " | ||
| 1580 | "encoding with %d slices (max %"PRIu32").\n", | ||
| 1581 | ctx->nb_slices, max_slices); | ||
| 1582 | return AVERROR(EINVAL); | ||
| 1583 | } | ||
| 1584 | |||
| 1585 | av_log(avctx, AV_LOG_VERBOSE, "Encoding pictures with %d slices " | ||
| 1586 | "(default size %d block rows).\n", | ||
| 1587 | ctx->nb_slices, ctx->slice_size); | ||
| 1588 | return 0; | ||
| 1589 | } | ||
| 1590 | |||
| 1447 | static av_cold int vaapi_encode_init_packed_headers(AVCodecContext *avctx) | 1591 | static av_cold int vaapi_encode_init_packed_headers(AVCodecContext *avctx) |
| 1448 | { | 1592 | { |
| 1449 | VAAPIEncodeContext *ctx = avctx->priv_data; | 1593 | VAAPIEncodeContext *ctx = avctx->priv_data; |
| @@ -1734,6 +1878,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx) · 284 unmodified lines | |||
| 1734 | if (err < 0) | 1878 | if (err < 0) |
| 1735 | goto fail; | 1879 | goto fail; |
| 1736 | 1880 | ||
| 1881 | err = vaapi_encode_init_slice_structure(avctx); | ||
| 1882 | if (err < 0) | ||
| 1883 | goto fail; | ||
| 1884 | |||
| 1737 | err = vaapi_encode_init_packed_headers(avctx); | 1885 | err = vaapi_encode_init_packed_headers(avctx); |
| 1738 | if (err < 0) | 1886 | if (err < 0) |
| 1739 | goto fail; | 1887 | goto fail; |
| @@ -52,6 +52,10 @@ enum { · 51 unmodified lines | |||
| 52 | 52 | ||
| 53 | typedef struct VAAPIEncodeSlice { | 53 | typedef struct VAAPIEncodeSlice { |
| 54 | int index; | 54 | int index; |
| 55 | int row_start; | ||
| 56 | int row_size; | ||
| 57 | int block_start; | ||
| 58 | int block_size; | ||
| 55 | void *priv_data; | 59 | void *priv_data; |
| 56 | void *codec_slice_params; | 60 | void *codec_slice_params; |
| 57 | } VAAPIEncodeSlice; | 61 | } VAAPIEncodeSlice; |
| @@ -125,6 +129,10 @@ typedef struct VAAPIEncodeContext { · 67 unmodified lines | |||
| 125 | int surface_width; | 129 | int surface_width; |
| 126 | int surface_height; | 130 | int surface_height; |
| 127 | 131 | ||
| 132 | // The block size for slice calculations. | ||
| 133 | int slice_block_width; | ||
| 134 | int slice_block_height; | ||
| 135 | |||
| 128 | // Everything above this point must be set before calling | 136 | // Everything above this point must be set before calling |
| 129 | // ff_vaapi_encode_init(). | 137 | // ff_vaapi_encode_init(). |
| 130 | 138 | ||
| @@ -224,6 +232,12 @@ typedef struct VAAPIEncodeContext { · 93 unmodified lines | |||
| 224 | int64_t dts_pts_diff; | 232 | int64_t dts_pts_diff; |
| 225 | int64_t ts_ring[MAX_REORDER_DELAY * 3]; | 233 | int64_t ts_ring[MAX_REORDER_DELAY * 3]; |
| 226 | 234 | ||
| 235 | // Slice structure. | ||
| 236 | int slice_block_rows; | ||
| 237 | int slice_block_cols; | ||
| 238 | int nb_slices; | ||
| 239 | int slice_size; | ||
| 240 | |||
| 227 | // Frame type decision. | 241 | // Frame type decision. |
| 228 | int gop_size; | 242 | int gop_size; |
| 229 | int p_per_i; | 243 | int p_per_i; |
| @@ -234,11 +248,19 @@ typedef struct VAAPIEncodeContext { · 4 unmodified lines | |||
| 234 | int end_of_stream; | 248 | int end_of_stream; |
| 235 | } VAAPIEncodeContext; | 249 | } VAAPIEncodeContext; |
| 236 | 250 | ||
| 251 | enum { | ||
| 252 | // Codec supports controlling the subdivision of pictures into slices. | ||
| 253 | FLAG_SLICE_CONTROL = 1 << 0, | ||
| 254 | }; | ||
| 255 | |||
| 237 | typedef struct VAAPIEncodeType { | 256 | typedef struct VAAPIEncodeType { |
| 238 | // List of supported profiles and corresponding VAAPI profiles. | 257 | // List of supported profiles and corresponding VAAPI profiles. |
| 239 | // (Must end with FF_PROFILE_UNKNOWN.) | 258 | // (Must end with FF_PROFILE_UNKNOWN.) |
| 240 | const VAAPIEncodeProfile *profiles; | 259 | const VAAPIEncodeProfile *profiles; |
| 241 | 260 | ||
| 261 | // Codec feature flags. | ||
| 262 | int flags; | ||
| 263 | |||
| 242 | // Perform any extra codec-specific configuration after the | 264 | // Perform any extra codec-specific configuration after the |
| 243 | // codec context is initialised (set up the private data and | 265 | // codec context is initialised (set up the private data and |
| 244 | // add any necessary global parameters). | 266 | // add any necessary global parameters). |