diff --git a/common/common.c b/common/common.c index 38a7e1e..8134d62 100644 --- a/common/common.c +++ b/common/common.c @@ -95,6 +95,8 @@ void x264_param_default( x264_param_t *param ) param->rc.f_pb_factor = 1.3; param->rc.i_aq_mode = X264_AQ_VARIANCE; param->rc.f_aq_strength = 1.0; + param->rc.i_aq_metric = X264_AQ_METRIC_GIT; + param->rc.f_aq_sensitivity = 10; param->rc.b_stat_write = 0; param->rc.psz_stat_out = "x264_2pass.log"; @@ -547,6 +549,10 @@ int x264_param_parse( x264_param_t *p, const char *name, const char *value ) p->rc.i_aq_mode = atoi(value); OPT("aq-strength") p->rc.f_aq_strength = atof(value); + OPT("aq-metric") + p->rc.i_aq_metric = atoi(value); + OPT("aq-sensitivity") + p->rc.f_aq_sensitivity = atof(value); OPT("pass") { int i = x264_clip3( atoi(value), 0, 3 ); @@ -899,7 +905,7 @@ char *x264_param2string( x264_param_t *p, int b_res ) s += sprintf( s, " pb_ratio=%.2f", p->rc.f_pb_factor ); s += sprintf( s, " aq=%d", p->rc.i_aq_mode ); if( p->rc.i_aq_mode ) - s += sprintf( s, ":%.2f", p->rc.f_aq_strength ); + s += sprintf( s, ":%.2f:%d:%.2f", p->rc.f_aq_strength, p->rc.i_aq_metric, p->rc.f_aq_sensitivity ); if( p->rc.psz_zones ) s += sprintf( s, " zones=%s", p->rc.psz_zones ); else if( p->rc.i_zones ) diff --git a/encoder/encoder.c b/encoder/encoder.c index ba7778e..9ecd1af 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -551,6 +551,9 @@ static int x264_validate_parameters( x264_t *h ) h->param.rc.f_aq_strength = x264_clip3f( h->param.rc.f_aq_strength, 0, 3 ); if( h->param.rc.f_aq_strength == 0 ) h->param.rc.i_aq_mode = 0; + h->param.rc.i_aq_metric = x264_clip3( h->param.rc.i_aq_metric, 0, 2 ); + if( h->param.rc.f_aq_sensitivity < 0 ) + h->param.rc.f_aq_sensitivity = 0; h->param.analyse.i_noise_reduction = x264_clip3( h->param.analyse.i_noise_reduction, 0, 1<<16 ); { diff --git a/encoder/ratecontrol.c b/encoder/ratecontrol.c index ea9cd25..2fd0439 100644 --- a/encoder/ratecontrol.c +++ b/encoder/ratecontrol.c @@ -60,6 +60,8 @@ typedef struct double decay; } predictor_t; +typedef int (*x264_aq_metric_t)( x264_t *h, int mb_x, int mb_y, x264_frame_t *frame ); + struct x264_ratecontrol_t { /* constants */ @@ -168,7 +170,7 @@ static inline double qscale2bits(ratecontrol_entry_t *rce, double qscale) } // Find the total AC energy of the block in all planes. -static NOINLINE int ac_energy_mb( x264_t *h, int mb_x, int mb_y, x264_frame_t *frame ) +static NOINLINE int aqm_GIT( x264_t *h, int mb_x, int mb_y, x264_frame_t *frame ) { /* This function contains annoying hacks because GCC has a habit of reordering emms * and putting it after floating point ops. As a result, we put the emms at the end of the @@ -191,15 +193,67 @@ static NOINLINE int ac_energy_mb( x264_t *h, int mb_x, int mb_y, x264_frame_t *f return var; } +static NOINLINE int aqm_BMver0( x264_t *h, int mb_x, int mb_y, x264_frame_t *frame ) +{ + /* FIXME: This array is larger than necessary because a bug in GCC causes an all-zero + * array to be placed in .bss despite .bss not being correctly aligned on some platforms (win32?) */ + DECLARE_ALIGNED_16( static uint8_t zero[17] ) = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; + DECLARE_ALIGNED_16( uint8_t avg_flat[16] ); + unsigned int var=0, sad, i; + for( i=0; i<3; i++ ) + { + int w = i ? 8 : 16; + int stride = frame->i_stride[i]; + int offset = h->mb.b_interlaced + ? w * (mb_x + (mb_y&~1) * stride) + (mb_y&1) * stride + : w * (mb_x + mb_y * stride); + int pix = i ? PIXEL_8x8 : PIXEL_16x16; + stride <<= h->mb.b_interlaced; + sad = h->pixf.sad[pix]( zero, 0, frame->plane[i]+offset, stride ); + memset( avg_flat, sad >> (i?6:8), sizeof(avg_flat) ); + var += h->pixf.ssd[pix]( avg_flat, 0, frame->plane[i]+offset, stride ); + } + var++; + x264_emms(); + return var; +} + +static NOINLINE int aqm_BMver1( x264_t *h, int mb_x, int mb_y, x264_frame_t *frame ) +{ + unsigned int var=0, sad, i; + for( i=0; i<3; i++ ) + { + int w = i ? 8 : 16; + int stride = frame->i_stride[i]; + int offset = h->mb.b_interlaced + ? w * (mb_x + (mb_y&~1) * stride) + (mb_y&1) * stride + : w * (mb_x + mb_y * stride); + int pix = i ? PIXEL_8x8 : PIXEL_16x16; + stride <<= h->mb.b_interlaced; + var += h->pixf.var[pix]( frame->plane[i]+offset, stride, &sad ); + } + var++; + x264_emms(); + return var; +} + void x264_adaptive_quant_frame( x264_t *h, x264_frame_t *frame ) { int mb_x, mb_y; + x264_aq_metric_t aq_metric_func; + + if( h->param.rc.i_aq_metric == X264_AQ_METRIC_BM_VER_0 ) + aq_metric_func = aqm_BMver0; + else if( h->param.rc.i_aq_metric == X264_AQ_METRIC_BM_VER_1 ) + aq_metric_func = aqm_BMver1; + else + aq_metric_func = aqm_GIT; + for( mb_y=0; mb_ysps->i_mb_height; mb_y++ ) for( mb_x=0; mb_xsps->i_mb_width; mb_x++ ) { - int energy = ac_energy_mb( h, mb_x, mb_y, frame ); - /* 10 constant chosen to result in approximately the same overall bitrate as without AQ. */ - float qp_adj = h->param.rc.f_aq_strength * 1.5 * (logf(energy) - 10.0); + int energy = aq_metric_func( h, mb_x, mb_y, frame ); + float qp_adj = h->param.rc.f_aq_strength * 1.5 * (logf(energy) - h->param.rc.f_aq_sensitivity); frame->f_qp_offset[mb_x + mb_y*h->mb.i_mb_stride] = qp_adj; if( h->frames.b_have_lowres ) frame->i_inv_qscale_factor[mb_x+mb_y*h->mb.i_mb_stride] = FIX8(pow(2.0,-qp_adj/6.0)); diff --git a/x264.c b/x264.c index 778ef2d..b7a3f35 100644 --- a/x264.c +++ b/x264.c @@ -205,6 +205,14 @@ static void Help( x264_param_t *defaults, int b_longhelp ) " textured areas. [%.1f]\n" " - 0.5: weak AQ\n" " - 1.5: strong AQ\n", defaults->rc.f_aq_strength ); + H1( " --aq-metric The metric used for AQ [%d]\n" + " - 0: Original VAQ (complexity mask)\n" + " - 1: Modified VAQ ver. 0 (BM ver. 0)\n" + " - 2: Modified VAQ ver. 1 (BM ver. 1)\n", defaults->rc.i_aq_metric ); + H1( " --aq-sensitivity \"Center\" of AQ curve. [%.1f]\n" + " - 5: most QPs are raised\n" + " - 10: good general-use sensitivity\n" + " - 15: most QPs are lowered\n", defaults->rc.f_aq_sensitivity ); H0( "\n" ); H0( " -p, --pass <1|2|3> Enable multipass ratecontrol\n" " - 1: First pass, creates stats file\n" @@ -440,6 +448,8 @@ static int Parse( int argc, char **argv, { "no-dct-decimate", no_argument, NULL, 0 }, { "aq-strength", required_argument, NULL, 0 }, { "aq-mode", required_argument, NULL, 0 }, + { "aq-metric", required_argument, NULL, 0 }, + { "aq-sensitivity", required_argument, NULL, 0 }, { "deadzone-inter", required_argument, NULL, '0' }, { "deadzone-intra", required_argument, NULL, '0' }, { "level", required_argument, NULL, 0 }, diff --git a/x264.h b/x264.h index 16b02c1..99734f1 100644 --- a/x264.h +++ b/x264.h @@ -88,6 +88,9 @@ typedef struct x264_t x264_t; #define X264_RC_ABR 2 #define X264_AQ_NONE 0 #define X264_AQ_VARIANCE 1 +#define X264_AQ_METRIC_GIT 0 +#define X264_AQ_METRIC_BM_VER_0 1 +#define X264_AQ_METRIC_BM_VER_1 2 #define X264_B_ADAPT_NONE 0 #define X264_B_ADAPT_FAST 1 #define X264_B_ADAPT_TRELLIS 2 @@ -273,6 +276,8 @@ typedef struct x264_param_t int i_aq_mode; /* psy adaptive QP. (X264_AQ_*) */ float f_aq_strength; + int i_aq_metric; + float f_aq_sensitivity; /* 2pass */ int b_stat_write; /* Enable stat writing in psz_stat_out */