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" ...@@ -41,7 +41,8 @@ extern "C"
{ {
USAGE_STREAM_FROM_SERVER = 0x0, USAGE_STREAM_FROM_SERVER = 0x0,
USAGE_LOCAL_FILE_PLAYBACK = 0x1, USAGE_LOCAL_FILE_PLAYBACK = 0x1,
USAGE_CONSTRAINED_QUALITY = 0x2 USAGE_CONSTRAINED_QUALITY = 0x2,
USAGE_CONSTANT_QUALITY = 0x3
} END_USAGE; } END_USAGE;
......
...@@ -153,7 +153,7 @@ static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx, ...@@ -153,7 +153,7 @@ static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx,
#else #else
RANGE_CHECK_HI(cfg, g_lag_in_frames, 25); RANGE_CHECK_HI(cfg, g_lag_in_frames, 25);
#endif #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_undershoot_pct, 1000);
RANGE_CHECK_HI(cfg, rc_overshoot_pct, 1000); RANGE_CHECK_HI(cfg, rc_overshoot_pct, 1000);
RANGE_CHECK_HI(cfg, rc_2pass_vbr_bias_pct, 100); 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, ...@@ -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_HI(vp8_cfg, arnr_strength, 6);
RANGE_CHECK(vp8_cfg, arnr_type, 1, 3); RANGE_CHECK(vp8_cfg, arnr_type, 1, 3);
RANGE_CHECK(vp8_cfg, cq_level, 0, 63); 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, RANGE_CHECK(vp8_cfg, cq_level,
cfg->rc_min_quantizer, cfg->rc_max_quantizer); cfg->rc_min_quantizer, cfg->rc_max_quantizer);
...@@ -327,17 +327,14 @@ static vpx_codec_err_t set_vp8e_config(VP8_CONFIG *oxcf, ...@@ -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_up_water_mark = cfg.rc_resize_up_thresh;
oxcf->resample_down_water_mark = cfg.rc_resize_down_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;
oxcf->end_usage = USAGE_LOCAL_FILE_PLAYBACK; } else if (cfg.rc_end_usage == VPX_CBR) {
} oxcf->end_usage = USAGE_STREAM_FROM_SERVER;
else if (cfg.rc_end_usage == VPX_CBR) } else if (cfg.rc_end_usage == VPX_CQ) {
{ oxcf->end_usage = USAGE_CONSTRAINED_QUALITY;
oxcf->end_usage = USAGE_STREAM_FROM_SERVER; } else if (cfg.rc_end_usage == VPX_Q) {
} oxcf->end_usage = USAGE_CONSTANT_QUALITY;
else if (cfg.rc_end_usage == VPX_CQ)
{
oxcf->end_usage = USAGE_CONSTRAINED_QUALITY;
} }
oxcf->target_bandwidth = cfg.rc_target_bitrate; oxcf->target_bandwidth = cfg.rc_target_bitrate;
......
...@@ -46,7 +46,8 @@ extern "C" ...@@ -46,7 +46,8 @@ extern "C"
typedef enum { typedef enum {
USAGE_STREAM_FROM_SERVER = 0x0, USAGE_STREAM_FROM_SERVER = 0x0,
USAGE_LOCAL_FILE_PLAYBACK = 0x1, USAGE_LOCAL_FILE_PLAYBACK = 0x1,
USAGE_CONSTRAINED_QUALITY = 0x2 USAGE_CONSTRAINED_QUALITY = 0x2,
USAGE_CONSTANT_QUALITY = 0x3,
} END_USAGE; } END_USAGE;
......
...@@ -1092,7 +1092,6 @@ static int estimate_cq(VP9_COMP *cpi, ...@@ -1092,7 +1092,6 @@ static int estimate_cq(VP9_COMP *cpi,
return q; return q;
} }
extern void vp9_new_framerate(VP9_COMP *cpi, double framerate); extern void vp9_new_framerate(VP9_COMP *cpi, double framerate);
void vp9_init_second_pass(VP9_COMP *cpi) { void vp9_init_second_pass(VP9_COMP *cpi) {
...@@ -2079,63 +2078,71 @@ void vp9_second_pass(VP9_COMP *cpi) { ...@@ -2079,63 +2078,71 @@ void vp9_second_pass(VP9_COMP *cpi) {
vp9_clear_system_state(); vp9_clear_system_state();
// Special case code for first frame. if (cpi->oxcf.end_usage == USAGE_CONSTANT_QUALITY) {
if (cpi->common.current_video_frame == 0) { cpi->active_worst_quality = cpi->oxcf.cq_level;
cpi->twopass.est_max_qcorrection_factor = 1.0; } else {
// Special case code for first frame.
// Set a cq_level in constrained quality mode. if (cpi->common.current_video_frame == 0) {
if (cpi->oxcf.end_usage == USAGE_CONSTRAINED_QUALITY) { int section_target_bandwidth =
int est_cq = estimate_cq(cpi, &cpi->twopass.total_left_stats, (int)(cpi->twopass.bits_left / frames_left);
(int)(cpi->twopass.bits_left / frames_left)); cpi->twopass.est_max_qcorrection_factor = 1.0;
cpi->cq_target_quality = cpi->oxcf.cq_level; // Set a cq_level in constrained quality mode.
if (est_cq > cpi->cq_target_quality) if (cpi->oxcf.end_usage == USAGE_CONSTRAINED_QUALITY) {
cpi->cq_target_quality = est_cq; int est_cq = estimate_cq(cpi, &cpi->twopass.total_left_stats,
} section_target_bandwidth);
cpi->cq_target_quality = cpi->oxcf.cq_level;
if (est_cq > cpi->cq_target_quality)
cpi->cq_target_quality = est_cq;
}
// guess at maxq needed in 2nd pass // guess at maxq needed in 2nd pass
cpi->twopass.maxq_max_limit = cpi->worst_quality; cpi->twopass.maxq_max_limit = cpi->worst_quality;
cpi->twopass.maxq_min_limit = cpi->best_quality; cpi->twopass.maxq_min_limit = cpi->best_quality;
tmp_q = estimate_max_q(cpi, &cpi->twopass.total_left_stats, 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->active_worst_quality = tmp_q;
cpi->ni_av_qi = tmp_q; cpi->ni_av_qi = tmp_q;
cpi->avg_q = vp9_convert_qindex_to_q(tmp_q); cpi->avg_q = vp9_convert_qindex_to_q(tmp_q);
#ifndef ONE_SHOT_Q_ESTIMATE #ifndef ONE_SHOT_Q_ESTIMATE
// Limit the maxq value returned subsequently. // Limit the maxq value returned subsequently.
// This increases the risk of overspend or underspend if the initial // This increases the risk of overspend or underspend if the initial
// estimate for the clip is bad, but helps prevent excessive // estimate for the clip is bad, but helps prevent excessive
// variation in Q, especially near the end of a clip // variation in Q, especially near the end of a clip
// where for example a small overspend may cause Q to crash // where for example a small overspend may cause Q to crash
adjust_maxq_qrange(cpi); adjust_maxq_qrange(cpi);
#endif #endif
} }
#ifndef ONE_SHOT_Q_ESTIMATE #ifndef ONE_SHOT_Q_ESTIMATE
// The last few frames of a clip almost always have to few or too many // The last few frames of a clip almost always have to few or too many
// bits and for the sake of over exact rate control we dont want to make // bits and for the sake of over exact rate control we dont want to make
// radical adjustments to the allowed quantizer range just to use up a // radical adjustments to the allowed quantizer range just to use up a
// few surplus bits or get beneath the target rate. // few surplus bits or get beneath the target rate.
else if ((cpi->common.current_video_frame < else if ((cpi->common.current_video_frame <
(((unsigned int)cpi->twopass.total_stats.count * 255) >> 8)) && (((unsigned int)cpi->twopass.total_stats.count * 255) >> 8)) &&
((cpi->common.current_video_frame + cpi->baseline_gf_interval) < ((cpi->common.current_video_frame + cpi->baseline_gf_interval) <
(unsigned int)cpi->twopass.total_stats.count)) { (unsigned int)cpi->twopass.total_stats.count)) {
if (frames_left < 1) int section_target_bandwidth =
frames_left = 1; (int)(cpi->twopass.bits_left / frames_left);
if (frames_left < 1)
tmp_q = estimate_max_q( frames_left = 1;
cpi,
&cpi->twopass.total_left_stats, tmp_q = estimate_max_q(
(int)(cpi->twopass.bits_left / frames_left)); cpi,
&cpi->twopass.total_left_stats,
// Make a damped adjustment to active max Q section_target_bandwidth);
cpi->active_worst_quality =
adjust_active_maxq(cpi->active_worst_quality, tmp_q); // Make a damped adjustment to active max Q
} cpi->active_worst_quality =
adjust_active_maxq(cpi->active_worst_quality, tmp_q);
}
#endif #endif
}
vp9_zero(this_frame); vp9_zero(this_frame);
if (EOF == input_stats(cpi, &this_frame)) if (EOF == input_stats(cpi, &this_frame))
return; return;
......
This diff is collapsed.
...@@ -148,7 +148,7 @@ static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx, ...@@ -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_threads, 64);
RANGE_CHECK_HI(cfg, g_lag_in_frames, MAX_LAG_BUFFERS); 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_undershoot_pct, 1000);
RANGE_CHECK_HI(cfg, rc_overshoot_pct, 1000); RANGE_CHECK_HI(cfg, rc_overshoot_pct, 1000);
RANGE_CHECK_HI(cfg, rc_2pass_vbr_bias_pct, 100); RANGE_CHECK_HI(cfg, rc_2pass_vbr_bias_pct, 100);
...@@ -262,13 +262,15 @@ static vpx_codec_err_t set_vp9e_config(VP9_CONFIG *oxcf, ...@@ -262,13 +262,15 @@ static vpx_codec_err_t set_vp9e_config(VP9_CONFIG *oxcf,
// VBR only supported for now. // VBR only supported for now.
// CBR code has been deprectated for experimental phase. // CBR code has been deprectated for experimental phase.
// CQ mode not yet tested // CQ mode not yet tested
oxcf->end_usage = USAGE_LOCAL_FILE_PLAYBACK; oxcf->end_usage = USAGE_LOCAL_FILE_PLAYBACK;
/*if (cfg.rc_end_usage == VPX_CQ) /*
oxcf->end_usage = USAGE_CONSTRAINED_QUALITY; if (cfg.rc_end_usage == VPX_CQ)
else oxcf->end_usage = USAGE_CONSTRAINED_QUALITY;
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->target_bandwidth = cfg.rc_target_bitrate;
oxcf->rc_max_intra_bitrate_pct = vp8_cfg.rc_max_intra_bitrate_pct; oxcf->rc_max_intra_bitrate_pct = vp8_cfg.rc_max_intra_bitrate_pct;
oxcf->best_allowed_q = cfg.rc_min_quantizer; oxcf->best_allowed_q = cfg.rc_min_quantizer;
......
...@@ -217,9 +217,10 @@ extern "C" { ...@@ -217,9 +217,10 @@ extern "C" {
/*!\brief Rate control mode */ /*!\brief Rate control mode */
enum vpx_rc_mode { enum vpx_rc_mode {
VPX_VBR, /**< Variable Bit Rate (VBR) mode */ VPX_VBR, /**< Variable Bit Rate (VBR) mode */
VPX_CBR, /**< Constant Bit Rate (CBR) 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[] = { ...@@ -1046,6 +1046,7 @@ static const struct arg_enum_list end_usage_enum[] = {
{"vbr", VPX_VBR}, {"vbr", VPX_VBR},
{"cbr", VPX_CBR}, {"cbr", VPX_CBR},
{"cq", VPX_CQ}, {"cq", VPX_CQ},
{"q", VPX_Q},
{NULL, 0} {NULL, 0}
}; };
static const arg_def_t end_usage = ARG_DEF_ENUM(NULL, "end-usage", 1, 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[] = { ...@@ -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, static const arg_def_t tune_ssim = ARG_DEF_ENUM(NULL, "tune", 1,
"Material to favor", tuning_enum); "Material to favor", tuning_enum);
static const arg_def_t cq_level = ARG_DEF(NULL, "cq-level", 1, 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, static const arg_def_t max_intra_rate_pct = ARG_DEF(NULL, "max-intra-rate", 1,
"Max I-frame bitrate (pct)"); "Max I-frame bitrate (pct)");
static const arg_def_t lossless = ARG_DEF(NULL, "lossless", 1, "Lossless mode"); 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