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).
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;
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")
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 );
}
#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 )\
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;
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 */
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) );
}
}
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 */
+ }
}
}
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;
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) )
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 );
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;
}
}
} 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
{
}
} 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
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 )
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 )
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 ----------------------- */
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;
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 */
{
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 );
}
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--;
}
{
int i;
char *opts = stats_buf;
+ char buf[12];
stats_in = strchr( stats_buf, '\n' );
if( !stats_in )
return -1;
/* 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 )
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 )
{
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;
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) ) \
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 )
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 );
" - 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" );
{ "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 },
#include <stdarg.h>
-#define X264_BUILD 77
+#define X264_BUILD 78
/* x264_t:
* opaque handler for encoder */
#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 };
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 */