diff --git a/common/common.c b/common/common.c index 9eed5c3..cbb94be 100644 --- a/common/common.c +++ b/common/common.c @@ -60,6 +60,17 @@ 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->b_nal_hrd = 0; + param->b_pic_struct = 0; + param->vui.hrd.i_bit_rate_scale = 0; + param->vui.hrd.i_cpb_size_scale = 0; + param->vui.hrd.i_bit_rate_value = 0; + param->vui.hrd.i_cpb_size_value = 0; + param->vui.hrd.b_cbr = 0; + param->i_fps_num = 25; param->i_fps_den = 1; param->i_level_idc = -1; @@ -157,6 +168,13 @@ void x264_param_default( x264_param_t *param ) param->b_annexb = 1; param->b_aud = 0; param->b_vfr_input = 1; + + param->b_pic_struct = 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 ) @@ -401,7 +419,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 = !p->b_interlaced; + } OPT("constrained-intra") p->b_constrained_intra = atobool(value); OPT("cqm") @@ -620,6 +645,12 @@ int x264_param_parse( x264_param_t *p, const char *name, const char *value ) p->b_annexb = atobool(value); OPT("force-cfr") p->b_vfr_input = !atobool(value); + OPT("pulldown") + b_error |= parse_enum( value, x264_pulldown_names, &p->i_pulldown ); + OPT("nal-hrd") + p->b_nal_hrd = atobool(value); + OPT("pic-struct") + p->b_pic_struct = atobool(value); else return X264_PARAM_BAD_NAME; #undef OPT @@ -695,6 +726,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; } @@ -906,6 +939,8 @@ 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 ); @@ -958,6 +993,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->b_nal_hrd ); return buf; } diff --git a/common/common.h b/common/common.h index 5d31d47..c0223f3 100644 --- a/common/common.h +++ b/common/common.h @@ -369,6 +369,12 @@ 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 */ @@ -450,7 +456,10 @@ struct x264_t x264_frame_t *fref1[16+3]; /* ref list 1 */ int b_ref_reorder[2]; - + /* hrd */ + int initial_cpb_removal_delay; + int current_cpb_delay; + int dpb_output_delay; /* Current MB DCT coeffs */ struct diff --git a/common/frame.c b/common/frame.c index 08ef87f..9970504 100644 --- a/common/frame.c +++ b/common/frame.c @@ -73,6 +73,11 @@ 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; + frame->orig = frame; /* all 4 luma planes allocated together, since the cacheline split code @@ -225,6 +230,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 = dst->i_dts = 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 786869e..ee18d82 100644 --- a/common/frame.h +++ b/common/frame.h @@ -40,8 +40,11 @@ typedef struct x264_frame int i_frame; /* Presentation frame number */ int i_coded; /* Coded frame number */ + int i_field_cnt; /* Presentation field count */ int i_frame_num; /* 7.4.3 frame_num */ int b_kept_as_ref; + int i_pic_struct; + int b_top_field_first; int b_keyframe; uint8_t b_fdec; uint8_t b_last_minigop_bframe; /* this frame is the last b in a sequence of bframes */ @@ -107,6 +110,9 @@ typedef struct x264_frame uint32_t i_pixel_sum; uint64_t i_pixel_ssd; + /* hrd */ + x264_hrd_t hrd_timing; + /* vbv */ uint8_t i_planned_type[X264_LOOKAHEAD_MAX+1]; int i_planned_satd[X264_LOOKAHEAD_MAX+1]; diff --git a/common/osdep.h b/common/osdep.h index abae9ac..78aed7a 100644 --- a/common/osdep.h +++ b/common/osdep.h @@ -212,6 +212,7 @@ static ALWAYS_INLINE uint16_t endian_fix16( uint16_t x ) #if defined(__GNUC__) && (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ > 3) #define x264_clz(x) __builtin_clz(x) +#define x264_ctz(x) __builtin_ctz(x) #else static int ALWAYS_INLINE x264_clz( uint32_t x ) { @@ -224,6 +225,22 @@ static int ALWAYS_INLINE x264_clz( uint32_t x ) x >>= y^4; return z + lut[x]; } + +static int ALWAYS_INLINE x264_ctz( uint32_t x ) +{ + static uint8_t lut[16] = {4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0}; + int y, n = 0; + if( !x ) + return 0; // just to be consistent with __builtin_ctz + + while( ( y = lut[x&0xF] ) == 4 ) + { + x >>= 4; + n += y; + } + n += lut[x&0xF]; + return n; +} #endif #ifdef USE_REAL_PTHREAD diff --git a/common/set.h b/common/set.h index e1b9cd9..a74e639 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 f97df1b..24732c3 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -47,6 +47,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 delta_tfi_divisor[9] = { 2, 1, 1, 2, 2, 3, 3, 4, 6 }; // Number of coded/displayed fields ( E-6 ) + /**************************************************************************** * ******************************* x264 libs ********************************** @@ -439,6 +441,7 @@ static int x264_validate_parameters( x264_t *h ) x264_log( h, X264_LOG_WARNING, "interlace + weightp is not implemented\n" ); h->param.analyse.i_weighted_pred = X264_WEIGHTP_NONE; } + h->param.b_pic_struct = 1; } /* Detect default ffmpeg settings and terminate with an error. */ @@ -744,6 +747,27 @@ static int x264_validate_parameters( x264_t *h ) h->param.analyse.b_ssim = 0; } + if( h->param.i_pulldown < X264_PULLDOWN_NONE || h->param.i_pulldown > X264_PULLDOWN_EURO ) + { + x264_log( h, X264_LOG_WARNING, "Invalid pulldown pattern, pulldown disabled\n" ); + h->param.i_pulldown = X264_PULLDOWN_NONE; + } + + if( h->param.i_pulldown != X264_PULLDOWN_NONE && h->param.b_vfr_input ) + { + x264_log( h, X264_LOG_WARNING, "Pulldown not compatible with variable framerate, pulldown disabled\n" ); + h->param.i_pulldown = X264_PULLDOWN_NONE; + } + + h->param.vui.b_timing_info_present |= ( h->param.i_pulldown != X264_PULLDOWN_NONE ); + h->param.b_pic_struct |= ( h->param.i_pulldown != X264_PULLDOWN_NONE ); + + if( h->param.b_nal_hrd && ( !h->param.rc.i_vbv_max_bitrate || !h->param.rc.i_vbv_buffer_size ) ) + { + x264_log( h, X264_LOG_WARNING, "NAL HRD parameters require VBV parameters\n" ); + h->param.b_nal_hrd = 0; + } + /* 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 ); @@ -769,6 +793,9 @@ static int x264_validate_parameters( x264_t *h ) BOOLIFY( rc.b_stat_write ); BOOLIFY( rc.b_stat_read ); BOOLIFY( rc.b_mb_tree ); + BOOLIFY( b_nal_hrd ); + BOOLIFY( b_pic_struct ); + BOOLIFY( vui.b_timing_info_present ); #undef BOOLIFY return 0; @@ -862,6 +889,102 @@ 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; + + /* Init pulldown */ + switch( h->param.i_pulldown ) + { + case X264_PULLDOWN_NONE: + h->param.i_pulldown = 0; + h->param.pulldown.mod = 0; + h->param.pulldown.pattern = NULL; + h->param.pulldown.f_fps_factor = 1; + break; + + case X264_PULLDOWN_32: + h->param.pulldown.mod = 4; + h->param.pulldown.pattern = x264_malloc( h->param.pulldown.mod*sizeof(int) ); + if( !h->param.pulldown.pattern ) + goto fail; + 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 = x264_malloc( h->param.pulldown.mod*sizeof(int) ); + if( !h->param.pulldown.pattern ) + goto fail; + 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 = x264_malloc( h->param.pulldown.mod*sizeof(int) ); + if( !h->param.pulldown.pattern ) + goto fail; + 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 = x264_malloc( h->param.pulldown.mod*sizeof(int) ); + if( !h->param.pulldown.pattern ) + goto fail; + 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 = x264_malloc( h->param.pulldown.mod*sizeof(int) ); + if( !h->param.pulldown.pattern ) + goto fail; + 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; + } + + /* Init HRD */ + if( h->param.b_nal_hrd ) + { + #define BR_SHIFT 6 + #define CPB_SHIFT 4 + + int bitrate = 1000*h->param.rc.i_vbv_max_bitrate; + int bufsize = 1000*h->param.rc.i_vbv_buffer_size; + + // FIXME wrong timings with cbr_flag + // h->param->vui.hrd.b_cbr = !!( h->param.rc.i_vbv_max_bitrate == h->param.rc.i_bitrate ); + + // normalize HRD size and rate to the value / scale notation + h->param.vui.hrd.i_bit_rate_scale = x264_clip3( x264_ctz( bitrate ) - BR_SHIFT, 0, 15 ); + h->param.vui.hrd.i_bit_rate_value = bitrate >> ( h->param.vui.hrd.i_bit_rate_scale + BR_SHIFT ); + h->param.vui.hrd.i_cpb_size_scale = x264_clip3( x264_ctz( bufsize ) - CPB_SHIFT, 0, 15 ); + h->param.vui.hrd.i_cpb_size_value = bufsize >> ( h->param.vui.hrd.i_cpb_size_scale + CPB_SHIFT ); + + x264_log( h, X264_LOG_INFO, "HRD bitrate: %ld bits/sec\n", + h->param.vui.hrd.i_bit_rate_value << ( h->param.vui.hrd.i_bit_rate_scale + BR_SHIFT ) ); + x264_log( h, X264_LOG_INFO, "CPB size: %lld bits\n", + h->param.vui.hrd.i_cpb_size_value << ( h->param.vui.hrd.i_cpb_size_scale + CPB_SHIFT ) ); + + #undef CPB_SHIFT + #undef BR_SHIFT + } h->sps = &h->sps_array[0]; x264_sps_init( h->sps, h->param.i_sps_id, &h->param ); @@ -917,7 +1040,7 @@ x264_t *x264_encoder_open( x264_param_t *param ) CHECKED_MALLOCZERO( h->frames.blank_unused, h->param.i_threads * 4 * sizeof(x264_frame_t *) ); h->i_ref0 = 0; h->i_ref1 = 0; - + h->i_coded_fields = h->i_disp_fields = 0; x264_rdo_init(); /* init CPU functions */ @@ -1097,6 +1220,30 @@ int x264_encoder_reconfig( x264_t *h, x264_param_t *param ) COPY( i_slice_max_size ); COPY( i_slice_max_mbs ); COPY( i_slice_count ); + + COPY( b_nal_hrd ); + COPY( b_pic_struct ); + + if( h->param.i_pulldown != param->i_pulldown ) + { + COPY( i_pulldown ); + COPY( pulldown.mod ); + COPY( pulldown.f_fps_factor ); + COPY( pulldown.i_delay ); + if( h->param.pulldown.pattern ) + { + x264_free( h->param.pulldown.pattern ); + h->param.pulldown.pattern = NULL; + } + if( param->pulldown.pattern ) + { + h->param.pulldown.pattern = x264_malloc( h->param.pulldown.mod*sizeof(int) ); + if( h->param.pulldown.pattern ) + return -1; + memcpy( h->param.pulldown.pattern, param->pulldown.pattern, h->param.pulldown.mod*sizeof(int) ); + } + } + #undef COPY mbcmp_init( h ); @@ -1187,11 +1334,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 ); @@ -1206,6 +1348,13 @@ int x264_encoder_headers( x264_t *h, x264_nal_t **pp_nal, int *pi_nal ) return -1; bs_flush( &h->out.bs ); + /* 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*/ @@ -1630,7 +1779,14 @@ 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; + 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 ) { @@ -2033,6 +2189,7 @@ int x264_encoder_encode( x264_t *h, { x264_t *thread_current, *thread_prev, *thread_oldest; int i_nal_type, i_nal_ref_idc, i_global_qp, i; + int overhead = NALU_OVERHEAD; if( h->param.i_threads > 1 && !h->param.b_sliced_threads ) { @@ -2080,6 +2237,21 @@ int x264_encoder_encode( x264_t *h, if( h->frames.i_bframe_delay && fenc->i_frame == h->frames.i_bframe_delay ) h->frames.i_bframe_delay_time = fenc->i_pts; + if( fenc->i_pic_struct == 0 ) // don't override if an app has set it before + { + if( ( h->param.i_pulldown != X264_PULLDOWN_NONE ) && ( h->param.pulldown.pattern ) ) + 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; + } + else if( ( fenc->i_pic_struct < 0 ) || ( fenc->i_pic_struct > 8 ) ) + fenc->i_pic_struct = 0; + + // add number of "displayed" fields + // fixme: case of field frames handled incorrectly + fenc->i_field_cnt = h->i_disp_fields; + h->i_disp_fields += delta_tfi_divisor[fenc->i_pic_struct]; + if( h->frames.b_have_lowres ) { if( h->param.analyse.i_weighted_pred == X264_WEIGHTP_FAKE || h->param.analyse.i_weighted_pred == X264_WEIGHTP_SMART ) @@ -2228,13 +2400,12 @@ 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; - if( h->param.b_intra_refresh && h->fenc->i_type == X264_TYPE_P ) { int pocdiff = (h->fdec->i_poc - h->fref0[0]->i_poc)/2; @@ -2261,17 +2432,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 ); @@ -2285,6 +2445,29 @@ 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; + + /* generate sei buffering period */ + if( h->sps->vui.b_nal_hrd_parameters_present || h->sps->vui.b_pic_struct_present ) + { + h->initial_cpb_removal_delay = x264_hrd_fullness( h, 8*overhead ); + + x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); + x264_sei_buffering_period_write( h, &h->out.bs, h->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 */ + 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; + } } if( h->fenc->i_type != X264_TYPE_IDR ) @@ -2296,6 +2479,33 @@ int x264_encoder_encode( x264_t *h, } } + /* FIXME support VFR */ + /* generate sei pic timing */ + if( h->sps->vui.b_pic_struct_present || h->sps->vui.b_nal_hrd_parameters_present ) + { + h->dpb_output_delay = h->fenc->i_field_cnt - h->i_coded_fields; + + // add a correction term for frames with 3+ fields + h->dpb_output_delay += h->param.pulldown.i_delay; + + // add a correction term for frame reordering + h->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, h->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; + } + + h->current_cpb_delay = h->i_cpb_delay; + + if( h->fenc->b_keyframe ) + h->i_cpb_delay = 0; + + h->i_cpb_delay += delta_tfi_divisor[h->fenc->i_pic_struct]; // number of fields we just added + h->i_coded_fields += delta_tfi_divisor[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 ); @@ -2399,6 +2609,8 @@ static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current, if( x264_ratecontrol_end( h, frame_size * 8 ) < 0 ) return -1; + pic_out->hrd_timing = h->fenc->hrd_timing; + x264_noise_reduction_update( thread_current ); /* ---------------------- Compute/Print statistics --------------------- */ @@ -2826,6 +3038,7 @@ void x264_encoder_close ( x264_t *h ) free( h->param.rc.psz_stat_out ); if( h->param.rc.psz_stat_in ) free( h->param.rc.psz_stat_in ); + x264_free( h->param.pulldown.pattern ); x264_cqm_delete( h ); x264_free( h->nal_buffer ); diff --git a/encoder/ratecontrol.c b/encoder/ratecontrol.c index 761ff2c..98ce650 100644 --- a/encoder/ratecontrol.c +++ b/encoder/ratecontrol.c @@ -146,6 +146,12 @@ struct x264_ratecontrol_t int i_zones; x264_zone_t *zones; x264_zone_t *prev_zone; + + /* hrd stuff */ + int initial_cpb_removal_delay; + int current_cpb_delay; + double nrt_first_access_unit; + double previous_cpb_final_arrival_time; }; @@ -1418,6 +1424,59 @@ int x264_ratecontrol_end( x264_t *h, int bits ) } update_vbv( h, bits ); + + if( h->sps->vui.b_pic_struct_present || h->sps->vui.b_nal_hrd_parameters_present ) + { + double cpb_nominal_removal_time; + rc->current_cpb_delay = h->current_cpb_delay; + + if( h->fenc->i_frame == 0 ) + { + // access unit initialises the HRD + h->fenc->hrd_timing.cpb_initial_arrival_time = 0; + rc->initial_cpb_removal_delay = h->initial_cpb_removal_delay; + cpb_nominal_removal_time = rc->nrt_first_access_unit = (double)rc->initial_cpb_removal_delay / 90000; + } + else + { + cpb_nominal_removal_time = rc->nrt_first_access_unit + + (double)rc->current_cpb_delay * h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale; + if( h->fenc->b_keyframe ) + { + rc->nrt_first_access_unit = cpb_nominal_removal_time; + rc->initial_cpb_removal_delay = h->initial_cpb_removal_delay; + } + + if( h->param.vui.hrd.b_cbr ) + h->fenc->hrd_timing.cpb_initial_arrival_time = rc->previous_cpb_final_arrival_time; + else + { + // NOTE Equation C-4 has initial_cpb_removal_delay_offset which is hardcoded to zero in x264. + double cpb_earliest_arrival_time = cpb_nominal_removal_time - (double)rc->initial_cpb_removal_delay / 90000; + h->fenc->hrd_timing.cpb_initial_arrival_time = X264_MAX( rc->previous_cpb_final_arrival_time, cpb_earliest_arrival_time ); + } + } + + // Equation C-6 + #define BR_SHIFT 6 + int bit_rate = h->param.vui.hrd.i_bit_rate_value << ( h->param.vui.hrd.i_bit_rate_scale + BR_SHIFT ); + #undef BR_SHIFT + h->fenc->hrd_timing.cpb_final_arrival_time = rc->previous_cpb_final_arrival_time = + h->fenc->hrd_timing.cpb_initial_arrival_time + (double)bits / bit_rate; + + if( cpb_nominal_removal_time >= h->fenc->hrd_timing.cpb_final_arrival_time ) + h->fenc->hrd_timing.cpb_removal_time = cpb_nominal_removal_time; + else + { + h->fenc->hrd_timing.cpb_removal_time = cpb_nominal_removal_time + + ceil( ( h->fenc->hrd_timing.cpb_final_arrival_time - cpb_nominal_removal_time ) * + (double)h->sps->vui.i_time_scale / h->sps->vui.i_num_units_in_tick ); + } + + h->fenc->hrd_timing.dpb_output_time = (double)h->dpb_output_delay * h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale + + h->fenc->hrd_timing.cpb_removal_time; + } + return 0; fail: x264_log(h, X264_LOG_ERROR, "ratecontrol_end: stats file could not be written to\n"); @@ -1573,6 +1632,22 @@ static void update_vbv( x264_t *h, int bits ) rct->buffer_fill_final = X264_MIN( rct->buffer_fill_final, rct->buffer_size ); } +int x264_hrd_fullness( x264_t *h, int overhead ) +{ + x264_ratecontrol_t *rct = h->thread[0]->rc; + double cpb_bits = rct->buffer_fill_final - overhead; + double bps = (1 << (h->param.vui.hrd.i_bit_rate_scale + 6))*h->param.vui.hrd.i_bit_rate_value; + double cpb_size = (1 << (h->param.vui.hrd.i_cpb_size_scale + 4 ))*h->param.vui.hrd.i_cpb_size_value; + double cpb_fullness = 90000.0*cpb_bits/bps; + + if( cpb_fullness < 0 || cpb_fullness > cpb_size ) + { + x264_log( h, X264_LOG_WARNING, "CPB %s: %.0lf bits in a %.0lf-bit buffer\n", + cpb_fullness < 0 ? "underflow" : "overflow", cpb_bits, cpb_size ); + } + return x264_clip3f( cpb_fullness + 0.5, 0, cpb_size ); // just lie if we are in a weird state +} + // provisionally update VBV according to the planned size of all frames currently in progress static void update_vbv_plan( x264_t *h, int overhead ) { @@ -2045,6 +2120,10 @@ void x264_thread_sync_ratecontrol( x264_t *cur, x264_t *prev, x264_t *next ) COPY(expected_bits_sum); COPY(wanted_bits_window); COPY(bframe_bits); + COPY(initial_cpb_removal_delay); + COPY(current_cpb_delay); + COPY(nrt_first_access_unit); + COPY(previous_cpb_final_arrival_time); #undef COPY } //FIXME row_preds[] (not strictly necessary, but would improve prediction) diff --git a/encoder/ratecontrol.h b/encoder/ratecontrol.h index 5a8d088..9b588c8 100644 --- a/encoder/ratecontrol.h +++ b/encoder/ratecontrol.h @@ -45,6 +45,6 @@ int x264_rc_analyse_slice( x264_t *h ); int x264_weighted_reference_duplicate( x264_t *h, int i_ref, const x264_weight_t *w ); void x264_threads_distribute_ratecontrol( x264_t *h ); void x264_threads_merge_ratecontrol( x264_t *h ); - +int x264_hrd_fullness( x264_t *h, int overhead ); #endif diff --git a/encoder/set.c b/encoder/set.c index 641eae9..d1afff4 100644 --- a/encoder/set.c +++ b/encoder/set.c @@ -28,6 +28,8 @@ #define bs_write_ue bs_write_ue_big +static int num_clock_ts[9] = { 1, 1, 1, 2, 2, 3, 3, 2, 3 }; // Table D-1 + static void transpose( uint8_t *buf, int w ) { int i, j; @@ -179,15 +181,44 @@ 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_timebase_num > 0 && param->i_timebase_den > 0 ) + sps->vui.b_timing_info_present = !!(param->i_timebase_num && param->i_timebase_den > 0) || !!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_timebase_num; sps->vui.i_time_scale = param->i_timebase_den * 2; + if( param->i_pulldown != X264_PULLDOWN_NONE ) + sps->vui.i_time_scale = (int)((float)(sps->vui.i_time_scale*param->pulldown.f_fps_factor + 0.5)); sps->vui.b_fixed_frame_rate = !param->b_vfr_input; } + sps->vui.b_vcl_hrd_parameters_present = 0; // we don't support VCL HRD + sps->vui.b_nal_hrd_parameters_present = param->b_nal_hrd; + if( sps->vui.b_nal_hrd_parameters_present ) + { + double bps = (1 << (param->vui.hrd.i_bit_rate_scale + 6))*param->vui.hrd.i_bit_rate_value; + double cpb_size = (1 << (param->vui.hrd.i_cpb_size_scale + 4))*param->vui.hrd.i_cpb_size_value; + + 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); + int max_delay = (int)(90000.0*cpb_size/bps + 0.5); + + 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; + + 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 = 2 + x264_clip3( 32 - x264_clz( max_delay ), 4, 22); + sps->vui.hrd.i_cpb_removal_delay_length = x264_clip3( 32 - x264_clz( max_cpb ), 4, 24 ); + sps->vui.hrd.i_dpb_output_delay_length = x264_clip3( 32 - x264_clz( max_dpb ), 4, 24 ); + sps->vui.hrd.i_time_offset_length = 0; + } + + sps->vui.b_pic_struct_present = param->b_pic_struct; + sps->vui.i_num_reorder_frames = param->i_bframe_pyramid ? 2 : param->i_bframe ? 1 : 0; /* extra slot with pyramid so that we don't have to override the * order of forgetting old pictures */ @@ -203,11 +234,10 @@ void x264_sps_init( x264_sps_t *sps, int i_id, x264_param_t *param ) sps->vui.i_max_bytes_per_pic_denom = 0; sps->vui.i_max_bits_per_mb_denom = 0; 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.i_log2_max_mv_length_vertical = (int)log2( param->analyse.i_mv_range*4-1 ) + 1; } } - void x264_sps_write( bs_t *s, x264_sps_t *sps ) { bs_write( s, 8, sps->i_profile_idc ); @@ -342,9 +372,30 @@ 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); + + 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 ); + + 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 ) { @@ -505,7 +556,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 ); @@ -526,6 +577,78 @@ 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 += num_clock_ts[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 < num_clock_ts[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 125f7e1..ff05d38 100644 --- a/encoder/set.h +++ b/encoder/set.h @@ -31,5 +31,7 @@ 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 ); 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/output/flv.c b/output/flv.c index 8a937cf..75e2753 100644 --- a/output/flv.c +++ b/output/flv.c @@ -158,9 +158,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 @@ -171,10 +171,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 @@ -200,7 +200,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 7889e4f..e2a51c2 100644 --- a/output/mp4.c +++ b/output/mp4.c @@ -233,13 +233,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 8c280aa..cd074e2 100644 --- a/x264.c +++ b/x264.c @@ -336,7 +336,8 @@ 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, --tff Enable pure-interlaced mode (top field first)\n" ); + H0( " --bff Enable pure-interlaced mode (bottom field first)\n" ); H2( " --constrained-intra Enable constrained intra prediction.\n" ); H0( "\n" ); H0( "Ratecontrol:\n" ); @@ -478,6 +479,11 @@ static void Help( x264_param_t *defaults, int longhelp ) strtable_lookup( x264_colmatrix_names, defaults->vui.i_colmatrix ) ); H2( " --chromaloc Specify chroma sample location (0 to 5) [%d]\n", defaults->vui.i_chroma_loc ); + + H0( " --nal-hrd, Signal HRD information (needed e.g. for blu-ray compliance\n" ); + H0( " requires vbv-maxrate and vbv-bufsize\n " ); + H2( " --pic-struct Send pic_struct in Picture Timing SEI (on for pulldown or interlaced) \n" ); + H0( "\n" ); H0( "Input/Output:\n" ); H0( "\n" ); @@ -510,6 +516,8 @@ static void Help( x264_param_t *defaults, int longhelp ) H2( " --sps-id Set SPS and PPS id numbers [%d]\n", defaults->i_sps_id ); H2( " --aud Use access unit delimiters\n" ); H2( " --force-cfr Force constant framerate timestamp generation\n" ); + H0( " --pulldown Use soft pulldown and Timing SEI to change frame rate\n" + " - 32, 64, double, triple, euro (default: none)\n" ); H0( "\n" ); } @@ -559,6 +567,8 @@ static struct option long_options[] = { "filter", required_argument, NULL, 0 }, { "deblock", required_argument, NULL, 'f' }, { "interlaced", no_argument, NULL, OPT_INTERLACED }, + { "tff", no_argument, NULL, 0 }, + { "bff", no_argument, NULL, 0 }, { "no-interlaced", no_argument, NULL, OPT_INTERLACED }, { "constrained-intra", no_argument, NULL, 0 }, { "cabac", no_argument, NULL, 0 }, @@ -664,6 +674,9 @@ static struct option long_options[] = { "transfer", required_argument, NULL, 0 }, { "colormatrix", required_argument, NULL, 0 }, { "chromaloc", required_argument, NULL, 0 }, + { "pic-struct", no_argument, NULL, 0 }, + { "nal-hrd", no_argument, NULL, 0 }, + { "pulldown", required_argument, NULL, 0 }, { "force-cfr", no_argument, NULL, 0 }, {0, 0, 0, 0} }; diff --git a/x264.h b/x264.h index 1223df7..9dd2566 100644 --- a/x264.h +++ b/x264.h @@ -35,7 +35,7 @@ #include -#define X264_BUILD 83 +#define X264_BUILD 84 /* x264_t: * opaque handler for encoder */ @@ -102,6 +102,13 @@ typedef struct x264_t x264_t; #define X264_B_PYRAMID_STRICT 1 #define X264_B_PYRAMID_NORMAL 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_b_pyramid_names[] = { "none", "strict", "normal", 0 }; @@ -111,6 +118,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. */ @@ -176,6 +184,9 @@ typedef struct x264_param_t int i_level_idc; int i_frame_total; /* number of frames to encode if known, else 0 */ + int b_pic_struct; /* Send pic_struct in Picture Timing SEI */ + int b_nal_hrd; /* Use Buffering and Picture Timing SEI's to signal HRD */ + struct { /* they will be reduced to be 0 < x <= 65535 and prime */ @@ -191,6 +202,20 @@ typedef struct x264_param_t int i_transfer; int i_colmatrix; int i_chroma_loc; /* both top & bottom */ + + int b_timing_info_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; + /* only for cbr_flag. will not add filler bytes */ + int b_cbr; + } hrd; + } vui; int i_fps_num; @@ -317,6 +342,17 @@ typedef struct x264_param_t int i_timebase_num; /* Timebase numerator */ int i_timebase_den; /* Timebase denominator */ + 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. */ int i_slice_max_mbs; /* Max number of MBs per slice; overrides i_slice_count. */ @@ -365,6 +401,16 @@ int x264_param_parse( x264_param_t *, const char *name, const char *value ); /**************************************************************************** * Picture structures and functions. ****************************************************************************/ + +typedef struct +{ + double cpb_initial_arrival_time; + double cpb_final_arrival_time; + double cpb_removal_time; + + double dpb_output_time; +} x264_hrd_t; + typedef struct { int i_csp; @@ -385,6 +431,10 @@ 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; /* Out: whether this frame is a keyframe. Important when using modes that result in * SEI recovery points being used instead of IDR frames. */ int b_keyframe; @@ -402,6 +452,8 @@ typedef struct x264_param_t *param; /* In: raw data */ x264_image_t img; + /* Out: HRD timing information */ + x264_hrd_t hrd_timing; /* private user data. libx264 doesn't touch this, not even copy it from input to output frames. */ void *opaque; @@ -442,6 +494,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.