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:
Arttu Ylä-Outinen 2015-05-04 12:20:41 +03:00
parent 664de9ade0
commit 93d2a95ddc
7 changed files with 77 additions and 73 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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