diff --git a/common/bs.h b/common/bs.h index d5db977..6f193a0 100644 --- a/common/bs.h +++ b/common/bs.h @@ -161,6 +161,13 @@ static inline void bs_align_1( bs_t *s ) 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 --git a/common/common.c b/common/common.c index fc8b06a..3520ffb 100644 --- a/common/common.c +++ b/common/common.c @@ -155,6 +155,8 @@ void x264_param_default( x264_param_t *param ) 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 ) @@ -395,7 +397,23 @@ int x264_param_parse( x264_param_t *p, const char *name, const char *value ) 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") @@ -602,6 +620,10 @@ int x264_param_parse( x264_param_t *p, const char *name, const char *value ) 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 --git a/common/common.h b/common/common.h index 12e1c26..df41135 100644 --- a/common/common.h +++ b/common/common.h @@ -339,7 +339,13 @@ struct x264_t 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 --git a/common/frame.c b/common/frame.c index b5fb692..89ce17d 100644 --- a/common/frame.c +++ b/common/frame.c @@ -69,6 +69,7 @@ x264_frame_t *x264_frame_new( x264_t *h, int b_fdec ) 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 --git a/common/frame.h b/common/frame.h index 02cc31a..37ccb66 100644 --- a/common/frame.h +++ b/common/frame.h @@ -34,6 +34,7 @@ typedef struct int i_poc; int i_type; int i_qpplus1; + int64_t i_cur_dts; int64_t i_pts; x264_param_t *param; diff --git a/common/set.h b/common/set.h index e1b9cd9..ce53101 100644 --- a/common/set.h +++ b/common/set.h @@ -125,6 +125,18 @@ typedef struct 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 --git a/encoder/encoder.c b/encoder/encoder.c index 87e0700..a67c80d 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -419,6 +419,32 @@ static int x264_validate_parameters( x264_t *h ) } } + 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" ); @@ -785,12 +811,13 @@ x264_t *x264_encoder_open( x264_param_t *param ) 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 ); @@ -1073,13 +1100,7 @@ int x264_encoder_headers( x264_t *h, x264_nal_t **pp_nal, int *pi_nal ) 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 ); @@ -1091,6 +1112,13 @@ int x264_encoder_headers( x264_t *h, x264_nal_t **pp_nal, int *pi_nal ) 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 ); @@ -1700,11 +1728,14 @@ int x264_encoder_encode( x264_t *h, 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; int i_nal_ref_idc; - - int i_global_qp; + int i_global_qp; + int cpb_removal_delay; + int64_t i_ptsoffset = 0; + int64_t i_ptsreferenceoffset = 0; if( h->param.i_threads > 1) { @@ -1849,7 +1880,19 @@ int x264_encoder_encode( x264_t *h, 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 ----------------------------- */ @@ -1889,17 +1932,6 @@ int x264_encoder_encode( x264_t *h, /* 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 ); @@ -1913,6 +1945,80 @@ int x264_encoder_encode( x264_t *h, 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 */ @@ -1949,6 +2055,7 @@ int x264_encoder_encode( x264_t *h, 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 ); } @@ -2003,7 +2110,9 @@ static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current, /* 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 --git a/encoder/ratecontrol.c b/encoder/ratecontrol.c index e361028..b296e11 100644 --- a/encoder/ratecontrol.c +++ b/encoder/ratecontrol.c @@ -70,6 +70,8 @@ struct x264_ratecontrol_t 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; @@ -91,6 +93,8 @@ struct x264_ratecontrol_t 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; @@ -146,7 +150,7 @@ struct x264_ratecontrol_t 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 ); @@ -363,6 +367,29 @@ int x264_ratecontrol_new( x264_t *h ) 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 ) { @@ -1236,7 +1263,7 @@ int x264_ratecontrol_slice_type( x264_t *h, int frame_num ) } /* 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; @@ -1338,7 +1365,7 @@ int x264_ratecontrol_end( x264_t *h, int bits ) } } - 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"); @@ -1475,7 +1502,8 @@ static void update_predictor( predictor_t *p, double q, double var, double bits } // 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; @@ -1490,8 +1518,11 @@ static void update_vbv( x264_t *h, int bits ) 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 --git a/encoder/ratecontrol.h b/encoder/ratecontrol.h index d3b9bec..bc047b9 100644 --- a/encoder/ratecontrol.h +++ b/encoder/ratecontrol.h @@ -36,7 +36,7 @@ void x264_ratecontrol_start( x264_t *, int i_force_qp, int overhead ); int x264_ratecontrol_slice_type( x264_t *, int i_frame ); 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 --git a/encoder/set.c b/encoder/set.c index e087f83..55900a6 100644 --- a/encoder/set.c +++ b/encoder/set.c @@ -206,8 +206,47 @@ void x264_sps_init( x264_sps_t *sps, int i_id, x264_param_t *param ) 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 ) { @@ -343,9 +382,45 @@ void x264_sps_write( bs_t *s, x264_sps_t *sps ) 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 ) { @@ -500,7 +575,7 @@ int x264_sei_version_write( x264_t *h, bs_t *s ) 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 ); @@ -510,6 +585,83 @@ fail: 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[] = { { 10, 1485, 99, 152064, 64, 175, 64, 64, 0, 0, 0, 1 }, diff --git a/encoder/set.h b/encoder/set.h index e76e651..ca3b56c 100644 --- a/encoder/set.h +++ b/encoder/set.h @@ -29,6 +29,8 @@ void x264_sps_write( bs_t *s, x264_sps_t *sps ); 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 --git a/x264.c b/x264.c index fc4e44f..03c95d6 100644 --- a/x264.c +++ b/x264.c @@ -222,7 +222,9 @@ static void Help( x264_param_t *defaults, int longhelp ) 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" ); @@ -386,6 +388,10 @@ static void Help( x264_param_t *defaults, int longhelp ) 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" ); } @@ -430,6 +436,8 @@ static struct option long_options[] = { "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 }, @@ -506,6 +514,9 @@ static struct option long_options[] = { "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 --git a/x264.h b/x264.h index 0c10c8c..0f7cf3e 100644 --- a/x264.h +++ b/x264.h @@ -210,6 +210,7 @@ typedef struct x264_param_t int i_cabac_init_idc; int b_interlaced; + int b_tff; int b_constrained_intra; int i_cqm_preset; @@ -277,6 +278,7 @@ typedef struct x264_param_t 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; @@ -306,6 +308,8 @@ typedef struct x264_param_t 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. */