diff --git a/README.md b/README.md index 0d96354d..87ae5f7a 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,10 @@ Options: - intra_pred_modes: Intra prediction modes. --key : Encryption key [16,213,27,56,255,127,242,112, 97,126,197,204,25,59,38,30] - + --stats-file-prefix : A prefix used for stats files that include + bits, lambda, distortion, and qp for each ctu. + These are meant for debugging and are not + written unless the prefix is defined. Video structure: -q, --qp : Quantization parameter [22] -p, --period : Period of intra pictures [64] diff --git a/configure.ac b/configure.ac index 13b00e8e..b9e23b9b 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ AC_CONFIG_SRCDIR([src/encmain.c]) # # Here is a somewhat sane guide to lib versioning: http://apr.apache.org/versioning.html ver_major=6 -ver_minor=2 +ver_minor=3 ver_release=0 # Prevents configure from adding a lot of defines to the CFLAGS diff --git a/doc/kvazaar.1 b/doc/kvazaar.1 index 86551277..07aa3a6b 100644 --- a/doc/kvazaar.1 +++ b/doc/kvazaar.1 @@ -100,7 +100,12 @@ a list of features separated with a '+'. [off] \fB\-\-key Encryption key [16,213,27,56,255,127,242,112, 97,126,197,204,25,59,38,30] - +.TP +\fB\-\-stats\-file\-prefix +A prefix used for stats files that include +bits, lambda, distortion, and qp for each ctu. +These are meant for debugging and are not +written unless the prefix is defined. .SS "Video structure:" .TP \fB\-q\fR, \fB\-\-qp diff --git a/src/cfg.c b/src/cfg.c index 02218fa8..b4afc857 100644 --- a/src/cfg.c +++ b/src/cfg.c @@ -165,6 +165,7 @@ int kvz_config_init(kvz_config *cfg) cfg->file_format = KVZ_FORMAT_AUTO; + cfg->stats_file_prefix = NULL; return 1; } @@ -1374,6 +1375,9 @@ int kvz_config_parse(kvz_config *cfg, const char *name, const char *value) } cfg->file_format = file_format; } + else if OPT("stats-file-prefix") { + cfg->stats_file_prefix = strdup(value); + } else { return 0; } diff --git a/src/cli.c b/src/cli.c index 809bad03..29aeb261 100644 --- a/src/cli.c +++ b/src/cli.c @@ -150,6 +150,7 @@ static const struct option long_options[] = { { "clip-neighbour", no_argument, NULL, 0 }, { "no-clip-neighbour", no_argument, NULL, 0 }, { "input-file-format", required_argument, NULL, 0 }, + { "stats-file-prefix", required_argument, NULL, 0 }, {0, 0, 0, 0} }; @@ -433,6 +434,10 @@ void print_help(void) " - intra_pred_modes: Intra prediction modes.\n" " --key : Encryption key [16,213,27,56,255,127,242,112,\n" " 97,126,197,204,25,59,38,30]\n" + " --stats-file-prefix : A prefix used for stats files that include\n" + " bits, lambda, distortion, and qp for each ctu.\n" + " These are meant for debugging and are not\n" + " written unless the prefix is defined.\n" "\n" /* Word wrap to this width to stay under 80 characters (including ") *************/ "Video structure:\n" diff --git a/src/encoder_state-bitstream.c b/src/encoder_state-bitstream.c index e92746c3..79fbc017 100644 --- a/src/encoder_state-bitstream.c +++ b/src/encoder_state-bitstream.c @@ -1064,7 +1064,7 @@ static void encoder_state_write_bitstream_main(encoder_state_t * const state) state->frame->total_bits_coded = state->previous_encoder_state->frame->total_bits_coded; } state->frame->total_bits_coded += newpos - curpos; - if(state->encoder_control->cfg.rc_algorithm == KVZ_OBA) { + if(state->encoder_control->cfg.rc_algorithm == KVZ_OBA || state->encoder_control->cfg.stats_file_prefix) { kvz_update_after_picture(state); } diff --git a/src/encoderstate.h b/src/encoderstate.h index c9e7f209..d34e2bfd 100644 --- a/src/encoderstate.h +++ b/src/encoderstate.h @@ -64,6 +64,8 @@ typedef struct lcu_stats_t { //! \brief Lambda value which was used for this LCU double lambda; + double adjust_lambda; + //! \brief Rate control alpha parameter double rc_alpha; @@ -73,6 +75,7 @@ typedef struct lcu_stats_t { int i_cost; int8_t qp; + int8_t adjust_qp; uint8_t skipped; } lcu_stats_t; diff --git a/src/kvazaar.h b/src/kvazaar.h index 507cea35..0b3c2e1c 100644 --- a/src/kvazaar.h +++ b/src/kvazaar.h @@ -448,6 +448,8 @@ typedef struct kvz_config uint8_t clip_neighbour; enum kvz_file_format file_format; + + char *stats_file_prefix; } kvz_config; /** diff --git a/src/rate_control.c b/src/rate_control.c index 27dd6bd0..96c63922 100644 --- a/src/rate_control.c +++ b/src/rate_control.c @@ -35,6 +35,11 @@ static const double MAX_LAMBDA = 10000; static kvz_rc_data *data; +static FILE *dist_file; +static FILE *bits_file; +static FILE *qp_file; +static FILE *lambda_file; + /** * \brief Clip lambda value to a valid range. */ @@ -87,6 +92,17 @@ kvz_rc_data * kvz_get_rc_data(const encoder_control_t * const encoder) { data->intra_alpha = 6.7542000000000000; data->intra_beta = 1.7860000000000000; + if(encoder->cfg.stats_file_prefix) { + char buff[128]; + sprintf(buff, "%sbits.txt", encoder->cfg.stats_file_prefix); + bits_file = fopen(buff, "w"); + sprintf(buff, "%sdist.txt", encoder->cfg.stats_file_prefix); + dist_file = fopen(buff, "w"); + sprintf(buff, "%sqp.txt", encoder->cfg.stats_file_prefix); + qp_file = fopen(buff, "w"); + sprintf(buff, "%slambda.txt", encoder->cfg.stats_file_prefix); + lambda_file = fopen(buff, "w"); + } return data; } @@ -791,6 +807,8 @@ static double qp_to_lambda(encoder_state_t* const state, int qp) state->lambda = qp_to_lambda(state, state->qp); state->lambda_sqrt = sqrt(state->lambda); + ctu->adjust_lambda = state->lambda; + ctu->adjust_qp = state->qp; //ctu->qp = state->qp; //ctu->lambda = state->lambda; } @@ -854,6 +872,22 @@ static void update_ck(encoder_state_t * const state, int ctu_index, int layer) } +static int calc_poc(encoder_state_t * const state) { + const encoder_control_t * const encoder = state->encoder_control; + if((encoder->cfg.open_gop && !encoder->cfg.gop_lowdelay) || !encoder->cfg.intra_period) { + return state->frame->poc; + } + if(!encoder->cfg.gop_len || encoder->cfg.open_gop || encoder->cfg.intra_period == 1 || encoder->cfg.gop_lowdelay) { + return state->frame->poc + state->frame->num / encoder->cfg.intra_period * encoder->cfg.intra_period; + } + if (!encoder->cfg.gop_lowdelay && !encoder->cfg.open_gop) { + return state->frame->poc + state->frame->num / (encoder->cfg.intra_period + 1) * (encoder->cfg.intra_period + 1); + } + assert(0); + return -1; +} + + void kvz_update_after_picture(encoder_state_t * const state) { double total_distortion = 0; double lambda = 0; @@ -875,6 +909,14 @@ void kvz_update_after_picture(encoder_state_t * const state) { pthread_mutex_unlock(&state->frame->new_ratecontrol->intra_lock); } + if (encoder->cfg.stats_file_prefix) { + int poc = calc_poc(state); + fprintf(dist_file, "%d %d %d\n", poc, encoder->in.width_in_lcu, encoder->in.height_in_lcu); + fprintf(bits_file, "%d %d %d\n", poc, encoder->in.width_in_lcu, encoder->in.height_in_lcu); + fprintf(qp_file, "%d %d %d\n", poc, encoder->in.width_in_lcu, encoder->in.height_in_lcu); + fprintf(lambda_file, "%d %d %d\n", poc, encoder->in.width_in_lcu, encoder->in.height_in_lcu); + } + 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++) { int ctu_distortion = 0; @@ -889,9 +931,23 @@ void kvz_update_after_picture(encoder_state_t * const state) { ctu->distortion = (double)ctu_distortion / ctu->pixels; total_distortion += (double)ctu_distortion / ctu->pixels; lambda += ctu->lambda / (state->encoder_control->in.width_in_lcu * state->encoder_control->in.height_in_lcu); - } + if(encoder->cfg.stats_file_prefix) { + fprintf(dist_file, "%f ", ctu->distortion); + fprintf(bits_file, "%d ", ctu->bits); + fprintf(qp_file, "%d ", ctu->adjust_qp ? ctu->adjust_qp : ctu->qp); + fprintf(lambda_file, "%f ", ctu->adjust_lambda ? ctu->adjust_lambda : ctu->lambda); + } + } + if (encoder->cfg.stats_file_prefix) { + fprintf(dist_file, "\n"); + fprintf(bits_file, "\n"); + fprintf(qp_file, "\n"); + fprintf(lambda_file, "\n"); + } } + if(encoder->cfg.stats_file_prefix && encoder->cfg.rc_algorithm != KVZ_OBA) return; + total_distortion /= (state->encoder_control->in.height_in_lcu * state->encoder_control->in.width_in_lcu); if (state->frame->is_irap) { pthread_mutex_lock(&state->frame->new_ratecontrol->intra_lock); @@ -1014,6 +1070,7 @@ void kvz_set_lcu_lambda_and_qp(encoder_state_t * const state, vector2d_t pos) { const encoder_control_t * const ctrl = state->encoder_control; + lcu_stats_t *lcu = kvz_get_lcu_stats(state, pos.x, pos.y); if (ctrl->cfg.roi.dqps != NULL) { vector2d_t lcu = { @@ -1032,7 +1089,6 @@ void kvz_set_lcu_lambda_and_qp(encoder_state_t * const state, } else if (ctrl->cfg.target_bitrate > 0) { - lcu_stats_t *lcu = kvz_get_lcu_stats(state, pos.x, pos.y); const uint32_t pixels = MIN(LCU_WIDTH, state->tile->frame->width - LCU_WIDTH * pos.x) * MIN(LCU_WIDTH, state->tile->frame->height - LCU_WIDTH * pos.y); @@ -1065,7 +1121,6 @@ void kvz_set_lcu_lambda_and_qp(encoder_state_t * const state, lambda); lambda = clip_lambda(lambda); - lcu->lambda = lambda; state->lambda = lambda; state->lambda_sqrt = sqrt(lambda); state->qp = lambda_to_qp(lambda); @@ -1076,13 +1131,16 @@ void kvz_set_lcu_lambda_and_qp(encoder_state_t * const state, state->lambda_sqrt = sqrt(state->frame->lambda); } + lcu->lambda = state->lambda; + lcu->qp = state->qp; + // Apply variance adaptive quantization if (ctrl->cfg.vaq) { - vector2d_t lcu = { + vector2d_t lcu_pos = { pos.x + state->tile->lcu_offset_x, pos.y + state->tile->lcu_offset_y }; - int id = lcu.x + lcu.y * state->tile->frame->width_in_lcu; + int id = lcu_pos.x + lcu_pos.y * state->tile->frame->width_in_lcu; int aq_offset = round(state->frame->aq_offsets[id]); state->qp += aq_offset; // Maximum delta QP is clipped between [-26, 25] according to ITU T-REC-H.265 specification chapter 7.4.9.10 Transform unit semantics @@ -1091,5 +1149,8 @@ void kvz_set_lcu_lambda_and_qp(encoder_state_t * const state, state->qp = CLIP_TO_QP(state->qp); state->lambda = qp_to_lambda(state, state->qp); state->lambda_sqrt = sqrt(state->lambda); + + lcu->adjust_lambda = state->lambda; + lcu->adjust_qp = state->qp; } }