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.
This commit is contained in:
Ari Koivula 2017-01-31 15:44:23 +02:00
parent 0d4d0e869c
commit 52904d3e9f
7 changed files with 85 additions and 17 deletions

View file

@ -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

View file

@ -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;
}

View file

@ -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 <string>|u<int> :\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 <string> : 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"

View file

@ -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) {

View file

@ -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);

View file

@ -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.

View file

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