Index: encoder/encoder.c =================================================================== --- encoder/encoder.c (revision 667) +++ encoder/encoder.c (working copy) @@ -523,6 +523,7 @@ h->param.rc.f_qblur = 0; if( h->param.rc.f_complexity_blur < 0 ) h->param.rc.f_complexity_blur = 0; + h->param.rc.f_aq_cssim = x264_clip3f( h->param.rc.f_aq_cssim, 0.0, 1.0 ); h->param.i_sps_id &= 31; Index: encoder/ratecontrol.c =================================================================== --- encoder/ratecontrol.c (revision 667) +++ encoder/ratecontrol.c (working copy) @@ -240,8 +240,13 @@ x264_log(h, X264_LOG_WARNING, "bitrate tolerance too small, using .01\n"); rc->rate_tolerance = 0.01; } + if( h->param.rc.b_aq_cssim && h->param.i_bframe && h->param.analyse.b_transform_8x8 ) + { + x264_log( h, X264_LOG_ERROR, "not implemented: aq-cssim + B-frames + 8x8dct\n" ); + return -1; + } - h->mb.b_variable_qp = rc->b_vbv && !rc->b_2pass; + h->mb.b_variable_qp = (rc->b_vbv && !rc->b_2pass) || h->param.rc.b_aq_cssim; if( rc->b_abr ) { @@ -765,6 +770,13 @@ x264_cpu_restore( h->param.cpu ); + if( h->param.rc.b_aq_cssim ) + { + // cssim wants to record the real qp. other types of aq might want the qp from before applying aq. + rc->qpa += h->mb.i_qp; + return; + } + h->fdec->i_row_bits[y] += bits; rc->qpa += rc->qpm; @@ -945,7 +957,9 @@ if( h->mb.b_variable_qp ) { - if( h->sh.i_type == SLICE_TYPE_B ) + if( h->param.rc.b_aq_cssim ) + rc->qp_constant[h->sh.i_type] = (int)( rc->qpa + .5 ); + else if( h->sh.i_type == SLICE_TYPE_B ) { rc->bframe_bits += bits; if( !h->frames.current[0] || !IS_X264_TYPE_B(h->frames.current[0]->i_type) ) Index: encoder/analyse.c =================================================================== --- encoder/analyse.c (revision 667) +++ encoder/analyse.c (working copy) @@ -29,6 +29,7 @@ #endif #include "common/common.h" +#include "common/cpu.h" #include "macroblock.h" #include "me.h" #include "ratecontrol.h" @@ -193,6 +194,9 @@ { memset( a, 0, sizeof( x264_mb_analysis_t ) ); + if( h->param.rc.b_aq_cssim ) + i_qp = h->mb.i_last_qp; + /* conduct the analysis using this lamda and QP */ a->i_qp = h->mb.i_qp = i_qp; h->mb.i_chroma_qp = i_chroma_qp_table[x264_clip3( i_qp + h->pps->i_chroma_qp_index_offset, 0, 51 )]; @@ -2029,7 +2033,46 @@ } } +static void aq_constant_ssim( x264_t *h, x264_mb_analysis_t *a ) +{ + int transform_bak = h->mb.b_transform_8x8; + int mbtype_bak = h->mb.i_type == P_SKIP ? P_L0 + : h->mb.i_type == B_SKIP ? B_DIRECT + : h->mb.i_type; + int qp = a->i_qp, bqp = qp; + int prev_dir = 0; + float bdssim; + x264_cpu_restore( h->param.cpu ); + bdssim = 2.0; + + while( qp >= 0 && qp <= 51 ) + { + float ssim, dssim; + int dir; + h->mb.i_type = mbtype_bak; + h->mb.b_transform_8x8 = transform_bak; + h->mb.i_qp = qp; + h->mb.i_chroma_qp = i_chroma_qp_table[x264_clip3( qp + h->pps->i_chroma_qp_index_offset, 0, 51 )]; + x264_macroblock_encode( h ); + x264_cpu_restore( h->param.cpu ); + ssim = x264_pixel_ssim_wxh( &h->pixf, h->mb.pic.p_fenc[0], FENC_STRIDE, h->mb.pic.p_fdec[0], FDEC_STRIDE, 16, 16 ); + dssim = fabs(ssim - h->param.rc.f_aq_cssim); + COPY2_IF_LT( bdssim, dssim, bqp, qp ); +// printf( "[%d] qp:%d ssim:%f\n", h->mb.i_mb_xy, qp, ssim ); + + dir = ssim < h->param.rc.f_aq_cssim ? -1 : 1; + if( dir * prev_dir < 0 ) + break; + if( dir == 1 && h->mb.i_cbp_luma == 0 ) + break; + qp += dir; + prev_dir = dir; + } + + h->mb.i_qp = bqp; +} + /***************************************************************************** * x264_macroblock_analyse: *****************************************************************************/ @@ -2561,6 +2604,9 @@ if( !analysis.b_mbrd ) x264_mb_analyse_transform( h ); + if( h->param.rc.b_aq_cssim ) + aq_constant_ssim( h, &analysis ); + h->mb.b_trellis = h->param.analyse.i_trellis; h->mb.b_noise_reduction = h->param.analyse.i_noise_reduction; } Index: x264.c =================================================================== --- x264.c (revision 667) +++ x264.c (working copy) @@ -204,6 +204,7 @@ " q= (force QP)\n" " or b= (bitrate multiplier)\n" ); H1( " --qpfile Force frametypes and QPs\n" ); + H1( " --aq-cssim Force all macroblocks to this SSIM\n" ); H0( "\n" ); H0( "Analysis:\n" ); H0( "\n" ); @@ -424,6 +425,7 @@ { "cplxblur",required_argument, NULL, 0 }, { "zones", required_argument, NULL, 0 }, { "qpfile", required_argument, NULL, OPT_QPFILE }, + { "aq-cssim",required_argument, NULL, 0 }, { "threads", required_argument, NULL, 0 }, { "thread-input", no_argument, NULL, OPT_THREAD_INPUT }, { "non-deterministic", no_argument, NULL, 0 }, Index: common/common.c =================================================================== --- common/common.c (revision 667) +++ common/common.c (working copy) @@ -515,6 +515,13 @@ p->rc.f_complexity_blur = atof(value); OPT("zones") p->rc.psz_zones = strdup(value); + OPT("aq-cssim") + { + p->rc.b_aq_cssim = 1; + p->rc.f_aq_cssim = atof(value); + p->rc.i_rc_method = X264_RC_CQP; // because aq-cssim replaces any other rc + p->rc.i_qp_constant = 26; + } OPT("psnr") p->analyse.b_psnr = atobool(value); OPT("ssim") @@ -906,10 +913,13 @@ p->i_keyint_max, p->i_keyint_min, p->i_scenecut_threshold, p->b_pre_scenecut ? "(pre)" : "" ); - s += sprintf( s, " rc=%s", p->rc.i_rc_method == X264_RC_ABR ? + s += sprintf( s, " rc=%s", p->rc.b_aq_cssim ? "cssim" + : p->rc.i_rc_method == X264_RC_ABR ? ( p->rc.b_stat_read ? "2pass" : p->rc.i_vbv_buffer_size ? "cbr" : "abr" ) : p->rc.i_rc_method == X264_RC_CRF ? "crf" : "cqp" ); - if( p->rc.i_rc_method == X264_RC_ABR || p->rc.i_rc_method == X264_RC_CRF ) + if( p->rc.b_aq_cssim ) + s += sprintf( s, " ssim=%.4f", p->rc.f_aq_cssim ); + else if( p->rc.i_rc_method == X264_RC_ABR || p->rc.i_rc_method == X264_RC_CRF ) { if( p->rc.i_rc_method == X264_RC_CRF ) s += sprintf( s, " crf=%.1f", p->rc.f_rf_constant ); Index: x264.h =================================================================== --- x264.h (revision 667) +++ x264.h (working copy) @@ -269,6 +269,9 @@ x264_zone_t *zones; /* ratecontrol overrides */ int i_zones; /* number of zone_t's */ char *psz_zones; /* alternate method of specifying zones */ + + int b_aq_cssim; + float f_aq_cssim; } rc; /* Muxing parameters */