Command line parameters for oba rc and implementation of the usage of the intra parameter

This commit is contained in:
Joose Sainio 2019-11-18 14:20:43 +02:00
parent eb73548af5
commit 6cc3bcd87e
7 changed files with 118 additions and 36 deletions

View file

@ -141,6 +141,8 @@ int kvz_config_init(kvz_config *cfg)
cfg->max_merge = 5; cfg->max_merge = 5;
cfg->early_skip = true; cfg->early_skip = true;
cfg->rc_algorithm = KVZ_NO_RC;
cfg->intra_bit_allocation = true;
return 1; return 1;
} }
@ -386,6 +388,8 @@ int kvz_config_parse(kvz_config *cfg, const char *name, const char *value)
static const char * const scaling_list_names[] = { "off", "custom", "default", NULL }; static const char * const scaling_list_names[] = { "off", "custom", "default", NULL };
static const char * const rc_algorithm_names[] = { "no-rc", "lambda", "oba", NULL };
static const char * const preset_values[11][25*2] = { static const char * const preset_values[11][25*2] = {
{ {
"ultrafast", "ultrafast",
@ -928,6 +932,8 @@ int kvz_config_parse(kvz_config *cfg, const char *name, const char *value)
cfg->gop_len = gop.g; cfg->gop_len = gop.g;
cfg->gop_lp_definition.d = gop.d; cfg->gop_lp_definition.d = gop.d;
cfg->gop_lp_definition.t = gop.t; cfg->gop_lp_definition.t = gop.t;
cfg->intra_bit_allocation = false;
} else if (atoi(value) == 8) { } else if (atoi(value) == 8) {
cfg->gop_lowdelay = 0; cfg->gop_lowdelay = 0;
// GOP // GOP
@ -979,8 +985,12 @@ int kvz_config_parse(kvz_config *cfg, const char *name, const char *value)
} }
else if OPT("bipred") else if OPT("bipred")
cfg->bipred = atobool(value); cfg->bipred = atobool(value);
else if OPT("bitrate") else if OPT("bitrate") {
cfg->target_bitrate = atoi(value); cfg->target_bitrate = atoi(value);
if (!cfg->rc_algorithm) {
cfg->rc_algorithm = KVZ_LAMBDA;
}
}
else if OPT("preset") { else if OPT("preset") {
int preset_line = 0; int preset_line = 0;
@ -1260,6 +1270,20 @@ int kvz_config_parse(kvz_config *cfg, const char *name, const char *value)
else if OPT("early-skip") { else if OPT("early-skip") {
cfg->early_skip = (bool)atobool(value); cfg->early_skip = (bool)atobool(value);
} }
else if OPT("rc-algorithm") {
int8_t rc_algorithm = 0;
if (!parse_enum(value, rc_algorithm_names, &rc_algorithm)) {
fprintf(stderr, "Invalid rate control algorithm %s. Valid values include %s, %s, and %s\n", value,
rc_algorithm_names[0],
rc_algorithm_names[1],
rc_algorithm_names[2]);
return 0;
}
cfg->rc_algorithm = rc_algorithm;
}
else if OPT("intra-bits") {
cfg->intra_bit_allocation = atobool(value);
}
else { else {
return 0; return 0;
} }
@ -1572,6 +1596,11 @@ int kvz_config_validate(const kvz_config *const cfg)
error = 1; error = 1;
} }
if(cfg->target_bitrate > 0 && cfg->rc_algorithm == 0) {
fprintf(stderr, "Bitrate set but rc-algorithm is turned off.\n");
error = 1;
}
return !error; return !error;
} }

View file

@ -137,6 +137,9 @@ static const struct option long_options[] = {
{ "max-merge", required_argument, NULL, 0 }, { "max-merge", required_argument, NULL, 0 },
{ "early-skip", no_argument, NULL, 0 }, { "early-skip", no_argument, NULL, 0 },
{ "no-early-skip", no_argument, NULL, 0 }, { "no-early-skip", no_argument, NULL, 0 },
{ "rc-algorithm", required_argument, NULL, 0 },
{ "intra-bits", no_argument, NULL, 0 },
{ "no-intra-bits", no_argument, NULL, 0 },
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
@ -410,6 +413,12 @@ void print_help(void)
" --bitrate <integer> : Target bitrate [0]\n" " --bitrate <integer> : Target bitrate [0]\n"
" - 0: Disable rate control.\n" " - 0: Disable rate control.\n"
" - N: Target N bits per second.\n" " - N: Target N bits per second.\n"
" --rc-algorithm <string>: Select used rc-algorithm. [lambda]\n"
" - lambda: rate control from:\n"
" DOI: 10.1109/TIP.2014.2336550 \n"
" - oba: DOI: 10.1109/TCSVT.2016.2589878\n"
" --intra-bits : Use Hadamard cost based allocation for intra\n"
" frames. Default on for gop 8 and off for lp-gop\n"
" --(no-)lossless : Use lossless coding. [disabled]\n" " --(no-)lossless : Use lossless coding. [disabled]\n"
" --mv-constraint <string> : Constrain movement vectors. [none]\n" " --mv-constraint <string> : Constrain movement vectors. [none]\n"
" - none: No constraint\n" " - none: No constraint\n"

View file

@ -51,8 +51,9 @@ static int encoder_state_config_frame_init(encoder_state_t * const state) {
state->frame->cur_gop_bits_coded = 0; state->frame->cur_gop_bits_coded = 0;
state->frame->prepared = 0; state->frame->prepared = 0;
state->frame->done = 1; state->frame->done = 1;
state->frame->rc_alpha = 6.7542000000000000;
state->frame->rc_beta = 1.7860000000000000; state->frame->rc_alpha = 3.2003;
state->frame->rc_beta = -1.367;
const encoder_control_t * const encoder = state->encoder_control; const encoder_control_t * const encoder = state->encoder_control;
const int num_lcus = encoder->in.width_in_lcu * encoder->in.height_in_lcu; const int num_lcus = encoder->in.width_in_lcu * encoder->in.height_in_lcu;

View file

@ -253,8 +253,10 @@ static int kvazaar_encode(kvz_encoder *enc,
CHECKPOINT_MARK("read source frame: %d", state->frame->num + enc->control->cfg.seek); CHECKPOINT_MARK("read source frame: %d", state->frame->num + enc->control->cfg.seek);
} }
// TODO: only when the OBA rc is used kvz_picture* frame = kvz_encoder_feed_frame(
kvz_picture* frame = kvz_encoder_feed_frame(&enc->input_buffer, state, pic_in, enc->frames_done); &enc->input_buffer, state, pic_in,
enc->frames_done || state->encoder_control->cfg.rc_algorithm != KVZ_OBA
);
if (frame) { if (frame) {
assert(state->frame->num == enc->frames_started); assert(state->frame->num == enc->frames_started);
// Start encoding. // Start encoding.
@ -272,11 +274,10 @@ static int kvazaar_encode(kvz_encoder *enc,
enc->cur_state_num = (enc->cur_state_num + 1) % (enc->num_encoder_states); enc->cur_state_num = (enc->cur_state_num + 1) % (enc->num_encoder_states);
} }
// TODO: OBA check
encoder_state_t *output_state = &enc->states[enc->out_state_num]; encoder_state_t *output_state = &enc->states[enc->out_state_num];
if ((!output_state->frame->done && if ((!output_state->frame->done &&
(pic_in == NULL || enc->cur_state_num == enc->out_state_num)) (pic_in == NULL || enc->cur_state_num == enc->out_state_num)) ||
|| state->frame->num == 0) { (state->frame->num == 0 && state->encoder_control->cfg.rc_algorithm == KVZ_OBA)) {
kvz_threadqueue_waitfor(enc->control->threadqueue, output_state->tqj_bitstream_written); kvz_threadqueue_waitfor(enc->control->threadqueue, output_state->tqj_bitstream_written);
// The job pointer must be set to NULL here since it won't be usable after // The job pointer must be set to NULL here since it won't be usable after

View file

@ -218,6 +218,12 @@ enum kvz_scalinglist {
KVZ_SCALING_LIST_DEFAULT = 2, KVZ_SCALING_LIST_DEFAULT = 2,
}; };
enum kvz_rc_algorithm
{
KVZ_NO_RC = 0,
KVZ_LAMBDA = 1,
KVZ_OBA = 2,
};
// Map from input format to chroma format. // Map from input format to chroma format.
#define KVZ_FORMAT2CSP(format) ((enum kvz_chroma_format)"\0\1\2\3"[format]) #define KVZ_FORMAT2CSP(format) ((enum kvz_chroma_format)"\0\1\2\3"[format])
@ -394,7 +400,15 @@ typedef struct kvz_config
/** \brief Enable Early Skip Mode Decision */ /** \brief Enable Early Skip Mode Decision */
uint8_t early_skip; uint8_t early_skip;
/** \brief Currently unused parameter for OBA rc */
int8_t frame_allocation; int8_t frame_allocation;
/** \brief used rc scheme, 0 for QP */
int8_t rc_algorithm;
/** \brief whether to use hadamard based bit allocation for intra frames or not */
uint8_t intra_bit_allocation;
} kvz_config; } kvz_config;
/** /**

View file

@ -68,6 +68,8 @@ kvz_rc_data * kvz_get_rc_data(const encoder_control_t * const encoder) {
data->intra_pic_bpp = 0.0; data->intra_pic_bpp = 0.0;
data->intra_pic_distortion = 0.0; data->intra_pic_distortion = 0.0;
data->intra_alpha = 6.7542000000000000;
data->intra_beta = 1.7860000000000000;
return data; return data;
} }
@ -292,10 +294,15 @@ static double pic_allocate_bits(encoder_state_t * const state)
state->frame->cur_gop_target_bits = state->frame->cur_gop_target_bits =
state->previous_encoder_state->frame->cur_gop_target_bits; state->previous_encoder_state->frame->cur_gop_target_bits;
} }
if(state->frame->is_irap) {
if (encoder->cfg.gop_len <= 0) {
return state->frame->cur_gop_target_bits;
}
if (state->frame->is_irap && encoder->cfg.intra_bit_allocation) {
double bits = state->frame->cur_gop_target_bits / MAX(encoder->cfg.gop_len, 1); double bits = state->frame->cur_gop_target_bits / MAX(encoder->cfg.gop_len, 1);
double alpha, beta = 0.5582; double alpha, beta = 0.5582;
if (bits*40 < encoder->cfg.width * encoder->cfg.height) { if (bits * 40 < encoder->cfg.width * encoder->cfg.height) {
alpha = 0.25; alpha = 0.25;
} }
else { else {
@ -304,10 +311,6 @@ static double pic_allocate_bits(encoder_state_t * const state)
return MAX(100, alpha*pow(state->frame->icost * 4 / bits, beta)*bits); return MAX(100, alpha*pow(state->frame->icost * 4 / bits, beta)*bits);
} }
if (encoder->cfg.gop_len <= 0) {
return state->frame->cur_gop_target_bits;
}
const double pic_weight = encoder->gop_layer_weights[ const double pic_weight = encoder->gop_layer_weights[
encoder->cfg.gop[state->frame->gop_offset].layer - 1]; encoder->cfg.gop[state->frame->gop_offset].layer - 1];
const double pic_target_bits = const double pic_target_bits =
@ -418,7 +421,8 @@ static INLINE double calculate_weights(encoder_state_t* const state, const int l
void kvz_estimate_pic_lambda(encoder_state_t * const state) { void kvz_estimate_pic_lambda(encoder_state_t * const state) {
const encoder_control_t * const encoder = state->encoder_control; const encoder_control_t * const encoder = state->encoder_control;
if(state->frame->is_irap) {
if(encoder->cfg.intra_bit_allocation && state->frame->is_irap) {
int total_cost = 0; int total_cost = 0;
for (int y = 0; y < encoder->cfg.height; y += 8) { for (int y = 0; y < encoder->cfg.height; y += 8) {
for (int x = 0; x < encoder->cfg.width; x += 8) { for (int x = 0; x < encoder->cfg.width; x += 8) {
@ -436,7 +440,11 @@ void kvz_estimate_pic_lambda(encoder_state_t * const state) {
double alpha; double alpha;
double beta; double beta;
if(state->frame->poc == 0) { if(state->frame->is_irap && encoder->cfg.intra_bit_allocation) {
alpha = state->frame->new_ratecontrol->intra_alpha;
beta = state->frame->new_ratecontrol->intra_beta;
}
else if(state->frame->poc == 0) {
alpha = state->frame->rc_alpha; alpha = state->frame->rc_alpha;
beta = state->frame->rc_beta; beta = state->frame->rc_beta;
} }
@ -455,9 +463,15 @@ void kvz_estimate_pic_lambda(encoder_state_t * const state) {
int32_t num_pixels = state->encoder_control->cfg.width * state->encoder_control->cfg.height; int32_t num_pixels = state->encoder_control->cfg.width * state->encoder_control->cfg.height;
double bpp = bits / num_pixels; double bpp = bits / num_pixels;
if (state->frame->is_irap) { if (state->frame->is_irap) {
state->frame->i_bits_left = bits; if(encoder->cfg.intra_bit_allocation) {
double temp = pow(state->frame->icost / num_pixels, BETA1); state->frame->i_bits_left = bits;
est_lambda = alpha / 256 * pow(temp/bpp, beta); double temp = pow(state->frame->icost / num_pixels, BETA1);
est_lambda = alpha / 256 * pow(temp/bpp, beta);
}
else {
// arbitrary reduction to the lambda for intra frames
est_lambda = alpha * pow(bpp, beta) * 0.5;
}
} }
else { else {
est_lambda = alpha * pow(bpp, beta); est_lambda = alpha * pow(bpp, beta);
@ -503,6 +517,7 @@ void kvz_estimate_pic_lambda(encoder_state_t * const state) {
} }
else { else {
for (int i = 0; i < ctu_count; ++i) { for (int i = 0; i < ctu_count; ++i) {
// TODO: This might be incorrect
state->frame->lcu_stats[i].weight = MAX(0.01, state->frame->lcu_stats[i].weight = MAX(0.01,
state->frame->lcu_stats[i].pixels * pow(est_lambda / state->frame->rc_alpha, state->frame->lcu_stats[i].pixels * pow(est_lambda / state->frame->rc_alpha,
1.0 / state->frame->rc_beta)); 1.0 / state->frame->rc_beta));
@ -529,17 +544,23 @@ static double get_ctu_bits(encoder_state_t * const state, vector2d_t pos) {
const int index = pos.x + pos.y * state->tile->frame->width_in_lcu; const int index = pos.x + pos.y * state->tile->frame->width_in_lcu;
if (state->frame->is_irap) { if (state->frame->is_irap) {
int cus_left = num_ctu - index + 1; if(encoder->cfg.intra_bit_allocation) {
int window = MIN(4, cus_left); int cus_left = num_ctu - index + 1;
double mad = kvz_get_lcu_stats(state, pos.x, pos.y)->i_cost; int window = MIN(4, cus_left);
double mad = kvz_get_lcu_stats(state, pos.x, pos.y)->i_cost;
pthread_mutex_lock(&state->frame->rc_lock); pthread_mutex_lock(&state->frame->rc_lock);
double bits_left = state->frame->cur_pic_target_bits - state->frame->cur_frame_bits_coded; double bits_left = state->frame->cur_pic_target_bits - state->frame->cur_frame_bits_coded;
double weighted_bits_left = (bits_left * window + (bits_left - state->frame->i_bits_left)*cus_left) / window; double weighted_bits_left = (bits_left * window + (bits_left - state->frame->i_bits_left)*cus_left) / window;
avg_bits = mad * weighted_bits_left / state->frame->remaining_weight; avg_bits = mad * weighted_bits_left / state->frame->remaining_weight;
state->frame->remaining_weight -= mad; state->frame->remaining_weight -= mad;
state->frame->i_bits_left -= state->frame->cur_pic_target_bits * mad / state->frame->icost; state->frame->i_bits_left -= state->frame->cur_pic_target_bits * mad / state->frame->icost;
pthread_mutex_unlock(&state->frame->rc_lock); pthread_mutex_unlock(&state->frame->rc_lock);
}
else {
avg_bits = state->frame->cur_pic_target_bits * ((double)state->frame->lcu_stats[index].pixels /
(state->encoder_control->in.height * state->encoder_control->in.width));
}
} }
else { else {
double total_weight = 0; double total_weight = 0;
@ -608,7 +629,11 @@ void kvz_set_ctu_qp_lambda(encoder_state_t * const state, vector2d_t pos) {
double alpha; double alpha;
double beta; double beta;
if (state->frame->is_irap) { if (state->frame->is_irap && encoder->cfg.intra_bit_allocation) {
alpha = state->frame->new_ratecontrol->intra_alpha;
beta = state->frame->new_ratecontrol->intra_beta;
}
else if(state->frame->num == 0) {
alpha = state->frame->rc_alpha; alpha = state->frame->rc_alpha;
beta = state->frame->rc_beta; beta = state->frame->rc_beta;
} }
@ -620,7 +645,7 @@ void kvz_set_ctu_qp_lambda(encoder_state_t * const state, vector2d_t pos) {
double est_lambda; double est_lambda;
int est_qp; int est_qp;
if (state->frame->is_irap) { if (state->frame->is_irap && encoder->cfg.intra_bit_allocation) {
double cost_per_pixel = (double)ctu->i_cost / ctu->pixels; double cost_per_pixel = (double)ctu->i_cost / ctu->pixels;
cost_per_pixel = pow(cost_per_pixel, BETA1); cost_per_pixel = pow(cost_per_pixel, BETA1);
est_lambda = alpha / 256.0 * pow(cost_per_pixel / bpp, beta); est_lambda = alpha / 256.0 * pow(cost_per_pixel / bpp, beta);
@ -777,14 +802,14 @@ void kvz_update_after_picture(encoder_state_t * const state) {
const encoder_control_t * const encoder = state->encoder_control; const encoder_control_t * const encoder = state->encoder_control;
const int layer = encoder->cfg.gop[state->frame->gop_offset].layer - (state->frame->is_irap ? 1 : 0); const int layer = encoder->cfg.gop[state->frame->gop_offset].layer - (state->frame->is_irap ? 1 : 0);
if (state->frame->is_irap) { if (state->frame->is_irap && encoder->cfg.intra_bit_allocation) {
double lnbpp = log(pow(state->frame->icost / pixels, BETA1)); double lnbpp = log(pow(state->frame->icost / pixels, BETA1));
double diff_lambda = state->frame->rc_beta * log(state->frame->cur_frame_bits_coded) - log(state->frame->cur_pic_target_bits); double diff_lambda = state->frame->new_ratecontrol->intra_beta * log(state->frame->cur_frame_bits_coded) - log(state->frame->cur_pic_target_bits);
diff_lambda = CLIP(-0.125, 0.125, 0.25*diff_lambda); diff_lambda = CLIP(-0.125, 0.125, 0.25*diff_lambda);
state->frame->rc_alpha *= exp(diff_lambda); state->frame->new_ratecontrol->intra_alpha *= exp(diff_lambda);
state->frame->rc_beta += diff_lambda / lnbpp; state->frame->new_ratecontrol->intra_beta += diff_lambda / lnbpp;
} }
for(int y_ctu = 0; y_ctu < state->encoder_control->in.height_in_lcu; y_ctu++) { for(int y_ctu = 0; y_ctu < state->encoder_control->in.height_in_lcu; y_ctu++) {
@ -804,7 +829,7 @@ void kvz_update_after_picture(encoder_state_t * const state) {
} }
} }
total_distortion /= (state->encoder_control->in.height_in_lcu * state->encoder_control->in.width_in_lcu); total_distortion /= (state->encoder_control->in.height_in_lcu * state->encoder_control->in.width_in_lcu);
if (state->frame->is_irap) { if (state->frame->is_irap && encoder->cfg.intra_bit_allocation) {
for (int y_ctu = 0; y_ctu < state->encoder_control->in.height_in_lcu; y_ctu++) { for (int y_ctu = 0; y_ctu < state->encoder_control->in.height_in_lcu; y_ctu++) {
for (int x_ctu = 0; x_ctu < state->encoder_control->in.width_in_lcu; x_ctu++) { for (int x_ctu = 0; x_ctu < state->encoder_control->in.width_in_lcu; x_ctu++) {
lcu_stats_t *ctu = kvz_get_lcu_stats(state, x_ctu, y_ctu); lcu_stats_t *ctu = kvz_get_lcu_stats(state, x_ctu, y_ctu);

View file

@ -43,6 +43,9 @@ typedef struct kvz_rc_data {
double intra_pic_distortion; double intra_pic_distortion;
double intra_pic_bpp; double intra_pic_bpp;
double intra_alpha;
double intra_beta;
pthread_mutex_t ck_frame_lock; pthread_mutex_t ck_frame_lock;
} kvz_rc_data; } kvz_rc_data;