diff --git a/common/common.c b/common/common.c index fe02d65..203fe6d 100644 --- a/common/common.c +++ b/common/common.c @@ -60,6 +60,19 @@ void x264_param_default( x264_param_t *param ) param->vui.i_transfer = 2; /* undef */ param->vui.i_colmatrix = 2; /* undef */ param->vui.i_chroma_loc= 0; /* left center */ + + param->vui.b_timing_info_present = 1; + param->vui.b_fixed_frame_rate = 1; + + param->vui.b_nal_hrd_parameters_present = 0; + param->vui.hrd.i_bit_rate_scale = 4; + param->vui.hrd.i_cpb_size_scale = 6; + param->vui.hrd.i_bit_rate_value = 0; + param->vui.hrd.i_cpb_size_value = 0; + param->vui.hrd.b_cbr = 0; + param->vui.hrd.b_calc_hrd = 0; + param->vui.hrd.i_initial_cpb_removal_delay = 0; + param->i_fps_num = 25; param->i_fps_den = 1; param->i_level_idc = -1; @@ -155,6 +168,12 @@ void x264_param_default( x264_param_t *param ) param->b_repeat_headers = 1; param->b_annexb = 1; param->b_aud = 0; + param->b_sei_pic_timing = 0; + param->b_tff = 1; + param->i_pulldown = 0; + param->pulldown.mod = 0; + param->pulldown.pattern = NULL; + param->pulldown.i_delay = 0; } static int parse_enum( const char *arg, const char * const *names, int *dst ) @@ -318,6 +337,19 @@ int x264_param_parse( x264_param_t *p, const char *name, const char *value ) p->vui.i_chroma_loc = atoi(value); b_error = ( p->vui.i_chroma_loc < 0 || p->vui.i_chroma_loc > 5 ); } + + OPT("vui-timing-info-present") + p->vui.b_timing_info_present = atobool(value); + OPT("vui-nal-hrd-parameters-present") + p->vui.b_nal_hrd_parameters_present = atobool(value); + OPT("vui-hrd-bit-rate-value") + p->vui.hrd.i_bit_rate_value = atoi(value); + OPT("vui-hrd-cpb-size-value") + p->vui.hrd.i_cpb_size_value = atoi(value); + OPT("vui-hrd-cbr") + p->vui.hrd.b_cbr = atobool(value); + OPT("vui-hrd-initial-cpb-removal-delay") + p->vui.hrd.i_initial_cpb_removal_delay = atoi(value); OPT("fps") { if( sscanf( value, "%d/%d", &p->i_fps_num, &p->i_fps_den ) == 2 ) @@ -395,7 +427,14 @@ 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_tff = p->b_interlaced = atobool(value); + OPT("tff") + p->b_tff = p->b_interlaced = atobool(value); + OPT("bff") + { p->b_interlaced = atobool(value); + p->b_tff = 0; + } OPT("constrained-intra") p->b_constrained_intra = atobool(value); OPT("cqm") @@ -610,6 +649,21 @@ int x264_param_parse( x264_param_t *p, const char *name, const char *value ) p->b_repeat_headers = atobool(value); OPT("annexb") p->b_annexb = atobool(value); + OPT("pulldown") + { + p->vui.b_timing_info_present = 1; + p->b_sei_pic_timing = 1; + b_error |= parse_enum( value, x264_pulldown_names, &p->i_pulldown ); + } + OPT("nal-hrd") + { + p->vui.b_nal_hrd_parameters_present = atobool(value); + if ( p->vui.b_nal_hrd_parameters_present ) p->vui.hrd.b_calc_hrd = 1; + } + OPT("sei-pic-timing") + { + p->b_sei_pic_timing = atobool(value); + } else return X264_PARAM_BAD_NAME; #undef OPT @@ -685,6 +739,8 @@ int x264_picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_heigh pic->img.i_stride[1] = i_width / 2; pic->img.i_stride[2] = i_width / 2; pic->param = NULL; + pic->b_top_field_first = 0; + pic->i_pic_struct = 0; return 0; } @@ -894,6 +950,7 @@ char *x264_param2string( x264_param_t *p, int b_res ) s += sprintf( s, " nr=%d", p->analyse.i_noise_reduction ); s += sprintf( s, " decimate=%d", p->analyse.b_dct_decimate ); s += sprintf( s, " mbaff=%d", p->b_interlaced ); + if ( p->b_interlaced ) s += sprintf( s, " %s", p->b_tff ? "tff" : "bff"); s += sprintf( s, " constrained_intra=%d", p->b_constrained_intra ); s += sprintf( s, " bframes=%d", p->i_bframe ); @@ -945,6 +1002,8 @@ char *x264_param2string( x264_param_t *p, int b_res ) s += sprintf( s, " zones" ); } + s += sprintf( s, " pulldown=%d", p->i_pulldown ); + s += sprintf( s, " nal_hrd=%d", p->vui.b_nal_hrd_parameters_present ); return buf; } diff --git a/common/common.h b/common/common.h index a0afeda..5719a6e 100644 --- a/common/common.h +++ b/common/common.h @@ -116,6 +116,7 @@ static inline int x264_clip3( int v, int i_min, int i_max ) return ( (v < i_min) ? i_min : (v > i_max) ? i_max : v ); } + static inline double x264_clip3f( double v, double f_min, double f_max ) { return ( (v < f_min) ? f_min : (v > f_max) ? f_max : v ); @@ -330,6 +331,13 @@ struct x264_t int i_poc_lsb; /* decoding only */ int i_poc; /* decoding only */ + int i_disp_fields; /* Number of displayed fields (both coded and implied via pic_struct) */ + int i_coded_fields; /* Number of coded fields (both coded and implied via pic_struct) */ + + int i_cpb_delay; /* Equal to number of fields preceding this field + * since last buffering_period SEI */ + + 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 8f42cde..5f4567b 100644 --- a/common/frame.c +++ b/common/frame.c @@ -73,7 +73,9 @@ x264_frame_t *x264_frame_new( x264_t *h, int b_fdec ) frame->i_frame_num = -1; frame->i_lines_completed = -1; frame->b_fdec = b_fdec; - + frame->b_top_field_first = h->param.b_tff; + frame->i_pic_struct = 0; // frame + frame->i_field_cnt = -1; /* all 4 luma planes allocated together, since the cacheline split code * requires them to be in-phase wrt cacheline alignment. */ if( h->param.analyse.i_subpel_refine && b_fdec ) @@ -217,6 +219,8 @@ int x264_frame_copy_picture( x264_t *h, x264_frame_t *dst, x264_picture_t *src ) dst->i_qpplus1 = src->i_qpplus1; dst->i_pts = src->i_pts; dst->param = src->param; + dst->b_top_field_first = src->b_top_field_first; + dst->i_pic_struct = src->i_pic_struct; for( i=0; i<3; i++ ) { diff --git a/common/frame.h b/common/frame.h index 81cfe3c..a153d05 100644 --- a/common/frame.h +++ b/common/frame.h @@ -37,9 +37,13 @@ typedef struct int64_t i_pts; x264_param_t *param; - int i_frame; /* Presentation frame number */ - int i_frame_num; /* Coded frame number */ + int i_frame; /* Presentation frame number */ + int i_field_cnt; /* Presentation field count */ + int i_frame_num; /* Coded frame number */ int b_kept_as_ref; + int i_pic_struct; + int b_top_field_first; + uint8_t b_fdec; uint8_t b_last_minigop_bframe; /* this frame is the last b in a sequence of bframes */ uint8_t i_bframes; /* number of bframes following this nonb in coded order */ diff --git a/common/set.h b/common/set.h index e1b9cd9..368e746 100644 --- a/common/set.h +++ b/common/set.h @@ -116,6 +116,26 @@ typedef struct int i_time_scale; int b_fixed_frame_rate; + int b_nal_hrd_parameters_present; + int b_vcl_hrd_parameters_present; + + struct + { + int i_cpb_cnt; + int i_bit_rate_scale; + int i_cpb_size_scale; + /* FIXME should be arrays */ + int i_bit_rate_value; + int i_cpb_size_value; + int b_cbr; + + int i_initial_cpb_removal_delay_length; + int i_cpb_removal_delay_length; + int i_dpb_output_delay_length; + int i_time_offset_length; + } hrd; + + int b_pic_struct_present; int b_bitstream_restriction; int b_motion_vectors_over_pic_boundaries; int i_max_bytes_per_pic_denom; diff --git a/encoder/encoder.c b/encoder/encoder.c index dd38ea7..46a6114 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -46,6 +46,8 @@ static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current, x264_nal_t **pp_nal, int *pi_nal, x264_picture_t *pic_out ); +static int DeltaTfiDivisor[9] = { 2, 1, 1, 2, 2, 3, 3, 4, 6 }; // Number of coded/displayed fields ( E-6 ) + /**************************************************************************** * ******************************* x264 libs ********************************** @@ -676,11 +678,94 @@ static int x264_validate_parameters( x264_t *h ) h->param.analyse.b_ssim = 0; } + switch ( h->param.i_pulldown ) + { + case X264_PULLDOWN_32: + h->param.pulldown.mod = 4; + h->param.pulldown.pattern = malloc(4*sizeof(int)); + h->param.pulldown.pattern[0] = 5; // TBT + h->param.pulldown.pattern[1] = 4; // BT + h->param.pulldown.pattern[2] = 6; // BTB + h->param.pulldown.pattern[3] = 3; // TB + h->param.pulldown.i_delay = 1; + h->param.pulldown.f_fps_factor = 1.25; + break; + + case X264_PULLDOWN_64: + h->param.pulldown.mod = 2; + h->param.pulldown.pattern = malloc(2*sizeof(int)); + h->param.pulldown.pattern[0] = 7; + h->param.pulldown.pattern[1] = 8; + h->param.pulldown.i_delay = 4; + h->param.pulldown.f_fps_factor = 2.5; + break; + + case X264_PULLDOWN_DOUBLE: + h->param.pulldown.mod = 1; + h->param.pulldown.pattern = malloc(1*sizeof(int)); + h->param.pulldown.pattern[0] = 7; + h->param.pulldown.i_delay = 2; + h->param.pulldown.f_fps_factor = 2; + break; + + case X264_PULLDOWN_TRIPLE: + h->param.pulldown.mod = 1; + h->param.pulldown.i_delay = 4; + h->param.pulldown.pattern = malloc(1*sizeof(int)); + h->param.pulldown.pattern[0] = 8; + h->param.pulldown.f_fps_factor = 3; + break; + + case X264_PULLDOWN_EURO: + h->param.pulldown.mod = 24; + h->param.pulldown.i_delay = 1; + h->param.pulldown.pattern = malloc(24*sizeof(int)); + memset( h->param.pulldown.pattern + 1, 4, 11 ); + memset( h->param.pulldown.pattern + 13, 3, 11 ); + h->param.pulldown.pattern[0] = 5; + h->param.pulldown.pattern[12] = 6; + h->param.pulldown.f_fps_factor = 25.0/24.0; + break; + + default: + h->param.i_pulldown = 0; + h->param.pulldown.mod = 0; + h->param.pulldown.pattern = NULL; + h->param.pulldown.f_fps_factor = 1; + break; + } + + h->param.b_sei_pic_timing += h->param.i_pulldown; + + 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.vui.b_nal_hrd_parameters_present = 0; + } + + if ( h->param.vui.b_nal_hrd_parameters_present && h->param.vui.hrd.b_calc_hrd ) + { + h->param.vui.hrd.b_cbr = 0; + + h->param.vui.hrd.i_initial_cpb_removal_delay = + (h->param.rc.i_vbv_buffer_size*90000) / h->param.rc.i_vbv_max_bitrate; + + // this is not optimal, but more readable + h->param.vui.hrd.i_bit_rate_scale = 4; // 10 - 6, i.e. kbit/sec + h->param.vui.hrd.i_cpb_size_scale = 6; // 10 - 4, i.e. kbits + + if ( !h->param.vui.hrd.i_bit_rate_value ) + h->param.vui.hrd.i_bit_rate_value = (3*h->param.rc.i_vbv_max_bitrate)/2; + if ( !h->param.vui.hrd.i_cpb_size_value ) + h->param.vui.hrd.i_cpb_size_value = (3*h->param.rc.i_vbv_buffer_size)/2; + } + /* ensure the booleans are 0 or 1 so they can be used in math */ #define BOOLIFY(x) h->param.x = !!h->param.x BOOLIFY( b_cabac ); BOOLIFY( b_deblocking_filter ); BOOLIFY( b_interlaced ); + BOOLIFY( b_sei_pic_timing ); BOOLIFY( analyse.b_transform_8x8 ); BOOLIFY( analyse.b_chroma_me ); BOOLIFY( analyse.b_fast_pskip ); @@ -777,6 +862,7 @@ x264_t *x264_encoder_open( x264_param_t *param ) h->i_frame = -1; h->i_frame_num = 0; h->i_idr_pic_id = 0; + h->i_cpb_delay = 0; h->sps = &h->sps_array[0]; x264_sps_init( h->sps, h->param.i_sps_id, &h->param ); @@ -826,6 +912,8 @@ x264_t *x264_encoder_open( x264_param_t *param ) CHECKED_MALLOCZERO( h->frames.current, (h->param.i_sync_lookahead + h->param.i_bframe + h->param.i_threads + 3) * sizeof(x264_frame_t *) ); + h->i_coded_fields = h->i_disp_fields = 0; + h->i_ref0 = 0; h->i_ref1 = 0; @@ -1060,11 +1148,6 @@ int x264_encoder_headers( x264_t *h, x264_nal_t **pp_nal, int *pi_nal ) 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; /* generate sequence parameters */ x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST ); @@ -1079,6 +1162,25 @@ int x264_encoder_headers( x264_t *h, x264_nal_t **pp_nal, int *pi_nal ) return -1; bs_flush( &h->out.bs ); + /* generate sei buffering period */ + if( h->sps->vui.b_nal_hrd_parameters_present || h->sps->vui.b_pic_struct_present ) + { + double initial_cpb_removal_delay = h->param.vui.hrd.i_initial_cpb_removal_delay; + initial_cpb_removal_delay *= h->param.rc.f_vbv_buffer_init; + + x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); + x264_sei_buffering_period_write( h, &h->out.bs, (int)initial_cpb_removal_delay ); + if( x264_nal_end( h ) ) + return -1; + } + + /* identify ourselves */ + 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*/ @@ -1279,7 +1381,17 @@ static inline void x264_slice_init( x264_t *h, int i_nal_type, int i_global_qp ) if( h->sps->i_poc_type == 0 ) { h->sh.i_poc_lsb = h->fdec->i_poc & ( (1 << h->sps->i_log2_max_poc_lsb) - 1 ); - h->sh.i_delta_poc_bottom = 0; /* XXX won't work for field */ + + if( h->param.b_interlaced ) + { + h->sh.i_delta_poc_bottom = h->fenc->b_top_field_first ? 1 : -1; + if ( h->sh.i_delta_poc_bottom == -1 ) + h->sh.i_poc_lsb = ( h->fdec->i_poc + 1 ) & ( (1 << h->sps->i_log2_max_poc_lsb) - 1 ); + } + else + { + h->sh.i_delta_poc_bottom = 0; + } } else if( h->sps->i_poc_type == 1 ) { @@ -1625,6 +1737,8 @@ int x264_encoder_encode( x264_t *h, int i_global_qp; + int overhead = NALU_OVERHEAD; + if( h->param.i_threads > 1) { int i = ++h->i_thread_phase; @@ -1668,6 +1782,28 @@ int x264_encoder_encode( x264_t *h, x264_frame_expand_border_mod16( h, fenc ); fenc->i_frame = h->frames.i_input++; + // fenc->i_pic_struct = 0; + + if ( h->param.vui.b_timing_info_present ) + { + if ( fenc->i_pic_struct == 0 ) // don't override if an app has set it before + { + + if ( h->param.i_pulldown != X264_PULLDOWN_NONE ) + { + fenc->i_pic_struct = h->param.pulldown.pattern[ fenc->i_frame % h->param.pulldown.mod]; + } + else + { + if ( h->param.b_interlaced ) fenc->i_pic_struct = ( fenc->b_top_field_first ) ? 3 : 4; + } + } + } + + // add number of "displayed" fields + // fixme: case of field frames handled incorrectly + fenc->i_field_cnt = h->i_disp_fields; + h->i_disp_fields += DeltaTfiDivisor[fenc->i_pic_struct]; if( h->frames.b_have_lowres ) x264_frame_init_lowres( h, fenc ); @@ -1764,8 +1900,6 @@ int x264_encoder_encode( x264_t *h, 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; - - /* ------------------- Init ----------------------------- */ /* build ref list 0/1 */ x264_reference_build_list( h, h->fdec->i_poc ); @@ -1796,16 +1930,44 @@ int x264_encoder_encode( x264_t *h, 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; } h->i_nal_type = i_nal_type; h->i_nal_ref_idc = i_nal_ref_idc; - int overhead = NALU_OVERHEAD; /* Write SPS and PPS */ if( i_nal_type == NAL_SLICE_IDR && h->param.b_repeat_headers ) { + + /* generate sequence parameters */ + x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST ); + x264_sps_write( &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; + + /* generate picture parameters */ + x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST ); + x264_pps_write( &h->out.bs, h->pps ); + if( x264_nal_end( h ) ) + return -1; + overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD; + + /* generate sei buffering period */ + if( h->sps->vui.b_nal_hrd_parameters_present || h->sps->vui.b_pic_struct_present ) + { + double initial_cpb_removal_delay = h->param.vui.hrd.i_initial_cpb_removal_delay; + initial_cpb_removal_delay *= X264_MIN(x264_vbv_fullness( h, 8*overhead ), 1.0); + + x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); + x264_sei_buffering_period_write( h, &h->out.bs, (int)initial_cpb_removal_delay ); + 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 */ @@ -1816,22 +1978,32 @@ int x264_encoder_encode( x264_t *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 ); - if( x264_nal_end( h ) ) - return -1; - overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD; + /* generate sei pic timing */ + if( h->sps->vui.b_pic_struct_present || h->sps->vui.b_nal_hrd_parameters_present ) + { + int dpb_output_delay = h->fenc->i_field_cnt - h->i_coded_fields; - /* generate picture parameters */ - x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST ); - x264_pps_write( &h->out.bs, h->pps ); + // add a correction term for frames with 3+ fields + dpb_output_delay += h->param.pulldown.i_delay; + + // add a correction term for frame reordering + dpb_output_delay += h->sps->vui.i_num_reorder_frames*2; + + x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); + x264_sei_pic_timing_write( h, &h->out.bs, h->i_cpb_delay, dpb_output_delay, h->fenc->i_pic_struct ); if( x264_nal_end( h ) ) return -1; overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD; } + if( i_nal_type == NAL_SLICE_IDR ) h->i_cpb_delay = 0; + + h->i_cpb_delay += DeltaTfiDivisor[h->fenc->i_pic_struct]; // number of fields we just added + h->i_coded_fields += DeltaTfiDivisor[h->fenc->i_pic_struct]; + + /* Init the rate control */ /* FIXME: Include slice header bit cost. */ x264_ratecontrol_start( h, h->fenc->i_qpplus1, overhead*8 ); diff --git a/encoder/ratecontrol.c b/encoder/ratecontrol.c index 16ac7de..4717772 100644 --- a/encoder/ratecontrol.c +++ b/encoder/ratecontrol.c @@ -1497,6 +1497,12 @@ static void update_vbv( x264_t *h, int bits ) rct->buffer_fill_final = X264_MIN( rct->buffer_fill_final, rct->buffer_size ); } +double x264_vbv_fullness( x264_t *h, int overhead ) +{ + x264_ratecontrol_t *rct = h->thread[0]->rc; + return (rct->buffer_fill_final + overhead)/rct->buffer_size; +} + // provisionally update VBV according to the planned size of all frames currently in progress static void update_vbv_plan( x264_t *h, int overhead ) { diff --git a/encoder/ratecontrol.h b/encoder/ratecontrol.h index d3b9bec..d8182f0 100644 --- a/encoder/ratecontrol.h +++ b/encoder/ratecontrol.h @@ -41,6 +41,6 @@ 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 *); int x264_rc_analyse_slice( x264_t *h ); - +double x264_vbv_fullness( x264_t *h, int overhead ); #endif diff --git a/encoder/set.c b/encoder/set.c index f6dd830..b0b3bef 100644 --- a/encoder/set.c +++ b/encoder/set.c @@ -29,6 +29,8 @@ #define bs_write_ue bs_write_ue_big +static int NumClockTS[9] = { 1, 1, 1, 2, 2, 3, 3, 2, 3 }; // Table D-1 + static void transpose( uint8_t *buf, int w ) { int i, j; @@ -180,21 +182,51 @@ void x264_sps_init( x264_sps_t *sps, int i_id, x264_param_t *param ) sps->vui.i_chroma_loc_bottom = param->vui.i_chroma_loc; } - sps->vui.b_timing_info_present = 0; - if( param->i_fps_num > 0 && param->i_fps_den > 0) + sps->vui.b_timing_info_present = param->vui.b_timing_info_present; + if( sps->vui.b_timing_info_present ) { - sps->vui.b_timing_info_present = 1; sps->vui.i_num_units_in_tick = param->i_fps_den; sps->vui.i_time_scale = param->i_fps_num * 2; - sps->vui.b_fixed_frame_rate = 1; + + if ( param->i_pulldown != X264_PULLDOWN_NONE ) + { + sps->vui.i_time_scale = (int)((float)(sps->vui.i_time_scale)*param->pulldown.f_fps_factor); + } + + sps->vui.b_fixed_frame_rate = param->vui.b_fixed_frame_rate; } + sps->vui.b_vcl_hrd_parameters_present = 0; + sps->vui.i_num_reorder_frames = param->b_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->vui.b_nal_hrd_parameters_present = param->vui.b_nal_hrd_parameters_present; + if( sps->vui.b_nal_hrd_parameters_present ) + { + int max_cpb = ceil( 2*param->i_keyint_max*param->pulldown.f_fps_factor + 1); + int max_dpb = ceil( (2 + 2*sps->vui.i_max_dec_frame_buffering)*param->pulldown.f_fps_factor + 1); + + sps->vui.hrd.i_cpb_cnt = 1; // FIXME no way to have more than one set of HRD params + sps->vui.hrd.i_bit_rate_scale = param->vui.hrd.i_bit_rate_scale; + sps->vui.hrd.i_cpb_size_scale = param->vui.hrd.i_cpb_size_scale; + //for( i = 0; i < sps->vui.hrd.i_cpb_cnt; i++ ) // FIXME no way to have more than one set of HRD params + //{ + sps->vui.hrd.i_bit_rate_value = param->vui.hrd.i_bit_rate_value; + sps->vui.hrd.i_cpb_size_value = param->vui.hrd.i_cpb_size_value; + sps->vui.hrd.b_cbr = param->vui.hrd.b_cbr; + //} + sps->vui.hrd.i_initial_cpb_removal_delay_length = X264_MIN( 32 - x264_clz( param->vui.hrd.i_initial_cpb_removal_delay ),24); + sps->vui.hrd.i_cpb_removal_delay_length = X264_MIN( 32 - x264_clz( max_cpb ), 24 ); + sps->vui.hrd.i_dpb_output_delay_length = X264_MIN( 32 - x264_clz( max_dpb ), 24 ) ; + sps->vui.hrd.i_time_offset_length = 0; + } + + sps->vui.b_pic_struct_present = param->b_sei_pic_timing; sps->vui.b_bitstream_restriction = 1; if( sps->vui.b_bitstream_restriction ) { @@ -341,9 +373,34 @@ 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, 0 ); /* vcl_hrd_parameters_present_flag */ - bs_write1( s, 0 ); /* pic_struct_present_flag */ + bs_write1( s, sps->vui.b_nal_hrd_parameters_present ); + if( sps->vui.b_nal_hrd_parameters_present ) + { + bs_write_ue( s, sps->vui.hrd.i_cpb_cnt - 1 ); + bs_write( s, 4, sps->vui.hrd.i_bit_rate_scale); + bs_write( s, 4, sps->vui.hrd.i_cpb_size_scale); + //for( i = 0; i < sps->vui.hrd.i_cpb_cnt; i++ ) // FIXME no way to have more than one set of HRD params + //{ + // + bs_write_ue( s, sps->vui.hrd.i_bit_rate_value - 1 ); + bs_write_ue( s, sps->vui.hrd.i_cpb_size_value - 1 ); + + bs_write1( s, sps->vui.hrd.b_cbr ); + //} + bs_write( s, 5, sps->vui.hrd.i_initial_cpb_removal_delay_length - 1 ); + bs_write( s, 5, sps->vui.hrd.i_cpb_removal_delay_length - 1); + bs_write( s, 5, sps->vui.hrd.i_dpb_output_delay_length - 1); + bs_write( s, 5, sps->vui.hrd.i_time_offset_length); + } + + bs_write1( s, sps->vui.b_vcl_hrd_parameters_present ); // must be 0 // FIXME no way to write VCL HRD + + if ( sps->vui.b_nal_hrd_parameters_present || sps->vui.b_vcl_hrd_parameters_present ) + { + bs_write1( s, 0); /* low_delay_hrd_flag */ + } + + bs_write1( s, sps->vui.b_pic_struct_present ); bs_write1( s, sps->vui.b_bitstream_restriction ); if( sps->vui.b_bitstream_restriction ) { @@ -368,7 +425,7 @@ void x264_pps_init( x264_pps_t *pps, int i_id, x264_param_t *param, x264_sps_t * pps->i_sps_id = sps->i_id; pps->b_cabac = param->b_cabac; - pps->b_pic_order = 0; + pps->b_pic_order = param->b_sei_pic_timing; pps->i_num_slice_groups = 1; pps->i_num_ref_idx_l0_active = 1; @@ -487,7 +544,7 @@ int x264_sei_version_write( x264_t *h, bs_t *s ) X264_BUILD, X264_VERSION, opts ); length = strlen(version)+1+16; - bs_write( s, 8, 0x5 ); // payload_type = user_data_unregistered + bs_write( s, 8, SEI_USER_DATA_UNREGISTERED ); // payload_size for( i = 0; i <= length-255; i += 255 ) bs_write( s, 8, 255 ); @@ -508,6 +565,80 @@ fail: return -1; } +void x264_sei_buffering_period_write( x264_t *h, bs_t *s, int initial_cpb_removal_delay ) +{ + x264_sps_t *sps = h->sps; + + int payload_size; // in bits + + payload_size = bs_size_ue( sps->i_id ); + if( sps->vui.b_nal_hrd_parameters_present ) + { + payload_size += sps->vui.hrd.i_initial_cpb_removal_delay_length * 2; + } + + bs_write( s, 8, SEI_BUFFERING_PERIOD ); + bs_write( s, 8, (payload_size + 7) / 8); + + bs_write_ue( s, sps->i_id ); + + + if( sps->vui.b_nal_hrd_parameters_present ) + { + bs_write( s, sps->vui.hrd.i_initial_cpb_removal_delay_length, initial_cpb_removal_delay ); + bs_write( s, sps->vui.hrd.i_initial_cpb_removal_delay_length, 0 ); + + } + + if( s->i_left&7 ) bs_write1( s, 1 ); + if( s->i_left&7 ) bs_align_0( s ); + + bs_rbsp_trailing( s ); +} + +void x264_sei_pic_timing_write( x264_t *h, bs_t *s, int cpb_removal_delay, int dpb_output_delay, int pic_struct ) +{ + x264_sps_t *sps = h->sps; + + int payload_size = 0; // in bits + + if( sps->vui.b_nal_hrd_parameters_present || sps->vui.b_vcl_hrd_parameters_present ) // if CpbDpbDelaysPresentFlag + { + payload_size += sps->vui.hrd.i_cpb_removal_delay_length + sps->vui.hrd.i_dpb_output_delay_length; + } + + if( sps->vui.b_pic_struct_present ) + { + payload_size += 4; // sizeof (pic_struct) + payload_size += NumClockTS[pic_struct]; + } + + bs_write( s, 8, SEI_PIC_TIMING ); + bs_write( s, 8, (payload_size + 7) / 8); + + if( sps->vui.b_nal_hrd_parameters_present || sps->vui.b_vcl_hrd_parameters_present ) + { + bs_write( s, sps->vui.hrd.i_cpb_removal_delay_length, cpb_removal_delay ); + bs_write( s, sps->vui.hrd.i_dpb_output_delay_length, dpb_output_delay ); + } + + if( sps->vui.b_pic_struct_present ) + { + int i = 0; + + bs_write( s, 4, pic_struct ); + + // non-existent clock data (we have fixed frame rate anyway) + for ( i = 0; i < NumClockTS[pic_struct]; i++ ) bs_write1( s, 0 ); + } + + if( s->i_left&7 ) bs_write1( s, 1 ); + if( s->i_left&7 ) bs_align_0( s ); + + bs_rbsp_trailing( 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..a53eedd 100644 --- a/encoder/set.h +++ b/encoder/set.h @@ -30,5 +30,7 @@ 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 ); int x264_sei_version_write( x264_t *h, bs_t *s ); int x264_validate_levels( x264_t *h, int verbose ); +void x264_sei_buffering_period_write( x264_t *h, bs_t *s, int initial_cpb_removal_delay ); +void x264_sei_pic_timing_write( x264_t *h, bs_t *s, int cpb_removal_delay, int dpb_output_delay, int pic_struct ); #endif diff --git a/x264.c b/x264.c index ec4a378..1c71ce3 100644 --- a/x264.c +++ b/x264.c @@ -385,6 +385,9 @@ 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" ); + H0( " --pulldown Use soft pulldown and Timing SEI to change frame rate\n" + " - 32, 64, double, triple, euro (default: none)\n" ); + H0( "\n" ); } @@ -525,6 +528,18 @@ static struct option long_options[] = { "transfer", required_argument, NULL, 0 }, { "colormatrix", required_argument, NULL, 0 }, { "chromaloc", required_argument, NULL, 0 }, + + { "sei-pic-timing", no_argument, NULL, 0 }, + { "vui-timing-info-present", required_argument, NULL, 0 }, + { "vui-nal-hrd-parameters-present", required_argument, NULL, 0 }, + { "vui-hrd-bit-rate-value", required_argument, NULL, 0 }, + { "vui-hrd-cpb-size-value", required_argument, NULL, 0 }, + { "vui-hrd-cbr", required_argument, NULL, 0 }, + { "vui-hrd-initial-cpb-removal-delay", required_argument, NULL, 0 }, + + { "nal-hrd", no_argument, NULL, 0 }, + { "pulldown", required_argument, NULL, 0 }, + {0, 0, 0, 0} }; diff --git a/x264.h b/x264.h index 7bc2d36..de72d2e 100644 --- a/x264.h +++ b/x264.h @@ -96,6 +96,13 @@ typedef struct x264_t x264_t; #define X264_B_ADAPT_FAST 1 #define X264_B_ADAPT_TRELLIS 2 +#define X264_PULLDOWN_NONE 0 +#define X264_PULLDOWN_32 1 +#define X264_PULLDOWN_64 2 +#define X264_PULLDOWN_DOUBLE 3 +#define X264_PULLDOWN_TRIPLE 4 +#define X264_PULLDOWN_EURO 5 + 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_overscan_names[] = { "undef", "show", "crop", 0 }; @@ -104,6 +111,7 @@ static const char * const x264_fullrange_names[] = { "off", "on", 0 }; static const char * const x264_colorprim_names[] = { "", "bt709", "undef", "", "bt470m", "bt470bg", "smpte170m", "smpte240m", "film", 0 }; static const char * const x264_transfer_names[] = { "", "bt709", "undef", "", "bt470m", "bt470bg", "smpte170m", "smpte240m", "linear", "log100", "log316", 0 }; static const char * const x264_colmatrix_names[] = { "GBR", "bt709", "undef", "", "fcc", "bt470bg", "smpte170m", "smpte240m", "YCgCo", 0 }; +static const char * const x264_pulldown_names[] = { "", "32", "64", "double", "triple", "euro", 0 }; /* Colorspace type * legacy only; nothing other than I420 is really supported. */ @@ -183,6 +191,25 @@ typedef struct x264_param_t int i_transfer; int i_colmatrix; int i_chroma_loc; /* both top & bottom */ + + int b_timing_info_present; + int b_fixed_frame_rate; + + int b_nal_hrd_parameters_present; + struct + { + int i_cpb_cnt; + int i_bit_rate_scale; + int i_cpb_size_scale; + /* FIXME should be arrays */ + int i_bit_rate_value; + int i_cpb_size_value; + int b_cbr; + + int i_initial_cpb_removal_delay; // this doesn't belong in VUI but is related + int b_calc_hrd; + } hrd; + } vui; int i_fps_num; @@ -302,6 +329,18 @@ 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_sei_pic_timing; /* put a pic_timing SEI after each frame. maybe useful for interlaced */ + + int i_pulldown; + int b_tff; + struct + { + int* pattern; + int mod; + float f_fps_factor; + int i_delay; + } pulldown; /* Slicing parameters */ int i_slice_max_size; /* Max size per slice in bytes; includes estimated NAL overhead. */ @@ -371,6 +410,12 @@ typedef struct int i_type; /* In: force quantizer for > 0 */ int i_qpplus1; + + /* In: parity, for interlaced pictures. used only if b_sei_pic_timing=1. */ + int b_top_field_first; + /* In: pic_struct, for pulldown/doubling/etc.. used only if b_pic_timing_sei=1. */ + int i_pic_struct; + /* In: user pts, Out: pts of encoded picture (user)*/ int64_t i_pts; /* In: custom encoding parameters to be set from this frame forwards @@ -420,6 +465,15 @@ enum nal_priority_e NAL_PRIORITY_HIGH = 2, NAL_PRIORITY_HIGHEST = 3, }; +enum sei_payload_type_e +{ + SEI_BUFFERING_PERIOD = 0, + SEI_PIC_TIMING = 1, + SEI_PAN_SCAN_RECT = 2, + SEI_USER_DATA_REGISTERED_ITU_T_T35 = 4, + SEI_USER_DATA_UNREGISTERED = 5, + SEI_RECOVERY_POINT = 6, +}; /* The data within the payload is already NAL-encapsulated; the ref_idc and type * are merely in the struct for easy access by the calling application.