mirror of
https://github.com/ultravideo/uvg266.git
synced 2024-11-27 19:24:06 +00:00
Implement rate control in lambda domain.
- Rate control adjusts the lambda value. - QP is selected according to lambda. - Bits are allocated for GOPs and individual pictures.
This commit is contained in:
parent
664de9ade0
commit
93d2a95ddc
|
@ -540,34 +540,42 @@ static int config_parse(config_t *cfg, const char *name, const char *value)
|
|||
cfg->gop[0].poc_offset = 8; cfg->gop[0].qp_offset = 1; cfg->gop[0].layer = 1; cfg->gop[0].qp_factor = 0.442; cfg->gop[0].is_ref = 1;
|
||||
cfg->gop[0].ref_pos_count = 0;
|
||||
cfg->gop[0].ref_neg_count = 3; cfg->gop[0].ref_neg[0] = 8; cfg->gop[0].ref_neg[1] = 12; cfg->gop[0].ref_neg[2] = 16;
|
||||
cfg->gop[0].weight = 6;
|
||||
|
||||
cfg->gop[1].poc_offset = 4; cfg->gop[1].qp_offset = 2; cfg->gop[1].layer = 2; cfg->gop[1].qp_factor = 0.3536; cfg->gop[1].is_ref = 1;
|
||||
cfg->gop[1].ref_neg_count = 2; cfg->gop[1].ref_neg[0] = 4; cfg->gop[1].ref_neg[1] = 8;
|
||||
cfg->gop[1].ref_pos_count = 1; cfg->gop[1].ref_pos[0] = 4;
|
||||
cfg->gop[1].weight = 6;
|
||||
|
||||
cfg->gop[2].poc_offset = 2; cfg->gop[2].qp_offset = 3; cfg->gop[2].layer = 3; cfg->gop[2].qp_factor = 0.3536; cfg->gop[2].is_ref = 1;
|
||||
cfg->gop[2].ref_neg_count = 2; cfg->gop[2].ref_neg[0] = 2; cfg->gop[2].ref_neg[1] = 6;
|
||||
cfg->gop[2].ref_pos_count = 2; cfg->gop[2].ref_pos[0] = 2; cfg->gop[2].ref_pos[1] = 6;
|
||||
cfg->gop[2].weight = 3;
|
||||
|
||||
cfg->gop[3].poc_offset = 1; cfg->gop[3].qp_offset = 4; cfg->gop[3].layer = 4; cfg->gop[3].qp_factor = 0.68; cfg->gop[3].is_ref = 0;
|
||||
cfg->gop[3].ref_neg_count = 1; cfg->gop[3].ref_neg[0] = 1;
|
||||
cfg->gop[3].ref_pos_count = 3; cfg->gop[3].ref_pos[0] = 1; cfg->gop[3].ref_pos[1] = 3; cfg->gop[3].ref_pos[2] = 7;
|
||||
cfg->gop[3].weight = 2;
|
||||
|
||||
cfg->gop[4].poc_offset = 3; cfg->gop[4].qp_offset = 4; cfg->gop[4].layer = 4; cfg->gop[4].qp_factor = 0.68; cfg->gop[4].is_ref = 0;
|
||||
cfg->gop[4].ref_neg_count = 2; cfg->gop[4].ref_neg[0] = 1; cfg->gop[4].ref_neg[1] = 3;
|
||||
cfg->gop[4].ref_pos_count = 2; cfg->gop[4].ref_pos[0] = 1; cfg->gop[4].ref_pos[1] = 5;
|
||||
cfg->gop[4].weight = 2;
|
||||
|
||||
cfg->gop[5].poc_offset = 6; cfg->gop[5].qp_offset = 3; cfg->gop[5].layer = 3; cfg->gop[5].qp_factor = 0.3536; cfg->gop[5].is_ref = 1;
|
||||
cfg->gop[5].ref_neg_count = 2; cfg->gop[5].ref_neg[0] = 2; cfg->gop[5].ref_neg[1] = 6;
|
||||
cfg->gop[5].ref_pos_count = 1; cfg->gop[5].ref_pos[0] = 2;
|
||||
cfg->gop[5].weight = 3;
|
||||
|
||||
cfg->gop[6].poc_offset = 5; cfg->gop[6].qp_offset = 4; cfg->gop[6].layer = 4; cfg->gop[6].qp_factor = 0.68; cfg->gop[6].is_ref = 0;
|
||||
cfg->gop[6].ref_neg_count = 2; cfg->gop[6].ref_neg[0] = 1; cfg->gop[6].ref_neg[1] = 5;
|
||||
cfg->gop[6].ref_pos_count = 2; cfg->gop[6].ref_pos[0] = 1; cfg->gop[6].ref_pos[1] = 3;
|
||||
cfg->gop[6].weight = 2;
|
||||
|
||||
cfg->gop[7].poc_offset = 7; cfg->gop[7].qp_offset = 4; cfg->gop[7].layer = 4; cfg->gop[7].qp_factor = 0.68; cfg->gop[7].is_ref = 0;
|
||||
cfg->gop[7].ref_neg_count = 3; cfg->gop[7].ref_neg[0] = 1; cfg->gop[7].ref_neg[1] = 3; cfg->gop[7].ref_neg[2] = 7;
|
||||
cfg->gop[7].ref_pos_count = 1; cfg->gop[7].ref_pos[0] = 1;
|
||||
cfg->gop[7].weight = 2;
|
||||
} else if(atoi(value)) {
|
||||
fprintf(stderr, "Input error: goplen must be 8\n");
|
||||
return 0;
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
typedef struct {
|
||||
double qp_factor;
|
||||
int8_t qp_offset; /*!< \brief QP offset */
|
||||
int8_t weight;
|
||||
int8_t poc_offset; /*!< \brief POC offset */
|
||||
int8_t layer; /*!< \brief Current layer */
|
||||
int8_t is_ref; /*!< \brief Flag if this picture is used as a reference */
|
||||
|
|
|
@ -34,7 +34,6 @@ struct encoder_state_t;
|
|||
|
||||
int encoder_state_init(struct encoder_state_t * child_state, struct encoder_state_t * parent_state);
|
||||
void encoder_state_finalize(struct encoder_state_t *state);
|
||||
void encoder_state_init_lambda(struct encoder_state_t *state);
|
||||
|
||||
|
||||
#endif // ENCODER_STATE_CTORS_DTORS_H_
|
||||
|
|
|
@ -49,37 +49,6 @@
|
|||
# define LMBD 1.0
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\brief Initializes lambda-value for current QP
|
||||
|
||||
Implementation closer to HM (Used HM12 as reference)
|
||||
*/
|
||||
void encoder_state_init_lambda(encoder_state_t * const state)
|
||||
{
|
||||
double qp = state->global->QP;
|
||||
double lambda_scale = 1.0 - CLIP(0.0, 0.5, 0.05*(double)state->encoder_control->cfg->gop_len);
|
||||
double qp_temp = qp - 12;
|
||||
double lambda;
|
||||
|
||||
// Default QP-factor from HM config
|
||||
double qp_factor = state->encoder_control->cfg->gop_len ? state->global->QP_factor : 0.4624;
|
||||
|
||||
if (state->global->slicetype == SLICE_I) {
|
||||
qp_factor=0.57*lambda_scale;
|
||||
}
|
||||
|
||||
lambda = qp_factor*pow( 2.0, qp_temp/3.0 );
|
||||
|
||||
if (state->global->slicetype != SLICE_I ) {
|
||||
lambda *= 0.95;
|
||||
}
|
||||
|
||||
lambda *= LMBD;
|
||||
|
||||
state->global->cur_lambda_cost = lambda;
|
||||
state->global->cur_lambda_cost_sqrt = sqrt(lambda);
|
||||
}
|
||||
|
||||
int encoder_state_match_children_of_previous_frame(encoder_state_t * const state) {
|
||||
int i;
|
||||
for (i = 0; state->children[i].encoder_control; ++i) {
|
||||
|
@ -802,20 +771,10 @@ static void encoder_state_new_frame(encoder_state_t * const state) {
|
|||
encoder_state_ref_sort(state);
|
||||
}
|
||||
|
||||
if (state->encoder_control->cfg->gop_len) {
|
||||
if (state->global->slicetype == SLICE_I) {
|
||||
state->global->QP = state->encoder_control->cfg->qp;
|
||||
state->global->QP_factor = 0.4624;
|
||||
}
|
||||
else {
|
||||
state->global->QP = state->encoder_control->cfg->qp +
|
||||
state->encoder_control->cfg->gop[state->global->gop_offset].qp_offset;
|
||||
state->global->QP_factor = state->encoder_control->cfg->gop[state->global->gop_offset].qp_factor;
|
||||
}
|
||||
|
||||
} else {
|
||||
state->global->QP = select_picture_QP(state);
|
||||
}
|
||||
double lambda = select_picture_lambda(state);
|
||||
state->global->cur_lambda_cost = lambda;
|
||||
state->global->cur_lambda_cost_sqrt = sqrt(lambda);
|
||||
state->global->QP = lambda_to_QP(lambda);
|
||||
|
||||
} else {
|
||||
//Clear the bitstream if it's not the main encoder
|
||||
|
@ -826,9 +785,6 @@ static void encoder_state_new_frame(encoder_state_t * const state) {
|
|||
//Leaf states have cabac and context
|
||||
cabac_start(&state->cabac);
|
||||
init_contexts(state, state->global->QP, state->global->slicetype);
|
||||
|
||||
// Initialize lambda value(s) to use in search
|
||||
encoder_state_init_lambda(state);
|
||||
}
|
||||
|
||||
//Clear the jobs
|
||||
|
|
|
@ -89,6 +89,9 @@ typedef struct {
|
|||
//! Number of bits written in the current GOP.
|
||||
uint64_t cur_gop_bits_coded;
|
||||
|
||||
//! Number of bits targeted for the current GOP.
|
||||
double cur_gop_target_bits;
|
||||
|
||||
} encoder_state_config_global_t;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -25,42 +25,77 @@
|
|||
static const int SMOOTHING_WINDOW = 40;
|
||||
|
||||
/**
|
||||
* \brief Select a QP for encoding the next picture
|
||||
* \brief Allocate bits for the current GOP.
|
||||
* \param state the main encoder state
|
||||
* \return the QP for the next picture, in range [0, 51]
|
||||
*
|
||||
* If GOPs are not used, allocates bits for a single picture.
|
||||
*
|
||||
* Sets the cur_gop_target_bits of the encoder state.
|
||||
*/
|
||||
int8_t select_picture_QP(const encoder_state_t * const state)
|
||||
static void gop_allocate_bits(encoder_state_t * const state)
|
||||
{
|
||||
const encoder_control_t * const encoder = state->encoder_control;
|
||||
|
||||
const double avg_bits_per_picture =
|
||||
encoder->cfg->target_bitrate / encoder->cfg->framerate;
|
||||
|
||||
// At this point, total_bits_coded of the current state contains the
|
||||
// number of bits written encoder->owf frames before the current frame.
|
||||
int bits_coded = state->global->total_bits_coded;
|
||||
int pictures_coded = MAX(0, state->global->frame - encoder->owf);
|
||||
|
||||
int gop_offset = (state->global->gop_offset - encoder->owf) % MAX(1, encoder->cfg->gop_len);
|
||||
// Only take fully coded GOPs into account.
|
||||
if (encoder->cfg->gop_len > 0 && gop_offset != encoder->cfg->gop_len - 1) {
|
||||
// Subtract number of bits in the partially coded GOP.
|
||||
bits_coded -= state->global->cur_gop_bits_coded;
|
||||
// Subtract number of pictures in the partially coded GOP.
|
||||
pictures_coded -= gop_offset + 1;
|
||||
}
|
||||
|
||||
double gop_target_bits =
|
||||
(avg_bits_per_picture * (pictures_coded + SMOOTHING_WINDOW) - bits_coded)
|
||||
* MAX(1, encoder->cfg->gop_len) / SMOOTHING_WINDOW;
|
||||
state->global->cur_gop_target_bits = MAX(200, gop_target_bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Select a lambda value for encoding the next picture
|
||||
* \param state the main encoder state
|
||||
* \return lambda for the next picture
|
||||
*/
|
||||
double select_picture_lambda(encoder_state_t * const state)
|
||||
{
|
||||
const encoder_control_t * const encoder = state->encoder_control;
|
||||
|
||||
if (encoder->cfg->target_bitrate <= 0) {
|
||||
// Rate control disabled.
|
||||
return encoder->cfg->qp;
|
||||
return exp((encoder->cfg->qp - 13.7223 - 0.5) / 4.2005);
|
||||
}
|
||||
|
||||
// At this point, total_bits_coded of the current state contains the
|
||||
// number of bits written encoder->owf frames before the current frame.
|
||||
const int bits_coded = state->global->total_bits_coded;
|
||||
const int pictures_coded = MAX(0, state->global->frame - encoder->owf);
|
||||
if (encoder->cfg->gop_len == 0 || state->global->gop_offset == 0) {
|
||||
// a new GOP begins at this frame
|
||||
gop_allocate_bits(state);
|
||||
} else {
|
||||
state->global->cur_gop_target_bits =
|
||||
state->previous_encoder_state->global->cur_gop_target_bits;
|
||||
}
|
||||
|
||||
const double avg_bits_per_picture =
|
||||
encoder->cfg->target_bitrate / encoder->cfg->framerate;
|
||||
|
||||
// TODO: use picture weights
|
||||
const double target_bits_current_picture =
|
||||
(avg_bits_per_picture * (pictures_coded + SMOOTHING_WINDOW) - bits_coded)
|
||||
/ SMOOTHING_WINDOW;
|
||||
const double target_bits_current_picture = (encoder->cfg->gop_len > 0)
|
||||
? (state->global->cur_gop_target_bits * encoder->cfg->gop[state->global->gop_offset].weight / 22.0)
|
||||
: state->global->cur_gop_target_bits
|
||||
;
|
||||
|
||||
// TODO: take the picture headers into account
|
||||
const int pixels_per_picture = encoder->in.width * encoder->in.height;
|
||||
const double target_bits_per_pixel = target_bits_current_picture / pixels_per_picture;
|
||||
|
||||
// The following magical constants, -5.7420835 and 18.598408755005686 are
|
||||
// based on the values given in
|
||||
//
|
||||
// K. McCann et al., "High Effiency Video Coding (HEVC) Test Model 16
|
||||
// (HM 16) Improved Encoder Description", JCTVC-S1002, October 2014,
|
||||
// (p. 52 - 54)
|
||||
const int QP = (int)(-5.7420835 * log(MAX(target_bits_per_pixel, 0.001)) + 18.598408755005686);
|
||||
return CLIP(0, 51, QP);
|
||||
const double lambda = 3.2003 * pow(target_bits_per_pixel, -1.367);
|
||||
return CLIP(0.1, 10000, lambda);
|
||||
}
|
||||
|
||||
int8_t lambda_to_QP(const double lambda)
|
||||
{
|
||||
int8_t qp = 4.2005 * log(lambda) + 13.7223 + 0.5;
|
||||
return CLIP(0, 51, qp);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
|
||||
#include "encoderstate.h"
|
||||
|
||||
int8_t select_picture_QP(const encoder_state_t * const state);
|
||||
double select_picture_lambda(encoder_state_t * const state);
|
||||
|
||||
int8_t lambda_to_QP(const double lambda);
|
||||
|
||||
#endif // RATE_CONTROL_H_
|
||||
|
|
Loading…
Reference in a new issue