diff --git a/common/common.c b/common/common.c index 6d1d7f0..5956cf1 100644 --- a/common/common.c +++ b/common/common.c @@ -156,6 +156,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; param->b_vfr_input = 1; param->b_dts_compress = 0; } @@ -402,7 +404,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") @@ -611,6 +629,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 950f48f..018bb3f 100644 --- a/common/common.h +++ b/common/common.h @@ -366,6 +366,12 @@ struct x264_t * 1 when sliced-threads is on. */ int i_nal_type; int i_nal_ref_idc; + 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; /* We use only one SPS and one PPS */ x264_sps_t sps_array[1]; diff --git a/common/frame.c b/common/frame.c index 40cc78f..fbf5248 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 b1852b3..fb801b2 100644 --- a/common/frame.h +++ b/common/frame.h @@ -34,6 +34,7 @@ typedef struct x264_frame int i_poc; int i_type; int i_qpplus1; + int64_t i_cur_dts; int64_t i_pts; int64_t i_reordered_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 d873cd0..19ca321 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -462,6 +462,37 @@ static int x264_validate_parameters( x264_t *h ) } } + if( h->param.b_nal_hrd ) + { + if( h->param.b_vfr_input ) + { + x264_log( h, X264_LOG_WARNING, "VFR input + NAL HRD is not implemented\n" ); + h->param.b_nal_hrd = 0; + } + else 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" ); @@ -856,13 +887,18 @@ x264_t *x264_encoder_open( x264_param_t *param ) x264_set_aspect_ratio( h, &h->param, 1 ); - x264_reduce_fraction( &h->param.i_fps_num, &h->param.i_fps_den ); - x264_reduce_fraction( &h->param.i_timebase_num, &h->param.i_timebase_den ); + if( !h->param.b_nal_hrd ) + { + x264_reduce_fraction( &h->param.i_fps_num, &h->param.i_fps_den ); + x264_reduce_fraction( &h->param.i_timebase_num, &h->param.i_timebase_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; if( h->param.b_dts_compress ) { /* h->i_dts_compress_multiplier == h->frames.i_bframe_delay + 1 */ @@ -1199,13 +1235,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 ); @@ -1218,6 +1248,13 @@ int x264_encoder_headers( x264_t *h, x264_nal_t **pp_nal, int *pi_nal ) 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; + frame_size = x264_encoder_encapsulate_nals( h ); /* now set output*/ @@ -2048,8 +2085,11 @@ 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, 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->i_thread_frames > 1 ) { @@ -2204,7 +2244,18 @@ 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; - + 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 ----------------------------- */ @@ -2278,17 +2329,6 @@ int x264_encoder_encode( x264_t *h, { if( 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 ); @@ -2302,17 +2342,82 @@ 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->param.b_nal_hrd && i_nal_type == NAL_SLICE_IDR ) + { + x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); + x264_sei_buffering_period_write( h, &h->param, &h->out.bs, h->sps ); + 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 ) + { + /* 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; + } } + else + if( h->param.b_nal_hrd && i_nal_type == NAL_SLICE_IDR ) + { + x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); + x264_sei_buffering_period_write( h, &h->param, &h->out.bs, h->sps ); + if( x264_nal_end( h ) ) + return -1; + overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD; + } if( h->fenc->i_type != X264_TYPE_IDR ) { x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); x264_sei_recovery_point_write( h, &h->out.bs, h->param.i_keyint_max ); - x264_nal_end( h ); + 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 && !h->sh.b_field_pic ) + { + // pic_struct + int pic_struct = 0; + int dpb_output_delay = h->fenc->i_pts * 2 + + (int)( 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_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); + x264_sei_picture_timing_write( &h->out.bs, h->sps, cpb_removal_delay, dpb_output_delay, pic_struct ); + if( x264_nal_end( h ) ) + return -1; + overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD; + 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; + } + } + /* Init the rate control */ /* FIXME: Include slice header bit cost. */ x264_ratecontrol_start( h, h->fenc->i_qpplus1, overhead*8 ); @@ -2357,6 +2462,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 ); } @@ -2436,7 +2542,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 63b3be6..489b98f 100644 --- a/encoder/ratecontrol.c +++ b/encoder/ratecontrol.c @@ -73,6 +73,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; @@ -94,6 +96,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; @@ -152,7 +156,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 ); @@ -406,6 +410,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 ) { @@ -1314,7 +1341,7 @@ void x264_ratecontrol_set_weights( x264_t *h, x264_frame_t *frm ) } /* 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; @@ -1425,7 +1452,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"); @@ -1562,7 +1589,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; @@ -1577,8 +1605,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 5a8d088..361b551 100644 --- a/encoder/ratecontrol.h +++ b/encoder/ratecontrol.h @@ -37,7 +37,7 @@ int x264_ratecontrol_slice_type( x264_t *, int i_frame ); 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 --git a/encoder/set.c b/encoder/set.c index f79919b..8624dc7 100644 --- a/encoder/set.c +++ b/encoder/set.c @@ -205,8 +205,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 ) { @@ -534,6 +609,90 @@ 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_realign( s ); + 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 ); + + bs_rbsp_trailing( s ); + bs_flush( 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_realign( s ); + 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_align_10( s ); + + bs_rbsp_trailing( s ); + bs_flush( 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 125f7e1..03a0a21 100644 --- a/encoder/set.h +++ b/encoder/set.h @@ -30,6 +30,8 @@ void x264_pps_init( x264_pps_t *pps, int i_id, x264_param_t *param, x264_sps_t * void x264_pps_write( bs_t *s, x264_pps_t *pps ); void x264_sei_recovery_point_write( x264_t *h, bs_t *s, int recovery_frame_cnt ); 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/output/flv.c b/output/flv.c index b3e5d16..03501e4 100644 --- a/output/flv.c +++ b/output/flv.c @@ -154,9 +154,9 @@ static int write_headers( hnd_t handle, x264_nal_t *p_nal ) flv_hnd_t *p_flv = handle; flv_buffer *c = p_flv->c; - int sei_size = p_nal[0].i_payload; - int sps_size = p_nal[1].i_payload; - int pps_size = p_nal[2].i_payload; + int sps_size = p_nal[0].i_payload; + int pps_size = p_nal[1].i_payload; + int sei_size = p_nal[2].i_payload; // SEI /* It is within the spec to write this as-is but for @@ -167,10 +167,10 @@ static int write_headers( hnd_t handle, x264_nal_t *p_nal ) return -1; p_flv->sei_len = sei_size; - memcpy( p_flv->sei, p_nal[0].p_payload, sei_size ); + memcpy( p_flv->sei, p_nal[2].p_payload, sei_size ); // SPS - uint8_t *sps = p_nal[1].p_payload + 4; + uint8_t *sps = p_nal[0].p_payload + 4; x264_put_byte( c, FLV_TAG_TYPE_VIDEO ); x264_put_be24( c, 0 ); // rewrite later @@ -196,7 +196,7 @@ static int write_headers( hnd_t handle, x264_nal_t *p_nal ) // PPS x264_put_byte( c, 1 ); // number of pps x264_put_be16( c, pps_size - 4 ); - flv_append_data( c, p_nal[2].p_payload + 4, pps_size - 4 ); + flv_append_data( c, p_nal[1].p_payload + 4, pps_size - 4 ); // rewrite data length info unsigned length = c->d_cur - p_flv->start; diff --git a/output/matroska.c b/output/matroska.c index 8e84f52..af11a36 100644 --- a/output/matroska.c +++ b/output/matroska.c @@ -107,13 +107,13 @@ static int write_headers( hnd_t handle, x264_nal_t *p_nal ) { mkv_hnd_t *p_mkv = handle; - int sei_size = p_nal[0].i_payload; - int sps_size = p_nal[1].i_payload - 4; - int pps_size = p_nal[2].i_payload - 4; + int sps_size = p_nal[0].i_payload - 4; + int pps_size = p_nal[1].i_payload - 4; + int sei_size = p_nal[2].i_payload; - uint8_t *sei = p_nal[0].p_payload; - uint8_t *sps = p_nal[1].p_payload + 4; - uint8_t *pps = p_nal[2].p_payload + 4; + uint8_t *sps = p_nal[0].p_payload + 4; + uint8_t *pps = p_nal[1].p_payload + 4; + uint8_t *sei = p_nal[2].p_payload; int ret; uint8_t *avcC; diff --git a/output/mp4.c b/output/mp4.c index e3ad9c6..b3bb702 100644 --- a/output/mp4.c +++ b/output/mp4.c @@ -227,13 +227,13 @@ static int write_headers( hnd_t handle, x264_nal_t *p_nal ) mp4_hnd_t *p_mp4 = handle; GF_AVCConfigSlot *p_slot; - int sei_size = p_nal[0].i_payload; - int sps_size = p_nal[1].i_payload - 4; - int pps_size = p_nal[2].i_payload - 4; + int sps_size = p_nal[0].i_payload - 4; + int pps_size = p_nal[1].i_payload - 4; + int sei_size = p_nal[2].i_payload; - uint8_t *sei = p_nal[0].p_payload; - uint8_t *sps = p_nal[1].p_payload + 4; - uint8_t *pps = p_nal[2].p_payload + 4; + uint8_t *sps = p_nal[0].p_payload + 4; + uint8_t *pps = p_nal[1].p_payload + 4; + uint8_t *sei = p_nal[2].p_payload; // SPS diff --git a/x264.c b/x264.c index 58bc1f4..71b4437 100644 --- a/x264.c +++ b/x264.c @@ -339,7 +339,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" ); @@ -512,6 +514,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" ); + H2( " --nal-hrd Use NAL HRD parameters\n" ); + H2( " --pulldown Use 3:2 pulldown\n" + " - 32: TBT,BT,BTB,TB pattern\n" + " - 64: triple,double *recommended for 720p\n" ); H2( " --force-cfr Force constant framerate timestamp generation\n" ); H0( "\n" ); } @@ -563,6 +569,8 @@ static struct option long_options[] = { "deblock", required_argument, NULL, 'f' }, { "interlaced", no_argument, NULL, OPT_INTERLACED }, { "no-interlaced", no_argument, NULL, OPT_INTERLACED }, + { "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 }, @@ -647,6 +655,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 2550864..3866dde 100644 --- a/x264.h +++ b/x264.h @@ -216,6 +216,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; @@ -284,6 +285,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; @@ -313,6 +315,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 */ int b_vfr_input; /* VFR input */ int i_timebase_num; /* Timebase numerator */ int i_timebase_den; /* Timebase denominator */