mirror of
https://github.com/ultravideo/uvg266.git
synced 2024-11-30 12:44:07 +00:00
Prepare for delta QPs at CU-level
- Replaces lcu_dqp_enabled with max_qp_delta_depth in encoder_control_t. - Fixes set_cu_qps so that it can handle quantization groups of arbitrary size. - Fixes computation of QP predictors so that it works for quantization groups of arbitrary size.
This commit is contained in:
parent
a3274de3b4
commit
a343f6d587
|
@ -435,7 +435,9 @@ static void encode_transform_coeff(encoder_state_t * const state,
|
||||||
const cu_info_t *cur_pu = kvz_cu_array_at_const(frame->cu_array, x, y);
|
const cu_info_t *cur_pu = kvz_cu_array_at_const(frame->cu_array, x, y);
|
||||||
// Round coordinates down to a multiple of 8 to get the location of the
|
// Round coordinates down to a multiple of 8 to get the location of the
|
||||||
// containing CU.
|
// containing CU.
|
||||||
const cu_info_t *cur_cu = kvz_cu_array_at_const(frame->cu_array, x & ~7, y & ~7);
|
const int x_cu = 8 * (x / 8);
|
||||||
|
const int y_cu = 8 * (y / 8);
|
||||||
|
const cu_info_t *cur_cu = kvz_cu_array_at_const(frame->cu_array, x_cu, y_cu);
|
||||||
|
|
||||||
// NxN signifies implicit transform split at the first transform level.
|
// NxN signifies implicit transform split at the first transform level.
|
||||||
// There is a similar implicit split for inter, but it is only used when
|
// There is a similar implicit split for inter, but it is only used when
|
||||||
|
@ -508,7 +510,8 @@ static void encode_transform_coeff(encoder_state_t * const state,
|
||||||
|
|
||||||
if (cb_flag_y | cb_flag_u | cb_flag_v) {
|
if (cb_flag_y | cb_flag_u | cb_flag_v) {
|
||||||
if (state->must_code_qp_delta) {
|
if (state->must_code_qp_delta) {
|
||||||
const int qp_delta = state->qp - state->ref_qp;
|
const int qp_pred = kvz_get_cu_ref_qp(state, x_cu, y_cu, state->last_qp);
|
||||||
|
const int qp_delta = cur_cu->qp - qp_pred;
|
||||||
const int qp_delta_abs = ABS(qp_delta);
|
const int qp_delta_abs = ABS(qp_delta);
|
||||||
cabac_data_t* cabac = &state->cabac;
|
cabac_data_t* cabac = &state->cabac;
|
||||||
|
|
||||||
|
@ -526,7 +529,6 @@ static void encode_transform_coeff(encoder_state_t * const state,
|
||||||
}
|
}
|
||||||
|
|
||||||
state->must_code_qp_delta = false;
|
state->must_code_qp_delta = false;
|
||||||
state->ref_qp = state->qp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
encode_transform_unit(state, x, y, depth);
|
encode_transform_unit(state, x, y, depth);
|
||||||
|
@ -957,6 +959,9 @@ void kvz_encode_coding_tree(encoder_state_t * const state,
|
||||||
const videoframe_t * const frame = state->tile->frame;
|
const videoframe_t * const frame = state->tile->frame;
|
||||||
const cu_info_t *cur_cu = kvz_cu_array_at_const(frame->cu_array, x, y);
|
const cu_info_t *cur_cu = kvz_cu_array_at_const(frame->cu_array, x, y);
|
||||||
|
|
||||||
|
const int cu_width = LCU_WIDTH >> depth;
|
||||||
|
const int half_cu = cu_width >> 1;
|
||||||
|
|
||||||
const cu_info_t *left_cu = NULL;
|
const cu_info_t *left_cu = NULL;
|
||||||
if (x > 0) {
|
if (x > 0) {
|
||||||
left_cu = kvz_cu_array_at_const(frame->cu_array, x - 1, y);
|
left_cu = kvz_cu_array_at_const(frame->cu_array, x - 1, y);
|
||||||
|
@ -973,13 +978,17 @@ void kvz_encode_coding_tree(encoder_state_t * const state,
|
||||||
uint16_t abs_x = x + state->tile->offset_x;
|
uint16_t abs_x = x + state->tile->offset_x;
|
||||||
uint16_t abs_y = y + state->tile->offset_y;
|
uint16_t abs_y = y + state->tile->offset_y;
|
||||||
|
|
||||||
// Check for slice border FIXME
|
// Check for slice border
|
||||||
bool border_x = ctrl->in.width < abs_x + (LCU_WIDTH >> depth);
|
bool border_x = ctrl->in.width < abs_x + cu_width;
|
||||||
bool border_y = ctrl->in.height < abs_y + (LCU_WIDTH >> depth);
|
bool border_y = ctrl->in.height < abs_y + cu_width;
|
||||||
bool border_split_x = ctrl->in.width >= abs_x + (LCU_WIDTH >> MAX_DEPTH) + (LCU_WIDTH >> (depth + 1));
|
bool border_split_x = ctrl->in.width >= abs_x + (LCU_WIDTH >> MAX_DEPTH) + half_cu;
|
||||||
bool border_split_y = ctrl->in.height >= abs_y + (LCU_WIDTH >> MAX_DEPTH) + (LCU_WIDTH >> (depth + 1));
|
bool border_split_y = ctrl->in.height >= abs_y + (LCU_WIDTH >> MAX_DEPTH) + half_cu;
|
||||||
bool border = border_x || border_y; /*!< are we in any border CU */
|
bool border = border_x || border_y; /*!< are we in any border CU */
|
||||||
|
|
||||||
|
if (depth <= ctrl->max_qp_delta_depth) {
|
||||||
|
state->must_code_qp_delta = true;
|
||||||
|
}
|
||||||
|
|
||||||
// When not in MAX_DEPTH, insert split flag and split the blocks if needed
|
// When not in MAX_DEPTH, insert split flag and split the blocks if needed
|
||||||
if (depth != MAX_DEPTH) {
|
if (depth != MAX_DEPTH) {
|
||||||
// Implisit split flag when on border
|
// Implisit split flag when on border
|
||||||
|
@ -999,25 +1008,22 @@ void kvz_encode_coding_tree(encoder_state_t * const state,
|
||||||
|
|
||||||
if (split_flag || border) {
|
if (split_flag || border) {
|
||||||
// Split blocks and remember to change x and y block positions
|
// Split blocks and remember to change x and y block positions
|
||||||
int offset = LCU_WIDTH >> (depth + 1);
|
|
||||||
|
|
||||||
kvz_encode_coding_tree(state, x, y, depth + 1);
|
kvz_encode_coding_tree(state, x, y, depth + 1);
|
||||||
|
|
||||||
// TODO: fix when other half of the block would not be completely over the border
|
|
||||||
if (!border_x || border_split_x) {
|
if (!border_x || border_split_x) {
|
||||||
kvz_encode_coding_tree(state, x + offset, y, depth + 1);
|
kvz_encode_coding_tree(state, x + half_cu, y, depth + 1);
|
||||||
}
|
}
|
||||||
if (!border_y || border_split_y) {
|
if (!border_y || border_split_y) {
|
||||||
kvz_encode_coding_tree(state, x, y + offset, depth + 1);
|
kvz_encode_coding_tree(state, x, y + half_cu, depth + 1);
|
||||||
}
|
}
|
||||||
if (!border || (border_split_x && border_split_y)) {
|
if (!border || (border_split_x && border_split_y)) {
|
||||||
kvz_encode_coding_tree(state, x + offset, y + offset, depth + 1);
|
kvz_encode_coding_tree(state, x + half_cu, y + half_cu, depth + 1);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->encoder_control->cfg.lossless) {
|
if (ctrl->cfg.lossless) {
|
||||||
cabac->cur_ctx = &cabac->ctx.cu_transquant_bypass;
|
cabac->cur_ctx = &cabac->ctx.cu_transquant_bypass;
|
||||||
CABAC_BIN(cabac, 1, "cu_transquant_bypass_flag");
|
CABAC_BIN(cabac, 1, "cu_transquant_bypass_flag");
|
||||||
}
|
}
|
||||||
|
@ -1053,7 +1059,7 @@ void kvz_encode_coding_tree(encoder_state_t * const state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
goto end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1068,7 +1074,6 @@ void kvz_encode_coding_tree(encoder_state_t * const state,
|
||||||
|
|
||||||
if (cur_cu->type == CU_INTER) {
|
if (cur_cu->type == CU_INTER) {
|
||||||
const int num_pu = kvz_part_mode_num_parts[cur_cu->part_size];
|
const int num_pu = kvz_part_mode_num_parts[cur_cu->part_size];
|
||||||
const int cu_width = LCU_WIDTH >> depth;
|
|
||||||
|
|
||||||
for (int i = 0; i < num_pu; ++i) {
|
for (int i = 0; i < num_pu; ++i) {
|
||||||
const int pu_x = PU_GET_X(cur_cu->part_size, cu_width, x, i);
|
const int pu_x = PU_GET_X(cur_cu->part_size, cu_width, x, i);
|
||||||
|
@ -1139,6 +1144,12 @@ void kvz_encode_coding_tree(encoder_state_t * const state,
|
||||||
assert(0);
|
assert(0);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
|
||||||
|
if (is_last_cu_in_qg(state, x, y, depth)) {
|
||||||
|
state->last_qp = cur_cu->qp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -347,12 +347,16 @@ encoder_control_t* kvz_encoder_control_init(const kvz_config *const cfg)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
encoder->lcu_dqp_enabled = cfg->target_bitrate > 0 || encoder->cfg.roi.dqps;
|
|
||||||
|
|
||||||
// NOTE: When tr_depth_inter is equal to 0, the transform is still split
|
// NOTE: When tr_depth_inter is equal to 0, the transform is still split
|
||||||
// for SMP and AMP partition units.
|
// for SMP and AMP partition units.
|
||||||
encoder->tr_depth_inter = 0;
|
encoder->tr_depth_inter = 0;
|
||||||
|
|
||||||
|
if (encoder->cfg.target_bitrate > 0 || encoder->cfg.roi.dqps) {
|
||||||
|
encoder->max_qp_delta_depth = 0;
|
||||||
|
} else {
|
||||||
|
encoder->max_qp_delta_depth = -1;
|
||||||
|
}
|
||||||
|
|
||||||
//Tiles
|
//Tiles
|
||||||
encoder->tiles_enable = encoder->cfg.tiles_width_count > 1 ||
|
encoder->tiles_enable = encoder->cfg.tiles_width_count > 1 ||
|
||||||
encoder->cfg.tiles_height_count > 1;
|
encoder->cfg.tiles_height_count > 1;
|
||||||
|
|
|
@ -118,7 +118,7 @@ typedef struct encoder_control_t
|
||||||
//! Picture weights when GOP is used.
|
//! Picture weights when GOP is used.
|
||||||
double gop_layer_weights[MAX_GOP_LAYERS];
|
double gop_layer_weights[MAX_GOP_LAYERS];
|
||||||
|
|
||||||
bool lcu_dqp_enabled;
|
int8_t max_qp_delta_depth;
|
||||||
|
|
||||||
int tr_depth_inter;
|
int tr_depth_inter;
|
||||||
|
|
||||||
|
|
|
@ -455,10 +455,10 @@ static void encoder_state_write_bitstream_pic_parameter_set(bitstream_t* stream,
|
||||||
WRITE_U(stream, 0, 1, "constrained_intra_pred_flag");
|
WRITE_U(stream, 0, 1, "constrained_intra_pred_flag");
|
||||||
WRITE_U(stream, encoder->cfg.trskip_enable, 1, "transform_skip_enabled_flag");
|
WRITE_U(stream, encoder->cfg.trskip_enable, 1, "transform_skip_enabled_flag");
|
||||||
|
|
||||||
if (encoder->lcu_dqp_enabled) {
|
if (encoder->max_qp_delta_depth >= 0) {
|
||||||
// Use separate QP for each LCU when rate control is enabled.
|
// Use separate QP for each LCU when rate control is enabled.
|
||||||
WRITE_U(stream, 1, 1, "cu_qp_delta_enabled_flag");
|
WRITE_U(stream, 1, 1, "cu_qp_delta_enabled_flag");
|
||||||
WRITE_UE(stream, 0, "diff_cu_qp_delta_depth");
|
WRITE_UE(stream, encoder->max_qp_delta_depth, "diff_cu_qp_delta_depth");
|
||||||
} else {
|
} else {
|
||||||
WRITE_U(stream, 0, 1, "cu_qp_delta_enabled_flag");
|
WRITE_U(stream, 0, 1, "cu_qp_delta_enabled_flag");
|
||||||
}
|
}
|
||||||
|
|
|
@ -312,6 +312,7 @@ int kvz_encoder_state_init(encoder_state_t * const child_state, encoder_state_t
|
||||||
child_state->children = MALLOC(encoder_state_t, 1);
|
child_state->children = MALLOC(encoder_state_t, 1);
|
||||||
child_state->children[0].encoder_control = NULL;
|
child_state->children[0].encoder_control = NULL;
|
||||||
child_state->crypto_hdl = NULL;
|
child_state->crypto_hdl = NULL;
|
||||||
|
child_state->must_code_qp_delta = false;
|
||||||
child_state->tqj_bitstream_written = NULL;
|
child_state->tqj_bitstream_written = NULL;
|
||||||
child_state->tqj_recon_done = NULL;
|
child_state->tqj_recon_done = NULL;
|
||||||
|
|
||||||
|
|
|
@ -526,68 +526,81 @@ static void encode_sao(encoder_state_t * const state,
|
||||||
/**
|
/**
|
||||||
* \brief Sets the QP for each CU in state->tile->frame->cu_array.
|
* \brief Sets the QP for each CU in state->tile->frame->cu_array.
|
||||||
*
|
*
|
||||||
* The QPs are used in deblocking.
|
* The QPs are used in deblocking and QP prediction.
|
||||||
*
|
*
|
||||||
* The delta QP for an LCU is coded when the first CU with coded block flag
|
* The QP delta for a quantization group is coded when the first CU with
|
||||||
* set is encountered. Hence, for the purposes of deblocking, all CUs
|
* coded block flag set is encountered. Hence, for the purposes of
|
||||||
* before the first one with cbf set use state->ref_qp and all CUs after
|
* deblocking and QP prediction, all CUs in before the first one that has
|
||||||
* that use state->qp.
|
* cbf set use the QP predictor and all CUs after that use (QP predictor
|
||||||
|
* + QP delta).
|
||||||
*
|
*
|
||||||
* \param state encoder state
|
* \param state encoder state
|
||||||
* \param x x-coordinate of the left edge of the root CU
|
* \param x x-coordinate of the left edge of the root CU
|
||||||
* \param y y-coordinate of the top edge of the root CU
|
* \param y y-coordinate of the top edge of the root CU
|
||||||
* \param depth depth in the CU quadtree
|
* \param depth depth in the CU quadtree
|
||||||
* \param coeffs_coded Used for tracking whether a CU with a residual
|
* \param last_qp QP of the last CU in the last quantization group
|
||||||
* has been encountered. Should be set to false at
|
* \param prev_qp -1 if QP delta has not been coded in current QG,
|
||||||
* the top level.
|
* otherwise the QP of the current QG
|
||||||
* \return Whether there were any CUs with residual or not.
|
|
||||||
*/
|
*/
|
||||||
static bool set_cu_qps(encoder_state_t *state, int x, int y, int depth, bool coeffs_coded)
|
static void set_cu_qps(encoder_state_t *state, int x, int y, int depth, int *last_qp, int *prev_qp)
|
||||||
{
|
{
|
||||||
if (state->qp == state->ref_qp) {
|
|
||||||
// If the QPs are equal there is no need to care about the residuals.
|
// Stop recursion if the CU is completely outside the frame.
|
||||||
coeffs_coded = true;
|
if (x >= state->tile->frame->width || y >= state->tile->frame->height) return;
|
||||||
}
|
|
||||||
|
|
||||||
cu_info_t *cu = kvz_cu_array_at(state->tile->frame->cu_array, x, y);
|
cu_info_t *cu = kvz_cu_array_at(state->tile->frame->cu_array, x, y);
|
||||||
const int cu_width = LCU_WIDTH >> depth;
|
const int cu_width = LCU_WIDTH >> depth;
|
||||||
coeffs_coded = coeffs_coded || cbf_is_set_any(cu->cbf, cu->depth);
|
|
||||||
|
|
||||||
if (!coeffs_coded && cu->depth > depth) {
|
if (depth <= state->encoder_control->max_qp_delta_depth) {
|
||||||
|
*prev_qp = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cu->depth > depth) {
|
||||||
// Recursively process sub-CUs.
|
// Recursively process sub-CUs.
|
||||||
const int d = cu_width >> 1;
|
const int d = cu_width >> 1;
|
||||||
coeffs_coded = set_cu_qps(state, x, y, depth + 1, coeffs_coded);
|
set_cu_qps(state, x, y, depth + 1, last_qp, prev_qp);
|
||||||
coeffs_coded = set_cu_qps(state, x + d, y, depth + 1, coeffs_coded);
|
set_cu_qps(state, x + d, y, depth + 1, last_qp, prev_qp);
|
||||||
coeffs_coded = set_cu_qps(state, x, y + d, depth + 1, coeffs_coded);
|
set_cu_qps(state, x, y + d, depth + 1, last_qp, prev_qp);
|
||||||
coeffs_coded = set_cu_qps(state, x + d, y + d, depth + 1, coeffs_coded);
|
set_cu_qps(state, x + d, y + d, depth + 1, last_qp, prev_qp);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (!coeffs_coded && cu->tr_depth > depth) {
|
bool cbf_found = *prev_qp >= 0;
|
||||||
|
|
||||||
|
if (cu->tr_depth > depth) {
|
||||||
// The CU is split into smaller transform units. Check whether coded
|
// The CU is split into smaller transform units. Check whether coded
|
||||||
// block flag is set for any of the TUs.
|
// block flag is set for any of the TUs.
|
||||||
const int tu_width = LCU_WIDTH >> cu->tr_depth;
|
const int tu_width = LCU_WIDTH >> cu->tr_depth;
|
||||||
for (int y_scu = y; y_scu < y + cu_width; y_scu += tu_width) {
|
for (int y_scu = y; !cbf_found && y_scu < y + cu_width; y_scu += tu_width) {
|
||||||
for (int x_scu = x; x_scu < x + cu_width; x_scu += tu_width) {
|
for (int x_scu = x; !cbf_found && x_scu < x + cu_width; x_scu += tu_width) {
|
||||||
cu_info_t *tu = kvz_cu_array_at(state->tile->frame->cu_array, x_scu, y_scu);
|
cu_info_t *tu = kvz_cu_array_at(state->tile->frame->cu_array, x_scu, y_scu);
|
||||||
if (cbf_is_set_any(tu->cbf, cu->depth)) {
|
if (cbf_is_set_any(tu->cbf, cu->depth)) {
|
||||||
coeffs_coded = true;
|
cbf_found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (cbf_is_set_any(cu->cbf, cu->depth)) {
|
||||||
|
cbf_found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t qp;
|
||||||
|
if (cbf_found) {
|
||||||
|
*prev_qp = qp = cu->qp;
|
||||||
|
} else {
|
||||||
|
qp = kvz_get_cu_ref_qp(state, x, y, *last_qp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the correct QP for all state->tile->frame->cu_array elements in
|
// Set the correct QP for all state->tile->frame->cu_array elements in
|
||||||
// the area covered by the CU.
|
// the area covered by the CU.
|
||||||
const int8_t qp = coeffs_coded ? state->qp : state->ref_qp;
|
|
||||||
|
|
||||||
for (int y_scu = y; y_scu < y + cu_width; y_scu += SCU_WIDTH) {
|
for (int y_scu = y; y_scu < y + cu_width; y_scu += SCU_WIDTH) {
|
||||||
for (int x_scu = x; x_scu < x + cu_width; x_scu += SCU_WIDTH) {
|
for (int x_scu = x; x_scu < x + cu_width; x_scu += SCU_WIDTH) {
|
||||||
kvz_cu_array_at(state->tile->frame->cu_array, x_scu, y_scu)->qp = qp;
|
kvz_cu_array_at(state->tile->frame->cu_array, x_scu, y_scu)->qp = qp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return coeffs_coded;
|
if (is_last_cu_in_qg(state, x, y, depth)) {
|
||||||
|
*last_qp = cu->qp;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -608,11 +621,13 @@ static void encoder_state_worker_encode_lcu(void * opaque)
|
||||||
|
|
||||||
encoder_state_recdata_to_bufs(state, lcu, state->tile->hor_buf_search, state->tile->ver_buf_search);
|
encoder_state_recdata_to_bufs(state, lcu, state->tile->hor_buf_search, state->tile->ver_buf_search);
|
||||||
|
|
||||||
if (encoder->cfg.deblock_enable) {
|
if (encoder->max_qp_delta_depth >= 0) {
|
||||||
if (encoder->lcu_dqp_enabled) {
|
int last_qp = state->last_qp;
|
||||||
set_cu_qps(state, lcu->position_px.x, lcu->position_px.y, 0, false);
|
int prev_qp = -1;
|
||||||
|
set_cu_qps(state, lcu->position_px.x, lcu->position_px.y, 0, &last_qp, &prev_qp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (encoder->cfg.deblock_enable) {
|
||||||
kvz_filter_deblock_lcu(state, lcu->position_px.x, lcu->position_px.y);
|
kvz_filter_deblock_lcu(state, lcu->position_px.x, lcu->position_px.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -635,9 +650,6 @@ static void encoder_state_worker_encode_lcu(void * opaque)
|
||||||
encode_sao(state, lcu->position.x, lcu->position.y, &frame->sao_luma[lcu->position.y * frame->width_in_lcu + lcu->position.x], &frame->sao_chroma[lcu->position.y * frame->width_in_lcu + lcu->position.x]);
|
encode_sao(state, lcu->position.x, lcu->position.y, &frame->sao_luma[lcu->position.y * frame->width_in_lcu + lcu->position.x], &frame->sao_chroma[lcu->position.y * frame->width_in_lcu + lcu->position.x]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// QP delta is not used when rate control is turned off.
|
|
||||||
state->must_code_qp_delta = encoder->lcu_dqp_enabled;
|
|
||||||
|
|
||||||
//Encode coding tree
|
//Encode coding tree
|
||||||
kvz_encode_coding_tree(state, lcu->position.x * LCU_WIDTH, lcu->position.y * LCU_WIDTH, 0);
|
kvz_encode_coding_tree(state, lcu->position.x * LCU_WIDTH, lcu->position.y * LCU_WIDTH, 0);
|
||||||
|
|
||||||
|
@ -709,7 +721,7 @@ static void encoder_state_encode_leaf(encoder_state_t * const state)
|
||||||
const encoder_control_t *ctrl = state->encoder_control;
|
const encoder_control_t *ctrl = state->encoder_control;
|
||||||
const kvz_config *cfg = &ctrl->cfg;
|
const kvz_config *cfg = &ctrl->cfg;
|
||||||
|
|
||||||
state->ref_qp = state->frame->QP;
|
state->last_qp = state->frame->QP;
|
||||||
|
|
||||||
if (cfg->crypto_features) {
|
if (cfg->crypto_features) {
|
||||||
state->crypto_hdl = kvz_crypto_create(cfg);
|
state->crypto_hdl = kvz_crypto_create(cfg);
|
||||||
|
@ -1362,3 +1374,27 @@ lcu_stats_t* kvz_get_lcu_stats(encoder_state_t *state, int lcu_x, int lcu_y)
|
||||||
state->encoder_control->in.width_in_lcu;
|
state->encoder_control->in.width_in_lcu;
|
||||||
return &state->frame->lcu_stats[index];
|
return &state->frame->lcu_stats[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvz_get_cu_ref_qp(const encoder_state_t *state, int x, int y, int last_qp)
|
||||||
|
{
|
||||||
|
const encoder_control_t *ctrl = state->encoder_control;
|
||||||
|
const cu_array_t *cua = state->tile->frame->cu_array;
|
||||||
|
// Quantization group width
|
||||||
|
const int qg_width = LCU_WIDTH >> MIN(ctrl->max_qp_delta_depth, kvz_cu_array_at_const(cua, x, y)->depth);
|
||||||
|
|
||||||
|
// Coordinates of the top-left corner of the quantization group
|
||||||
|
const int x_qg = x & ~(qg_width - 1);
|
||||||
|
const int y_qg = y & ~(qg_width - 1);
|
||||||
|
|
||||||
|
int qp_pred_a = last_qp;
|
||||||
|
if (x_qg % LCU_WIDTH > 0) {
|
||||||
|
qp_pred_a = kvz_cu_array_at_const(cua, x_qg - 1, y_qg)->qp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qp_pred_b = last_qp;
|
||||||
|
if (y_qg % LCU_WIDTH > 0) {
|
||||||
|
qp_pred_b = kvz_cu_array_at_const(cua, x_qg, y_qg - 1)->qp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((qp_pred_a + qp_pred_b + 1) >> 1);
|
||||||
|
}
|
||||||
|
|
|
@ -268,10 +268,17 @@ typedef struct encoder_state_t {
|
||||||
bool must_code_qp_delta;
|
bool must_code_qp_delta;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Reference for computing QP delta for the next LCU that is coded
|
* \brief QP value of the last CU in the last coded quantization group.
|
||||||
* next. Updated whenever a QP delta is coded.
|
*
|
||||||
|
* A quantization group is a square of width
|
||||||
|
* (LCU_WIDTH >> encoder_control->max_qp_delta_depth). All CUs of in the
|
||||||
|
* same quantization group share the QP predictor value, but may have
|
||||||
|
* different QP values.
|
||||||
|
*
|
||||||
|
* Set to the frame QP at the beginning of a wavefront row or a tile and
|
||||||
|
* updated when the last CU of a quantization group is coded.
|
||||||
*/
|
*/
|
||||||
int8_t ref_qp;
|
int8_t last_qp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Coeffs for the LCU.
|
* \brief Coeffs for the LCU.
|
||||||
|
@ -297,6 +304,8 @@ void kvz_encoder_create_ref_lists(const encoder_state_t *const state);
|
||||||
lcu_stats_t* kvz_get_lcu_stats(encoder_state_t *state, int lcu_x, int lcu_y);
|
lcu_stats_t* kvz_get_lcu_stats(encoder_state_t *state, int lcu_x, int lcu_y);
|
||||||
|
|
||||||
|
|
||||||
|
int kvz_get_cu_ref_qp(const encoder_state_t *state, int x, int y, int last_qp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the parameter sets should be written with the current frame.
|
* Whether the parameter sets should be written with the current frame.
|
||||||
*/
|
*/
|
||||||
|
@ -309,6 +318,30 @@ static INLINE bool encoder_state_must_write_vps(const encoder_state_t *state)
|
||||||
(vps_period >= 0 && frame == 0);
|
(vps_period >= 0 && frame == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Returns true if the CU is the last CU in its containing
|
||||||
|
* quantization group.
|
||||||
|
*
|
||||||
|
* \param state encoder state
|
||||||
|
* \param x x-coordinate of the left edge of the CU
|
||||||
|
* \param y y-cooradinate of the top edge of the CU
|
||||||
|
* \param depth depth in the CU tree
|
||||||
|
* \return true, if it's the last CU in its QG, otherwise false
|
||||||
|
*/
|
||||||
|
static INLINE bool is_last_cu_in_qg(const encoder_state_t *state, int x, int y, int depth)
|
||||||
|
{
|
||||||
|
if (state->encoder_control->max_qp_delta_depth < 0) return false;
|
||||||
|
|
||||||
|
const int cu_width = LCU_WIDTH >> depth;
|
||||||
|
const int qg_width = LCU_WIDTH >> state->encoder_control->max_qp_delta_depth;
|
||||||
|
const int right = x + cu_width;
|
||||||
|
const int bottom = y + cu_width;
|
||||||
|
return (right % qg_width == 0 || right >= state->tile->frame->width) &&
|
||||||
|
(bottom % qg_width == 0 || bottom >= state->tile->frame->height);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static const uint8_t g_group_idx[32] = {
|
static const uint8_t g_group_idx[32] = {
|
||||||
0, 1, 2, 3, 4, 4, 5, 5, 6, 6,
|
0, 1, 2, 3, 4, 4, 5, 5, 6, 6,
|
||||||
6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
|
6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
|
||||||
|
|
|
@ -262,7 +262,7 @@ static bool is_on_8x8_grid(int x, int y, edge_dir dir)
|
||||||
|
|
||||||
static int8_t get_qp_y_pred(const encoder_state_t* state, int x, int y, edge_dir dir)
|
static int8_t get_qp_y_pred(const encoder_state_t* state, int x, int y, edge_dir dir)
|
||||||
{
|
{
|
||||||
if (!state->encoder_control->lcu_dqp_enabled) {
|
if (state->encoder_control->max_qp_delta_depth < 0) {
|
||||||
return state->qp;
|
return state->qp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,7 @@ static void lcu_fill_cu_info(lcu_t *lcu, int x_local, int y_local, int width, in
|
||||||
to->type = cu->type;
|
to->type = cu->type;
|
||||||
to->depth = cu->depth;
|
to->depth = cu->depth;
|
||||||
to->part_size = cu->part_size;
|
to->part_size = cu->part_size;
|
||||||
|
to->qp = cu->qp;
|
||||||
|
|
||||||
if (cu->type == CU_INTRA) {
|
if (cu->type == CU_INTRA) {
|
||||||
to->intra.mode = cu->intra.mode;
|
to->intra.mode = cu->intra.mode;
|
||||||
|
@ -413,6 +414,7 @@ static double search_cu(encoder_state_t * const state, int x, int y, int depth,
|
||||||
cur_cu->tr_depth = depth > 0 ? depth : 1;
|
cur_cu->tr_depth = depth > 0 ? depth : 1;
|
||||||
cur_cu->type = CU_NOTSET;
|
cur_cu->type = CU_NOTSET;
|
||||||
cur_cu->part_size = SIZE_2Nx2N;
|
cur_cu->part_size = SIZE_2Nx2N;
|
||||||
|
cur_cu->qp = state->qp;
|
||||||
|
|
||||||
// If the CU is completely inside the frame at this depth, search for
|
// If the CU is completely inside the frame at this depth, search for
|
||||||
// prediction modes at this depth.
|
// prediction modes at this depth.
|
||||||
|
|
|
@ -1746,6 +1746,7 @@ void kvz_search_cu_smp(encoder_state_t * const state,
|
||||||
cur_pu->type = CU_INTER;
|
cur_pu->type = CU_INTER;
|
||||||
cur_pu->part_size = part_mode;
|
cur_pu->part_size = part_mode;
|
||||||
cur_pu->depth = depth;
|
cur_pu->depth = depth;
|
||||||
|
cur_pu->qp = state->qp;
|
||||||
|
|
||||||
double cost = MAX_INT;
|
double cost = MAX_INT;
|
||||||
uint32_t bitcost = MAX_INT;
|
uint32_t bitcost = MAX_INT;
|
||||||
|
|
Loading…
Reference in a new issue