From e0846c9c8c374a07911ced982ce68949886d0f21 Mon Sep 17 00:00:00 2001 From: Paul Wilkins Date: Fri, 7 Jan 2011 18:29:37 +0000 Subject: [PATCH] CQ Mode The merge includes hooks to for CQ mode and other code changes merged from the test branch. CQ mode attempts to maintain a more stable quantizer within a clip whilst also trying to adhere to a guidline maximum bitrate. The existing target data rate parameter is used to specify the guideline maximum bitrate. A new parameter allows the user to specify a target CQ level. For normal (non kf/gf/arf) frames, the quantizer will not drop BELOW the user specified value (0-63). However, in some cases the encoder may choose to impose a target CQ that is above that specified by the user, if it estimates that consistent use of the target value is not compatible with guideline maximum bitrate. Change-Id: I2221f9eecae8cc3c431d36caf83503941b25e4c1 --- vp8/common/onyx.h | 5 +- vp8/encoder/firstpass.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++-- vp8/encoder/onyx_if.c | 71 +++++++++++++++------------ vp8/encoder/onyx_int.h | 1 + vp8/encoder/ratectrl.c | 15 ++++-- vp8/vp8_cx_iface.c | 13 ++++- vpx/vp8cx.h | 2 + vpx/vpx_encoder.h | 3 +- vpxenc.c | 8 +-- 9 files changed, 196 insertions(+), 48 deletions(-) diff --git a/vp8/common/onyx.h b/vp8/common/onyx.h index 3724b11..426b8fc 100644 --- a/vp8/common/onyx.h +++ b/vp8/common/onyx.h @@ -46,7 +46,8 @@ extern "C" typedef enum { USAGE_STREAM_FROM_SERVER = 0x0, - USAGE_LOCAL_FILE_PLAYBACK = 0x1 + USAGE_LOCAL_FILE_PLAYBACK = 0x1, + USAGE_CONSTRAINED_QUALITY = 0x2 } END_USAGE; @@ -150,6 +151,7 @@ extern "C" int fixed_q; int worst_allowed_q; int best_allowed_q; + int cq_level; // allow internal resizing ( currently disabled in the build !!!!!) int allow_spatial_resampling; @@ -187,7 +189,6 @@ extern "C" int arnr_strength ; int arnr_type ; - struct vpx_fixed_buf two_pass_stats_in; struct vpx_codec_pkt_list *output_pkt_list; diff --git a/vp8/encoder/firstpass.c b/vp8/encoder/firstpass.c index ce273b9..0c79ade 100644 --- a/vp8/encoder/firstpass.c +++ b/vp8/encoder/firstpass.c @@ -58,6 +58,7 @@ extern const int vp8_gf_boost_qadjustment[QINDEX_RANGE]; #define KF_MB_INTRA_MIN 300 #define GF_MB_INTRA_MIN 200 + #define DOUBLE_DIVIDE_CHECK(X) ((X)<0?(X)-.000001:(X)+.000001) #define POW1 (double)cpi->oxcf.two_pass_vbrbias/100.0 @@ -66,6 +67,19 @@ extern const int vp8_gf_boost_qadjustment[QINDEX_RANGE]; static int vscale_lookup[7] = {0, 1, 1, 2, 2, 3, 3}; static int hscale_lookup[7] = {0, 0, 1, 1, 2, 2, 3}; + +const int cq_level[QINDEX_RANGE] = +{ + 0,0,1,1,2,3,3,4,4,5,6,6,7,8,8,9, + 9,10,11,11,12,13,13,14,15,15,16,17,17,18,19,20, + 20,21,22,22,23,24,24,25,26,27,27,28,29,30,30,31, + 32,33,33,34,35,36,36,37,38,39,39,40,41,42,42,43, + 44,45,46,46,47,48,49,50,50,51,52,53,54,55,55,56, + 57,58,59,60,60,61,62,63,64,65,66,67,67,68,69,70, + 71,72,73,74,75,75,76,77,78,79,80,81,82,83,84,85, + 86,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100 +}; + void vp8_find_next_key_frame(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame); int vp8_input_stats(VP8_COMP *cpi, FIRSTPASS_STATS *fps); @@ -889,7 +903,7 @@ void vp8_first_pass(VP8_COMP *cpi) } extern const int vp8_bits_per_mb[2][QINDEX_RANGE]; -#define BASE_ERRPERMB 150 +#define BASE_ERRPERMB 100 static int estimate_max_q(VP8_COMP *cpi, double section_err, int section_target_bandwitdh, int Height, int Width) { int Q; @@ -945,7 +959,7 @@ static int estimate_max_q(VP8_COMP *cpi, double section_err, int section_target_ corr_high = (corr_high < 0.05) ? 0.05 : (corr_high > 5.0) ? 5.0 : corr_high; - // Try and pick a Q that should be high enough to encode the + // Try and pick a max Q that will be high enough to encode the // content at the given rate. for (Q = cpi->maxq_min_limit; Q < cpi->maxq_max_limit; Q++) { @@ -966,6 +980,15 @@ static int estimate_max_q(VP8_COMP *cpi, double section_err, int section_target_ break; } + // Restriction on active max q for constrained quality mode. + if ( (cpi->oxcf.end_usage == USAGE_CONSTRAINED_QUALITY) && + (Q < cpi->cq_target_quality) ) + //(Q < cpi->oxcf.cq_target_quality) ) + { + Q = cpi->cq_target_quality; + //Q = cpi->oxcf.cq_target_quality; + } + return Q; } static int estimate_q(VP8_COMP *cpi, double section_err, int section_target_bandwitdh, int Height, int Width) @@ -1114,6 +1137,79 @@ static int estimate_kf_group_q(VP8_COMP *cpi, double section_err, int section_ta return Q; } + +// For cq mode estimate a cq level that matches the observed +// complexity and data rate. +static int estimate_cq(VP8_COMP *cpi, double section_err, + int section_target_bandwitdh, int Height, int Width) +{ + int Q; + int num_mbs = ((Height * Width) / (16 * 16)); + int target_norm_bits_per_mb; + + double err_per_mb = section_err / num_mbs; + double correction_factor; + double corr_high; + double speed_correction = 1.0; + double pow_highq = 0.90; + double pow_lowq = 0.40; + double clip_iiratio; + double clip_iifactor; + + target_norm_bits_per_mb = (section_target_bandwitdh < (1 << 20)) + ? (512 * section_target_bandwitdh) / num_mbs + : 512 * (section_target_bandwitdh / num_mbs); + + // Corrections for higher compression speed settings + // (reduced compression expected) + if ((cpi->compressor_speed == 3) || (cpi->compressor_speed == 1)) + { + if (cpi->oxcf.cpu_used <= 5) + speed_correction = 1.04 + (cpi->oxcf.cpu_used * 0.04); + else + speed_correction = 1.25; + } + // II ratio correction factor for clip as a whole + clip_iiratio = cpi->total_stats->intra_error / + DOUBLE_DIVIDE_CHECK(cpi->total_stats->coded_error); + clip_iifactor = 1.0 - ((clip_iiratio - 10.0) * 0.025); + if (clip_iifactor < 0.80) + clip_iifactor = 0.80; + + // Correction factor used for Q values >= 20 + corr_high = pow(err_per_mb / BASE_ERRPERMB, pow_highq); + corr_high = (corr_high < 0.05) ? 0.05 : (corr_high > 5.0) ? 5.0 : corr_high; + + // Try and pick a Q that can encode the content at the given rate. + for (Q = 0; Q < MAXQ; Q++) + { + int bits_per_mb_at_this_q; + + if (Q < 50) + { + correction_factor = + pow( err_per_mb / BASE_ERRPERMB, (pow_lowq + Q * 0.01)); + + correction_factor = (correction_factor < 0.05) ? 0.05 + : (correction_factor > 5.0) ? 5.0 + : correction_factor; + } + else + correction_factor = corr_high; + + bits_per_mb_at_this_q = + (int)( .5 + correction_factor * + speed_correction * + clip_iifactor * + (double)vp8_bits_per_mb[INTER_FRAME][Q] / 1.0); + + if (bits_per_mb_at_this_q <= target_norm_bits_per_mb) + break; + } + + return cq_level[Q]; +} + extern void vp8_new_frame_rate(VP8_COMP *cpi, double framerate); void vp8_init_second_pass(VP8_COMP *cpi) @@ -1767,7 +1863,9 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame) vp8_avg_stats(§ionstats); - cpi->section_intra_rating = sectionstats.intra_error / DOUBLE_DIVIDE_CHECK(sectionstats.coded_error); + cpi->section_intra_rating = + sectionstats.intra_error / + DOUBLE_DIVIDE_CHECK(sectionstats.coded_error); Ratio = sectionstats.intra_error / DOUBLE_DIVIDE_CHECK(sectionstats.coded_error); //if( (Ratio > 11) ) //&& (sectionstats.pcnt_second_ref < .20) ) @@ -1994,9 +2092,26 @@ void vp8_second_pass(VP8_COMP *cpi) if (cpi->common.current_video_frame == 0) { - // guess at 2nd pass max q cpi->est_max_qcorrection_factor = 1.0; + // Experimental code to try and set a cq_level in constrained + // quality mode. + if ( cpi->oxcf.end_usage == USAGE_CONSTRAINED_QUALITY ) + { + int est_cq; + + est_cq = + estimate_cq( cpi, + (cpi->total_coded_error_left / frames_left), + (int)(cpi->bits_left / frames_left), + cpi->common.Height, cpi->common.Width); + + 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 cpi->maxq_max_limit = cpi->worst_quality; cpi->maxq_min_limit = cpi->best_quality; tmp_q = estimate_max_q( cpi, @@ -2005,7 +2120,7 @@ void vp8_second_pass(VP8_COMP *cpi) cpi->common.Height, cpi->common.Width); - // Limit the maxq value retuned subsequently. + // Limit the maxq value returned subsequently. // This increases the risk of overspend if the initial // estimate for the clip is bad, but helps prevent excessive // variation in Q, especially near the end of a clip @@ -2018,6 +2133,7 @@ void vp8_second_pass(VP8_COMP *cpi) cpi->active_worst_quality = tmp_q; cpi->ni_av_qi = tmp_q; } + // 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 // radical adjustments to the allowed quantizer range just to use up a diff --git a/vp8/encoder/onyx_if.c b/vp8/encoder/onyx_if.c index 96e7a6c..22b25e3 100644 --- a/vp8/encoder/onyx_if.c +++ b/vp8/encoder/onyx_if.c @@ -155,25 +155,25 @@ extern const int vp8cx_base_skip_false_prob[128]; // Tables relating active max Q to active min Q static const int kf_low_motion_minq[QINDEX_RANGE] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, - 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 10,10, - 11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18, - 19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26, - 27,27,28,28,29,29,30,30,31,32,33,34,35,36,37,38, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2, + 3,3,3,3,3,3,4,4,4,5,5,5,5,5,6,6, + 6,6,7,7,8,8,8,8,9,9,10,10,10,10,11,11, + 11,11,12,12,13,13,13,13,14,14,15,15,15,15,16,16, + 16,16,17,17,18,18,18,18,19,20,20,21,21,22,23,23 }; static const int kf_high_motion_minq[QINDEX_RANGE] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, - 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, - 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,10, - 11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18, - 19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26, - 27,27,28,28,29,29,30,30,31,31,32,32,33,33,34,34, - 35,35,36,36,37,38,39,40,41,42,43,44,45,46,47,48, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,2,2,2,2,3,3,3,3, + 3,3,3,3,4,4,4,4,5,5,5,5,5,5,6,6, + 6,6,7,7,8,8,8,8,9,9,10,10,10,10,11,11, + 11,11,12,12,13,13,13,13,14,14,15,15,15,15,16,16, + 16,16,17,17,18,18,18,18,19,19,20,20,20,20,21,21, + 21,21,22,22,23,23,24,25,25,26,26,27,28,28,29,30 }; static const int gf_low_motion_minq[QINDEX_RANGE] = { @@ -195,7 +195,7 @@ static const int gf_mid_motion_minq[QINDEX_RANGE] = 22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29, 30,30,31,31,32,32,33,33,34,34,35,35,36,36,37,37, 38,39,39,40,40,41,41,42,42,43,43,44,45,46,47,48, - 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64, + 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64 }; static const int gf_high_motion_minq[QINDEX_RANGE] = { @@ -206,7 +206,7 @@ static const int gf_high_motion_minq[QINDEX_RANGE] = 25,25,26,26,27,27,28,28,29,29,30,30,31,31,32,32, 33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40, 41,41,42,42,43,44,45,46,47,48,49,50,51,52,53,54, - 55,56,57,58,59,60,62,64,66,68,70,72,74,76,78,80, + 55,56,57,58,59,60,62,64,66,68,70,72,74,76,78,80 }; static const int inter_minq[QINDEX_RANGE] = { @@ -2026,6 +2026,9 @@ void vp8_change_config(VP8_PTR ptr, VP8_CONFIG *oxcf) cpi->active_best_quality = cpi->oxcf.best_allowed_q; cpi->buffered_mode = (cpi->oxcf.optimal_buffer_level > 0) ? TRUE : FALSE; + // Experimental cq target value + cpi->cq_target_quality = oxcf->cq_level; + cpi->rolling_target_bits = cpi->av_per_frame_bandwidth; cpi->rolling_actual_bits = cpi->av_per_frame_bandwidth; cpi->long_rolling_target_bits = cpi->av_per_frame_bandwidth; @@ -3515,12 +3518,14 @@ static BOOL recode_loop_test( VP8_COMP *cpi, { // General over and under shoot tests if ( ((cpi->projected_frame_size > high_limit) && (q < maxq)) || - ((cpi->projected_frame_size < low_limit) && (q > minq)) ) + ((cpi->projected_frame_size < low_limit) && (q > minq)) || + ((cpi->oxcf.end_usage == USAGE_CONSTRAINED_QUALITY) && + (q > cpi->cq_target_quality) && + (cpi->projected_frame_size < + ((cpi->this_frame_target * 7) >> 3))) ) { force_recode = TRUE; } - // Specific rate control mode related tests - // TBD } return force_recode; @@ -3811,15 +3816,7 @@ static void encode_frame_to_data_rate cpi->active_best_quality = gf_high_motion_minq[Q]; else cpi->active_best_quality = gf_mid_motion_minq[Q]; - - /*cpi->active_best_quality = gf_arf_minq[Q]; - tmp = (cpi->gfu_boost > 1000) ? 600 : cpi->gfu_boost - 400; - //tmp = (cpi->gfu_boost > 1000) ? 600 : - //(cpi->gfu_boost < 400) ? 0 : cpi->gfu_boost - 400; - tmp = 128 - (tmp >> 4); - cpi->active_best_quality = (cpi->active_best_quality * tmp)>>7;*/ - - } + } // KEY FRAMES else { @@ -3832,6 +3829,14 @@ static void encode_frame_to_data_rate else { cpi->active_best_quality = inter_minq[Q]; + + // For the constant/constrained quality mode we dont want + // the quality to rise above the cq level. + if ((cpi->oxcf.end_usage == USAGE_CONSTRAINED_QUALITY) && + (cpi->active_best_quality < cpi->cq_target_quality) ) + { + cpi->active_best_quality = cpi->cq_target_quality; + } } // If CBR and the buffer is as full then it is reasonable to allow higher quality on the frames @@ -4566,7 +4571,8 @@ static void encode_frame_to_data_rate (cpi->oxcf.starting_buffer_level-cpi->bits_off_target), (int)cpi->total_actual_bits, cm->base_qindex, cpi->active_best_quality, cpi->active_worst_quality, - cpi->avg_frame_qindex, cpi->zbin_over_quant, + cpi->cq_target_quality, cpi->zbin_over_quant, + //cpi->avg_frame_qindex, cpi->zbin_over_quant, cm->refresh_golden_frame, cm->refresh_alt_ref_frame, cm->frame_type, cpi->gfu_boost, cpi->est_max_qcorrection_factor, (int)cpi->bits_left, @@ -4584,7 +4590,8 @@ static void encode_frame_to_data_rate (cpi->oxcf.starting_buffer_level-cpi->bits_off_target), (int)cpi->total_actual_bits, cm->base_qindex, cpi->active_best_quality, cpi->active_worst_quality, - cpi->avg_frame_qindex, cpi->zbin_over_quant, + cpi->cq_target_quality, cpi->zbin_over_quant, + //cpi->avg_frame_qindex, cpi->zbin_over_quant, cm->refresh_golden_frame, cm->refresh_alt_ref_frame, cm->frame_type, cpi->gfu_boost, cpi->est_max_qcorrection_factor, (int)cpi->bits_left, diff --git a/vp8/encoder/onyx_int.h b/vp8/encoder/onyx_int.h index 6a21d0f..c5dc0c1 100644 --- a/vp8/encoder/onyx_int.h +++ b/vp8/encoder/onyx_int.h @@ -447,6 +447,7 @@ typedef struct int best_quality; int active_best_quality; + int cq_target_quality; int maxq_max_limit; int maxq_min_limit; diff --git a/vp8/encoder/ratectrl.c b/vp8/encoder/ratectrl.c index 8455b7b..b69a196 100644 --- a/vp8/encoder/ratectrl.c +++ b/vp8/encoder/ratectrl.c @@ -1550,12 +1550,21 @@ void vp8_compute_frame_size_bounds(VP8_COMP *cpi, int *frame_under_shoot_limit, *frame_under_shoot_limit = cpi->this_frame_target * 5 / 8; } } - // VBR + // VBR and CQ mode // Note that tighter restrictions here can help quality but hurt encode speed else { - *frame_over_shoot_limit = cpi->this_frame_target * 11 / 8; - *frame_under_shoot_limit = cpi->this_frame_target * 5 / 8; + // Stron overshoot limit for constrained quality + if (cpi->oxcf.end_usage == USAGE_CONSTRAINED_QUALITY) + { + *frame_over_shoot_limit = cpi->this_frame_target * 11 / 8; + *frame_under_shoot_limit = cpi->this_frame_target * 2 / 8; + } + else + { + *frame_over_shoot_limit = cpi->this_frame_target * 11 / 8; + *frame_under_shoot_limit = cpi->this_frame_target * 5 / 8; + } } } } diff --git a/vp8/vp8_cx_iface.c b/vp8/vp8_cx_iface.c index f959207..4c7aca5 100644 --- a/vp8/vp8_cx_iface.c +++ b/vp8/vp8_cx_iface.c @@ -38,6 +38,7 @@ struct vp8_extracfg unsigned int arnr_strength; /* alt_ref Noise Reduction Strength */ unsigned int arnr_type; /* alt_ref filter type */ vp8e_tuning tuning; + unsigned int cq_level; /* constrained quality level */ }; @@ -69,6 +70,7 @@ static const struct extraconfig_map extracfg_map[] = 3, /* arnr_strength */ 3, /* arnr_type*/ 0, /* tuning*/ + 10, /* cq_level */ } } }; @@ -148,7 +150,7 @@ static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx, #else RANGE_CHECK_HI(cfg, g_lag_in_frames, 0); #endif - RANGE_CHECK(cfg, rc_end_usage, VPX_VBR, VPX_CBR); + RANGE_CHECK(cfg, rc_end_usage, VPX_VBR, VPX_CQ); RANGE_CHECK_HI(cfg, rc_undershoot_pct, 100); RANGE_CHECK_HI(cfg, rc_2pass_vbr_bias_pct, 100); RANGE_CHECK(cfg, kf_mode, VPX_KF_DISABLED, VPX_KF_AUTO); @@ -190,6 +192,7 @@ static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx, RANGE_CHECK(vp8_cfg, arnr_max_frames, 0, 15); 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 (cfg->g_pass == VPX_RC_LAST_PASS) { @@ -298,6 +301,10 @@ static vpx_codec_err_t set_vp8e_config(VP8_CONFIG *oxcf, { oxcf->end_usage = USAGE_STREAM_FROM_SERVER; } + else if (cfg.rc_end_usage == VPX_CQ) + { + oxcf->end_usage = USAGE_CONSTRAINED_QUALITY; + } oxcf->target_bandwidth = cfg.rc_target_bitrate; @@ -339,6 +346,7 @@ static vpx_codec_err_t set_vp8e_config(VP8_CONFIG *oxcf, oxcf->arnr_type = vp8_cfg.arnr_type; oxcf->tuning = vp8_cfg.tuning; + oxcf->cq_level = vp8_cfg.cq_level; /* printf("Current VP8 Settings: \n"); @@ -453,6 +461,7 @@ static vpx_codec_err_t set_param(vpx_codec_alg_priv_t *ctx, MAP(VP8E_SET_ARNR_STRENGTH , xcfg.arnr_strength); MAP(VP8E_SET_ARNR_TYPE , xcfg.arnr_type); MAP(VP8E_SET_TUNING, xcfg.tuning); + MAP(VP8E_SET_CQ_LEVEL, xcfg.cq_level); } @@ -1034,6 +1043,7 @@ static vpx_codec_ctrl_fn_map_t vp8e_ctf_maps[] = {VP8E_SET_ARNR_STRENGTH , set_param}, {VP8E_SET_ARNR_TYPE , set_param}, {VP8E_SET_TUNING, set_param}, + {VP8E_SET_CQ_LEVEL, set_param}, { -1, NULL}, }; @@ -1069,7 +1079,6 @@ static vpx_codec_enc_cfg_map_t vp8e_usage_cfg_map[] = 4, /* rc_min_quantizer */ 63, /* rc_max_quantizer */ - 95, /* rc_undershoot_pct */ 200, /* rc_overshoot_pct */ diff --git a/vpx/vp8cx.h b/vpx/vp8cx.h index 3ed1cf1..d574c44 100644 --- a/vpx/vp8cx.h +++ b/vpx/vp8cx.h @@ -141,6 +141,7 @@ enum vp8e_enc_control_id VP8E_SET_ARNR_STRENGTH , /**< control function to set the filter strength for the arf */ VP8E_SET_ARNR_TYPE , /**< control function to set the type of filter to use for the arf*/ VP8E_SET_TUNING, /**< control function to set visual tuning */ + VP8E_SET_CQ_LEVEL, /**< control function to set constrained quality level */ }; /*!\brief vpx 1-D scaling mode @@ -267,6 +268,7 @@ VPX_CTRL_USE_TYPE(VP8E_SET_ARNR_MAXFRAMES, unsigned int) VPX_CTRL_USE_TYPE(VP8E_SET_ARNR_STRENGTH , unsigned int) VPX_CTRL_USE_TYPE(VP8E_SET_ARNR_TYPE , unsigned int) VPX_CTRL_USE_TYPE(VP8E_SET_TUNING, vp8e_tuning) +VPX_CTRL_USE_TYPE(VP8E_SET_CQ_LEVEL , unsigned int) VPX_CTRL_USE_TYPE(VP8E_GET_LAST_QUANTIZER, int *) VPX_CTRL_USE_TYPE(VP8E_GET_LAST_QUANTIZER_64, int *) diff --git a/vpx/vpx_encoder.h b/vpx/vpx_encoder.h index 3acb199..0d53f41 100644 --- a/vpx/vpx_encoder.h +++ b/vpx/vpx_encoder.h @@ -179,7 +179,8 @@ extern "C" { enum vpx_rc_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 */ }; diff --git a/vpxenc.c b/vpxenc.c index cb911be..2c133e7 100755 --- a/vpxenc.c +++ b/vpxenc.c @@ -917,7 +917,7 @@ static const arg_def_t resize_up_thresh = ARG_DEF(NULL, "resize-up", 1, static const arg_def_t resize_down_thresh = ARG_DEF(NULL, "resize-down", 1, "Downscale threshold (buf %)"); static const arg_def_t end_usage = ARG_DEF(NULL, "end-usage", 1, - "VBR=0 | CBR=1"); + "VBR=0 | CBR=1 | CQ=2"); static const arg_def_t target_bitrate = ARG_DEF(NULL, "target-bitrate", 1, "Bitrate (kbps)"); static const arg_def_t min_quantizer = ARG_DEF(NULL, "min-q", 1, @@ -1000,12 +1000,14 @@ 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"); static const arg_def_t *vp8_args[] = { &cpu_used, &auto_altref, &noise_sens, &sharpness, &static_thresh, &token_parts, &arnr_maxframes, &arnr_strength, &arnr_type, - &tune_ssim, NULL + &tune_ssim, &cq_level, NULL }; static const int vp8_arg_ctrl_map[] = { @@ -1013,7 +1015,7 @@ static const int vp8_arg_ctrl_map[] = VP8E_SET_NOISE_SENSITIVITY, VP8E_SET_SHARPNESS, VP8E_SET_STATIC_THRESHOLD, VP8E_SET_TOKEN_PARTITIONS, VP8E_SET_ARNR_MAXFRAMES, VP8E_SET_ARNR_STRENGTH , VP8E_SET_ARNR_TYPE, - VP8E_SET_TUNING, 0 + VP8E_SET_TUNING, VP8E_SET_CQ_LEVEL, 0 }; #endif -- 2.7.4