From 6cc3bcd87e1a86115ad0898785b695efa6f34cf2 Mon Sep 17 00:00:00 2001 From: Joose Sainio Date: Mon, 18 Nov 2019 14:20:43 +0200 Subject: [PATCH] Command line parameters for oba rc and implementation of the usage of the intra parameter --- src/cfg.c | 31 ++++++++++++- src/cli.c | 9 ++++ src/encoder_state-ctors_dtors.c | 5 +- src/kvazaar.c | 11 +++-- src/kvazaar.h | 14 ++++++ src/rate_control.c | 81 +++++++++++++++++++++------------ src/rate_control.h | 3 ++ 7 files changed, 118 insertions(+), 36 deletions(-) diff --git a/src/cfg.c b/src/cfg.c index 950a0af1..106fc261 100644 --- a/src/cfg.c +++ b/src/cfg.c @@ -141,6 +141,8 @@ int kvz_config_init(kvz_config *cfg) cfg->max_merge = 5; cfg->early_skip = true; + cfg->rc_algorithm = KVZ_NO_RC; + cfg->intra_bit_allocation = true; 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 rc_algorithm_names[] = { "no-rc", "lambda", "oba", NULL }; + static const char * const preset_values[11][25*2] = { { "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_lp_definition.d = gop.d; cfg->gop_lp_definition.t = gop.t; + + cfg->intra_bit_allocation = false; } else if (atoi(value) == 8) { cfg->gop_lowdelay = 0; // GOP @@ -979,8 +985,12 @@ int kvz_config_parse(kvz_config *cfg, const char *name, const char *value) } else if OPT("bipred") cfg->bipred = atobool(value); - else if OPT("bitrate") + else if OPT("bitrate") { cfg->target_bitrate = atoi(value); + if (!cfg->rc_algorithm) { + cfg->rc_algorithm = KVZ_LAMBDA; + } + } else if OPT("preset") { 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") { 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 { return 0; } @@ -1572,6 +1596,11 @@ int kvz_config_validate(const kvz_config *const cfg) 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; } diff --git a/src/cli.c b/src/cli.c index ebb68729..668fd236 100644 --- a/src/cli.c +++ b/src/cli.c @@ -137,6 +137,9 @@ static const struct option long_options[] = { { "max-merge", required_argument, NULL, 0 }, { "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} }; @@ -410,6 +413,12 @@ void print_help(void) " --bitrate : Target bitrate [0]\n" " - 0: Disable rate control.\n" " - N: Target N bits per second.\n" + " --rc-algorithm : 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" " --mv-constraint : Constrain movement vectors. [none]\n" " - none: No constraint\n" diff --git a/src/encoder_state-ctors_dtors.c b/src/encoder_state-ctors_dtors.c index 84c15940..a5f424d7 100644 --- a/src/encoder_state-ctors_dtors.c +++ b/src/encoder_state-ctors_dtors.c @@ -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->prepared = 0; 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 int num_lcus = encoder->in.width_in_lcu * encoder->in.height_in_lcu; diff --git a/src/kvazaar.c b/src/kvazaar.c index 194e5d1e..9b0d782e 100644 --- a/src/kvazaar.c +++ b/src/kvazaar.c @@ -253,8 +253,10 @@ static int kvazaar_encode(kvz_encoder *enc, 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(&enc->input_buffer, state, pic_in, enc->frames_done); + kvz_picture* frame = kvz_encoder_feed_frame( + &enc->input_buffer, state, pic_in, + enc->frames_done || state->encoder_control->cfg.rc_algorithm != KVZ_OBA + ); if (frame) { assert(state->frame->num == enc->frames_started); // 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); } - // TODO: OBA check encoder_state_t *output_state = &enc->states[enc->out_state_num]; if ((!output_state->frame->done && - (pic_in == NULL || enc->cur_state_num == enc->out_state_num)) - || state->frame->num == 0) { + (pic_in == NULL || enc->cur_state_num == enc->out_state_num)) || + (state->frame->num == 0 && state->encoder_control->cfg.rc_algorithm == KVZ_OBA)) { 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 diff --git a/src/kvazaar.h b/src/kvazaar.h index 1ed47168..f9f24802 100644 --- a/src/kvazaar.h +++ b/src/kvazaar.h @@ -218,6 +218,12 @@ enum kvz_scalinglist { 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. #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 */ uint8_t early_skip; + + /** \brief Currently unused parameter for OBA rc */ 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; /** diff --git a/src/rate_control.c b/src/rate_control.c index 818f72a8..cd19094d 100644 --- a/src/rate_control.c +++ b/src/rate_control.c @@ -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_distortion = 0.0; + data->intra_alpha = 6.7542000000000000; + data->intra_beta = 1.7860000000000000; return data; } @@ -292,10 +294,15 @@ static double pic_allocate_bits(encoder_state_t * const state) 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 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; } 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); } - if (encoder->cfg.gop_len <= 0) { - return state->frame->cur_gop_target_bits; - } - const double pic_weight = encoder->gop_layer_weights[ encoder->cfg.gop[state->frame->gop_offset].layer - 1]; 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) { 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; for (int y = 0; y < encoder->cfg.height; y += 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 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; 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; double bpp = bits / num_pixels; if (state->frame->is_irap) { - state->frame->i_bits_left = bits; - double temp = pow(state->frame->icost / num_pixels, BETA1); - est_lambda = alpha / 256 * pow(temp/bpp, beta); + if(encoder->cfg.intra_bit_allocation) { + state->frame->i_bits_left = bits; + 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 { est_lambda = alpha * pow(bpp, beta); @@ -503,6 +517,7 @@ void kvz_estimate_pic_lambda(encoder_state_t * const state) { } else { 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].pixels * pow(est_lambda / state->frame->rc_alpha, 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; if (state->frame->is_irap) { - int cus_left = num_ctu - index + 1; - int window = MIN(4, cus_left); - double mad = kvz_get_lcu_stats(state, pos.x, pos.y)->i_cost; + if(encoder->cfg.intra_bit_allocation) { + int cus_left = num_ctu - index + 1; + 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); - 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; - avg_bits = mad * weighted_bits_left / state->frame->remaining_weight; - state->frame->remaining_weight -= mad; - state->frame->i_bits_left -= state->frame->cur_pic_target_bits * mad / state->frame->icost; - pthread_mutex_unlock(&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 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; + state->frame->remaining_weight -= mad; + state->frame->i_bits_left -= state->frame->cur_pic_target_bits * mad / state->frame->icost; + 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 { 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 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; 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; 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; cost_per_pixel = pow(cost_per_pixel, BETA1); 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 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 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); - state->frame->rc_alpha *= exp(diff_lambda); - state->frame->rc_beta += diff_lambda / lnbpp; + state->frame->new_ratecontrol->intra_alpha *= exp(diff_lambda); + 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++) { @@ -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); - 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 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); diff --git a/src/rate_control.h b/src/rate_control.h index 213e7503..176f823a 100644 --- a/src/rate_control.h +++ b/src/rate_control.h @@ -43,6 +43,9 @@ typedef struct kvz_rc_data { double intra_pic_distortion; double intra_pic_bpp; + double intra_alpha; + double intra_beta; + pthread_mutex_t ck_frame_lock; } kvz_rc_data;