diff mbox

[FFmpeg-devel,1/3] libaomenc: Add support for tiles

Message ID 20180909220812.16171-1-sw@jkqxz.net
State Accepted
Headers show

Commit Message

Mark Thompson Sept. 9, 2018, 10:08 p.m. UTC
Adds an option to specify the number of tile rows and columns, then uses
equal-sized tiles to fill the frame.
---
Useful for testing to make arbitrary arrangements of tiles, though the requirement to set the superblock size for the whole stream at the top level rather than letting it be set dynamically is slightly unfortunate.

The rounding error is placed at the left/top here, it might be better to place it around the sides instead?  (The most important detail is likely to be in the centre of the frame, so make the tiles there smaller?  I'm not sure whether this argument is actually valid, though.)


 libavcodec/libaomenc.c | 55 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

Comments

Mark Thompson Sept. 9, 2018, 10:22 p.m. UTC | #1
On 09/09/18 23:08, Mark Thompson wrote:
> Adds an option to specify the number of tile rows and columns, then uses
> equal-sized tiles to fill the frame.
> ---
> Useful for testing to make arbitrary arrangements of tiles, though the requirement to set the superblock size for the whole stream at the top level rather than letting it be set dynamically is slightly unfortunate.
> 
> The rounding error is placed at the left/top here, it might be better to place it around the sides instead?  (The most important detail is likely to be in the centre of the frame, so make the tiles there smaller?  I'm not sure whether this argument is actually valid, though.)
> 
> 
>  libavcodec/libaomenc.c | 55 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 55 insertions(+)
> 
> diff --git a/libavcodec/libaomenc.c b/libavcodec/libaomenc.c
> index 9431179886..0b4fa71fc7 100644
> --- a/libavcodec/libaomenc.c
> +++ b/libavcodec/libaomenc.c
> ...
> @@ -431,6 +438,51 @@ static av_cold int aom_init(AVCodecContext *avctx,
>  
>      enccfg.g_error_resilient = ctx->error_resilient;
>  
> +    if (ctx->tile_cols && ctx->tile_rows) {
> +        int sb_size, sb_width, sb_height;
> +        int cols_per_tile, cols_step, rows_per_tile, rows_step, i;
> +
> +        if (avctx->width / ctx->tile_cols < 128 ||
> +            avctx->width / ctx->tile_rows < 128) {

avctx->height for the second test, of course.

There could be some rounding here too, though I'm not sure what the 128x128 vs. 64x64 tradeoff actually involves - if the frame is very small do we want the largest superblock size?

> +            // User has requested more tiles than would fit with 128x128
> +            // superblocks, so assume they want 64x64.
> +            sb_size = 64;
> +            superblock_size = AOM_SUPERBLOCK_SIZE_64X64;
> +        } else {
> +            sb_size = 128;
> +            superblock_size = AOM_SUPERBLOCK_SIZE_128X128;
> +        }
> +
> +        if (avctx->width  / ctx->tile_cols < sb_size ||
> +            avctx->height / ctx->tile_rows < sb_size) {

This should probably be rounded with the test the other way:

+        if ((avctx->width  + sb_size - 1) / sb_size < ctx->tile_cols ||
+            (avctx->height + sb_size - 1) / sb_size < ctx->tile_rows) {

> +            av_log(avctx, AV_LOG_ERROR, "Invalid tile sizing: tiles must be "
> +                   "at least one superblock wide and high.\n");
> +            return AVERROR(EINVAL);
> +        }
> +        if (ctx->tile_cols > MAX_TILE_WIDTHS ||
> +            ctx->tile_rows > MAX_TILE_HEIGHTS) {
> +            av_log(avctx, AV_LOG_ERROR, "Invalid tile sizing: at most %dx%d "
> +                   "tiles allowed.\n", MAX_TILE_WIDTHS, MAX_TILE_HEIGHTS);
> +            return AVERROR(EINVAL);
> +        }
> +
> +        enccfg.tile_width_count  = ctx->tile_cols;
> +        enccfg.tile_height_count = ctx->tile_rows;
> +
> +        sb_width   = (avctx->width  + sb_size - 1) / sb_size;
> +        sb_height  = (avctx->height + sb_size - 1) / sb_size;
> +
> +        cols_per_tile = sb_width / ctx->tile_cols;
> +        cols_step     = sb_width % ctx->tile_cols;
> +        for (i = 0; i < ctx->tile_cols; i++)
> +            enccfg.tile_widths[i] = cols_per_tile + (i < cols_step);
> +
> +        rows_per_tile = sb_height / ctx->tile_rows;
> +        rows_step     = sb_height % ctx->tile_rows;
> +        for (i = 0; i < ctx->tile_rows; i++)
> +            enccfg.tile_heights[i] = rows_per_tile + (i < rows_step);
> +    }
> ...
diff mbox

Patch

diff --git a/libavcodec/libaomenc.c b/libavcodec/libaomenc.c
index 9431179886..0b4fa71fc7 100644
--- a/libavcodec/libaomenc.c
+++ b/libavcodec/libaomenc.c
@@ -68,6 +68,7 @@  typedef struct AOMEncoderContext {
     int static_thresh;
     int drop_threshold;
     int noise_sensitivity;
+    int tile_cols, tile_rows;
 } AOMContext;
 
 static const char *const ctlidstr[] = {
@@ -79,6 +80,7 @@  static const char *const ctlidstr[] = {
     [AV1E_SET_COLOR_PRIMARIES]  = "AV1E_SET_COLOR_PRIMARIES",
     [AV1E_SET_MATRIX_COEFFICIENTS] = "AV1E_SET_MATRIX_COEFFICIENTS",
     [AV1E_SET_TRANSFER_CHARACTERISTICS] = "AV1E_SET_TRANSFER_CHARACTERISTICS",
+    [AV1E_SET_SUPERBLOCK_SIZE]  = "AV1E_SET_SUPERBLOCK_SIZE",
 };
 
 static av_cold void log_encoder_error(AVCodecContext *avctx, const char *desc)
@@ -143,6 +145,10 @@  static av_cold void dump_enc_cfg(AVCodecContext *avctx,
            width, "kf_mode:",     cfg->kf_mode,
            width, "kf_min_dist:", cfg->kf_min_dist,
            width, "kf_max_dist:", cfg->kf_max_dist);
+    av_log(avctx, level, "tile settings\n"
+                         "  %*s%d\n  %*s%d\n",
+           width, "tile_width_count:",  cfg->tile_width_count,
+           width, "tile_height_count:", cfg->tile_height_count);
     av_log(avctx, level, "\n");
 }
 
@@ -294,6 +300,7 @@  static av_cold int aom_init(AVCodecContext *avctx,
     int res;
     aom_img_fmt_t img_fmt;
     aom_codec_caps_t codec_caps = aom_codec_get_caps(iface);
+    aom_superblock_size_t superblock_size = AOM_SUPERBLOCK_SIZE_DYNAMIC;
 
     av_log(avctx, AV_LOG_INFO, "%s\n", aom_codec_version_str());
     av_log(avctx, AV_LOG_VERBOSE, "%s\n", aom_codec_build_config());
@@ -431,6 +438,51 @@  static av_cold int aom_init(AVCodecContext *avctx,
 
     enccfg.g_error_resilient = ctx->error_resilient;
 
+    if (ctx->tile_cols && ctx->tile_rows) {
+        int sb_size, sb_width, sb_height;
+        int cols_per_tile, cols_step, rows_per_tile, rows_step, i;
+
+        if (avctx->width / ctx->tile_cols < 128 ||
+            avctx->width / ctx->tile_rows < 128) {
+            // User has requested more tiles than would fit with 128x128
+            // superblocks, so assume they want 64x64.
+            sb_size = 64;
+            superblock_size = AOM_SUPERBLOCK_SIZE_64X64;
+        } else {
+            sb_size = 128;
+            superblock_size = AOM_SUPERBLOCK_SIZE_128X128;
+        }
+
+        if (avctx->width  / ctx->tile_cols < sb_size ||
+            avctx->height / ctx->tile_rows < sb_size) {
+            av_log(avctx, AV_LOG_ERROR, "Invalid tile sizing: tiles must be "
+                   "at least one superblock wide and high.\n");
+            return AVERROR(EINVAL);
+        }
+        if (ctx->tile_cols > MAX_TILE_WIDTHS ||
+            ctx->tile_rows > MAX_TILE_HEIGHTS) {
+            av_log(avctx, AV_LOG_ERROR, "Invalid tile sizing: at most %dx%d "
+                   "tiles allowed.\n", MAX_TILE_WIDTHS, MAX_TILE_HEIGHTS);
+            return AVERROR(EINVAL);
+        }
+
+        enccfg.tile_width_count  = ctx->tile_cols;
+        enccfg.tile_height_count = ctx->tile_rows;
+
+        sb_width   = (avctx->width  + sb_size - 1) / sb_size;
+        sb_height  = (avctx->height + sb_size - 1) / sb_size;
+
+        cols_per_tile = sb_width / ctx->tile_cols;
+        cols_step     = sb_width % ctx->tile_cols;
+        for (i = 0; i < ctx->tile_cols; i++)
+            enccfg.tile_widths[i] = cols_per_tile + (i < cols_step);
+
+        rows_per_tile = sb_height / ctx->tile_rows;
+        rows_step     = sb_height % ctx->tile_rows;
+        for (i = 0; i < ctx->tile_rows; i++)
+            enccfg.tile_heights[i] = rows_per_tile + (i < rows_step);
+    }
+
     dump_enc_cfg(avctx, &enccfg);
     /* Construct Encoder Context */
     res = aom_codec_enc_init(&ctx->encoder, iface, &enccfg, flags);
@@ -454,6 +506,8 @@  static av_cold int aom_init(AVCodecContext *avctx,
     codecctl_int(avctx, AV1E_SET_TRANSFER_CHARACTERISTICS, avctx->color_trc);
     set_color_range(avctx);
 
+    codecctl_int(avctx, AV1E_SET_SUPERBLOCK_SIZE, superblock_size);
+
     // provide dummy value to initialize wrapper, values will be updated each _encode()
     aom_img_wrap(&ctx->rawimg, img_fmt, avctx->width, avctx->height, 1,
                  (unsigned char*)1);
@@ -746,6 +800,7 @@  static const AVOption options[] = {
     { "static-thresh",    "A change threshold on blocks below which they will be skipped by the encoder", OFFSET(static_thresh), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE },
     { "drop-threshold",   "Frame drop threshold", offsetof(AOMContext, drop_threshold), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, VE },
     { "noise-sensitivity", "Noise sensitivity", OFFSET(noise_sensitivity), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 4, VE},
+    { "tiles",            "Tile rows x columns", OFFSET(tile_cols), AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, 0, VE },
     { NULL }
 };