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; /**