int i_last_dqp; /* last delta qp */
int b_variable_qp; /* whether qp is allowed to vary per macroblock */
int b_lossless;
+ int b_direct_auto_read; /* take stats for --direct auto from the 2pass log */
+ int b_direct_auto_write; /* analyse direct modes, to use and/or save */
/* B_direct and weighted prediction */
int dist_scale_factor[16][16];
/* XXX: both omit the cost of MBs coded as P_SKIP */
int i_intra_cost;
int i_inter_cost;
+ /* Adaptive direct mv pred */
+ int i_direct_score[2];
} frame;
/* Cummulated stats */
int64_t i_mb_count_8x8dct[2];
int64_t i_mb_count_size[2][7];
int64_t i_mb_count_ref[2][16];
+ /* */
+ int i_direct_score[2];
+ int i_direct_frames[2];
} stat;
return 1;
}
-int x264_mb_predict_mv_direct16x16( x264_t *h )
+int x264_mb_predict_mv_direct16x16( x264_t *h, int *b_changed )
{
int b_available;
if( h->param.analyse.i_direct_mv_pred == X264_DIRECT_PRED_NONE )
else
b_available = x264_mb_predict_mv_direct16x16_temporal( h );
+ if( b_changed != NULL && b_available )
+ {
+ int type_col = h->fref1[0]->mb_type[ h->mb.i_mb_xy ];
+ if( IS_INTRA(type_col) || type_col == P_SKIP )
+ {
+ *b_changed = h->mb.cache.direct_ref[0][0] != h->mb.cache.ref[0][X264_SCAN8_0]
+ || h->mb.cache.direct_ref[1][0] != h->mb.cache.ref[1][X264_SCAN8_0]
+ || *(uint32_t*)h->mb.cache.direct_mv[0][X264_SCAN8_0] != *(uint32_t*)h->mb.cache.mv[0][X264_SCAN8_0]
+ || *(uint32_t*)h->mb.cache.direct_mv[1][X264_SCAN8_0] != *(uint32_t*)h->mb.cache.mv[1][X264_SCAN8_0];
+ }
+ else
+ {
+ int i, l;
+ *b_changed = 0;
+ for( l = 0; l < 2; l++ )
+ for( i = 0; i < 4; i++ )
+ *b_changed |= h->mb.cache.direct_ref[l][i] != h->mb.cache.ref[l][x264_scan8[i*4]];
+ *b_changed = *b_changed || memcmp(h->mb.cache.direct_mv, h->mb.cache.mv, sizeof(h->mb.cache.mv));
+ }
+ if( !*b_changed )
+ return b_available;
+ }
+
/* cache ref & mv */
if( b_available )
{
/* x264_mb_predict_mv:
* set mvp with predicted mv for all blocks except SKIP and DIRECT
* h->mb. need valid ref/partition/sub of current block to be valid
- * and valid mv/ref from other blocks . */
+ * and valid mv/ref from other blocks. */
void x264_mb_predict_mv( x264_t *h, int i_list, int idx, int i_width, int mvp[2] );
/* x264_mb_predict_mv_direct16x16:
* set h->mb.cache.mv and h->mb.cache.ref for B_SKIP or B_DIRECT
- * h->mb. need only valid values from other blocks
- * return 1 on success, 0 on failure */
-int x264_mb_predict_mv_direct16x16( x264_t *h );
+ * h->mb. need only valid values from other blocks.
+ * return 1 on success, 0 on failure.
+ * if b_changed != NULL, set it to whether refs or mvs differ from
+ * before this functioncall. */
+int x264_mb_predict_mv_direct16x16( x264_t *h, int *b_changed );
/* x264_mb_load_mv_direct8x8:
* set h->mb.cache.mv and h->mb.cache.ref for B_DIRECT
* must be called only after x264_mb_predict_mv_direct16x16 */
int i_bskip_cost = COST_MAX;
int b_skip = 0;
- analysis.b_direct_available = x264_mb_predict_mv_direct16x16( h );
+ h->mb.i_type = B_SKIP;
+ if( h->mb.b_direct_auto_write )
+ {
+ /* direct=auto heuristic: prefer whichever mode allows more Skip macroblocks */
+ for( i = 0; i < 2; i++ )
+ {
+ int b_changed = 1;
+ h->sh.b_direct_spatial_mv_pred ^= 1;
+ analysis.b_direct_available = x264_mb_predict_mv_direct16x16( h, i && analysis.b_direct_available ? &b_changed : NULL );
+ if( analysis.b_direct_available )
+ {
+ if( b_changed )
+ {
+ x264_mb_mc( h );
+ b_skip = x264_macroblock_probe_bskip( h );
+ }
+ h->stat.frame.i_direct_score[ h->sh.b_direct_spatial_mv_pred ] += b_skip;
+ }
+ }
+ }
+ else
+ analysis.b_direct_available = x264_mb_predict_mv_direct16x16( h, NULL );
+
if( analysis.b_direct_available )
{
- h->mb.i_type = B_SKIP;
- x264_mb_mc( h );
+ if( !h->mb.b_direct_auto_write )
+ x264_mb_mc( h );
if( h->mb.b_lossless )
{
/* chance of skip is too small to bother */
return;
}
}
- else
+ else if( !h->mb.b_direct_auto_write )
{
/* Conditioning the probe on neighboring block types
* doesn't seem to help speed or quality. */
sh->i_redundant_pic_cnt = 0;
- sh->b_direct_spatial_mv_pred = ( param->analyse.i_direct_mv_pred == X264_DIRECT_PRED_SPATIAL );
+ if( !h->mb.b_direct_auto_read )
+ {
+ if( h->mb.b_direct_auto_write )
+ sh->b_direct_spatial_mv_pred = ( h->stat.i_direct_score[1] > h->stat.i_direct_score[0] );
+ else
+ sh->b_direct_spatial_mv_pred = ( param->analyse.i_direct_mv_pred == X264_DIRECT_PRED_SPATIAL );
+ }
+ /* else b_direct_spatial_mv_pred was read from the 2pass statsfile */
sh->b_num_ref_idx_override = 0;
sh->i_num_ref_idx_l0_active = 1;
h->param.i_bframe_bias = x264_clip3( h->param.i_bframe_bias, -90, 100 );
h->param.b_bframe_pyramid = h->param.b_bframe_pyramid && h->param.i_bframe > 1;
h->param.b_bframe_adaptive = h->param.b_bframe_adaptive && h->param.i_bframe > 0;
+ h->mb.b_direct_auto_write = h->param.analyse.i_direct_mv_pred == X264_DIRECT_PRED_AUTO
+ && h->param.i_bframe
+ && ( h->param.rc.b_stat_write || !h->param.rc.b_stat_read );
h->param.i_deblocking_filter_alphac0 = x264_clip3( h->param.i_deblocking_filter_alphac0, -6, 6 );
h->param.i_deblocking_filter_beta = x264_clip3( h->param.i_deblocking_filter_beta, -6, 6 );
for( i = 0; i < 16; i++ )
h->stat.i_mb_count_ref[h->sh.i_type][i] += h->stat.frame.i_mb_count_ref[i];
}
+ if( i_slice_type == SLICE_TYPE_B )
+ {
+ h->stat.i_direct_frames[ h->sh.b_direct_spatial_mv_pred ] ++;
+ if( h->mb.b_direct_auto_write )
+ {
+ //FIXME somewhat arbitrary time constants
+ if( h->stat.i_direct_score[0] + h->stat.i_direct_score[1] > h->mb.i_mb_count )
+ {
+ for( i = 0; i < 2; i++ )
+ h->stat.i_direct_score[i] = h->stat.i_direct_score[i] * 9/10;
+ }
+ for( i = 0; i < 2; i++ )
+ h->stat.i_direct_score[i] += h->stat.frame.i_direct_score[i];
+ }
+ }
if( h->param.analyse.b_psnr )
{
100. * h->stat.i_mb_count_8x8dct[1] / h->stat.i_mb_count_8x8dct[0] );
}
+ if( h->param.analyse.i_direct_mv_pred == X264_DIRECT_PRED_AUTO
+ && h->stat.i_slice_count[SLICE_TYPE_B] )
+ {
+ x264_log( h, X264_LOG_INFO, "direct mvs spatial:%.1f%% temporal:%.1f%%\n",
+ h->stat.i_direct_frames[1] * 100. / h->stat.i_slice_count[SLICE_TYPE_B],
+ h->stat.i_direct_frames[0] * 100. / h->stat.i_slice_count[SLICE_TYPE_B] );
+ }
+
if( h->param.i_frame_reference > 1 )
{
int i_slice;
int p_count;
int s_count;
float blurred_complexity;
+ char direct_mode;
} ratecontrol_entry_t;
typedef struct
return -1;
}
rce = &rc->entry[frame_number];
+ rce->direct_mode = 0;
- e += sscanf(p, " in:%*d out:%*d type:%c q:%f itex:%d ptex:%d mv:%d misc:%d imb:%d pmb:%d smb:%d",
+ e += sscanf(p, " in:%*d out:%*d type:%c q:%f itex:%d ptex:%d mv:%d misc:%d imb:%d pmb:%d smb:%d d:%c",
&pict_type, &qp, &rce->i_tex_bits, &rce->p_tex_bits,
- &rce->mv_bits, &rce->misc_bits, &rce->i_count, &rce->p_count, &rce->s_count);
+ &rce->mv_bits, &rce->misc_bits, &rce->i_count, &rce->p_count,
+ &rce->s_count, &rce->direct_mode);
switch(pict_type){
case 'I': rce->kept_as_ref = 1;
case 'b': rce->pict_type = SLICE_TYPE_B; break;
default: e = -1; break;
}
- if(e != 10){
+ if(e < 10){
x264_log(h, X264_LOG_ERROR, "statistics are damaged at line %d, parser out=%d\n", i, e);
return -1;
}
void x264_ratecontrol_start( x264_t *h, int i_slice_type, int i_force_qp )
{
x264_ratecontrol_t *rc = h->rc;
+ ratecontrol_entry_t *rce = NULL;
x264_cpu_restore( h->param.cpu );
rc->qp_force = i_force_qp;
rc->slice_type = i_slice_type;
+ if( h->param.rc.b_stat_read )
+ {
+ int frame = h->fenc->i_frame;
+ assert( frame >= 0 && frame < rc->num_entries );
+ rce = h->rc->rce = &h->rc->entry[frame];
+
+ if( i_slice_type == SLICE_TYPE_B
+ && h->param.analyse.i_direct_mv_pred == X264_DIRECT_PRED_AUTO )
+ {
+ h->sh.b_direct_spatial_mv_pred = ( rce->direct_mode == 's' );
+ h->mb.b_direct_auto_read = ( rce->direct_mode == 's' || rce->direct_mode == 't' );
+ }
+ }
+
if( i_force_qp )
{
rc->qpa = rc->qp = i_force_qp - 1;
}
else if( rc->b_2pass )
{
- int frame = h->fenc->i_frame;
- ratecontrol_entry_t *rce;
- assert( frame >= 0 && frame < rc->num_entries );
- rce = h->rc->rce = &h->rc->entry[frame];
-
rce->new_qscale = rate_estimate_qscale( h, i_slice_type );
rc->qpa = rc->qp = rce->new_qp =
x264_clip3( (int)(qscale2qp(rce->new_qscale) + 0.5), 0, 51 );
char c_type = rc->slice_type==SLICE_TYPE_I ? (h->fenc->i_poc==0 ? 'I' : 'i')
: rc->slice_type==SLICE_TYPE_P ? 'P'
: h->fenc->b_kept_as_ref ? 'B' : 'b';
+ int dir_frame = h->stat.frame.i_direct_score[1] - h->stat.frame.i_direct_score[0];
+ int dir_avg = h->stat.i_direct_score[1] - h->stat.i_direct_score[0];
+ char c_direct = h->mb.b_direct_auto_write ?
+ ( dir_frame>0 ? 's' : dir_frame<0 ? 't' :
+ dir_avg>0 ? 's' : dir_avg<0 ? 't' : '-' )
+ : '-';
fprintf( rc->p_stat_file_out,
- "in:%d out:%d type:%c q:%.2f itex:%d ptex:%d mv:%d misc:%d imb:%d pmb:%d smb:%d;\n",
+ "in:%d out:%d type:%c q:%.2f itex:%d ptex:%d mv:%d misc:%d imb:%d pmb:%d smb:%d d:%c;\n",
h->fenc->i_frame, h->i_frame-1,
c_type, rc->qpa,
h->stat.frame.i_itex_bits, h->stat.frame.i_ptex_bits,
h->stat.frame.i_hdr_bits, h->stat.frame.i_misc_bits,
h->stat.frame.i_mb_count_i,
h->stat.frame.i_mb_count_p,
- h->stat.frame.i_mb_count_skip);
+ h->stat.frame.i_mb_count_skip,
+ c_direct);
}
if( rc->b_abr )
" - none, all\n"
" (p4x4 requires p8x8. i8x8 requires --8x8dct.)\n"
" --direct <string> Direct MV prediction mode [\"%s\"]\n"
- " - none, spatial, temporal\n"
+ " - none, spatial, temporal, auto\n"
" -w, --weightb Weighted prediction for B-frames\n"
" --me <string> Integer pixel motion estimation method [\"%s\"]\n"
" - dia: diamond search, radius 1 (fast)\n"
#include <stdarg.h>
-#define X264_BUILD 44
+#define X264_BUILD 45
/* x264_t:
* opaque handler for decoder and encoder */
#define X264_DIRECT_PRED_NONE 0
#define X264_DIRECT_PRED_SPATIAL 1
#define X264_DIRECT_PRED_TEMPORAL 2
+#define X264_DIRECT_PRED_AUTO 3
#define X264_ME_DIA 0
#define X264_ME_HEX 1
#define X264_ME_UMH 2
#define X264_CQM_JVT 1
#define X264_CQM_CUSTOM 2
-static const char * const x264_direct_pred_names[] = { "none", "spatial", "temporal", 0 };
+static const char * const x264_direct_pred_names[] = { "none", "spatial", "temporal", "auto", 0 };
static const char * const x264_motion_est_names[] = { "dia", "hex", "umh", "esa", 0 };
/* Colorspace type