diff -uNr x264_org/common/bs.h x264/common/bs.h --- x264_org/common/bs.h Wed Dec 9 12:48:00 2009 +++ x264/common/bs.h Wed Dec 9 15:20:45 2009 @@ -161,6 +161,13 @@ bs_flush( s ); } +static inline void bs_align_10( bs_t *s ) +{ + if( s->i_left&7 ) + bs_write1( s, 1 ); + bs_flush( s ); +} + /* golomb functions */ static const uint8_t x264_ue_size_tab[256] = diff -uNr x264_org/common/common.c x264/common/common.c --- x264_org/common/common.c Wed Dec 9 12:48:00 2009 +++ x264/common/common.c Wed Dec 9 15:20:45 2009 @@ -156,6 +156,8 @@ param->b_repeat_headers = 1; param->b_annexb = 1; param->b_aud = 0; + param->b_nal_hrd = 0; + param->i_pulldown = 0; } static int parse_enum( const char *arg, const char * const *names, int *dst ) @@ -398,7 +400,23 @@ OPT("cabac-idc") p->i_cabac_init_idc = atoi(value); OPT("interlaced") + { + p->b_interlaced = atobool(value); + p->b_nal_hrd = atobool(value); // this is only a bool + p->b_tff = 1; + } + OPT("tff") + { p->b_interlaced = atobool(value); + p->b_nal_hrd = atobool(value); // this is only a bool + p->b_tff = 1; + } + OPT("bff") + { + p->b_interlaced = atobool(value); + p->b_nal_hrd = atobool(value); // this is only a bool + p->b_tff = 0; + } OPT("constrained-intra") p->b_constrained_intra = atobool(value); OPT("cqm") @@ -607,6 +625,10 @@ p->analyse.b_ssim = atobool(value); OPT("aud") p->b_aud = atobool(value); + OPT2("pulldown","pd") + p->i_pulldown = atoi(value); + OPT("nal-hrd") + p->b_nal_hrd = atobool(value); OPT("sps-id") p->i_sps_id = atoi(value); OPT("global-header") diff -uNr x264_org/common/common.h x264/common/common.h --- x264_org/common/common.h Wed Dec 9 12:48:00 2009 +++ x264/common/common.h Wed Dec 9 15:20:45 2009 @@ -368,7 +368,13 @@ int i_poc_msb; /* decoding only */ int i_poc_lsb; /* decoding only */ int i_poc; /* decoding only */ - + int i_dframe; + int i_cpb_delay; /* Equal to number of fields preceding this field + * since last buffering_period SEI */ + int64_t i_dts_last_key; + int64_t i_prev_dts_dur; + int64_t i_prev_dts; + int i_pic_struct_prev; int i_thread_num; /* threads only */ int i_nal_type; /* threads only */ int i_nal_ref_idc; /* threads only */ diff -uNr x264_org/common/frame.c x264/common/frame.c --- x264_org/common/frame.c Wed Dec 9 12:48:00 2009 +++ x264/common/frame.c Wed Dec 9 15:20:45 2009 @@ -69,6 +69,7 @@ frame->i_type = X264_TYPE_AUTO; frame->i_qpplus1 = 0; frame->i_pts = -1; + frame->i_cur_dts = 0; frame->i_frame = -1; frame->i_frame_num = -1; frame->i_lines_completed = -1; diff -uNr x264_org/common/frame.h x264/common/frame.h --- x264_org/common/frame.h Wed Dec 9 12:48:00 2009 +++ x264/common/frame.h Wed Dec 9 15:20:45 2009 @@ -34,6 +34,7 @@ int i_poc; int i_type; int i_qpplus1; + int64_t i_cur_dts; int64_t i_pts; x264_param_t *param; diff -uNr x264_org/common/set.h x264/common/set.h --- x264_org/common/set.h Wed Dec 9 12:48:00 2009 +++ x264/common/set.h Wed Dec 9 15:20:45 2009 @@ -125,6 +125,18 @@ int i_num_reorder_frames; int i_max_dec_frame_buffering; + int b_nal_hrd_parameters_present; + int i_pulldown_type; + int b_pic_struct_present_flag; + struct + { + int i_bit_rate; + int i_cpb_size; + int b_cbr; + int i_initial_cpb_removal_delay_length; + int i_cpb_removal_delay_length; + int i_dpb_output_delay_length; + } nal_hrd_parameters; /* FIXME to complete */ } vui; diff -uNr x264_org/encoder/encoder.c x264/encoder/encoder.c --- x264_org/encoder/encoder.c Wed Dec 9 12:48:00 2009 +++ x264/encoder/encoder.c Wed Dec 9 15:28:25 2009 @@ -460,6 +460,32 @@ } } + if( h->param.b_nal_hrd ) + { + if( h->param.rc.i_vbv_max_bitrate == 0 || h->param.rc.i_vbv_buffer_size == 0 ) + { + x264_log( h, X264_LOG_WARNING, "NAL HRD parameters require VBV max bitrate and buffer size to be specified\n" ); + h->param.b_nal_hrd = 0; + } + } + + if( h->param.i_pulldown > 0 ) // 32 or 64 + { + if( h->param.b_nal_hrd == 0 ) + { + x264_log( h, X264_LOG_WARNING, "pulldown requires NAL HRD parameters setting\n" ); + h->param.i_pulldown = 0; + } + else + { + if( h->param.i_pulldown != 32 && h->param.i_pulldown != 64 ) + { + x264_log( h, X264_LOG_WARNING, "32/64 only acceptable values for --pulldown. Forcing to 32 (3:2)" ); + h->param.i_pulldown = 32; + } + } + } + if( h->param.rc.i_rc_method < 0 || h->param.rc.i_rc_method > 2 ) { x264_log( h, X264_LOG_ERROR, "no ratecontrol method specified\n" ); @@ -831,12 +857,13 @@ x264_set_aspect_ratio( h, param, 1 ); - x264_reduce_fraction( &h->param.i_fps_num, &h->param.i_fps_den ); - /* Init x264_t */ h->i_frame = -1; h->i_frame_num = 0; h->i_idr_pic_id = 0; + h->i_cpb_delay = 0; + h->i_dts_last_key = 0; + h->i_dframe = -1; h->sps = &h->sps_array[0]; x264_sps_init( h->sps, h->param.i_sps_id, &h->param ); @@ -1152,13 +1179,7 @@ h->out.i_nal = 0; bs_init( &h->out.bs, h->out.p_bitstream, h->out.i_bitstream ); - /* Write SEI, SPS and PPS. */ - x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); - if( x264_sei_version_write( h, &h->out.bs ) ) - return -1; - if( x264_nal_end( h ) ) - return -1; - + /* Write SPS, PPS and SEI. */ /* generate sequence parameters */ x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST ); x264_sps_write( &h->out.bs, h->sps ); @@ -1170,6 +1191,13 @@ x264_pps_write( &h->out.bs, h->pps ); if( x264_nal_end( h ) ) return -1; + + /* identify ourself */ + x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); + if( x264_sei_version_write( h, &h->out.bs ) ) + return -1; + if( x264_nal_end( h ) ) + return -1; bs_flush( &h->out.bs ); frame_size = x264_encoder_encapsulate_nals( h ); @@ -1991,8 +2019,11 @@ x264_picture_t *pic_in, x264_picture_t *pic_out ) { + static const int tbt_pattern[] = { 5, 4, 6, 3 }; x264_t *thread_current, *thread_prev, *thread_oldest; - int i_nal_type, i_nal_ref_idc, i_global_qp, i; + int i_nal_type, i_nal_ref_idc, i_global_qp, i, cpb_removal_delay; + int64_t i_ptsoffset = 0; + int64_t i_ptsreferenceoffset = 0; if( h->param.i_threads > 1 && !h->param.b_sliced_threads ) { @@ -2143,7 +2174,19 @@ h->fdec->i_frame = h->fenc->i_frame; h->fenc->b_kept_as_ref = h->fdec->b_kept_as_ref = i_nal_ref_idc != NAL_PRIORITY_DISPOSABLE && h->param.i_keyint_max > 1; - + h->i_dframe++; + if( !h->sps->vui.i_pulldown_type ) + i_ptsreferenceoffset = h->sps->vui.i_num_units_in_tick * + (h->param.i_bframe ? ( h->param.i_bframe_pyramid ? 4 : 2 ) : 0 ) ; + else + { + i_ptsreferenceoffset = h->sps->vui.i_num_reorder_frames * ( h->sps->vui.i_num_units_in_tick * 3 ); + i_ptsoffset = (h->fenc->i_frame&1) ? (h->sps->vui.i_num_units_in_tick*((h->fenc->i_frame/2)+1)) : + (h->sps->vui.i_num_units_in_tick*((int64_t)(h->fenc->i_frame/2))); + } // pulldown > 0 + h->fdec->i_cur_dts = h->i_prev_dts + h->i_prev_dts_dur; + h->i_prev_dts = h->fdec->i_cur_dts; + h->i_prev_dts_dur = h->sps->vui.i_num_units_in_tick * ( ((h->fenc->i_frame)&1) ? 2 : ( h->sps->vui.i_pulldown_type ? 3 : 2 ) ); /* ------------------- Init ----------------------------- */ @@ -2194,17 +2237,6 @@ /* Write SPS and PPS */ if( i_nal_type == NAL_SLICE_IDR && h->param.b_repeat_headers ) { - if( h->fenc->i_frame == 0 ) - { - /* identify ourself */ - x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); - if( x264_sei_version_write( h, &h->out.bs ) ) - return -1; - if( x264_nal_end( h ) ) - return -1; - overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD; - } - /* generate sequence parameters */ x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST ); x264_sps_write( &h->out.bs, h->sps ); @@ -2218,6 +2250,80 @@ if( x264_nal_end( h ) ) return -1; overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD; + + if( h->fenc->i_frame == 0 ) + { + if( !h->param.b_nal_hrd ) + { + /* identify ourself */ + x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); + if( x264_sei_version_write( h, &h->out.bs ) ) + return -1; + bs_rbsp_trailing(&h->out.bs); + if( x264_nal_end( h ) ) + return -1; + overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD; + } + } + } + + if( h->param.b_nal_hrd ) + { + int dpb_output_delay = 0; + int pic_struct = 0; + + x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); + + // write bp sei then close and reopen nal to create separate nal from pt sei + if( i_nal_type == NAL_SLICE_IDR ) + { + x264_sei_buffering_period_write( h, &h->param, &h->out.bs, h->sps ); + if( h->fenc->i_frame == 0 ) // write identifying data after bp in same nal + { + /* identify ourself */ + if( x264_sei_version_write( h, &h->out.bs ) ) + return -1; + } + bs_rbsp_trailing(&h->out.bs); + if( x264_nal_end( h ) ) + return -1; + overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD; + x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); + } + if( !h->sh.b_field_pic ) + { + // pic_struct + dpb_output_delay = (int)( h->fenc->i_pts*2 + i_ptsoffset + i_ptsreferenceoffset + - h->fdec->i_cur_dts ) / h->sps->vui.i_num_units_in_tick; + cpb_removal_delay = (h->fdec->i_cur_dts - h->i_dts_last_key) / h->sps->vui.i_num_units_in_tick; + if( !h->param.b_interlaced ) + { + if( h->sps->vui.i_pulldown_type == 32 ) + pic_struct = tbt_pattern[(h->fenc->i_frame) & 3]; + else if( h->sps->vui.i_pulldown_type == 64 ) + { + pic_struct = (h->fenc->i_frame & 1) ? 7 : 8; + cpb_removal_delay *= 2; + dpb_output_delay *=2; + } + } + else + { + // tff = 3, bff = 4 + pic_struct = h->param.b_tff ? 3 : 4; + } + x264_sei_picture_timing_write( &h->out.bs, h->sps, cpb_removal_delay, dpb_output_delay, pic_struct); + bs_rbsp_trailing(&h->out.bs); + cpb_removal_delay = h->i_cpb_delay; + if( i_nal_type == NAL_SLICE_IDR ) + { + cpb_removal_delay = 0; + h->i_dts_last_key = h->fdec->i_cur_dts ; + } + } + if( x264_nal_end( h ) ) + return -1; + overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD; } /* Init the rate control */ @@ -2264,6 +2370,7 @@ if( (intptr_t)x264_slices_write( h ) ) return -1; + h->i_cpb_delay = cpb_removal_delay; return x264_encoder_frame_end( thread_oldest, thread_current, pp_nal, pi_nal, pic_out ); } @@ -2318,7 +2425,9 @@ /* update rc */ x264_emms(); - if( x264_ratecontrol_end( h, frame_size * 8 ) < 0 ) + + int duration_this_frame = ((h->fenc->i_frame)&1) ? 2 : ( h->sps->vui.i_pulldown_type ? 3 : 2 ); + if( x264_ratecontrol_end( h, frame_size * 8, duration_this_frame ) < 0 ) return -1; x264_noise_reduction_update( thread_current ); diff -uNr x264_org/encoder/ratecontrol.c x264/encoder/ratecontrol.c --- x264_org/encoder/ratecontrol.c Wed Dec 9 12:48:00 2009 +++ x264/encoder/ratecontrol.c Wed Dec 9 15:20:45 2009 @@ -73,6 +73,8 @@ int b_vbv; int b_vbv_min_rate; double fps; + double fps_vfr_low; + double fps_vfr_high; /* variable frame rate support (final side only)*/ double bitrate; double rate_tolerance; double qcompress; @@ -94,6 +96,8 @@ double buffer_fill_final; /* real buffer as of the last finished frame */ double buffer_fill; /* planned buffer, if all in-progress frames hit their bit budget */ double buffer_rate; /* # of bits added to buffer_fill after each frame */ + double buffer_rate_vfr_low; /* # bits added to buffer on a long frame time */ + double buffer_rate_vfr_high;/* # bits added to buffer on a short frame time */ predictor_t *pred; /* predict frame size from satd */ int single_frame_vbv; @@ -152,7 +156,7 @@ static int parse_zones( x264_t *h ); static int init_pass2(x264_t *); static float rate_estimate_qscale( x264_t *h ); -static void update_vbv( x264_t *h, int bits ); +static void update_vbv( x264_t *h, int bits, int vfrlowhigh ); //high = 3 static void update_vbv_plan( x264_t *h, int overhead ); static double predict_size( predictor_t *p, double q, double var ); static void update_predictor( predictor_t *p, double q, double var, double bits ); @@ -397,6 +401,29 @@ rc->fps = (float) h->param.i_fps_num / h->param.i_fps_den; else rc->fps = 25.0; + rc->fps_vfr_low = + rc->fps_vfr_high = + rc->fps; + if( !h->param.b_interlaced ) + { + if( h->sps->vui.i_pulldown_type > 0 && h->sps->vui.b_nal_hrd_parameters_present ) + { + if( h->sps->vui.i_pulldown_type == 32 ) + { + rc->fps_vfr_low = 19.98; + rc->fps = 23.976; // fps average for prediction + rc->fps_vfr_high = 29.97; + } + else + { + rc->fps_vfr_low = 19.98; // 39.96; + rc->fps = 23.976; // fps average for prediction // 47.952; + rc->fps_vfr_high = 29.97; // 59.94; + } + } + } + rc->buffer_rate_vfr_low = h->param.rc.i_vbv_max_bitrate * 1000 / rc->fps_vfr_low; + rc->buffer_rate_vfr_high= h->param.rc.i_vbv_max_bitrate * 1000 / rc->fps_vfr_high; if( h->param.rc.b_mb_tree ) { @@ -1324,7 +1351,7 @@ } /* After encoding one frame, save stats and update ratecontrol state */ -int x264_ratecontrol_end( x264_t *h, int bits ) +int x264_ratecontrol_end( x264_t *h, int bits, int vfrlowhigh ) { x264_ratecontrol_t *rc = h->rc; const int *mbs = h->stat.frame.i_mb_count; @@ -1435,7 +1462,7 @@ } } - update_vbv( h, bits ); + update_vbv( h, bits, vfrlowhigh ); return 0; fail: x264_log(h, X264_LOG_ERROR, "ratecontrol_end: stats file could not be written to\n"); @@ -1572,7 +1599,8 @@ } // update VBV after encoding a frame -static void update_vbv( x264_t *h, int bits ) +// vfrlowhigh 2 == high default == low when non vfr stream low == avg +static void update_vbv( x264_t *h, int bits, int vfrlowhigh ) { x264_ratecontrol_t *rcc = h->rc; x264_ratecontrol_t *rct = h->thread[0]->rc; @@ -1587,8 +1615,11 @@ if( rct->buffer_fill_final < 0 ) x264_log( h, X264_LOG_WARNING, "VBV underflow (frame %d, %.0f bits)\n", h->i_frame, rct->buffer_fill_final ); rct->buffer_fill_final = X264_MAX( rct->buffer_fill_final, 0 ); - rct->buffer_fill_final += rct->buffer_rate; + // we will honor vfr (when applicable) for the final buffer while prediction routines + // will continue to use the average frame rate + rct->buffer_fill_final += vfrlowhigh == 3 ? rcc->buffer_rate_vfr_high : rcc->buffer_rate_vfr_low; rct->buffer_fill_final = X264_MIN( rct->buffer_fill_final, rct->buffer_size ); + h->param.rc.i_buffer_fill_final = x264_clip3f( rct->buffer_fill_final, 0, rct->buffer_size - 1000 ); } // provisionally update VBV according to the planned size of all frames currently in progress diff -uNr x264_org/encoder/ratecontrol.h x264/encoder/ratecontrol.h --- x264_org/encoder/ratecontrol.h Wed Dec 9 12:48:00 2009 +++ x264/encoder/ratecontrol.h Wed Dec 9 15:20:45 2009 @@ -37,7 +37,7 @@ void x264_ratecontrol_set_weights( x264_t *h, x264_frame_t *frm ); void x264_ratecontrol_mb( x264_t *, int bits ); int x264_ratecontrol_qp( x264_t * ); -int x264_ratecontrol_end( x264_t *, int bits ); +int x264_ratecontrol_end( x264_t *, int bits, int vfrlowhigh ); void x264_ratecontrol_summary( x264_t * ); void x264_ratecontrol_set_estimated_size( x264_t *, int bits ); int x264_ratecontrol_get_estimated_size( x264_t const *); diff -uNr x264_org/encoder/set.c x264/encoder/set.c --- x264_org/encoder/set.c Wed Dec 9 12:48:00 2009 +++ x264/encoder/set.c Wed Dec 9 15:20:45 2009 @@ -205,8 +205,47 @@ sps->vui.i_log2_max_mv_length_horizontal = sps->vui.i_log2_max_mv_length_vertical = (int)(log(param->analyse.i_mv_range*4-1)/log(2)) + 1; } -} + sps->vui.b_nal_hrd_parameters_present = param->b_nal_hrd; + if( sps->vui.b_nal_hrd_parameters_present ) + { + sps->vui.nal_hrd_parameters.i_bit_rate = param->rc.i_vbv_max_bitrate * 1000; + sps->vui.nal_hrd_parameters.i_cpb_size = param->rc.i_vbv_buffer_size * 1000; + + sps->vui.nal_hrd_parameters.i_initial_cpb_removal_delay_length = 0; + while( (1 << ++sps->vui.nal_hrd_parameters.i_initial_cpb_removal_delay_length) < + ( param->rc.i_vbv_buffer_size * 90000.0 / param->rc.i_vbv_max_bitrate ) ); + + sps->vui.nal_hrd_parameters.i_cpb_removal_delay_length = 0; + while( (1 << ++sps->vui.nal_hrd_parameters.i_cpb_removal_delay_length) < + (2 * (param->i_keyint_max * ((param->i_pulldown/32)+1) ))); + + sps->vui.nal_hrd_parameters.i_dpb_output_delay_length = 0; + while( (1 << ++sps->vui.nal_hrd_parameters.i_dpb_output_delay_length) < + ( ( 2 * (sps->i_num_ref_frames + X264_MAX(sps->vui.i_num_reorder_frames, param->i_bframe) ) * + ( ( param->i_pulldown/32) + 1 ) ) ) ); + } + + sps->vui.b_pic_struct_present_flag = 0; + sps->vui.i_pulldown_type = param->i_pulldown; + if( !param->b_interlaced ) + { + if( sps->vui.i_pulldown_type > 0 && sps->vui.b_nal_hrd_parameters_present ) + { // pulldown and hrd go together + sps->vui.b_pic_struct_present_flag = 1; + sps->vui.i_num_units_in_tick = 1001; + if( sps->vui.i_pulldown_type == 64 ) + sps->vui.i_time_scale = 60000*2; + else + sps->vui.i_time_scale = 30000*2; + } + } + else + { + if( sps->vui.b_nal_hrd_parameters_present ) + sps->vui.b_pic_struct_present_flag = 1; + } +} void x264_sps_write( bs_t *s, x264_sps_t *sps ) { @@ -342,9 +381,45 @@ bs_write1( s, sps->vui.b_fixed_frame_rate ); } - bs_write1( s, 0 ); /* nal_hrd_parameters_present_flag */ + bs_write1( s, sps->vui.b_nal_hrd_parameters_present ); + if( sps->vui.b_nal_hrd_parameters_present ) + { + int bit_rate_scale = 0, cpb_size_scale = 0; + unsigned int bit_rate_tmp, cpb_size_tmp; + + bs_write_ue( s, 0 ); /* cpb_cnt_minus1 */ + + bit_rate_tmp = ( sps->vui.nal_hrd_parameters.i_bit_rate + (1 << 5) ) >> 6; + while( ( bit_rate_tmp & 1) == 0 ) + { + bit_rate_tmp >>= 1; + bit_rate_scale++; + if (bit_rate_scale == 15) + break; + } + cpb_size_tmp = ( sps->vui.nal_hrd_parameters.i_cpb_size + (1 << 3) ) >> 4; + while( ( cpb_size_tmp & 1) == 0 ) + { + cpb_size_tmp >>= 1; + cpb_size_scale++; + if (cpb_size_scale == 15) + break; + } + bs_write( s, 4, bit_rate_scale ); + bs_write( s, 4, cpb_size_scale ); + bs_write_ue_big( s, bit_rate_tmp - 1 ); + bs_write_ue_big( s, cpb_size_tmp - 1 ); + bs_write1( s, 0 ); + bs_write( s, 5, sps->vui.nal_hrd_parameters.i_initial_cpb_removal_delay_length - 1 ); + bs_write( s, 5, sps->vui.nal_hrd_parameters.i_cpb_removal_delay_length - 1 ); + bs_write( s, 5, sps->vui.nal_hrd_parameters.i_dpb_output_delay_length - 1 ); + bs_write( s, 5, 24 ); // time_offset_length + } + bs_write1( s, 0 ); /* vcl_hrd_parameters_present_flag */ - bs_write1( s, 0 ); /* pic_struct_present_flag */ + if( sps->vui.b_nal_hrd_parameters_present ) /* or VCL HRD */ + bs_write1( s, 0); /* low_delay_hrd_flag */ + bs_write1( s, sps->vui.b_pic_struct_present_flag ); /* pic_struct_present_flag */ bs_write1( s, sps->vui.b_bitstream_restriction ); if( sps->vui.b_bitstream_restriction ) { @@ -499,7 +574,7 @@ for( i = 0; i < length-16; i++ ) bs_write( s, 8, version[i] ); - bs_rbsp_trailing( s ); + bs_align_10( s ); // does nothing here.. but dangit it belongs x264_free( opts ); x264_free( version ); @@ -507,6 +582,83 @@ fail: x264_free( opts ); return -1; +} + +void x264_sei_buffering_period_write( x264_t *h, x264_param_t *param, bs_t *s, x264_sps_t *sps ) +{ + int payload_size; + + bs_write( s, 8, 0x0 ); // payload_type = buffering_period + payload_size = bs_size_ue( sps->i_id ); + if( sps->vui.b_nal_hrd_parameters_present ) + payload_size += sps->vui.nal_hrd_parameters.i_initial_cpb_removal_delay_length * 2; + + bs_write( s, 8, (payload_size + 7) / 8); + bs_write_ue( s, sps->i_id ); + + if ( sps->vui.b_nal_hrd_parameters_present ) + { + /* NOTE: This should be a loop for each CPB, but we currently only support one */ + + double buffer_rate = param->rc.i_vbv_max_bitrate; + double buffer_size = param->rc.i_vbv_buffer_size; + double buffer_fill_final; + if( h->param.rc.i_buffer_fill_final > 0 ) + buffer_fill_final = h->param.rc.i_buffer_fill_final / 1000; + else + buffer_fill_final = buffer_size * param->rc.f_vbv_buffer_init; + assert( ( buffer_fill_final * 90000. / ( buffer_rate ) ) < + pow( 2, sps->vui.nal_hrd_parameters.i_initial_cpb_removal_delay_length ) ); + bs_write( s, sps->vui.nal_hrd_parameters.i_initial_cpb_removal_delay_length, + buffer_fill_final * 90000. / ( buffer_rate ) ); + bs_write( s, sps->vui.nal_hrd_parameters.i_initial_cpb_removal_delay_length, + ( buffer_size - buffer_fill_final ) * 90000. / ( buffer_rate ) ); + } + + /* VCL HRD parameters should go here */ + bs_align_10( s ); +} + +void x264_sei_picture_timing_write( bs_t *s, x264_sps_t *sps, int cpb_removal_delay, int dpb_output_delay, int pic_struct ) +{ + int payload_size; + + static const int num_clockts[] = { 1, 1, 1, 2, 2, 3, 3, 2, 3 }; + bs_write( s, 8, 0x1 ); // payload_type = pic_timing + + payload_size = 0; + if( sps->vui.b_nal_hrd_parameters_present ) + { + payload_size += sps->vui.nal_hrd_parameters.i_cpb_removal_delay_length + + sps->vui.nal_hrd_parameters.i_dpb_output_delay_length; + if( sps->vui.b_pic_struct_present_flag ) + { + payload_size += 4; + payload_size += num_clockts[pic_struct]; // pad #num_clockts bytes for 0 (no time code) + } + } + bs_write( s, 8, (payload_size + 7) / 8); + + if( sps->vui.b_nal_hrd_parameters_present ) + { + /* NOTE: This should be a loop for each CPB, but we currently only support one */ + + assert( cpb_removal_delay < pow( 2, sps->vui.nal_hrd_parameters.i_cpb_removal_delay_length ) ); + bs_write( s, sps->vui.nal_hrd_parameters.i_cpb_removal_delay_length, + cpb_removal_delay); + assert( dpb_output_delay < pow( 2, sps->vui.nal_hrd_parameters.i_dpb_output_delay_length ) ); + bs_write( s, sps->vui.nal_hrd_parameters.i_dpb_output_delay_length, + dpb_output_delay); + if( sps->vui.b_pic_struct_present_flag ) + { + bs_write( s, 4, pic_struct ); + bs_write( s, num_clockts[ pic_struct ], 0 ); + } + } + + /* VCL HRD parameters should go here */ + //bs_rbsp_trailing( s ); + bs_align_10( s ); } const x264_level_t x264_levels[] = diff -uNr x264_org/encoder/set.h x264/encoder/set.h --- x264_org/encoder/set.h Wed Dec 9 12:48:00 2009 +++ x264/encoder/set.h Wed Dec 9 15:20:45 2009 @@ -29,6 +29,8 @@ void x264_pps_init( x264_pps_t *pps, int i_id, x264_param_t *param, x264_sps_t *sps ); void x264_pps_write( bs_t *s, x264_pps_t *pps ); int x264_sei_version_write( x264_t *h, bs_t *s ); +void x264_sei_buffering_period_write( x264_t *h, x264_param_t *param, bs_t *s, x264_sps_t *sps ); +void x264_sei_picture_timing_write( bs_t *s, x264_sps_t *sps, int cpb_removal_delay, int dpb_output_delay, int pic_struct ); int x264_validate_levels( x264_t *h, int verbose ); #endif diff -uNr x264_org/x264.c x264/x264.c --- x264_org/x264.c Wed Dec 9 12:48:00 2009 +++ x264/x264.c Wed Dec 9 15:20:45 2009 @@ -285,7 +285,9 @@ else H1( " --slices Number of slices per frame\n" ); H2( " --slice-max-size Limit the size of each slice in bytes\n"); H2( " --slice-max-mbs Limit the size of each slice in macroblocks\n"); - H0( " --interlaced Enable pure-interlaced mode\n" ); + H0( " --interlaced Enable pure-interlaced mode (tff)\n" ); + H0( " --tff Alias for --interlaced\n" ); + H0( " --bff Enable pure-interlaced mode (bff)\n" ); H2( " --constrained-intra Enable constrained intra prediction.\n" ); H0( "\n" ); H0( "Ratecontrol:\n" ); @@ -458,6 +460,10 @@ H2( " --dump-yuv Save reconstructed frames\n" ); H2( " --sps-id Set SPS and PPS id numbers [%d]\n", defaults->i_sps_id ); H2( " --aud Use access unit delimiters\n" ); + H1( " --nal-hrd Use NAL HRD parameters\n" ); + H1( " --pulldown Use 3:2 pulldown\n" + " - 32: TBT,BT,BTB,TB pattern\n" + " - 64: triple,double *recommended for 720p\n" ); H0( "\n" ); } @@ -504,6 +510,8 @@ { "filter", required_argument, NULL, 0 }, { "deblock", required_argument, NULL, 'f' }, { "interlaced", no_argument, NULL, 0 }, + { "tff", no_argument, NULL, 0 }, + { "bff", no_argument, NULL, 0 }, { "constrained-intra", no_argument, NULL, 0 }, { "cabac", no_argument, NULL, 0 }, { "no-cabac", no_argument, NULL, 0 }, @@ -585,6 +593,9 @@ { "dump-yuv", required_argument, NULL, 0 }, { "sps-id", required_argument, NULL, 0 }, { "aud", no_argument, NULL, 0 }, + { "nal-hrd", no_argument, NULL, 0 }, + { "pulldown", required_argument, NULL, 0 }, + { "pd", required_argument, NULL, 0 }, { "nr", required_argument, NULL, 0 }, { "cqm", required_argument, NULL, 0 }, { "cqmfile", required_argument, NULL, 0 }, diff -uNr x264_org/x264.h x264/x264.h --- x264_org/x264.h Wed Dec 9 12:48:00 2009 +++ x264/x264.h Wed Dec 9 15:20:45 2009 @@ -214,6 +214,7 @@ int i_cabac_init_idc; int b_interlaced; + int b_tff; int b_constrained_intra; int i_cqm_preset; @@ -282,6 +283,7 @@ int i_vbv_max_bitrate; int i_vbv_buffer_size; float f_vbv_buffer_init; /* <=1: fraction of buffer_size. >1: kbit */ + int64_t i_buffer_fill_final; float f_ip_factor; float f_pb_factor; @@ -311,6 +313,8 @@ int b_annexb; /* if set, place start codes (4 bytes) before NAL units, * otherwise place size (4 bytes) before NAL units. */ int i_sps_id; /* SPS and PPS id number */ + int b_nal_hrd; /* Add NAL HRD parameters to bitstream */ + int i_pulldown; /* 3:2 pulldown 32 = 3:2 64 = 6:4 0 = off */ /* Slicing parameters */ int i_slice_max_size; /* Max size per slice in bytes; includes estimated NAL overhead. */