diff --git a/common/common.c b/common/common.c index 1f01030..5d3db7c 100644 --- a/common/common.c +++ b/common/common.c @@ -93,8 +93,29 @@ void x264_param_default( x264_param_t *param ) param->rc.i_qp_step = 4; param->rc.f_ip_factor = 1.4; 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_mode = X264_AQ_ORE; + param->rc.i_aq_metric = 0; + param->rc.f_aq_strength = 0.5; + param->rc.f_aq_strength_up[0] = 0; + param->rc.f_aq_strength_up[1] = 0; + param->rc.f_aq_strength_up[2] = 0; + param->rc.f_aq_strength_up[3] = 0; + param->rc.f_aq_strength_down[0] = 0; + param->rc.f_aq_strength_down[1] = 0; + param->rc.f_aq_strength_down[2] = 0; + param->rc.f_aq_strength_down[3] = 0; + param->rc.f_aq_sensitivity = 10; + param->rc.f_aq_ifactor[0] = 1.0; + param->rc.f_aq_ifactor[1] = 1.0; + param->rc.f_aq_pfactor[0] = 1.0; + param->rc.f_aq_pfactor[1] = 1.0; + param->rc.f_aq_bfactor[0] = 1.0; + param->rc.f_aq_bfactor[1] = 1.0; + param->rc.b_aq_boundary = 0; + param->rc.i_aq_boundary[0] = 192; + param->rc.i_aq_boundary[1] = 64; + param->rc.i_aq_boundary[2] = 24; + param->rc.b_aq_debug = 0; param->rc.i_lookahead = 40; param->rc.b_stat_write = 0; @@ -551,8 +572,93 @@ int x264_param_parse( x264_param_t *p, const char *name, const char *value ) p->rc.f_pb_factor = atof(value); OPT("aq-mode") p->rc.i_aq_mode = atoi(value); + OPT("aq-metric") + p->rc.i_aq_metric = atoi(value); OPT("aq-strength") - p->rc.f_aq_strength = atof(value); + { + if( 7 == sscanf( value, "%f:%f:%f:%f:%f:%f:%f", + &p->rc.f_aq_strength_up[0], &p->rc.f_aq_strength_down[0], &p->rc.f_aq_strength_up[1], &p->rc.f_aq_strength_down[1], + &p->rc.f_aq_strength_up[2], &p->rc.f_aq_strength_down[2], &p->rc.f_aq_strength_up[3] ) || + 7 == sscanf( value, "%f,%f,%f,%f,%f,%f,%f", + &p->rc.f_aq_strength_up[0], &p->rc.f_aq_strength_down[0], &p->rc.f_aq_strength_up[1], &p->rc.f_aq_strength_down[1], + &p->rc.f_aq_strength_up[2], &p->rc.f_aq_strength_down[2], &p->rc.f_aq_strength_up[3] ) ) + { + p->rc.f_aq_strength = 0.0; + } + else if( 2 == sscanf( value, "%f:%f", &p->rc.f_aq_strength_up[0], &p->rc.f_aq_strength_down[0] ) || + 2 == sscanf( value, "%f,%f", &p->rc.f_aq_strength_up[0], &p->rc.f_aq_strength_down[0] ) ) + { + p->rc.f_aq_strength = 0.0; + for( i=1; i<4; i++ ) + { + p->rc.f_aq_strength_up[i] = p->rc.f_aq_strength_up[0]; + p->rc.f_aq_strength_down[i] = p->rc.f_aq_strength_down[0]; + } + } + else if( sscanf( value, "%f", &p->rc.f_aq_strength ) ) + { + for( i=0; i<4; i++ ) + { + p->rc.f_aq_strength_up[i] = p->rc.f_aq_strength; + p->rc.f_aq_strength_down[i] = p->rc.f_aq_strength; + } + } + } + OPT("aq-sensitivity") + p->rc.f_aq_sensitivity = atof(value); + OPT("aq-ifactor") + if( 2 == sscanf( value, "%f:%f", &p->rc.f_aq_ifactor[0], &p->rc.f_aq_ifactor[1] ) || + 2 == sscanf( value, "%f,%f", &p->rc.f_aq_ifactor[0], &p->rc.f_aq_ifactor[1] ) ) + { } + else if( sscanf( value, "%f", &p->rc.f_aq_ifactor[0] ) ) + { + p->rc.f_aq_ifactor[1] = p->rc.f_aq_ifactor[0]; + } + else + { + p->rc.f_aq_ifactor[1] = p->rc.f_aq_ifactor[0] = 1.0; + } + OPT("aq-pfactor") + if( 2 == sscanf( value, "%f:%f", &p->rc.f_aq_pfactor[0], &p->rc.f_aq_pfactor[1] ) || + 2 == sscanf( value, "%f,%f", &p->rc.f_aq_pfactor[0], &p->rc.f_aq_pfactor[1] ) ) + { } + else if( sscanf( value, "%f", &p->rc.f_aq_pfactor[0] ) ) + { + p->rc.f_aq_pfactor[1] = p->rc.f_aq_pfactor[0]; + } + else + { + p->rc.f_aq_pfactor[1] = p->rc.f_aq_pfactor[0] = 1.0; + } + OPT("aq-bfactor") + if( 2 == sscanf( value, "%f:%f", &p->rc.f_aq_bfactor[0], &p->rc.f_aq_bfactor[1] ) || + 2 == sscanf( value, "%f,%f", &p->rc.f_aq_bfactor[0], &p->rc.f_aq_bfactor[1] ) ) + { } + else if( sscanf( value, "%f", &p->rc.f_aq_bfactor[0] ) ) + { + p->rc.f_aq_bfactor[1] = p->rc.f_aq_bfactor[0]; + } + else + { + p->rc.f_aq_bfactor[1] = p->rc.f_aq_bfactor[0] = 1.0; + } + OPT("aq-boundary") + { + if( 3 == sscanf( value, "%d:%d:%d", &p->rc.i_aq_boundary[0], &p->rc.i_aq_boundary[1], &p->rc.i_aq_boundary[2] ) || + 3 == sscanf( value, "%d,%d,%d", &p->rc.i_aq_boundary[0], &p->rc.i_aq_boundary[1], &p->rc.i_aq_boundary[2] ) ) + { + p->rc.b_aq_boundary = 1; + } + else + { + p->rc.i_aq_boundary[0] = p->rc.i_aq_boundary[1] = p->rc.i_aq_boundary[2] = 0; + } + } + OPT("aq-debug") + { + p->rc.b_aq_debug = 1; + p->rc.psz_aq_debug_name = strdup(value); + } OPT("pass") { int i = x264_clip3( atoi(value), 0, 3 ); @@ -897,7 +1003,17 @@ 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, " aq-metric=%d", p->rc.i_aq_metric ); + s += sprintf( s, " aq-strength=%.2f:%.2f:%.2f:%.2f:%.2f:%.2f:%.2f", + p->rc.f_aq_strength_up[0], p->rc.f_aq_strength_down[0], p->rc.f_aq_strength_up[1], p->rc.f_aq_strength_down[1], + p->rc.f_aq_strength_up[2], p->rc.f_aq_strength_down[2], p->rc.f_aq_strength_up[3] ); + s += sprintf( s, " aq-sensitivity=%.2f", p->rc.f_aq_sensitivity ); + s += sprintf( s, " aq-ifactor=%.2f:%.2f", p->rc.f_aq_ifactor[0], p->rc.f_aq_ifactor[1] ); + s += sprintf( s, " aq-pfactor=%.2f:%.2f", p->rc.f_aq_pfactor[0], p->rc.f_aq_pfactor[1] ); + s += sprintf( s, " aq-bfactor=%.2f:%.2f", p->rc.f_aq_bfactor[0], p->rc.f_aq_bfactor[1] ); + s += sprintf( s, " aq-boundary=%d:%d:%d", p->rc.i_aq_boundary[0], p->rc.i_aq_boundary[1], p->rc.i_aq_boundary[2] ); + } if( p->rc.psz_zones ) s += sprintf( s, " zones=%s", p->rc.psz_zones ); else if( p->rc.i_zones ) diff --git a/common/common.h b/common/common.h index fa0f9c8..0dc761f 100644 --- a/common/common.h +++ b/common/common.h @@ -618,6 +618,13 @@ struct x264_t /* Metrics */ int64_t i_ssd[3]; double f_ssim; + + /* AQ stats per frame */ + uint64_t i_aq_count[5]; + uint64_t i_aq_result[5][3]; + uint64_t i_aq_change[101]; + int i_aq_change_min; + int i_aq_change_max; } frame; /* Cumulated stats */ @@ -643,7 +650,13 @@ struct x264_t /* */ int i_direct_score[2]; int i_direct_frames[2]; - + /* */ + uint64_t i_aq_count_total; + uint64_t i_aq_count[5]; + uint64_t i_aq_result[5][3]; + uint64_t i_aq_change[101]; + int i_aq_change_min; + int i_aq_change_max; } stat; void *scratch_buffer; /* for any temporary storage that doesn't want repeated malloc */ diff --git a/common/frame.c b/common/frame.c index 2e5ba44..0490d7e 100644 --- a/common/frame.c +++ b/common/frame.c @@ -143,6 +143,7 @@ x264_frame_t *x264_frame_new( x264_t *h ) { CHECKED_MALLOC( frame->f_qp_offset, h->mb.i_mb_count * sizeof(float) ); CHECKED_MALLOC( frame->f_qp_offset_aq, h->mb.i_mb_count * sizeof(float) ); + CHECKED_MALLOC( frame->i_aq_type, h->mb.i_mb_count * sizeof(uint8_t) ); if( h->frames.b_have_lowres ) /* shouldn't really be initialized, just silences a valgrind false-positive in x264_mbtree_propagate_cost_sse2 */ CHECKED_MALLOCZERO( frame->i_inv_qscale_factor, (h->mb.i_mb_count+3) * sizeof(uint16_t) ); @@ -185,6 +186,7 @@ void x264_frame_delete( x264_frame_t *frame ) } x264_free( frame->f_qp_offset ); x264_free( frame->f_qp_offset_aq ); + x264_free( frame->i_aq_type ); x264_free( frame->i_inv_qscale_factor ); x264_free( frame->i_row_bits ); x264_free( frame->i_row_qp ); diff --git a/common/frame.h b/common/frame.h index 9ca83f9..1e08785 100644 --- a/common/frame.h +++ b/common/frame.h @@ -87,6 +87,7 @@ typedef struct int *i_row_qp; float *f_qp_offset; float *f_qp_offset_aq; + uint8_t *i_aq_type; // OreAQ mode type int b_intra_calculated; uint16_t *i_intra_cost; uint16_t *i_propagate_cost; diff --git a/common/pixel.c b/common/pixel.c index 292cdf5..2c2d9de 100644 --- a/common/pixel.c +++ b/common/pixel.c @@ -67,6 +67,14 @@ PIXEL_SAD_C( x264_pixel_sad_8x4, 8, 4 ) PIXEL_SAD_C( x264_pixel_sad_4x8, 4, 8 ) PIXEL_SAD_C( x264_pixel_sad_4x4, 4, 4 ) +static int x264_pixel_count_8x8( uint8_t *pix, int i_pix, uint32_t threshold ) +{ + int x, y, sum = 0; + for( y=0; y<8; y++, pix += i_pix ) + for( x=0; x<8; x++ ) + sum += pix[x] > (uint8_t)threshold; + return sum; +} /**************************************************************************** * pixel_ssd_WxH @@ -643,6 +651,7 @@ void x264_pixel_init( int cpu, x264_pixel_function_t *pixf ) pixf->ssim_4x4x2_core = ssim_4x4x2_core; pixf->ssim_end4 = ssim_end4; pixf->var2_8x8 = pixel_var2_8x8; + pixf->count_8x8 = x264_pixel_count_8x8; #ifdef HAVE_MMX if( cpu&X264_CPU_MMX ) diff --git a/common/pixel.h b/common/pixel.h index 53f9956..bfb178c 100644 --- a/common/pixel.h +++ b/common/pixel.h @@ -93,6 +93,8 @@ typedef struct int (*ads[7])( int enc_dc[4], uint16_t *sums, int delta, uint16_t *cost_mvx, int16_t *mvs, int width, int thresh ); + int (*count_8x8)( uint8_t *pix, int i_pix, uint32_t threshold ); + /* calculate satd or sad of V, H, and DC modes. * may be NULL, in which case just use pred+satd instead. */ void (*intra_mbcmp_x3_16x16)( uint8_t *fenc, uint8_t *fdec , int res[3] ); diff --git a/encoder/analyse.c b/encoder/analyse.c index a2e64fe..146ece7 100644 --- a/encoder/analyse.c +++ b/encoder/analyse.c @@ -184,7 +184,10 @@ const float x264_log2_lut[128] = { /* Avoid an int/float conversion. */ const float x264_log2_lz_lut[32] = { - 31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0 + 31.00024, 30.00024, 29.00024, 28.00024, 27.00024, 26.00024, 25.00024, 24.00024, + 23.00024, 22.00024, 21.00024, 20.00024, 19.00024, 18.00024, 17.00024, 16.00024, + 15.00024, 14.00024, 13.00024, 12.00024, 11.00024, 10.00024, 9.00024, 8.00024, + 7.00024, 6.00024, 5.00024, 4.00024, 3.00024, 2.00024, 1.00024, 0.00024 }; // should the intra and inter lambdas be different? diff --git a/encoder/encoder.c b/encoder/encoder.c index d84457d..a55a394 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -337,6 +337,8 @@ fail: static int x264_validate_parameters( x264_t *h ) { + int i; + #ifdef HAVE_MMX if( !(x264_cpu_detect() & X264_CPU_SSE) ) { @@ -569,9 +571,52 @@ static int x264_validate_parameters( x264_t *h ) h->mb.i_psy_trellis = 0; h->param.analyse.i_chroma_qp_offset = x264_clip3(h->param.analyse.i_chroma_qp_offset, -12, 12); h->param.rc.i_aq_mode = x264_clip3( h->param.rc.i_aq_mode, 0, 2 ); - 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, 1 ); + h->param.rc.f_aq_strength = x264_clip3f( h->param.rc.f_aq_strength, -3, 3 ); + for( i=0; i<4; i++ ) + { + h->param.rc.f_aq_strength_up[i] = x264_clip3f( h->param.rc.f_aq_strength_up[i], -3, 3 ); + h->param.rc.f_aq_strength_down[i] = x264_clip3f( h->param.rc.f_aq_strength_down[i], -3, 3 ); + } + if( h->param.rc.f_aq_strength_up[0] == 0 && h->param.rc.f_aq_strength_down[0] == 0 && + h->param.rc.f_aq_strength_up[1] == 0 && h->param.rc.f_aq_strength_down[1] == 0 && + h->param.rc.f_aq_strength_up[2] == 0 && h->param.rc.f_aq_strength_down[2] == 0 && + h->param.rc.f_aq_strength_up[3] == 0 ) + { + if( h->param.rc.f_aq_strength == 0 ) + h->param.rc.i_aq_mode = 0; + else + { + for( i=0; i<4; i++ ) + { + h->param.rc.f_aq_strength_up[i] = h->param.rc.f_aq_strength; + h->param.rc.f_aq_strength_down[i] = h->param.rc.f_aq_strength; + } + } + } + if( h->param.rc.f_aq_sensitivity < 0 ) + h->param.rc.f_aq_sensitivity = 0; + h->param.rc.f_aq_ifactor[0] = x264_clip3f( h->param.rc.f_aq_ifactor[0], -10, 10 ); + h->param.rc.f_aq_ifactor[1] = x264_clip3f( h->param.rc.f_aq_ifactor[1], -10, 10 ); + h->param.rc.f_aq_pfactor[0] = x264_clip3f( h->param.rc.f_aq_pfactor[0], -10, 10 ); + h->param.rc.f_aq_pfactor[1] = x264_clip3f( h->param.rc.f_aq_pfactor[1], -10, 10 ); + h->param.rc.f_aq_bfactor[0] = x264_clip3f( h->param.rc.f_aq_bfactor[0], -10, 10 ); + h->param.rc.f_aq_bfactor[1] = x264_clip3f( h->param.rc.f_aq_bfactor[1], -10, 10 ); + h->param.rc.i_aq_boundary[0] = x264_clip3( h->param.rc.i_aq_boundary[0], 0, 255 ); + h->param.rc.i_aq_boundary[1] = x264_clip3( h->param.rc.i_aq_boundary[1], 0, 255 ); + h->param.rc.i_aq_boundary[2] = x264_clip3( h->param.rc.i_aq_boundary[2], 0, 255 ); + if( !h->param.rc.b_aq_boundary || + h->param.rc.i_aq_boundary[0] <= h->param.rc.i_aq_boundary[1] || h->param.rc.i_aq_boundary[1] <= h->param.rc.i_aq_boundary[2] ) + { + h->param.rc.i_aq_boundary[0] = h->param.vui.b_fullrange ? 205 : 192; + h->param.rc.i_aq_boundary[1] = h->param.vui.b_fullrange ? 56 : 64; + h->param.rc.i_aq_boundary[2] = h->param.vui.b_fullrange ? 9 : 24; + } + if( h->param.rc.b_aq_debug && h->param.rc.b_mb_tree ) + { + x264_log( h, X264_LOG_WARNING, "aq-debug + mb-tree is not supported\n" ); + h->param.rc.b_mb_tree = 0; + } /* MB-tree requires AQ to be on, even if the strength is zero. */ if( !h->param.rc.i_aq_mode && h->param.rc.b_mb_tree ) { @@ -922,6 +967,27 @@ int x264_encoder_reconfig( x264_t *h, x264_param_t *param ) COPY( analyse.b_mixed_references ); COPY( analyse.f_psy_rd ); COPY( analyse.f_psy_trellis ); + COPY( rc.i_aq_mode ); + COPY( rc.i_aq_metric ); + COPY( rc.f_aq_strength ); + COPY( rc.f_aq_strength_up[0] ); + COPY( rc.f_aq_strength_up[1] ); + COPY( rc.f_aq_strength_up[2] ); + COPY( rc.f_aq_strength_up[3] ); + COPY( rc.f_aq_strength_down[0] ); + COPY( rc.f_aq_strength_down[1] ); + COPY( rc.f_aq_strength_down[2] ); + COPY( rc.f_aq_sensitivity ); + COPY( rc.f_aq_ifactor[0] ); + COPY( rc.f_aq_ifactor[1] ); + COPY( rc.f_aq_pfactor[0] ); + COPY( rc.f_aq_pfactor[1] ); + COPY( rc.f_aq_bfactor[0] ); + COPY( rc.f_aq_bfactor[1] ); + COPY( rc.b_aq_boundary ); + COPY( rc.i_aq_boundary[0] ); + COPY( rc.i_aq_boundary[1] ); + COPY( rc.i_aq_boundary[2] ); // can only twiddle these if they were enabled to begin with: if( h->param.analyse.i_me_method >= X264_ME_ESA || param->analyse.i_me_method < X264_ME_ESA ) COPY( analyse.i_me_method ); @@ -1844,6 +1910,23 @@ static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current, snprintf( psz_message + strlen(psz_message), 80 - strlen(psz_message), " SSIM Y:%.5f", ssim_y ); } + + if( h->param.rc.i_aq_mode == X264_AQ_ORE ) + { + h->stat.i_aq_count_total += h->mb.i_mb_count; + for( i=0; i<5; i++ ) + { + h->stat.i_aq_count[i] += h->stat.frame.i_aq_count[i]; + h->stat.i_aq_result[i][0] += h->stat.frame.i_aq_result[i][0]; + h->stat.i_aq_result[i][1] += h->stat.frame.i_aq_result[i][1]; + h->stat.i_aq_result[i][2] += h->stat.frame.i_aq_result[i][2]; + } + for( i=h->stat.frame.i_aq_change_min+50; i<=h->stat.frame.i_aq_change_max+50; i++ ) + h->stat.i_aq_change[i] += h->stat.frame.i_aq_change[i]; + h->stat.i_aq_change_min = X264_MIN(h->stat.i_aq_change_min, h->stat.frame.i_aq_change_min); + h->stat.i_aq_change_max = X264_MAX(h->stat.i_aq_change_max, h->stat.frame.i_aq_change_max); + } + psz_message[79] = '\0'; x264_log( h, X264_LOG_DEBUG, @@ -2095,6 +2178,46 @@ void x264_encoder_close ( x264_t *h ) } } + if( h->stat.i_aq_count_total ) + { +#define GET_AQ_RESULT(x) \ + 100.0 / h->stat.i_aq_count_total * h->stat.i_aq_count[x], \ + h->stat.i_aq_count[x] ? (100.0 / h->stat.i_aq_count[x] * h->stat.i_aq_result[x][0]) : 0, \ + h->stat.i_aq_count[x] ? (100.0 / h->stat.i_aq_count[x] * h->stat.i_aq_result[x][1]) : 0, \ + h->stat.i_aq_count[x] ? (100.0 / h->stat.i_aq_count[x] * h->stat.i_aq_result[x][2]) : 0 + char buf[1500]; + char *p = buf; + + if( h->stat.i_aq_count[0] ) + x264_log( h, X264_LOG_INFO, + "AQ Result MBTree MB:%5.2f%% QP Up:%5.2f%% Down:%5.2f%% Even:%5.2f%%\n", + GET_AQ_RESULT(0)); + + if( h->stat.i_aq_count[1] || h->stat.i_aq_count[2] || h->stat.i_aq_count[3] || h->stat.i_aq_count[4] ) + { + x264_log( h, X264_LOG_INFO, + "AQ Result Bright MB:%5.2f%% QP Up:%5.2f%% Down:%5.2f%% Even:%5.2f%%\n", + GET_AQ_RESULT(1)); + + x264_log( h, X264_LOG_INFO, + "AQ Result Middle MB:%5.2f%% QP Up:%5.2f%% Down:%5.2f%% Even:%5.2f%%\n", + GET_AQ_RESULT(2)); + + x264_log( h, X264_LOG_INFO, + "AQ Result Dark MB:%5.2f%% QP Up:%5.2f%% Down:%5.2f%% Even:%5.2f%%\n", + GET_AQ_RESULT(3)); + + x264_log( h, X264_LOG_INFO, + "AQ Result M.Dark MB:%5.2f%% QP Up:%5.2f%% Down:%5.2f%% Even:%5.2f%%\n", + GET_AQ_RESULT(4)); + } + + for(i = h->stat.i_aq_change_min; i<=h->stat.i_aq_change_max; i++) + p += sprintf(p, " %d:%.2f%%", -i, 100.0 / h->stat.i_aq_count_total * h->stat.i_aq_change[i+50]); + x264_log( h, X264_LOG_INFO, "AQ change value %s\n", buf); + + } + if( h->param.analyse.b_ssim ) { x264_log( h, X264_LOG_INFO, diff --git a/encoder/ratecontrol.c b/encoder/ratecontrol.c index fd9aa94..a25aaf3 100644 --- a/encoder/ratecontrol.c +++ b/encoder/ratecontrol.c @@ -61,6 +61,8 @@ typedef struct double offset; } predictor_t; +typedef uint32_t (*x264_aq_metric_t)( x264_t *h, int mb_x, int mb_y, x264_frame_t *frame, uint32_t *energy ); + struct x264_ratecontrol_t { /* constants */ @@ -135,11 +137,17 @@ struct x264_ratecontrol_t int bframes; /* # consecutive B-frames before this P-frame */ int bframe_bits; /* total cost of those frames */ + /* AQ stuff */ + float aq_threshold; + FILE *p_aq_debug; + void *aqlog; + int i_zones; x264_zone_t *zones; x264_zone_t *prev_zone; }; +#include "AQDebugLog.h" static int parse_zones( x264_t *h ); static int init_pass2(x264_t *); @@ -176,7 +184,7 @@ static inline double qscale2bits(ratecontrol_entry_t *rce, double qscale) } // Find the total AC energy of the block in all planes. -static NOINLINE uint32_t ac_energy_mb( x264_t *h, int mb_x, int mb_y, x264_frame_t *frame ) +static NOINLINE uint32_t ac_energy_mb( x264_t *h, int mb_x, int mb_y, x264_frame_t *frame, uint32_t *energy ) { /* 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 @@ -192,37 +200,205 @@ static NOINLINE uint32_t ac_energy_mb( x264_t *h, int mb_x, int mb_y, x264_frame : 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 ); + energy[i] = h->pixf.var[pix]( frame->plane[i]+offset, stride ); + var += energy[i]; } x264_emms(); return var; } +static NOINLINE uint32_t aqm_mod( x264_t *h, int mb_x, int mb_y, x264_frame_t *frame, uint32_t *energy ) +{ + ALIGNED_16( static uint8_t zero[17] ) = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; + ALIGNED_16( static uint8_t avg_flat[16] ); + uint32_t 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) ); + energy[i] = h->pixf.ssd[pix]( avg_flat, 0, frame->plane[i]+offset, stride ); + var += energy[i]; + } + x264_emms(); + return var; +} + +static NOINLINE void get_image_mb( x264_t *h, int mb_x, int mb_y, x264_frame_t *frame, int *luma, int *bluePoint ) +{ + ALIGNED_16( static uint8_t zero[17] ) = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; + int stride[3]; + int offset[3]; + int i; + + for( i=0; i<3; i++ ) + { + int w = i ? 8 : 16; + stride[i] = frame->i_stride[i]; + offset[i] = h->mb.b_interlaced + ? w * (mb_x + (mb_y&~1) * stride[i]) + (mb_y&1) * stride[i] + : w * (mb_x + mb_y * stride[i]); + stride[i] <<= h->mb.b_interlaced; + } + + *luma = h->pixf.sad[PIXEL_16x16](frame->plane[0]+offset[0], stride[0], zero, 0)>>8; + *bluePoint = + (h->pixf.count_8x8(frame->plane[1]+offset[1], stride[1], 0x81818181) >= 40) && + (h->pixf.count_8x8(frame->plane[2]+offset[2], stride[2], 0x87878787) <= 24); +} + +static NOINLINE int x264_adjust_OreAQ( x264_t *h, int mb_x, int mb_y, x264_frame_t *frame, uint32_t *energy ) +{ + uint8_t mode; + int bluePoint = 0; + int luma = 0; + float energy_y, energy_uv, f_qp_adj; + uint32_t _energy_y, _energy_uv; + + get_image_mb( h, mb_x, mb_y, frame, &luma, &bluePoint ); + x264_emms(); + + _energy_y = X264_MAX( energy[0], 1 ); + _energy_uv = X264_MAX( energy[1] + energy[2], 1 ); + + if(h->param.rc.b_aq_debug) + x264_write_aqlog_body( h, mb_x + mb_y*h->mb.i_mb_stride, frame, luma, bluePoint, (float)_energy_y, (float)_energy_uv ); + + // logf(energy) = (1.0397 * (log2_lut[(energy<>24)&0x7f] - lz + 31.00024f) / 1.5 + // energy_y = 1.2 * (logf(_energy_y ) - (h->rc->aq_threshold * .91) + .5); + // energy_uv = 0.8 * (logf(_energy_uv) - (h->rc->aq_threshold * .91) + .5); + energy_y = 0.83176f * x264_log2( _energy_y ) - (h->rc->aq_threshold * 1.092f) + .5f; + energy_uv = 0.55451f * x264_log2( _energy_uv ) - (h->rc->aq_threshold * 0.728f) + .5f; + f_qp_adj = 0.0; + + // lumaの平均値から処理分岐 + if(luma > h->param.rc.i_aq_boundary[0]){ + // *** 明部 *** + // Y & UV Flat -> qp up + // Y Flat / UV Bump -> qp y up + // Y Bump / UV Flat -> even + // Y & UV Bump -> qp down + mode = 0x00; + + // qp up + if(!bluePoint && energy_y < 0 && energy_uv < 0) + f_qp_adj = X264_MIN(energy_y, energy_uv); + // qp y up + else if(!bluePoint && energy_y < 0 && energy_uv >= 0) + f_qp_adj = energy_y; + // qp down + else if (energy_y >= 0 && energy_uv >= 0) + f_qp_adj = X264_MAX(energy_y, energy_uv) * .5; + } else if(luma > h->param.rc.i_aq_boundary[1]){ + // *** 中間部 *** + // Y & UV Flat -> qp up + // Y Flat / UV Bump -> even + // Y Bump / UV Flat -> qp mix down + // Y & UV Bump -> qp down + mode = 0x01; + + // qp up + if(!bluePoint && energy_y < 0 && energy_uv < 0) + f_qp_adj = X264_MAX(energy_y, energy_uv); + // qp mix down + else if (energy_y >= 0 && energy_uv < 0) + f_qp_adj = X264_MAX(energy_y + (!bluePoint * energy_uv), 0) * .5; + // qp down + else if (energy_y >= 0 && energy_uv >= 0) + f_qp_adj = X264_MAX(energy_y, bluePoint * energy_uv); + } else if(luma > h->param.rc.i_aq_boundary[2]){ + // *** 暗部 *** + // Y & UV Flat -> qp up + // Y Flat / UV Bump -> qp uv down + // Y Bump / UV Flat -> qp y down + // Y & UV Bump -> qp down + mode = 0x02; + + // qp up + if(energy_y < 0 && energy_uv < 0) + f_qp_adj = X264_MAX(energy_y, energy_uv) * .5; + // qp uv down + else if (energy_y < 0 && energy_uv >= 0) + f_qp_adj = energy_uv * 1.25; + // qp y down + else if (energy_y >= 0 && energy_uv < 0) + f_qp_adj = energy_y * 1.25; + // qp down + else if (energy_y >= 0 && energy_uv >= 0) + f_qp_adj = X264_MAX(energy_y, energy_uv) * 1.25; + } else { + // *** 超暗部 *** + // Y & UV Flat -> qp double up + // Y Flat / UV Bump -> qp y up + // Y Bump / UV Flat -> qp uv up + // Y & UV Bump -> even + mode = 0x03; + + // qp double up + if(energy_y < 0 && energy_uv < 0) + f_qp_adj = energy_y + energy_uv; + // qp mix up + else if (energy_y < 0 && energy_uv >= 0) + f_qp_adj = energy_y; + // qp uv down + else if (energy_y >= 0 && energy_uv < 0) + f_qp_adj = energy_uv * .5; + } + + // qp down + if(f_qp_adj > 0) + f_qp_adj *= h->param.rc.f_aq_strength_down[mode]; + else + f_qp_adj *= h->param.rc.f_aq_strength_up[mode]; + + frame->i_aq_type[mb_x + mb_y*h->mb.i_mb_stride] = mode + 0x01; + + return -f_qp_adj; +} + void x264_adaptive_quant_frame( x264_t *h, x264_frame_t *frame ) { /* constants chosen to result in approximately the same overall bitrate as without AQ. * FIXME: while they're written in 5 significant digits, they're only tuned to 2. */ int mb_x, mb_y; - float strength; + float strength = 0.f; float avg_adj = 0.f; + uint32_t energy_yuv[3] = {0, 0, 0}; + x264_aq_metric_t aq_metric_func; + /* Need to init it anyways for MB tree. */ - if( h->param.rc.f_aq_strength == 0 ) + if( h->param.rc.f_aq_strength_up[0] == 0 && h->param.rc.f_aq_strength_down[0] == 0 && + h->param.rc.f_aq_strength_up[1] == 0 && h->param.rc.f_aq_strength_down[1] == 0 && + h->param.rc.f_aq_strength_up[2] == 0 && h->param.rc.f_aq_strength_down[2] == 0 && + h->param.rc.f_aq_strength_up[3] == 0 ) { int mb_xy; memset( frame->f_qp_offset, 0, h->mb.i_mb_count * sizeof(float) ); memset( frame->f_qp_offset_aq, 0, h->mb.i_mb_count * sizeof(float) ); + memset( frame->i_aq_type, 0, h->mb.i_mb_count * sizeof(uint8_t) ); if( h->frames.b_have_lowres ) for( mb_xy = 0; mb_xy < h->mb.i_mb_count; mb_xy++ ) frame->i_inv_qscale_factor[mb_xy] = 256; return; } + if( h->param.rc.i_aq_metric == 0 ) + aq_metric_func = ac_energy_mb; + else + aq_metric_func = aqm_mod; + if( h->param.rc.i_aq_mode == X264_AQ_AUTOVARIANCE ) { for( mb_y = 0; mb_y < h->sps->i_mb_height; mb_y++ ) for( mb_x = 0; mb_x < h->sps->i_mb_width; mb_x++ ) { - uint32_t energy = ac_energy_mb( h, mb_x, mb_y, frame ); + uint32_t energy = aq_metric_func( h, mb_x, mb_y, frame, energy_yuv ); float qp_adj = x264_log2( energy + 2 ); qp_adj *= qp_adj; frame->f_qp_offset[mb_x + mb_y*h->mb.i_mb_stride] = qp_adj; @@ -232,7 +408,7 @@ void x264_adaptive_quant_frame( x264_t *h, x264_frame_t *frame ) strength = h->param.rc.f_aq_strength * avg_adj * (1.f / 6000.f); } else - strength = h->param.rc.f_aq_strength * 1.0397f; + h->rc->aq_threshold = logf(powf(h->param.rc.f_aq_sensitivity,4)/2.0); for( mb_y = 0; mb_y < h->sps->i_mb_height; mb_y++ ) for( mb_x = 0; mb_x < h->sps->i_mb_width; mb_x++ ) @@ -245,8 +421,8 @@ void x264_adaptive_quant_frame( x264_t *h, x264_frame_t *frame ) } else { - uint32_t energy = ac_energy_mb( h, mb_x, mb_y, frame ); - qp_adj = strength * (x264_log2( X264_MAX(energy, 1) ) - 14.427f); + uint32_t energy = aq_metric_func( h, mb_x, mb_y, frame, energy_yuv ); + qp_adj = x264_adjust_OreAQ( h, mb_x, mb_y, frame, energy_yuv ); } frame->f_qp_offset[mb_x + mb_y*h->mb.i_mb_stride] = frame->f_qp_offset_aq[mb_x + mb_y*h->mb.i_mb_stride] = qp_adj; @@ -265,8 +441,38 @@ void x264_adaptive_quant_frame( x264_t *h, x264_frame_t *frame ) *****************************************************************************/ void x264_adaptive_quant( x264_t *h ) { + int i_qp_adj; + float factor, f_qp_adj; + x264_emms(); - h->mb.i_qp = x264_clip3( h->rc->f_qpm + h->fenc->f_qp_offset[h->mb.i_mb_xy] + .5, h->param.rc.i_qp_min, h->param.rc.i_qp_max ); + + f_qp_adj = h->fenc->f_qp_offset[h->mb.i_mb_xy]; + if( h->sh.i_type == SLICE_TYPE_I ) + factor = h->param.rc.f_aq_ifactor[f_qp_adj<0]; + else if( h->sh.i_type == SLICE_TYPE_P ) + factor = h->param.rc.f_aq_pfactor[f_qp_adj<0]; + else if( h->sh.i_type == SLICE_TYPE_B ) + factor = h->param.rc.f_aq_bfactor[f_qp_adj<0]; + else + factor = 1.0; + + if( h->param.rc.i_aq_mode == X264_AQ_ORE ) + { + h->mb.i_qp = (int)(h->rc->f_qpm + factor * f_qp_adj + .5); + i_qp_adj = x264_clip3( h->rc->f_qpm - h->mb.i_qp + .5, -50, 50 ); + + h->stat.frame.i_aq_change[i_qp_adj + 50] += 1; + h->stat.frame.i_aq_count[ h->fenc->i_aq_type[h->mb.i_mb_xy] ] += 1; + h->stat.frame.i_aq_result[ h->fenc->i_aq_type[h->mb.i_mb_xy] ][ (i_qp_adj<0?0:(i_qp_adj>0?1:2)) ] += 1; + h->stat.frame.i_aq_change_min = X264_MIN(h->stat.frame.i_aq_change_min, i_qp_adj); + h->stat.frame.i_aq_change_max = X264_MAX(h->stat.frame.i_aq_change_max, i_qp_adj); + + h->mb.i_qp = x264_clip3( h->mb.i_qp, h->param.rc.i_qp_min, h->param.rc.i_qp_max ); + } + else if( h->param.rc.i_aq_mode == X264_AQ_AUTOVARIANCE ) + { + h->mb.i_qp = x264_clip3( h->rc->f_qpm + factor * f_qp_adj + .5, h->param.rc.i_qp_min, h->param.rc.i_qp_max ); + } } int x264_macroblock_tree_read( x264_t *h, x264_frame_t *frame ) @@ -290,6 +496,8 @@ int x264_macroblock_tree_read( x264_t *h, x264_frame_t *frame ) if( fread( rc->qp_buffer, sizeof(uint16_t), h->mb.i_mb_count, rc->p_mbtree_stat_file_in ) != h->mb.i_mb_count ) goto fail; + if( fread( frame->i_aq_type, sizeof(uint8_t), h->mb.i_mb_count, rc->p_mbtree_stat_file_in ) != h->mb.i_mb_count ) + goto fail; for( i = 0; i < h->mb.i_mb_count; i++ ) frame->f_qp_offset[i] = ((float)(int16_t)endian_fix16( rc->qp_buffer[i] )) * (1/256.0); @@ -665,6 +873,21 @@ int x264_ratecontrol_new( x264_t *h ) if( h->param.rc.b_mb_tree && (h->param.rc.b_stat_read || h->param.rc.b_stat_write) ) CHECKED_MALLOC( rc->qp_buffer, h->mb.i_mb_count * sizeof(uint16_t) ); + /* Open AQ Log file */ + if( h->param.rc.b_aq_debug ) + { + rc->p_aq_debug = fopen( h->param.rc.psz_aq_debug_name, "wb" ); + if( rc->p_aq_debug == NULL ) + { + x264_log(h, X264_LOG_ERROR, "ratecontrol_init: can't open AQ debug file\n"); + return -1; + } + setvbuf( rc->p_aq_debug, NULL, _IOFBF, 1024*1024*8 ); // 8M Buffer + x264_write_aqlog_header( h, "OreAQ04", 0, + h->param.rc.f_aq_strength_up, h->param.rc.f_aq_strength_down, h->param.rc.f_aq_sensitivity, 0, + h->param.rc.i_aq_boundary ); + } + for( i=0; iparam.i_threads; i++ ) { h->thread[i]->rc = rc+i; @@ -855,6 +1078,8 @@ void x264_ratecontrol_delete( x264_t *h ) rc->zones[i].param->param_free( rc->zones[i].param ); x264_free( rc->zones ); } + if( rc->p_aq_debug ) + fclose( rc->p_aq_debug ); x264_free( rc ); } @@ -1224,6 +1449,8 @@ int x264_ratecontrol_end( x264_t *h, int bits ) goto fail; if( fwrite( rc->qp_buffer, sizeof(uint16_t), h->mb.i_mb_count, rc->p_mbtree_stat_file_out ) < h->mb.i_mb_count ) goto fail; + if( fwrite( h->fenc->i_aq_type, sizeof(uint8_t), h->mb.i_mb_count, rc->p_mbtree_stat_file_out ) < h->mb.i_mb_count ) + goto fail; } } diff --git a/x264.c b/x264.c index 32e6570..c3de42c 100644 --- a/x264.c +++ b/x264.c @@ -216,12 +216,28 @@ static void Help( x264_param_t *defaults, int b_longhelp ) H1( " --chroma-qp-offset QP difference between chroma and luma [%d]\n", defaults->analyse.i_chroma_qp_offset ); H1( " --aq-mode AQ method [%d]\n" " - 0: Disabled\n" - " - 1: Variance AQ (complexity mask)\n" + " - 1: OreAQ\n" " - 2: Auto-variance AQ (experimental)\n", defaults->rc.i_aq_mode ); H0( " --aq-strength Reduces blocking and blurring in flat and\n" " textured areas. [%.1f]\n" " - 0.5: weak AQ\n" - " - 1.5: strong AQ\n", defaults->rc.f_aq_strength ); + " - 1.5: strong AQ\n" + " or \n" + " Set QP up/down strength.\n", defaults->rc.f_aq_strength ); + H0( " --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 ); + H1( " --aq-ifactor AQ strength factor of I-frames [%.1f:%.1f]\n", defaults->rc.f_aq_ifactor[0], defaults->rc.f_aq_ifactor[1] ); + H1( " --aq-pfactor AQ strength factor of P-frames [%.1f:%.1f]\n", defaults->rc.f_aq_pfactor[0], defaults->rc.f_aq_pfactor[1] ); + H1( " --aq-bfactor AQ strength factor of B-frames [%.1f:%.1f]\n", defaults->rc.f_aq_bfactor[0], defaults->rc.f_aq_bfactor[1] ); + H1( " --aq-boundary AQ boundary. \n" + " fullrange=off: [192:64:24]\n" + " fullrange=on : [205:56:9]\n" + " #1: Bright-Middle\n" + " #2: Middle-Dark\n" + " #3: Dark-M.Dark\n" ); + H0( " --aq-debug Filename for AQ debug log [\"%s\"]\n", defaults->rc.psz_aq_debug_name ); H0( "\n" ); H0( " -p, --pass <1|2|3> Enable multipass ratecontrol\n" " - 1: First pass, creates stats file\n" @@ -438,7 +454,13 @@ static struct option long_options[] = { "no-fast-pskip", no_argument, NULL, 0 }, { "no-dct-decimate", no_argument, NULL, 0 }, { "aq-strength", required_argument, NULL, 0 }, + { "aq-sensitivity", required_argument, NULL, 0 }, + { "aq-ifactor", required_argument, NULL, 0 }, + { "aq-pfactor", required_argument, NULL, 0 }, + { "aq-bfactor", required_argument, NULL, 0 }, { "aq-mode", required_argument, NULL, 0 }, + { "aq-boundary", required_argument, NULL, 0 }, + { "aq-debug", 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 7fa508d..ed82787 100644 --- a/x264.h +++ b/x264.h @@ -36,6 +36,7 @@ #include #define X264_BUILD 72 +#define X264_SUB_BUILD 2 // OreAQ /* x264_t: * opaque handler for encoder */ @@ -90,7 +91,7 @@ typedef struct x264_t x264_t; #define X264_RC_CRF 1 #define X264_RC_ABR 2 #define X264_AQ_NONE 0 -#define X264_AQ_VARIANCE 1 +#define X264_AQ_ORE 1 #define X264_AQ_AUTOVARIANCE 2 #define X264_B_ADAPT_NONE 0 #define X264_B_ADAPT_FAST 1 @@ -274,7 +275,18 @@ typedef struct x264_param_t float f_pb_factor; int i_aq_mode; /* psy adaptive QP. (X264_AQ_*) */ + int i_aq_metric; float f_aq_strength; + float f_aq_strength_up[4]; /* { Bright, Middle, Dark, M.Dark } */ + float f_aq_strength_down[4]; /* { Bright, Middle, Dark, NULL } */ + float f_aq_sensitivity; + float f_aq_ifactor[2]; /* { Up, Down } */ + float f_aq_pfactor[2]; /* { Up, Down } */ + float f_aq_bfactor[2]; /* { Up, Down } */ + int b_aq_boundary; + int i_aq_boundary[3]; + int b_aq_debug; + char *psz_aq_debug_name; int b_mb_tree; /* Macroblock-tree ratecontrol. */ int i_lookahead;