Make B-pyramid spec-compliant
authorLamont Alston <wewk584@gmail.com>
Tue, 13 Oct 2009 06:32:16 +0000 (23:32 -0700)
committerJason Garrett-Glaser <darkshikari@gmail.com>
Mon, 19 Oct 2009 09:29:03 +0000 (02:29 -0700)
The rules of the specification with regard to picture buffering for pyramid coding are widely ignored.
x264's b-pyramid implementation, despite being practically identical to that proposed by the original paper, was technically not compliant.
Now it is.
Two modes are now available:
1) strict b-pyramid, while worse for compression, follows the rule mandated by Blu-ray (no P-frames can reference B-frames)
2) normal b-pyramid, which is like the old mode except fully compliant.
This patch also adds MMCO support (necessary for compliant pyramid in some cases).
MB-tree still doesn't support b-pyramid (but will soon).

12 files changed:
common/common.c
common/common.h
common/frame.h
common/macroblock.c
encoder/encoder.c
encoder/lookahead.c
encoder/ratecontrol.c
encoder/set.c
encoder/slicetype.c
muxers.c
x264.c
x264.h

index fe02d65..fc8b06a 100644 (file)
@@ -75,7 +75,7 @@ void    x264_param_default( x264_param_t *param )
     param->i_scenecut_threshold = 40;
     param->i_bframe_adaptive = X264_B_ADAPT_FAST;
     param->i_bframe_bias = 0;
-    param->b_bframe_pyramid = 0;
+    param->i_bframe_pyramid = 0;
     param->b_interlaced = 0;
     param->b_constrained_intra = 0;
 
@@ -366,7 +366,7 @@ int x264_param_parse( x264_param_t *p, const char *name, const char *value )
     OPT("b-bias")
         p->i_bframe_bias = atoi(value);
     OPT("b-pyramid")
-        p->b_bframe_pyramid = atobool(value);
+        b_error |= parse_enum( value, x264_b_pyramid_names, &p->i_bframe_pyramid );
     OPT("nf")
         p->b_deblocking_filter = !atobool(value);
     OPT2("filter", "deblock")
@@ -900,7 +900,7 @@ char *x264_param2string( x264_param_t *p, int b_res )
     if( p->i_bframe )
     {
         s += sprintf( s, " b_pyramid=%d b_adapt=%d b_bias=%d direct=%d wpredb=%d",
-                      p->b_bframe_pyramid, p->i_bframe_adaptive, p->i_bframe_bias,
+                      p->i_bframe_pyramid, p->i_bframe_adaptive, p->i_bframe_bias,
                       p->analyse.i_direct_mv_pred, p->analyse.b_weighted_bipred );
     }
 
index 5ec141c..12e1c26 100644 (file)
@@ -34,6 +34,7 @@
 #define X264_MIN4(a,b,c,d) X264_MIN((a),X264_MIN3((b),(c),(d)))
 #define X264_MAX4(a,b,c,d) X264_MAX((a),X264_MAX3((b),(c),(d)))
 #define XCHG(type,a,b) do{ type t = a; a = b; b = t; } while(0)
+#define IS_DISPOSABLE(type) ( type == X264_TYPE_B )
 #define FIX8(f) ((int)(f*(1<<8)+.5))
 
 #define CHECKED_MALLOC( var, size )\
@@ -224,11 +225,20 @@ typedef struct
 
     int b_ref_pic_list_reordering_l0;
     int b_ref_pic_list_reordering_l1;
-    struct {
+    struct
+    {
         int idc;
         int arg;
     } ref_pic_list_order[2][16];
 
+    int i_mmco_remove_from_end;
+    int i_mmco_command_count;
+    struct /* struct for future expansion */
+    {
+        int i_difference_of_pic_nums;
+        int i_poc;
+    } mmco[16];
+
     int i_cabac_init_idc;
 
     int i_qp;
index c9e1bfd..02cc31a 100644 (file)
@@ -38,7 +38,8 @@ typedef struct
     x264_param_t *param;
 
     int     i_frame;    /* Presentation frame number */
-    int     i_frame_num; /* Coded frame number */
+    int     i_dts; /* Coded frame number */
+    int     i_frame_num; /* 7.4.3 frame_num */
     int     b_kept_as_ref;
     uint8_t b_fdec;
     uint8_t b_last_minigop_bframe; /* this frame is the last b in a sequence of bframes */
index 7d014ab..dc75e34 100644 (file)
@@ -694,7 +694,7 @@ int x264_macroblock_cache_init( x264_t *h )
 
     for( i=0; i<2; i++ )
     {
-        int i_refs = X264_MIN(16, (i ? 1 : h->param.i_frame_reference) + h->param.b_bframe_pyramid) << h->param.b_interlaced;
+        int i_refs = X264_MIN(16, (i ? 1 + !!h->param.i_bframe_pyramid : h->param.i_frame_reference) ) << h->param.b_interlaced;
         for( j=0; j < i_refs; j++ )
             CHECKED_MALLOC( h->mb.mvr[i][j], 2 * i_mb_count * sizeof(int16_t) );
     }
index 3a47410..812262b 100644 (file)
@@ -272,7 +272,17 @@ static void x264_slice_header_write( bs_t *s, x264_slice_header_t *sh, int i_nal
         }
         else
         {
-            bs_write1( s, 0 );  /* adaptive_ref_pic_marking_mode_flag */
+            bs_write1( s, sh->i_mmco_command_count > 0 ); /* adaptive_ref_pic_marking_mode_flag */
+            if( sh->i_mmco_command_count > 0 )
+            {
+                int i;
+                for( i = 0; i < sh->i_mmco_command_count; i++ )
+                {
+                    bs_write_ue( s, 1 ); /* mark short term ref as unused */
+                    bs_write_ue( s, sh->mmco[i].i_difference_of_pic_nums - 1 );
+                }
+                bs_write_ue( s, 0 ); /* end command list */
+            }
         }
     }
 
@@ -498,7 +508,9 @@ static int x264_validate_parameters( x264_t *h )
     if( h->param.i_keyint_max == 1 )
         h->param.i_bframe = 0;
     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;
+    if( h->param.i_bframe <= 1 )
+        h->param.i_bframe_pyramid = X264_B_PYRAMID_NONE;
+    h->param.i_bframe_pyramid = x264_clip3( h->param.i_bframe_pyramid, X264_B_PYRAMID_NONE, X264_B_PYRAMID_NORMAL );
     if( !h->param.i_bframe )
     {
         h->param.i_bframe_adaptive = X264_B_ADAPT_NONE;
@@ -602,10 +614,10 @@ static int x264_validate_parameters( x264_t *h )
         h->param.rc.i_aq_mode = 1;
         h->param.rc.f_aq_strength = 0;
     }
-    if( h->param.rc.b_mb_tree && h->param.b_bframe_pyramid )
+    if( h->param.rc.b_mb_tree && h->param.i_bframe_pyramid )
     {
         x264_log( h, X264_LOG_WARNING, "b-pyramid + mb-tree is not supported\n" );
-        h->param.b_bframe_pyramid = 0;
+        h->param.i_bframe_pyramid = X264_B_PYRAMID_NONE;
     }
     h->param.analyse.i_noise_reduction = x264_clip3( h->param.analyse.i_noise_reduction, 0, 1<<16 );
     if( h->param.analyse.i_subpel_refine == 10 && (h->param.analyse.i_trellis != 2 || !h->param.rc.i_aq_mode) )
@@ -980,7 +992,7 @@ int x264_encoder_reconfig( x264_t *h, x264_param_t *param )
     if( h->pps->b_transform_8x8_mode )
         COPY( analyse.b_transform_8x8 );
     if( h->frames.i_max_ref1 > 1 )
-        COPY( b_bframe_pyramid );
+        COPY( i_bframe_pyramid );
     COPY( i_slice_max_size );
     COPY( i_slice_max_mbs );
     COPY( i_slice_count );
@@ -1091,6 +1103,21 @@ int x264_encoder_headers( x264_t *h, x264_nal_t **pp_nal, int *pi_nal )
     return frame_size;
 }
 
+/* Check to see whether we have chosen a reference list ordering different
+ * from the standard's default. */
+static inline void x264_reference_check_reorder( x264_t *h )
+{
+    int i;
+    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;
+        }
+}
+
 static inline void x264_reference_build_list( x264_t *h, int i_poc )
 {
     int i;
@@ -1125,6 +1152,15 @@ static inline void x264_reference_build_list( x264_t *h, int i_poc )
             }
         }
     } while( !b_ok );
+
+    if( h->sh.i_mmco_remove_from_end )
+        for( i = h->i_ref0-1; i >= h->i_ref0 - h->sh.i_mmco_remove_from_end; i-- )
+        {
+            int diff = h->i_frame_num - h->fref0[i]->i_frame_num;
+            h->sh.mmco[h->sh.i_mmco_command_count].i_poc = h->fref0[i]->i_poc;
+            h->sh.mmco[h->sh.i_mmco_command_count++].i_difference_of_pic_nums = diff;
+        }
+
     /* Order ref1 from lower to higher poc (bubble sort) for B-frame */
     do
     {
@@ -1140,6 +1176,8 @@ static inline void x264_reference_build_list( x264_t *h, int i_poc )
         }
     } while( !b_ok );
 
+    x264_reference_check_reorder( h );
+
     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
@@ -1226,6 +1264,7 @@ static void x264_fdec_filter_row( x264_t *h, int mb_y )
 
 static inline int x264_reference_update( x264_t *h )
 {
+    int i, j;
     if( !h->fdec->b_kept_as_ref )
     {
         if( h->param.i_threads > 1 )
@@ -1238,9 +1277,15 @@ static inline int x264_reference_update( x264_t *h )
         return 0;
     }
 
+    /* apply mmco from previous frame. */
+    for( i = 0; i < h->sh.i_mmco_command_count; i++ )
+        for( j = 0; h->frames.reference[j]; j++ )
+            if( h->frames.reference[j]->i_poc == h->sh.mmco[i].i_poc )
+                x264_frame_push_unused( h, x264_frame_shift( &h->frames.reference[j] ) );
+
     /* move frame in the buffer */
     x264_frame_push( h->frames.reference, h->fdec );
-    if( h->frames.reference[h->frames.i_max_dpb] )
+    if( h->frames.reference[h->sps->i_num_ref_frames] )
         x264_frame_push_unused( h, x264_frame_shift( h->frames.reference ) );
     h->fdec = x264_frame_pop_unused( h, 1 );
     if( !h->fdec )
@@ -1256,6 +1301,40 @@ static inline void x264_reference_reset( x264_t *h )
     h->fenc->i_poc = 0;
 }
 
+static inline void x264_reference_hierarchy_reset( x264_t *h )
+{
+    int i, ref;
+    int b_hasdelayframe = 0;
+    if( !h->param.i_bframe_pyramid )
+        return;
+
+    /* look for delay frames -- chain must only contain frames that are disposable */
+    for( i = 0; h->frames.current[i] && IS_DISPOSABLE( h->frames.current[i]->i_type ); i++ )
+        b_hasdelayframe |= h->frames.current[i]->i_dts
+                        != h->frames.current[i]->i_frame + h->sps->vui.i_num_reorder_frames;
+
+    if( h->param.i_bframe_pyramid != X264_B_PYRAMID_STRICT && !b_hasdelayframe )
+        return;
+
+    /* Remove last BREF. There will never be old BREFs in the
+     * dpb during a BREF decode when pyramid == STRICT */
+    for( ref = 0; h->frames.reference[ref]; ref++ )
+    {
+        if( h->param.i_bframe_pyramid == X264_B_PYRAMID_STRICT
+            && h->frames.reference[ref]->i_type == X264_TYPE_BREF )
+        {
+            int diff = h->i_frame_num - h->frames.reference[ref]->i_frame_num;
+            h->sh.mmco[h->sh.i_mmco_command_count++].i_difference_of_pic_nums = diff;
+            x264_frame_push_unused( h, x264_frame_pop( h->frames.reference ) );
+            h->b_ref_reorder[0] = 1;
+            break;
+        }
+    }
+
+    /* Prepare to room in the dpb for the delayed display time of the later b-frame's */
+    h->sh.i_mmco_remove_from_end = X264_MAX( ref + 2 - h->frames.i_max_dpb, 0 );
+}
+
 static inline void x264_slice_init( x264_t *h, int i_nal_type, int i_global_qp )
 {
     /* ------------------------ Create slice header  ----------------------- */
@@ -1622,7 +1701,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, i;
+    int     i_nal_type;
     int     i_nal_ref_idc;
 
     int   i_global_qp;
@@ -1721,35 +1800,41 @@ int     x264_encoder_encode( x264_t *h,
         h->frames.i_last_idr = h->fenc->i_frame;
         h->i_frame_num = 0;
     }
+    h->sh.i_mmco_command_count = 0;
+    h->sh.i_mmco_remove_from_end = 0;
+    h->b_ref_reorder[0] =
+    h->b_ref_reorder[1] = 0;
 
     /* ------------------- Setup frame context ----------------------------- */
     /* 5: Init data dependent of frame type */
     if( h->fenc->i_type == X264_TYPE_IDR )
     {
         /* reset ref pictures */
-        x264_reference_reset( h );
-
         i_nal_type    = NAL_SLICE_IDR;
         i_nal_ref_idc = NAL_PRIORITY_HIGHEST;
         h->sh.i_type = SLICE_TYPE_I;
+        x264_reference_reset( h );
     }
     else if( h->fenc->i_type == X264_TYPE_I )
     {
         i_nal_type    = NAL_SLICE;
         i_nal_ref_idc = NAL_PRIORITY_HIGH; /* Not completely true but for now it is (as all I/P are kept as ref)*/
         h->sh.i_type = SLICE_TYPE_I;
+        x264_reference_hierarchy_reset( h );
     }
     else if( h->fenc->i_type == X264_TYPE_P )
     {
         i_nal_type    = NAL_SLICE;
         i_nal_ref_idc = NAL_PRIORITY_HIGH; /* Not completely true but for now it is (as all I/P are kept as ref)*/
         h->sh.i_type = SLICE_TYPE_P;
+        x264_reference_hierarchy_reset( h );
     }
     else if( h->fenc->i_type == X264_TYPE_BREF )
     {
         i_nal_type    = NAL_SLICE;
-        i_nal_ref_idc = NAL_PRIORITY_HIGH; /* maybe add MMCO to forget it? -> low */
+        i_nal_ref_idc = h->param.i_bframe_pyramid == X264_B_PYRAMID_STRICT ? NAL_PRIORITY_LOW : NAL_PRIORITY_HIGH;
         h->sh.i_type = SLICE_TYPE_B;
+        x264_reference_hierarchy_reset( h );
     }
     else    /* B frame */
     {
@@ -1839,20 +1924,10 @@ int     x264_encoder_encode( x264_t *h,
     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;
-        }
+        x264_reference_check_reorder( h );
+    }
 
     if( h->sh.i_type == SLICE_TYPE_B )
         x264_macroblock_bipred_init( h );
index 968d0fc..b211c96 100644 (file)
@@ -217,18 +217,22 @@ static void x264_lookahead_encoder_shift( x264_t *h )
     }
     if( h->lookahead->ofbuf.list[i_frames] )
     {
+        int i_dts = h->lookahead->ofbuf.list[0]->i_frame;
+        h->lookahead->ofbuf.list[bframes]->i_dts = i_dts;
         x264_frame_push( h->frames.current, x264_frame_shift( &h->lookahead->ofbuf.list[bframes] ) );
         h->lookahead->ofbuf.i_size--;
-        if( h->param.b_bframe_pyramid && bframes > 1 )
+        if( h->param.i_bframe_pyramid && bframes > 1 )
         {
             x264_frame_t *mid = x264_frame_shift( &h->lookahead->ofbuf.list[bframes/2] );
             h->lookahead->ofbuf.i_size--;
             mid->i_type = X264_TYPE_BREF;
+            mid->i_dts = ++i_dts;
             x264_frame_push( h->frames.current, mid );
             bframes--;
         }
         while( bframes-- )
         {
+            h->lookahead->ofbuf.list[0]->i_dts = ++i_dts;
             x264_frame_push( h->frames.current, x264_frame_shift( h->lookahead->ofbuf.list ) );
             h->lookahead->ofbuf.i_size--;
         }
index ef17e09..6e8c29f 100644 (file)
@@ -525,6 +525,7 @@ int x264_ratecontrol_new( x264_t *h )
         {
             int i;
             char *opts = stats_buf;
+            char buf[12];
             stats_in = strchr( stats_buf, '\n' );
             if( !stats_in )
                 return -1;
@@ -541,8 +542,9 @@ int x264_ratecontrol_new( x264_t *h )
 
             /* since B-adapt doesn't (yet) take into account B-pyramid,
              * the converse is not a problem */
-            if( strstr( opts, "b_pyramid=1" ) && !h->param.b_bframe_pyramid )
-                x264_log( h, X264_LOG_WARNING, "1st pass used B-pyramid, 2nd doesn't\n" );
+            sprintf( buf, "b_pyramid=%d", h->param.i_bframe_pyramid );
+            if( !strstr( opts, buf ) )
+                x264_log( h, X264_LOG_WARNING, "different B-pyramid setting than 1st pass\n" );
 
             if( ( p = strstr( opts, "keyint=" ) ) && sscanf( p, "keyint=%d", &i )
                 && h->param.i_keyint_max != i )
index f6dd830..e087f83 100644 (file)
@@ -189,11 +189,13 @@ void x264_sps_init( x264_sps_t *sps, int i_id, x264_param_t *param )
         sps->vui.b_fixed_frame_rate = 1;
     }
 
-    sps->vui.i_num_reorder_frames = param->b_bframe_pyramid ? 2 : param->i_bframe ? 1 : 0;
+    sps->vui.i_num_reorder_frames = param->i_bframe_pyramid ? 2 : param->i_bframe ? 1 : 0;
     /* extra slot with pyramid so that we don't have to override the
      * order of forgetting old pictures */
     sps->vui.i_max_dec_frame_buffering =
-    sps->i_num_ref_frames = X264_MIN(16, X264_MAX(param->i_frame_reference, 1 + sps->vui.i_num_reorder_frames));
+    sps->i_num_ref_frames = X264_MIN(16, X264_MAX3(param->i_frame_reference, 1 + sps->vui.i_num_reorder_frames,
+                                                   param->i_bframe_pyramid ? 4 : 1 ));
+    sps->i_num_ref_frames -= param->i_bframe_pyramid == X264_B_PYRAMID_STRICT;
 
     sps->vui.b_bitstream_restriction = 1;
     if( sps->vui.b_bitstream_restriction )
@@ -540,7 +542,7 @@ int x264_validate_levels( x264_t *h, int verbose )
 {
     int ret = 0;
     int mbs = h->sps->i_mb_width * h->sps->i_mb_height;
-    int dpb = mbs * 384 * h->sps->i_num_ref_frames;
+    int dpb = mbs * 384 * h->sps->vui.i_max_dec_frame_buffering;
     int cbp_factor = h->sps->i_profile_idc==PROFILE_HIGH ? 5 : 4;
 
     const x264_level_t *l = x264_levels;
@@ -554,7 +556,7 @@ int x264_validate_levels( x264_t *h, int verbose )
                h->sps->i_mb_width, h->sps->i_mb_height, l->frame_size );
     if( dpb > l->dpb )
         ERROR( "DPB size (%d frames, %d bytes) > level limit (%d frames, %d bytes)\n",
-                h->sps->i_num_ref_frames, dpb, (int)(l->dpb / (384*mbs)), l->dpb );
+                h->sps->vui.i_max_dec_frame_buffering, dpb, (int)(l->dpb / (384*mbs)), l->dpb );
 
 #define CHECK( name, limit, val ) \
     if( (val) > (limit) ) \
index bea12f1..b4c5e52 100644 (file)
@@ -937,6 +937,13 @@ void x264_slicetype_decide( x264_t *h )
     for( bframes = 0;; bframes++ )
     {
         frm = h->lookahead->next.list[bframes];
+        if( h->param.i_bframe_pyramid < X264_B_PYRAMID_NORMAL && !h->param.rc.b_stat_read
+            && frm->i_type == X264_TYPE_BREF )
+        {
+            frm->i_type = X264_TYPE_B;
+            x264_log( h, X264_LOG_WARNING, "Externally supplied B-ref at frame %d incompatible with B-pyramid %s\n",
+                      frm->i_frame, x264_b_pyramid_names[h->param.i_bframe_pyramid] );
+        }
 
         /* Limit GOP size */
         if( frm->i_frame - h->lookahead->i_last_idr >= h->param.i_keyint_max )
index c9b8dfa..1a8187d 100644 (file)
--- a/muxers.c
+++ b/muxers.c
@@ -726,7 +726,7 @@ int set_param_mp4( hnd_t handle, x264_param_t *p_param )
 
     p_mp4->i_time_res = p_param->i_fps_num;
     p_mp4->i_time_inc = p_param->i_fps_den;
-    p_mp4->i_init_delay = p_param->i_bframe ? (p_param->b_bframe_pyramid ? 2 : 1) : 0;
+    p_mp4->i_init_delay = p_param->i_bframe ? (p_param->i_bframe_pyramid ? 2 : 1) : 0;
     p_mp4->i_init_delay *= p_mp4->i_time_inc;
     fprintf( stderr, "mp4 [info]: initial delay %d (scale %d)\n",
              p_mp4->i_init_delay, p_mp4->i_time_res );
diff --git a/x264.c b/x264.c
index a440ab7..fc4e44f 100644 (file)
--- a/x264.c
+++ b/x264.c
@@ -207,7 +207,11 @@ static void Help( x264_param_t *defaults, int longhelp )
         "                                  - 1: Fast\n"
         "                                  - 2: Optimal (slow with high --bframes)\n", defaults->i_bframe_adaptive );
     H2( "      --b-bias <integer>      Influences how often B-frames are used [%d]\n", defaults->i_bframe_bias );
-    H1( "      --b-pyramid             Keep some B-frames as references\n" );
+    H1( "      --b-pyramid <string>    Keep some B-frames as references [%s]\n"
+        "                                  - none: Disabled\n"
+        "                                  - strict: Strictly heirarchical pyramid\n"
+        "                                  - normal: Non-strict (not Blu-ray compatible)\n",
+        strtable_lookup( x264_b_pyramid_names, defaults->i_bframe_pyramid ) );
     H1( "      --no-cabac              Disable CABAC\n" );
     H1( "  -r, --ref <integer>         Number of reference frames [%d]\n", defaults->i_frame_reference );
     H1( "      --no-deblock            Disable loop filter\n" );
@@ -416,7 +420,7 @@ static struct option long_options[] =
     { "b-adapt",     required_argument, NULL, 0 },
     { "no-b-adapt",        no_argument, NULL, 0 },
     { "b-bias",      required_argument, NULL, 0 },
-    { "b-pyramid",         no_argument, NULL, 0 },
+    { "b-pyramid",   required_argument, NULL, 0 },
     { "min-keyint",  required_argument, NULL, 'i' },
     { "keyint",      required_argument, NULL, 'I' },
     { "scenecut",    required_argument, NULL, 0 },
diff --git a/x264.h b/x264.h
index 7bc2d36..0c10c8c 100644 (file)
--- a/x264.h
+++ b/x264.h
@@ -35,7 +35,7 @@
 
 #include <stdarg.h>
 
-#define X264_BUILD 77
+#define X264_BUILD 78
 
 /* x264_t:
  *      opaque handler for encoder */
@@ -95,9 +95,13 @@ typedef struct x264_t x264_t;
 #define X264_B_ADAPT_NONE            0
 #define X264_B_ADAPT_FAST            1
 #define X264_B_ADAPT_TRELLIS         2
+#define X264_B_PYRAMID_NONE          0
+#define X264_B_PYRAMID_STRICT        1
+#define X264_B_PYRAMID_NORMAL        2
 
 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", "tesa", 0 };
+static const char * const x264_b_pyramid_names[] = { "none", "strict", "normal", 0 };
 static const char * const x264_overscan_names[] = { "undef", "show", "crop", 0 };
 static const char * const x264_vidformat_names[] = { "component", "pal", "ntsc", "secam", "mac", "undef", 0 };
 static const char * const x264_fullrange_names[] = { "off", "on", 0 };
@@ -196,7 +200,7 @@ typedef struct x264_param_t
     int         i_bframe;   /* how many b-frame between 2 references pictures */
     int         i_bframe_adaptive;
     int         i_bframe_bias;
-    int         b_bframe_pyramid;   /* Keep some B-frames as references */
+    int         i_bframe_pyramid;   /* Keep some B-frames as references: 0=off, 1=strict heirarchical, 2=normal */
 
     int         b_deblocking_filter;
     int         i_deblocking_filter_alphac0;    /* [-6, 6] -6 light filter, 6 strong */