From 52904d3e9f8286b2ac70c1e422b8ea650b5f1e9e Mon Sep 17 00:00:00 2001 From: Ari Koivula Date: Tue, 31 Jan 2017 15:44:23 +0200 Subject: [PATCH] Add --slices=tiles and --slices=wpp This encapsulates tiles or WPP rows into their own slices, making it possible to send them as soon as they are done, instead of waiting for the other substreams to finish and coding the substream offsets in the slice header. --- configure.ac | 2 +- src/cfg.c | 24 +++++++++++++++++++++--- src/cli.c | 13 +++++-------- src/encoder.c | 28 +++++++++++++++++++++++++--- src/encoder_state-bitstream.c | 13 +++++++++++++ src/encoderstate.c | 10 ++++++++-- src/kvazaar.h | 12 ++++++++++++ 7 files changed, 85 insertions(+), 17 deletions(-) diff --git a/configure.ac b/configure.ac index 4fd845e9..9a5fa105 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ AC_CONFIG_SRCDIR([src/encmain.c]) # # Here is a somewhat sane guide to lib versioning: http://apr.apache.org/versioning.html ver_major=3 -ver_minor=14 +ver_minor=15 ver_release=0 # Prevents configure from adding a lot of defines to the CFLAGS diff --git a/src/cfg.c b/src/cfg.c index 2f996b6e..a017142e 100644 --- a/src/cfg.c +++ b/src/cfg.c @@ -126,6 +126,8 @@ int kvz_config_init(kvz_config *cfg) cfg->roi.height = 0; cfg->roi.dqps = NULL; + cfg->slices = KVZ_SLICES_NONE; + return 1; } @@ -732,9 +734,20 @@ int kvz_config_parse(kvz_config *cfg, const char *name, const char *value) // -1 means automatic selection cfg->owf = -1; } - } else if OPT("slice-addresses") { - fprintf(stderr, "--slice-addresses doesn't do anything, because slices are not implemented.\n"); - return parse_slice_specification(value, &cfg->slice_count, &cfg->slice_addresses_in_ts); + } else if OPT("slices") { + if (!strcmp(value, "tiles")) { + cfg->slices = KVZ_SLICES_TILES; + return 1; + } else if (!strcmp(value, "wpp")) { + cfg->slices = KVZ_SLICES_WPP; + return 1; + } else if (!strcmp(value, "tiles+wpp")) { + cfg->slices = KVZ_SLICES_TILES | KVZ_SLICES_WPP; + return 1; + } else { + return parse_slice_specification(value, &cfg->slice_count, &cfg->slice_addresses_in_ts); + } + } else if OPT("threads") { cfg->threads = atoi(value); if (cfg->threads == 0 && !strcmp(value, "auto")) { @@ -1279,5 +1292,10 @@ int kvz_config_validate(const kvz_config *const cfg) error = 1; } + if ((cfg->slices & KVZ_SLICES_WPP) && !cfg->wpp) { + fprintf(stderr, "Input error: --slices=wpp does not work without --wpp.\n"); + error = 1; + } + return !error; } diff --git a/src/cli.c b/src/cli.c index eb93630d..5c39a9f0 100644 --- a/src/cli.c +++ b/src/cli.c @@ -84,7 +84,7 @@ static const struct option long_options[] = { { "wpp", no_argument, NULL, 0 }, { "no-wpp", no_argument, NULL, 0 }, { "owf", required_argument, NULL, 0 }, - { "slice-addresses", required_argument, NULL, 0 }, + { "slices", required_argument, NULL, 0 }, { "threads", required_argument, NULL, 0 }, { "cpuid", required_argument, NULL, 0 }, { "pu-depth-inter", required_argument, NULL, 0 }, @@ -461,13 +461,10 @@ void print_help(void) " positions of tiles rows separation coordinates.\n" " Can also be u followed by and a single int n,\n" " in which case it produces rows of uniform height.\n" - /* - " --slice-addresses |u :\n" - " Specifies a comma separated list of LCU\n" - " positions in tile scan order of tile separations.\n" - " Can also be u followed by and a single int n,\n" - " in which case it produces uniform slice length.\n" - */ + " --slices : Control how slices are used\n" + " - tiles: put tiles in independent slices\n" + " - wpp: put rows in dependent slices\n" + " - tiles+wpp: do both\n" "\n" /* Word wrap to this width to stay under 80 characters (including ") ************/ "Video Usability Information:\n" diff --git a/src/encoder.c b/src/encoder.c index bda22f4e..cde08163 100644 --- a/src/encoder.c +++ b/src/encoder.c @@ -194,8 +194,6 @@ encoder_control_t* kvz_encoder_control_init(kvz_config *const cfg) { encoder->rdo = 1; encoder->full_intra_search = 0; - encoder->pps.dependent_slice_segments_enabled_flag = 0; - // Interlacing encoder->in.source_scan_type = (int8_t)cfg->source_scan_type; encoder->vui.field_seq_flag = encoder->cfg->source_scan_type != 0; @@ -378,8 +376,32 @@ encoder_control_t* kvz_encoder_control_init(kvz_config *const cfg) { } } + if (cfg->slices & KVZ_SLICES_WPP) { + // Each WPP row will be put into a dependent slice. + encoder->pps.dependent_slice_segments_enabled_flag = 1; + } + //Slices - { + if (cfg->slices & KVZ_SLICES_TILES) { + // Configure a single independent slice per tile. + + int *slice_addresses_in_ts; + encoder->slice_count = encoder->tiles_num_tile_columns * encoder->tiles_num_tile_rows; + encoder->slice_addresses_in_ts = slice_addresses_in_ts = MALLOC(int, encoder->slice_count); + + int slice_id = 0; + for (int tile_row = 0; tile_row < encoder->tiles_num_tile_rows; ++tile_row) { + for (int tile_col = 0; tile_col < encoder->tiles_num_tile_columns; ++tile_col) { + int x = tiles_col_bd[tile_col]; + int y = tiles_row_bd[tile_row]; + int rs = y * encoder->in.width_in_lcu + x; + int ts = tiles_ctb_addr_rs_to_ts[rs]; + slice_addresses_in_ts[slice_id] = ts; + slice_id += 1; + } + } + + } else { int *slice_addresses_in_ts; encoder->slice_count = encoder->cfg->slice_count; if (encoder->slice_count == 0) { diff --git a/src/encoder_state-bitstream.c b/src/encoder_state-bitstream.c index f235b2bb..19f4c656 100644 --- a/src/encoder_state-bitstream.c +++ b/src/encoder_state-bitstream.c @@ -809,6 +809,11 @@ void kvz_encoder_state_write_bitstream_slice_header( #endif bool first_slice_segment_in_pic = (state->slice->start_in_rs == 0); + if ((state->encoder_control->cfg->slices & KVZ_SLICES_WPP) + && state->wfrow->lcu_offset_y > 0) + { + first_slice_segment_in_pic = false; + } WRITE_U(stream, first_slice_segment_in_pic, 1, "first_slice_segment_in_pic_flag"); @@ -827,6 +832,9 @@ void kvz_encoder_state_write_bitstream_slice_header( int lcu_cnt = encoder->in.width_in_lcu * encoder->in.height_in_lcu; int num_bits = kvz_math_ceil_log2(lcu_cnt); int slice_start_rs = state->slice->start_in_rs; + if (state->encoder_control->cfg->slices & KVZ_SLICES_WPP) { + slice_start_rs += state->wfrow->lcu_offset_y * state->tile->frame->width_in_lcu; + } WRITE_U(stream, slice_start_rs, num_bits, "slice_segment_address"); } @@ -941,6 +949,11 @@ static void encoder_state_write_bitstream_children(encoder_state_t * const state for (int i = 0; state->children[i].encoder_control; ++i) { if (state->children[i].type == ENCODER_STATE_TYPE_SLICE) { encoder_state_write_slice_header(&state->stream, &state->children[i], true); + } else if (state->children[i].type == ENCODER_STATE_TYPE_WAVEFRONT_ROW) { + if ((state->encoder_control->cfg->slices & KVZ_SLICES_WPP) && i != 0) { + // Add header for dependent WPP row slice. + encoder_state_write_slice_header(&state->stream, &state->children[i], false); + } } kvz_encoder_state_write_bitstream(&state->children[i]); kvz_bitstream_move(&state->stream, &state->children[i].stream); diff --git a/src/encoderstate.c b/src/encoderstate.c index 1308037b..39294703 100644 --- a/src/encoderstate.c +++ b/src/encoderstate.c @@ -331,7 +331,13 @@ static void encoder_state_worker_encode_lcu(void * opaque) kvz_encode_coding_tree(state, lcu->position.x << MAX_DEPTH, lcu->position.y << MAX_DEPTH, 0); bool end_of_slice_segment_flag; - { + if (state->encoder_control->cfg->slices & KVZ_SLICES_WPP) { + // Slice segments end after each WPP row. + end_of_slice_segment_flag = lcu->last_column; + } else if (state->encoder_control->cfg->slices & KVZ_SLICES_TILES) { + // Slices end after each tile. + end_of_slice_segment_flag = lcu->last_column && lcu->last_row; + } else { // Slice ends after the last row of the last tile. int last_tile_id = -1 + encoder->tiles_num_tile_columns * encoder->tiles_num_tile_rows; bool is_last_tile = state->tile->id == last_tile_id; @@ -528,7 +534,7 @@ static void encoder_state_worker_encode_children(void * opaque) encoder_state_t *sub_state = opaque; encoder_state_encode(sub_state); - if (sub_state->type == ENCODER_STATE_TYPE_WAVEFRONT_ROW) { + if (sub_state->is_leaf && sub_state->type == ENCODER_STATE_TYPE_WAVEFRONT_ROW) { // Set the last wavefront job of this row as the job that completes // the bitstream for this wavefront row state. diff --git a/src/kvazaar.h b/src/kvazaar.h index c7442147..68b3ffa8 100644 --- a/src/kvazaar.h +++ b/src/kvazaar.h @@ -188,6 +188,16 @@ enum kvz_chroma_format { KVZ_CSP_444 = 3, }; +/** + * \brief Chroma subsampling format used for encoding. + * \since 3.15.0 + */ +enum kvz_slices { + KVZ_SLICES_NONE, + KVZ_SLICES_TILES = (1 << 0), /*!< \brief Put each tile in a slice. */ + KVZ_SLICES_WPP = (1 << 1), /*!< \brief Put each row in a slice. */ +}; + // Map from input format to chroma format. #define KVZ_FORMAT2CSP(format) ((enum kvz_chroma_format)"\0\1\2\3"[format]) @@ -325,6 +335,8 @@ typedef struct kvz_config int32_t height; uint8_t *dqps; } roi; /*!< \since 3.14.0 \brief Map of delta QPs for region of interest coding. */ + + unsigned slices; /*!< \since 3.15.0 \brief How to map slices to frame. */ } kvz_config; /**