Reorder reference frames optimally on second pass
authorJason Garrett-Glaser <darkshikari@gmail.com>
Sat, 3 Oct 2009 07:59:02 +0000 (00:59 -0700)
committerJason Garrett-Glaser <darkshikari@gmail.com>
Wed, 7 Oct 2009 00:33:54 +0000 (17:33 -0700)
About +0.1-0.2% compression at normal bitrates, up to +1% at very low bitrates.
Only works if the first pass uses the same number of refs as the second (i.e. not with fast first pass).
Thus, only worthwhile at insanely slow speeds: as such, enable slow-firstpass by default with preset placebo.
Note that this changes the stats file format!

encoder/encoder.c
encoder/ratecontrol.c
encoder/ratecontrol.h
x264.c

index f2dbf70..dd38ea7 100644 (file)
@@ -1138,20 +1138,6 @@ static inline void x264_reference_build_list( x264_t *h, int i_poc )
         }
     } while( !b_ok );
 
-    /* In the standard, a P-frame's ref list is sorted by frame_num.
-     * We use POC, but check whether explicit reordering is needed */
-    h->b_ref_reorder[0] =
-    h->b_ref_reorder[1] = 0;
-    if( h->sh.i_type == SLICE_TYPE_P )
-    {
-        for( i = 0; i < h->i_ref0 - 1; i++ )
-            if( h->fref0[i]->i_frame_num < h->fref0[i+1]->i_frame_num )
-            {
-                h->b_ref_reorder[0] = 1;
-                break;
-            }
-    }
-
     h->i_ref1 = X264_MIN( h->i_ref1, h->frames.i_max_ref1 );
     h->i_ref0 = X264_MIN( h->i_ref0, h->frames.i_max_ref0 );
     h->i_ref0 = X264_MIN( h->i_ref0, h->param.i_frame_reference ); // if reconfig() has lowered the limit
@@ -1446,24 +1432,26 @@ static int x264_slice_write( x264_t *h )
 
         /* accumulate mb stats */
         h->stat.frame.i_mb_count[h->mb.i_type]++;
-        if( h->param.i_log_level >= X264_LOG_INFO )
+
+        if( !IS_INTRA(h->mb.i_type) && !IS_SKIP(h->mb.i_type) && !IS_DIRECT(h->mb.i_type) )
         {
-            if( !IS_SKIP(h->mb.i_type) && !IS_INTRA(h->mb.i_type) && !IS_DIRECT(h->mb.i_type) )
-            {
-                if( h->mb.i_partition != D_8x8 )
+            if( h->mb.i_partition != D_8x8 )
                     h->stat.frame.i_mb_partition[h->mb.i_partition] += 4;
                 else
                     for( i = 0; i < 4; i++ )
                         h->stat.frame.i_mb_partition[h->mb.i_sub_partition[i]] ++;
-                if( h->param.i_frame_reference > 1 )
-                    for( i_list = 0; i_list <= (h->sh.i_type == SLICE_TYPE_B); i_list++ )
-                        for( i = 0; i < 4; i++ )
-                        {
-                            i_ref = h->mb.cache.ref[i_list][ x264_scan8[4*i] ];
-                            if( i_ref >= 0 )
-                                h->stat.frame.i_mb_count_ref[i_list][i_ref] ++;
-                        }
-            }
+            if( h->param.i_frame_reference > 1 )
+                for( i_list = 0; i_list <= (h->sh.i_type == SLICE_TYPE_B); i_list++ )
+                    for( i = 0; i < 4; i++ )
+                    {
+                        i_ref = h->mb.cache.ref[i_list][ x264_scan8[4*i] ];
+                        if( i_ref >= 0 )
+                            h->stat.frame.i_mb_count_ref[i_list][i_ref] ++;
+                    }
+        }
+
+        if( h->param.i_log_level >= X264_LOG_INFO )
+        {
             if( h->mb.i_cbp_luma || h->mb.i_cbp_chroma )
             {
                 int cbpsum = (h->mb.i_cbp_luma&1) + ((h->mb.i_cbp_luma>>1)&1)
@@ -1632,7 +1620,7 @@ int     x264_encoder_encode( x264_t *h,
                              x264_picture_t *pic_out )
 {
     x264_t *thread_current, *thread_prev, *thread_oldest;
-    int     i_nal_type;
+    int     i_nal_type, i;
     int     i_nal_ref_idc;
 
     int   i_global_qp;
@@ -1790,21 +1778,22 @@ int     x264_encoder_encode( x264_t *h,
     h->out.i_nal = 0;
     bs_init( &h->out.bs, h->out.p_bitstream, h->out.i_bitstream );
 
-    if(h->param.b_aud){
+    if( h->param.b_aud )
+    {
         int pic_type;
 
-        if(h->sh.i_type == SLICE_TYPE_I)
+        if( h->sh.i_type == SLICE_TYPE_I )
             pic_type = 0;
-        else if(h->sh.i_type == SLICE_TYPE_P)
+        else if( h->sh.i_type == SLICE_TYPE_P )
             pic_type = 1;
-        else if(h->sh.i_type == SLICE_TYPE_B)
+        else if( h->sh.i_type == SLICE_TYPE_B )
             pic_type = 2;
         else
             pic_type = 7;
 
-        x264_nal_start(h, NAL_AUD, NAL_PRIORITY_DISPOSABLE);
-        bs_write(&h->out.bs, 3, pic_type);
-        bs_rbsp_trailing(&h->out.bs);
+        x264_nal_start( h, NAL_AUD, NAL_PRIORITY_DISPOSABLE );
+        bs_write( &h->out.bs, 3, pic_type );
+        bs_rbsp_trailing( &h->out.bs );
         if( x264_nal_end( h ) )
             return -1;
     }
@@ -1851,6 +1840,22 @@ int     x264_encoder_encode( x264_t *h,
     pic_out->i_qpplus1 =
     h->fdec->i_qpplus1 = i_global_qp + 1;
 
+    if( h->param.rc.b_stat_read && h->sh.i_type != SLICE_TYPE_I )
+        x264_reference_build_list_optimal( h );
+
+    /* Check to see whether we have chosen a reference list ordering different
+     * from the standard's default. */
+    h->b_ref_reorder[0] =
+    h->b_ref_reorder[1] = 0;
+    for( i = 0; i < h->i_ref0 - 1; i++ )
+        /* P and B-frames use different default orders. */
+        if( h->sh.i_type == SLICE_TYPE_P ? h->fref0[i]->i_frame_num < h->fref0[i+1]->i_frame_num
+                                         : h->fref0[i]->i_poc < h->fref0[i+1]->i_poc )
+        {
+            h->b_ref_reorder[0] = 1;
+            break;
+        }
+
     /* ------------------------ Create slice header  ----------------------- */
     x264_slice_init( h, i_nal_type, i_global_qp );
 
index a88bbe4..16ac7de 100644 (file)
@@ -31,6 +31,7 @@
 #include "common/common.h"
 #include "common/cpu.h"
 #include "ratecontrol.h"
+#include "me.h"
 
 typedef struct
 {
@@ -49,6 +50,8 @@ typedef struct
     int s_count;
     float blurred_complexity;
     char direct_mode;
+    int refcount[16];
+    int refs;
 } ratecontrol_entry_t;
 
 typedef struct
@@ -301,6 +304,33 @@ fail:
     return -1;
 }
 
+int x264_reference_build_list_optimal( x264_t *h )
+{
+    ratecontrol_entry_t *rce = h->rc->rce;
+    x264_frame_t *frames[16];
+    int ref, i;
+
+    if( rce->refs != h->i_ref0 )
+        return -1;
+
+    memcpy( frames, h->fref0, sizeof(frames) );
+
+    /* For now don't reorder ref 0; it seems to lower quality
+       in most cases due to skips. */
+    for( ref = 1; ref < h->i_ref0; ref++ )
+    {
+        int max = -1;
+        int bestref = 1;
+        for( i = 1; i < h->i_ref0; i++ )
+            /* Favor lower POC as a tiebreaker. */
+            COPY2_IF_GT( max, rce->refcount[i], bestref, i );
+        rce->refcount[bestref] = -1;
+        h->fref0[ref] = frames[bestref];
+    }
+
+    return 0;
+}
+
 static char *x264_strcat_filename( char *input, char *suffix )
 {
     char *output = x264_malloc( strlen( input ) + strlen( suffix ) + 1 );
@@ -581,6 +611,7 @@ int x264_ratecontrol_new( x264_t *h )
             int e;
             char *next;
             float qp;
+            int ref;
 
             next= strchr(p, ';');
             if(next)
@@ -603,6 +634,20 @@ int x264_ratecontrol_new( x264_t *h )
                    &rce->mv_bits, &rce->misc_bits, &rce->i_count, &rce->p_count,
                    &rce->s_count, &rce->direct_mode);
 
+            p = strstr( p, "ref:" );
+            if( !p )
+                goto parse_error;
+            p += 4;
+            for( ref = 0; ref < 16; ref++ )
+            {
+                if( sscanf( p, " %d", &rce->refcount[ref] ) != 1 )
+                    break;
+                p = strchr( p+1, ' ' );
+                if( !p )
+                    goto parse_error;
+            }
+            rce->refs = ref;
+
             switch(pict_type)
             {
                 case 'I': rce->kept_as_ref = 1;
@@ -614,6 +659,7 @@ int x264_ratecontrol_new( x264_t *h )
             }
             if(e < 10)
             {
+parse_error:
                 x264_log(h, X264_LOG_ERROR, "statistics are damaged at line %d, parser out=%d\n", i, e);
                 return -1;
             }
@@ -1222,7 +1268,7 @@ int x264_ratecontrol_end( x264_t *h, int bits )
                           dir_avg>0 ? 's' : dir_avg<0 ? 't' : '-' )
                         : '-';
         if( fprintf( rc->p_stat_file_out,
-                 "in:%d out:%d type:%c q:%.2f tex:%d mv:%d misc:%d imb:%d pmb:%d smb:%d d:%c;\n",
+                 "in:%d out:%d type:%c q:%.2f tex:%d mv:%d misc:%d imb:%d pmb:%d smb:%d d:%c ref:",
                  h->fenc->i_frame, h->i_frame,
                  c_type, rc->qpa_rc,
                  h->stat.frame.i_tex_bits,
@@ -1232,7 +1278,19 @@ int x264_ratecontrol_end( x264_t *h, int bits )
                  h->stat.frame.i_mb_count_p,
                  h->stat.frame.i_mb_count_skip,
                  c_direct) < 0 )
-             goto fail;
+            goto fail;
+
+        for( i = 0; i < h->i_ref0; i++ )
+        {
+            int refcount = h->param.b_interlaced ? h->stat.frame.i_mb_count_ref[0][i*2]
+                                                 + h->stat.frame.i_mb_count_ref[0][i*2+1] :
+                                                   h->stat.frame.i_mb_count_ref[0][i];
+            if( fprintf( rc->p_stat_file_out, "%d ", refcount ) < 0 )
+                goto fail;
+        }
+
+        if( fprintf( rc->p_stat_file_out, ";\n" ) < 0 )
+            goto fail;
 
         /* Don't re-write the data in multi-pass mode. */
         if( h->param.rc.b_mb_tree && h->fenc->b_kept_as_ref && !h->param.rc.b_stat_read )
index 36a174d..d3b9bec 100644 (file)
@@ -30,6 +30,7 @@ void x264_ratecontrol_delete( x264_t * );
 void x264_adaptive_quant_frame( x264_t *h, x264_frame_t *frame );
 void x264_adaptive_quant( x264_t * );
 int  x264_macroblock_tree_read( x264_t *h, x264_frame_t *frame );
+int  x264_reference_build_list_optimal( x264_t *h );
 void x264_thread_sync_ratecontrol( x264_t *cur, x264_t *prev, x264_t *next );
 void x264_ratecontrol_start( x264_t *, int i_force_qp, int overhead );
 int  x264_ratecontrol_slice_type( x264_t *, int i_frame );
diff --git a/x264.c b/x264.c
index 9c5a928..87eef2e 100644 (file)
--- a/x264.c
+++ b/x264.c
@@ -660,6 +660,7 @@ static int  Parse( int argc, char **argv,
                 param->analyse.i_trellis = 2;
                 param->i_bframe = 16;
                 param->rc.i_lookahead = 60;
+                b_turbo = 0;
             }
             else
             {