Commit e378a89b authored by Deb Mukherjee's avatar Deb Mukherjee

Support a constant quality mode in VP9

Adds a new end-usage option for constant quality encoding in vpx. This
first version implemented for VP9, encodes all regular inter frames
using the quality specified in the --cq-level= option, while encoding
all key frames and golden/altref frames at a quality better than that.

The current performance on derfraw300 is +0.910% up from bitrate control,
but achieved without multiple recode loops per frame.

The decision for qp for each altref/golden/key frame will be improved
in subsequent patches based on better use of stats from the first pass.
Further, the qp for regular inter frames may also be varied around the
provided cq-level.

Change-Id: I6c4a2a68563679d60e0616ebcb11698578615fb3
parent e4e86458
......@@ -41,7 +41,8 @@ extern "C"
{
USAGE_STREAM_FROM_SERVER = 0x0,
USAGE_LOCAL_FILE_PLAYBACK = 0x1,
USAGE_CONSTRAINED_QUALITY = 0x2
USAGE_CONSTRAINED_QUALITY = 0x2,
USAGE_CONSTANT_QUALITY = 0x3
} END_USAGE;
......
......@@ -153,7 +153,7 @@ static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx,
#else
RANGE_CHECK_HI(cfg, g_lag_in_frames, 25);
#endif
RANGE_CHECK(cfg, rc_end_usage, VPX_VBR, VPX_CQ);
RANGE_CHECK(cfg, rc_end_usage, VPX_VBR, VPX_Q);
RANGE_CHECK_HI(cfg, rc_undershoot_pct, 1000);
RANGE_CHECK_HI(cfg, rc_overshoot_pct, 1000);
RANGE_CHECK_HI(cfg, rc_2pass_vbr_bias_pct, 100);
......@@ -204,7 +204,7 @@ static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx,
RANGE_CHECK_HI(vp8_cfg, arnr_strength, 6);
RANGE_CHECK(vp8_cfg, arnr_type, 1, 3);
RANGE_CHECK(vp8_cfg, cq_level, 0, 63);
if(finalize && cfg->rc_end_usage == VPX_CQ)
if (finalize && (cfg->rc_end_usage == VPX_CQ || cfg->rc_end_usage == VPX_Q))
RANGE_CHECK(vp8_cfg, cq_level,
cfg->rc_min_quantizer, cfg->rc_max_quantizer);
......@@ -327,17 +327,14 @@ static vpx_codec_err_t set_vp8e_config(VP8_CONFIG *oxcf,
oxcf->resample_up_water_mark = cfg.rc_resize_up_thresh;
oxcf->resample_down_water_mark = cfg.rc_resize_down_thresh;
if (cfg.rc_end_usage == VPX_VBR)
{
if (cfg.rc_end_usage == VPX_VBR) {
oxcf->end_usage = USAGE_LOCAL_FILE_PLAYBACK;
}
else if (cfg.rc_end_usage == VPX_CBR)
{
} else if (cfg.rc_end_usage == VPX_CBR) {
oxcf->end_usage = USAGE_STREAM_FROM_SERVER;
}
else if (cfg.rc_end_usage == VPX_CQ)
{
} else if (cfg.rc_end_usage == VPX_CQ) {
oxcf->end_usage = USAGE_CONSTRAINED_QUALITY;
} else if (cfg.rc_end_usage == VPX_Q) {
oxcf->end_usage = USAGE_CONSTANT_QUALITY;
}
oxcf->target_bandwidth = cfg.rc_target_bitrate;
......
......@@ -46,7 +46,8 @@ extern "C"
typedef enum {
USAGE_STREAM_FROM_SERVER = 0x0,
USAGE_LOCAL_FILE_PLAYBACK = 0x1,
USAGE_CONSTRAINED_QUALITY = 0x2
USAGE_CONSTRAINED_QUALITY = 0x2,
USAGE_CONSTANT_QUALITY = 0x3,
} END_USAGE;
......
......@@ -1092,7 +1092,6 @@ static int estimate_cq(VP9_COMP *cpi,
return q;
}
extern void vp9_new_framerate(VP9_COMP *cpi, double framerate);
void vp9_init_second_pass(VP9_COMP *cpi) {
......@@ -2079,14 +2078,19 @@ void vp9_second_pass(VP9_COMP *cpi) {
vp9_clear_system_state();
if (cpi->oxcf.end_usage == USAGE_CONSTANT_QUALITY) {
cpi->active_worst_quality = cpi->oxcf.cq_level;
} else {
// Special case code for first frame.
if (cpi->common.current_video_frame == 0) {
int section_target_bandwidth =
(int)(cpi->twopass.bits_left / frames_left);
cpi->twopass.est_max_qcorrection_factor = 1.0;
// Set a cq_level in constrained quality mode.
if (cpi->oxcf.end_usage == USAGE_CONSTRAINED_QUALITY) {
int est_cq = estimate_cq(cpi, &cpi->twopass.total_left_stats,
(int)(cpi->twopass.bits_left / frames_left));
section_target_bandwidth);
cpi->cq_target_quality = cpi->oxcf.cq_level;
if (est_cq > cpi->cq_target_quality)
......@@ -2098,7 +2102,7 @@ void vp9_second_pass(VP9_COMP *cpi) {
cpi->twopass.maxq_min_limit = cpi->best_quality;
tmp_q = estimate_max_q(cpi, &cpi->twopass.total_left_stats,
(int)(cpi->twopass.bits_left / frames_left));
section_target_bandwidth);
cpi->active_worst_quality = tmp_q;
cpi->ni_av_qi = tmp_q;
......@@ -2123,19 +2127,22 @@ void vp9_second_pass(VP9_COMP *cpi) {
(((unsigned int)cpi->twopass.total_stats.count * 255) >> 8)) &&
((cpi->common.current_video_frame + cpi->baseline_gf_interval) <
(unsigned int)cpi->twopass.total_stats.count)) {
int section_target_bandwidth =
(int)(cpi->twopass.bits_left / frames_left);
if (frames_left < 1)
frames_left = 1;
tmp_q = estimate_max_q(
cpi,
&cpi->twopass.total_left_stats,
(int)(cpi->twopass.bits_left / frames_left));
section_target_bandwidth);
// Make a damped adjustment to active max Q
cpi->active_worst_quality =
adjust_active_maxq(cpi->active_worst_quality, tmp_q);
}
#endif
}
vp9_zero(this_frame);
if (EOF == input_stats(cpi, &this_frame))
return;
......
......@@ -2725,7 +2725,8 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
delta_qindex = compute_qdelta(cpi, last_boosted_q,
(last_boosted_q * 0.75));
cpi->active_best_quality = MAX(qindex + delta_qindex, cpi->best_quality);
cpi->active_best_quality = MAX(qindex + delta_qindex,
cpi->best_quality);
} else {
int high = 5000;
int low = 400;
......@@ -2746,7 +2747,6 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
cpi->active_best_quality = kf_low_motion_minq[q] + adjustment;
}
// Allow somewhat lower kf minq with small image formats.
if ((cm->width * cm->height) <= (352 * 288)) {
q_adj_factor -= 0.25;
......@@ -2755,14 +2755,14 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
// Make a further adjustment based on the kf zero motion measure.
q_adj_factor += 0.05 - (0.001 * (double)cpi->kf_zeromotion_pct);
// Convert the adjustment factor to a qindex delta on active_best_quality.
// Convert the adjustment factor to a qindex delta
// on active_best_quality.
q_val = vp9_convert_qindex_to_q(cpi->active_best_quality);
cpi->active_best_quality +=
compute_qdelta(cpi, q_val, (q_val * q_adj_factor));
}
#else
double current_q;
// Force the KF quantizer to be 30% of the active_worst_quality.
current_q = vp9_convert_qindex_to_q(cpi->active_worst_quality);
cpi->active_best_quality = cpi->active_worst_quality
......@@ -2779,13 +2779,11 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
cpi->avg_frame_qindex < cpi->active_worst_quality) {
q = cpi->avg_frame_qindex;
}
// For constrained quality dont allow Q less than the cq level
if (cpi->oxcf.end_usage == USAGE_CONSTRAINED_QUALITY &&
q < cpi->cq_target_quality) {
q = cpi->cq_target_quality;
}
if (cpi->gfu_boost > high) {
cpi->active_best_quality = gf_low_motion_minq[q];
} else if (cpi->gfu_boost < low) {
......@@ -2802,6 +2800,42 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
// Constrained quality use slightly lower active best.
if (cpi->oxcf.end_usage == USAGE_CONSTRAINED_QUALITY)
cpi->active_best_quality = cpi->active_best_quality * 15 / 16;
// TODO(debargha): Refine the logic below
if (cpi->oxcf.end_usage == USAGE_CONSTANT_QUALITY) {
if (!cpi->refresh_alt_ref_frame) {
if (cpi->gfu_boost > high) {
cpi->active_best_quality = cpi->cq_target_quality * 14 / 16;
} else if (cpi->gfu_boost < low) {
cpi->active_best_quality = cpi->cq_target_quality;
} else {
const int gap = high - low;
const int offset = high - cpi->gfu_boost;
const int qdiff = cpi->cq_target_quality * 2 / 16;
const int adjustment = ((offset * qdiff) + (gap >> 1)) / gap;
cpi->active_best_quality = cpi->cq_target_quality * 14 / 16
+ adjustment;
}
} else {
if (cpi->frames_since_key > 1) {
if (cpi->gfu_boost > high) {
cpi->active_best_quality = cpi->cq_target_quality * 6 / 16;
} else if (cpi->gfu_boost < low) {
cpi->active_best_quality = cpi->cq_target_quality * 10 / 16;
} else {
const int gap = high - low;
const int offset = high - cpi->gfu_boost;
const int qdiff = cpi->cq_target_quality * 4 / 16;
const int adjustment = ((offset * qdiff) + (gap >> 1)) / gap;
cpi->active_best_quality = cpi->cq_target_quality * 6 / 16
+ adjustment;
}
}
}
}
} else {
if (cpi->oxcf.end_usage == USAGE_CONSTANT_QUALITY) {
cpi->active_best_quality = cpi->cq_target_quality;
} else {
#ifdef ONE_SHOT_Q_ESTIMATE
#ifdef STRICT_ONE_SHOT_Q
......@@ -2826,6 +2860,12 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
cpi->active_best_quality = cpi->cq_target_quality;
}
}
/*
if (cm->current_video_frame == 1)
printf("q/active_best/worst_quality = %d %d %d\n",
q, cpi->active_best_quality, cpi->active_worst_quality);
*/
}
// Clip the active best and worst quality values to limits
if (cpi->active_worst_quality > cpi->worst_quality)
......@@ -2841,7 +2881,9 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
cpi->active_worst_quality = cpi->active_best_quality;
// Special case code to try and match quality with forced key frames
if ((cm->frame_type == KEY_FRAME) && cpi->this_key_frame_forced) {
if (cpi->oxcf.end_usage == USAGE_CONSTANT_QUALITY) {
q = cpi->active_best_quality;
} else if ((cm->frame_type == KEY_FRAME) && cpi->this_key_frame_forced) {
q = cpi->last_boosted_qindex;
} else {
// Determine initial Q to try
......@@ -2853,7 +2895,8 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
#if CONFIG_MULTIPLE_ARF
// Force the quantizer determined by the coding order pattern.
if (cpi->multi_arf_enabled && (cm->frame_type != KEY_FRAME)) {
if (cpi->multi_arf_enabled && (cm->frame_type != KEY_FRAME) &&
cpi->oxcf.end_usage != USAGE_CONSTANT_QUALITY) {
double new_q;
double current_q = vp9_convert_qindex_to_q(cpi->active_worst_quality);
int level = cpi->this_frame_weight;
......@@ -2988,6 +3031,9 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
active_worst_qchanged = 0;
// Special case handling for forced key frames
if (cpi->oxcf.end_usage == USAGE_CONSTANT_QUALITY) {
loop = 0;
} else {
if ((cm->frame_type == KEY_FRAME) && cpi->this_key_frame_forced) {
int last_q = q;
int kf_err = vp9_calc_ss_err(cpi->Source,
......@@ -3026,12 +3072,11 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
q = clamp(q, q_low, q_high);
loop = q != last_q;
}
// Is the projected frame size out of range and are we allowed to attempt to recode.
else if (recode_loop_test(cpi,
frame_over_shoot_limit, frame_under_shoot_limit,
} else if (recode_loop_test(
cpi, frame_over_shoot_limit, frame_under_shoot_limit,
q, top_index, bottom_index)) {
// Is the projected frame size out of range and are we allowed
// to attempt to recode.
int last_q = q;
int retries = 0;
......@@ -3044,14 +3089,15 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
q_low = q < q_high ? q + 1 : q_high;
if (undershoot_seen || loop_count > 1) {
// Update rate_correction_factor unless cpi->active_worst_quality
// has changed.
// Update rate_correction_factor unless
// cpi->active_worst_quality has changed.
if (!active_worst_qchanged)
vp9_update_rate_correction_factors(cpi, 1);
q = (q_high + q_low + 1) / 2;
} else {
// Update rate_correction_factor unless cpi->active_worst_quality has changed.
// Update rate_correction_factor unless
// cpi->active_worst_quality has changed.
if (!active_worst_qchanged)
vp9_update_rate_correction_factors(cpi, 0);
......@@ -3070,13 +3116,15 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
q_high = q > q_low ? q - 1 : q_low;
if (overshoot_seen || loop_count > 1) {
// Update rate_correction_factor unless cpi->active_worst_quality has changed.
// Update rate_correction_factor unless
// cpi->active_worst_quality has changed.
if (!active_worst_qchanged)
vp9_update_rate_correction_factors(cpi, 1);
q = (q_high + q_low) / 2;
} else {
// Update rate_correction_factor unless cpi->active_worst_quality has changed.
// Update rate_correction_factor unless
// cpi->active_worst_quality has changed.
if (!active_worst_qchanged)
vp9_update_rate_correction_factors(cpi, 0);
......@@ -3107,6 +3155,7 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
} else {
loop = 0;
}
}
if (cpi->is_src_frame_alt_ref)
loop = 0;
......@@ -3338,9 +3387,9 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
// in this frame.
// update_base_skip_probs(cpi);
#if 0 && CONFIG_INTERNAL_STATS
#if 0 // CONFIG_INTERNAL_STATS
{
FILE *f = fopen("tmp.stt", "a");
FILE *f = fopen("tmp.stt", cm->current_video_frame ? "a" : "w");
int recon_err;
vp9_clear_system_state(); // __asm emms;
......@@ -3349,7 +3398,7 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
&cm->yv12_fb[cm->new_fb_idx]);
if (cpi->twopass.total_left_stats.coded_error != 0.0)
fprintf(f, "%10d %10d %10d %10d %10d %10d %10d %10d"
fprintf(f, "%10d %10d %10d %10d %10d %10d %10d %10d %10d"
"%7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f"
"%6d %6d %5d %5d %5d %8.2f %10d %10.3f"
"%10.3f %8d %10d %10d %10d\n",
......@@ -3359,6 +3408,7 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
(int)cpi->total_target_vs_actual,
(int)(cpi->oxcf.starting_buffer_level - cpi->bits_off_target),
(int)cpi->total_actual_bits,
cm->base_qindex,
vp9_convert_qindex_to_q(cm->base_qindex),
(double)vp9_dc_quant(cm->base_qindex, 0) / 4.0,
vp9_convert_qindex_to_q(cpi->active_best_quality),
......@@ -3377,7 +3427,7 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
cpi->tot_recode_hits, recon_err, cpi->kf_boost,
cpi->kf_zeromotion_pct);
else
fprintf(f, "%10d %10d %10d %10d %10d %10d %10d %10d"
fprintf(f, "%10d %10d %10d %10d %10d %10d %10d %10d %10d"
"%7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f"
"%5d %5d %5d %8d %8d %8.2f %10d %10.3f"
"%8d %10d %10d %10d\n",
......@@ -3388,6 +3438,7 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi,
(int)cpi->total_target_vs_actual,
(int)(cpi->oxcf.starting_buffer_level - cpi->bits_off_target),
(int)cpi->total_actual_bits,
cm->base_qindex,
vp9_convert_qindex_to_q(cm->base_qindex),
(double)vp9_dc_quant(cm->base_qindex, 0) / 4.0,
vp9_convert_qindex_to_q(cpi->active_best_quality),
......
......@@ -148,7 +148,7 @@ static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx,
RANGE_CHECK_HI(cfg, g_threads, 64);
RANGE_CHECK_HI(cfg, g_lag_in_frames, MAX_LAG_BUFFERS);
RANGE_CHECK(cfg, rc_end_usage, VPX_VBR, VPX_CQ);
RANGE_CHECK(cfg, rc_end_usage, VPX_VBR, VPX_Q);
RANGE_CHECK_HI(cfg, rc_undershoot_pct, 1000);
RANGE_CHECK_HI(cfg, rc_overshoot_pct, 1000);
RANGE_CHECK_HI(cfg, rc_2pass_vbr_bias_pct, 100);
......@@ -263,10 +263,12 @@ static vpx_codec_err_t set_vp9e_config(VP9_CONFIG *oxcf,
// CBR code has been deprectated for experimental phase.
// CQ mode not yet tested
oxcf->end_usage = USAGE_LOCAL_FILE_PLAYBACK;
/*if (cfg.rc_end_usage == VPX_CQ)
/*
if (cfg.rc_end_usage == VPX_CQ)
oxcf->end_usage = USAGE_CONSTRAINED_QUALITY;
else
oxcf->end_usage = USAGE_LOCAL_FILE_PLAYBACK;*/
*/
if (cfg.rc_end_usage == VPX_Q)
oxcf->end_usage = USAGE_CONSTANT_QUALITY;
oxcf->target_bandwidth = cfg.rc_target_bitrate;
oxcf->rc_max_intra_bitrate_pct = vp8_cfg.rc_max_intra_bitrate_pct;
......
......@@ -219,7 +219,8 @@ extern "C" {
enum vpx_rc_mode {
VPX_VBR, /**< Variable Bit Rate (VBR) mode */
VPX_CBR, /**< Constant Bit Rate (CBR) mode */
VPX_CQ /**< Constant Quality (CQ) mode */
VPX_CQ, /**< Constrained Quality (CQ) mode */
VPX_Q, /**< Constant Quality (Q) mode */
};
......
......@@ -1046,6 +1046,7 @@ static const struct arg_enum_list end_usage_enum[] = {
{"vbr", VPX_VBR},
{"cbr", VPX_CBR},
{"cq", VPX_CQ},
{"q", VPX_Q},
{NULL, 0}
};
static const arg_def_t end_usage = ARG_DEF_ENUM(NULL, "end-usage", 1,
......@@ -1126,7 +1127,7 @@ static const struct arg_enum_list tuning_enum[] = {
static const arg_def_t tune_ssim = ARG_DEF_ENUM(NULL, "tune", 1,
"Material to favor", tuning_enum);
static const arg_def_t cq_level = ARG_DEF(NULL, "cq-level", 1,
"Constrained Quality Level");
"Constant/Constrained Quality level");
static const arg_def_t max_intra_rate_pct = ARG_DEF(NULL, "max-intra-rate", 1,
"Max I-frame bitrate (pct)");
static const arg_def_t lossless = ARG_DEF(NULL, "lossless", 1, "Lossless mode");
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment