diff -uNrp c/common/common.c b/common/common.c --- c/common/common.c 2010-02-24 10:20:41 +0200 +++ b/common/common.c 2010-02-26 21:24:25 +0200 @@ -158,6 +158,10 @@ void x264_param_default( x264_param_t *p param->b_aud = 0; param->b_vfr_input = 1; param->b_dts_compress = 0; + param->i_nal_hrd = X264_NAL_HRD_NONE; + param->b_tff = 1; + param->b_pic_struct = 0; + param->b_pulldown = 0; } static int x264_param_apply_preset( x264_param_t *param, const char *preset ) @@ -676,6 +680,13 @@ int x264_param_parse( x264_param_t *p, c p->i_cabac_init_idc = atoi(value); OPT("interlaced") p->b_interlaced = atobool(value); + OPT("tff") + p->b_interlaced = p->b_tff = 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") @@ -895,6 +906,10 @@ int x264_param_parse( x264_param_t *p, c p->b_annexb = atobool(value); OPT("force-cfr") p->b_vfr_input = !atobool(value); + OPT("nal-hrd") + b_error |= parse_enum( value, x264_nal_hrd_names, &p->i_nal_hrd ); + OPT("pic-struct") + p->b_pic_struct = atobool(value); else return X264_PARAM_BAD_NAME; #undef OPT @@ -970,6 +985,7 @@ int x264_picture_alloc( x264_picture_t * pic->img.i_stride[1] = i_width / 2; pic->img.i_stride[2] = i_width / 2; pic->param = NULL; + pic->i_pic_struct = PIC_STRUCT_AUTO; return 0; } @@ -1180,7 +1196,8 @@ char *x264_param2string( x264_param_t *p s += sprintf( s, " slice_max_mbs=%d", p->i_slice_max_mbs ); 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 ); + s += sprintf( s, " interlaced=%s", p->b_interlaced ? p->b_tff ? "tff" : "bff" : "0" ); + s += sprintf( s, " constrained_intra=%d", p->b_constrained_intra ); s += sprintf( s, " bframes=%d", p->i_bframe ); @@ -1233,6 +1250,9 @@ char *x264_param2string( x264_param_t *p s += sprintf( s, " zones" ); } + s += sprintf( s, " pulldown=%d", p->b_pulldown ); + if( p->rc.i_vbv_buffer_size ) + s += sprintf( s, " nal_hrd=%s", x264_nal_hrd_names[p->i_nal_hrd] ); return buf; } diff -uNrp c/common/common.h b/common/common.h --- c/common/common.h 2010-02-23 13:09:14 +0200 +++ b/common/common.h 2010-02-26 21:27:24 +0200 @@ -67,6 +67,9 @@ do {\ #define X264_WEIGHTP_FAKE (-1) +#define NALU_OVERHEAD 5 // startcode + NAL type costs 5 bytes per frame +#define FILLER_OVERHEAD (NALU_OVERHEAD+1) + /**************************************************************************** * Includes ****************************************************************************/ @@ -214,6 +217,17 @@ enum slice_type_e static const char slice_type_to_char[] = { 'P', 'B', 'I', 'S', 'S' }; +enum sei_payload_type_e +{ + SEI_BUFFERING_PERIOD = 0, + SEI_PIC_TIMING = 1, + SEI_PAN_SCAN_RECT = 2, + SEI_FILLER = 3, + SEI_USER_DATA_REGISTERED = 4, + SEI_USER_DATA_UNREGISTERED = 5, + SEI_RECOVERY_POINT = 6, +}; + typedef struct { x264_sps_t *sps; @@ -367,6 +381,13 @@ struct x264_t int i_nal_type; int i_nal_ref_idc; + 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_last_duration; /* Duration of last frame */ + /* We use only one SPS and one PPS */ x264_sps_t sps_array[1]; x264_sps_t *sps; @@ -449,7 +470,8 @@ struct x264_t x264_frame_t *fref1[16+3]; /* ref list 1 */ int b_ref_reorder[2]; - + /* hrd */ + int initial_cpb_removal_delay; /* Current MB DCT coeffs */ struct diff -uNrp c/common/frame.c b/common/frame.c --- c/common/frame.c 2010-02-26 21:21:04 +0200 +++ b/common/frame.c 2010-02-26 21:27:58 +0200 @@ -73,6 +73,13 @@ x264_frame_t *x264_frame_new( x264_t *h, frame->i_frame_num = -1; frame->i_lines_completed = -1; frame->b_fdec = b_fdec; + frame->i_pic_struct = PIC_STRUCT_AUTO; + frame->i_field_cnt = -1; + frame->i_duration = + frame->i_cpb_duration = + frame->i_dpb_output_delay = + frame->i_cpb_delay = 0; + frame->orig = frame; /* all 4 luma planes allocated together, since the cacheline split code @@ -227,6 +234,7 @@ int x264_frame_copy_picture( x264_t *h, dst->i_qpplus1 = src->i_qpplus1; dst->i_pts = dst->i_reordered_pts = src->i_pts; dst->param = src->param; + dst->i_pic_struct = src->i_pic_struct; for( i=0; i<3; i++ ) { diff -uNrp c/common/frame.h b/common/frame.h --- c/common/frame.h 2010-02-23 13:09:14 +0200 +++ b/common/frame.h 2010-02-26 21:28:42 +0200 @@ -36,12 +36,18 @@ typedef struct x264_frame int i_qpplus1; int64_t i_pts; int64_t i_reordered_pts; + int i_duration; /* in timebase units. used for vfr */ + int i_cpb_duration; + int i_cpb_delay; /* in 2 * timebase units */ + int i_dpb_output_delay; x264_param_t *param; 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_keyframe; uint8_t b_fdec; uint8_t b_last_minigop_bframe; /* this frame is the last b in a sequence of bframes */ @@ -109,6 +115,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 -uNrp c/common/osdep.h b/common/osdep.h --- c/common/osdep.h 2010-02-15 12:48:41 +0200 +++ b/common/osdep.h 2010-02-26 21:29:15 +0200 @@ -221,6 +221,7 @@ static ALWAYS_INLINE uint16_t endian_fix #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 ) { @@ -233,6 +234,18 @@ static int ALWAYS_INLINE x264_clz( uint3 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, z = (((x & 0xffff) - 1) >> 27) & 16; + x >>= z; + z += y = (((x & 0xff) - 1) >> 28) & 8; + x >>= y; + z += y = (((x & 0xf) - 1) >> 29) & 4; + x >>= y; + return z + lut[x&0xf]; +} #endif #ifdef USE_REAL_PTHREAD diff -uNrp c/common/set.h b/common/set.h --- c/common/set.h 2010-02-15 12:48:41 +0200 +++ b/common/set.h 2010-02-26 21:30:04 +0200 @@ -116,6 +116,27 @@ 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; + int i_bit_rate_value; + int i_cpb_size_value; + int i_bit_rate_unscaled; + int i_cpb_size_unscaled; + int b_cbr_hrd; + + 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 -uNrp c/encoder/encoder.c b/encoder/encoder.c --- c/encoder/encoder.c 2010-02-26 21:21:04 +0200 +++ b/encoder/encoder.c 2010-02-26 21:38:16 +0200 @@ -39,8 +39,6 @@ //#define DEBUG_MB_TYPE -#define NALU_OVERHEAD 5 // startcode + NAL type costs 5 bytes per frame - #define bs_write_ue bs_write_ue_big static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current, @@ -777,6 +775,26 @@ static int x264_validate_parameters( x26 h->param.analyse.b_ssim = 0; } + // pulldown is a form of vfr + if( h->param.b_pulldown ) + h->param.b_vfr_input = 1; + + if( h->param.b_pulldown || h->param.b_interlaced ) + h->param.b_pic_struct = 1; + + if( h->param.i_nal_hrd && !h->param.rc.i_vbv_buffer_size ) + { + x264_log( h, X264_LOG_WARNING, "NAL HRD parameters require VBV parameters\n" ); + h->param.i_nal_hrd = X264_NAL_HRD_NONE; + } + + if( h->param.i_nal_hrd == X264_NAL_HRD_CBR && ( h->param.rc.i_bitrate != h->param.rc.i_vbv_max_bitrate || + ( h->param.rc.i_vbv_max_bitrate && !h->param.rc.i_bitrate ) ) ) + { + x264_log( h, X264_LOG_WARNING, "CBR HRD requires constant bitrate\n" ); + h->param.i_nal_hrd = X264_NAL_HRD_VBR; + } + /* 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 ); @@ -790,6 +808,8 @@ static int x264_validate_parameters( x26 BOOLIFY( b_aud ); BOOLIFY( b_repeat_headers ); BOOLIFY( b_annexb ); + BOOLIFY( b_vfr_input ); + BOOLIFY( b_pulldown ); BOOLIFY( analyse.b_transform_8x8 ); BOOLIFY( analyse.b_weighted_bipred ); BOOLIFY( analyse.b_chroma_me ); @@ -907,6 +927,7 @@ x264_t *x264_encoder_open( x264_param_t } else h->i_dts_compress_multiplier = 1; + h->i_cpb_delay = 0; h->sps = &h->sps_array[0]; x264_sps_init( h->sps, h->param.i_sps_id, &h->param ); @@ -934,6 +955,7 @@ x264_t *x264_encoder_open( x264_param_t h->frames.i_delay += h->i_thread_frames - 1; h->frames.i_delay = X264_MIN( h->frames.i_delay, X264_LOOKAHEAD_MAX ); h->frames.i_delay += h->param.i_sync_lookahead; + h->frames.i_delay += h->param.b_vfr_input && h->param.rc.i_vbv_buffer_size > 0; h->frames.i_bframe_delay = h->param.i_bframe ? (h->param.i_bframe_pyramid ? 2 : 1) : 0; h->frames.i_max_ref0 = h->param.i_frame_reference; @@ -961,7 +983,7 @@ x264_t *x264_encoder_open( x264_param_t CHECKED_MALLOCZERO( h->frames.blank_unused, h->i_thread_frames * 4 * sizeof(x264_frame_t *) ); h->i_ref0 = 0; h->i_ref1 = 0; - + h->i_coded_fields = h->i_disp_fields = h->i_last_duration = 0; x264_rdo_init(); /* init CPU functions */ @@ -1069,6 +1091,12 @@ x264_t *x264_encoder_open( x264_param_t if( x264_ratecontrol_new( h ) < 0 ) goto fail; + if( h->param.i_nal_hrd ) + { + x264_log( h, X264_LOG_DEBUG, "HRD bitrate: %i bits/sec\n", h->sps->vui.hrd.i_bit_rate_unscaled ); + x264_log( h, X264_LOG_DEBUG, "CPB size: %i bits\n", h->sps->vui.hrd.i_cpb_size_unscaled ); + } + if( h->param.psz_dump_yuv ) { /* create or truncate the reconstructed video file */ @@ -1142,6 +1170,7 @@ int x264_encoder_reconfig( x264_t *h, x2 COPY( i_slice_max_size ); COPY( i_slice_max_mbs ); COPY( i_slice_count ); + /* VBV can't be turned on if it wasn't on to begin with */ if( h->param.rc.i_vbv_max_bitrate > 0 && h->param.rc.i_vbv_buffer_size > 0 && param->rc.i_vbv_max_bitrate > 0 && param->rc.i_vbv_buffer_size > 0 ) @@ -1151,6 +1180,9 @@ int x264_encoder_reconfig( x264_t *h, x2 COPY( rc.i_bitrate ); } COPY( rc.f_rf_constant ); + + if( h->param.b_pulldown != param->b_pulldown && h->param.b_pic_struct ) + COPY( b_pulldown ); #undef COPY mbcmp_init( h ); @@ -1211,10 +1243,14 @@ static int x264_nal_end( x264_t *h ) return x264_nal_check_buffer( h ); } -static int x264_encoder_encapsulate_nals( x264_t *h ) +static int x264_encoder_encapsulate_nals( x264_t *h, int start ) { - int nal_size = 0, i; - for( i = 0; i < h->out.i_nal; i++ ) + int nal_size = 0, previous_nal_size = 0, i; + + for( i = 0; i < start; i++ ) + previous_nal_size += h->out.nal[i].i_payload; + + for( i = start; i < h->out.i_nal; i++ ) nal_size += h->out.nal[i].i_payload; /* Worst-case NAL unit escaping: reallocate the buffer if it's too small. */ @@ -1223,13 +1259,15 @@ static int x264_encoder_encapsulate_nals uint8_t *buf = x264_malloc( nal_size * 2 + h->out.i_nal * 4 ); if( !buf ) return -1; + if( previous_nal_size ) + memcpy( buf, h->nal_buffer, previous_nal_size ); x264_free( h->nal_buffer ); h->nal_buffer = buf; } - uint8_t *nal_buffer = h->nal_buffer; + uint8_t *nal_buffer = h->nal_buffer + previous_nal_size; - for( i = 0; i < h->out.i_nal; i++ ) + for( i = start; i < h->out.i_nal; i++ ) { int long_startcode = !i || h->out.nal[i].i_type == NAL_SPS || h->out.nal[i].i_type == NAL_PPS; int size = x264_nal_encode( nal_buffer, &h->out.nal[i], h->param.b_annexb, long_startcode ); @@ -1238,7 +1276,7 @@ static int x264_encoder_encapsulate_nals nal_buffer += size; } - return nal_buffer - h->nal_buffer; + return nal_buffer - (h->nal_buffer + previous_nal_size); } /**************************************************************************** @@ -1252,11 +1290,6 @@ int x264_encoder_headers( x264_t *h, x26 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 ); @@ -1270,7 +1303,14 @@ int x264_encoder_headers( x264_t *h, x26 if( x264_nal_end( h ) ) return -1; - frame_size = x264_encoder_encapsulate_nals( h ); + /* 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, 0 ); /* now set output*/ *pi_nal = h->out.i_nal; @@ -1694,7 +1734,14 @@ static inline void x264_slice_init( x264 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->param.b_tff ? 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 ) { @@ -2104,6 +2151,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->i_thread_frames > 1 ) { @@ -2151,6 +2199,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 < PIC_STRUCT_AUTO ) || ( fenc->i_pic_struct > PIC_STRUCT_TRIPLE ) ) + fenc->i_pic_struct = PIC_STRUCT_AUTO; + + if( fenc->i_pic_struct == PIC_STRUCT_AUTO ) + { + int b_interlaced = fenc->param ? fenc->param->b_interlaced : h->param.b_interlaced; + if( b_interlaced ) + { + int b_tff = fenc->param ? fenc->param->b_tff : h->param.b_tff; + fenc->i_pic_struct = b_tff ? PIC_STRUCT_TOP_BOTTOM : PIC_STRUCT_BOTTOM_TOP; + } + else + fenc->i_pic_struct = PIC_STRUCT_PROGRESSIVE; + } + 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 ) @@ -2299,13 +2362,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; @@ -2327,21 +2389,11 @@ int x264_encoder_encode( x264_t *h, h->fdec->i_pir_end_col = h->fdec->f_pir_position+0.5; } - /* Write SPS and PPS */ if( h->fenc->b_keyframe ) { + /* Write SPS and PPS */ 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 ); @@ -2358,6 +2410,32 @@ int x264_encoder_encode( x264_t *h, 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->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->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; + } + } + if( h->fenc->i_type != X264_TYPE_IDR ) { int time_to_recovery = X264_MIN( h->sps->i_mb_width - 1, h->param.i_keyint_max ) + h->param.i_bframe; @@ -2368,6 +2446,16 @@ int x264_encoder_encode( x264_t *h, } } + /* generate sei pic timing */ + if( h->sps->vui.b_pic_struct_present || h->sps->vui.b_nal_hrd_parameters_present ) + { + x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); + x264_sei_pic_timing_write( h, &h->out.bs, h->fenc->i_cpb_delay, h->fenc->i_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; + } + /* Init the rate control */ /* FIXME: Include slice header bit cost. */ x264_ratecontrol_start( h, h->fenc->i_qpplus1, overhead*8 ); @@ -2441,13 +2529,7 @@ static int x264_encoder_frame_end( x264_ x264_frame_push_unused( thread_current, h->fenc ); - /* End bitstream, set output */ - *pi_nal = h->out.i_nal; - *pp_nal = h->out.nal; - - frame_size = x264_encoder_encapsulate_nals( h ); - - h->out.i_nal = 0; + frame_size = x264_encoder_encapsulate_nals( h, 0 ); /* Set output picture properties */ if( h->sh.i_type == SLICE_TYPE_I ) @@ -2494,9 +2576,38 @@ static int x264_encoder_frame_end( x264_ /* update rc */ x264_emms(); - if( x264_ratecontrol_end( h, frame_size * 8 ) < 0 ) + int filler = 0; + if( x264_ratecontrol_end( h, frame_size * 8, &filler ) < 0 ) return -1; + pic_out->hrd_timing = h->fenc->hrd_timing; + + while( filler > 0 ) + { + int f; + if( h->param.i_slice_max_size && filler > h->param.i_slice_max_size ) + { + int next_size = filler - h->param.i_slice_max_size; + int overflow = next_size < FILLER_OVERHEAD ? FILLER_OVERHEAD - next_size : 0; + f = h->param.i_slice_max_size - FILLER_OVERHEAD - overflow; + } + else + f = X264_MAX( 0, filler - FILLER_OVERHEAD ); + + x264_nal_start( h, NAL_FILLER, NAL_PRIORITY_DISPOSABLE ); + x264_filler_write( h, &h->out.bs, f ); + if( x264_nal_end( h ) ) + return -1; + frame_size += x264_encoder_encapsulate_nals( h, h->out.i_nal-1 ); + filler -= f + FILLER_OVERHEAD; + } + + /* End bitstream, set output */ + *pi_nal = h->out.i_nal; + *pp_nal = h->out.nal; + + h->out.i_nal = 0; + x264_noise_reduction_update( thread_current ); /* ---------------------- Compute/Print statistics --------------------- */ diff -uNrp c/encoder/ratecontrol.c b/encoder/ratecontrol.c --- c/encoder/ratecontrol.c 2010-02-26 21:21:04 +0200 +++ b/encoder/ratecontrol.c 2010-02-26 21:44:25 +0200 @@ -94,6 +94,7 @@ 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 vbv_max_rate; /* # of bits added to buffer_fill per second */ predictor_t *pred; /* predict frame size from satd */ int single_frame_vbv; @@ -149,13 +150,18 @@ struct x264_ratecontrol_t int i_zones; x264_zone_t *zones; x264_zone_t *prev_zone; + + /* hrd stuff */ + int initial_cpb_removal_delay; + double nrt_first_access_unit; + double previous_cpb_final_arrival_time; }; 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 int update_vbv( x264_t *h, int bits ); 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 ); @@ -400,6 +406,16 @@ void x264_ratecontrol_init_reconfigurabl if( !b_init && rc->b_2pass ) return; + if( h->param.rc.i_rc_method == X264_RC_CRF ) + { + /* Arbitrary rescaling to make CRF somewhat similar to QP. + * Try to compensate for MB-tree's effects as well. */ + double base_cplx = h->mb.i_mb_count * (h->param.i_bframe ? 120 : 80); + double mbtree_offset = h->param.rc.b_mb_tree ? (1.0-h->param.rc.f_qcompress)*13.5 : 0; + rc->rate_factor_constant = pow( base_cplx, 1 - rc->qcompress ) + / qp2qscale( h->param.rc.f_rf_constant + mbtree_offset ); + } + if( h->param.rc.i_vbv_max_bitrate > 0 && h->param.rc.i_vbv_buffer_size > 0 ) { if( h->param.rc.i_vbv_buffer_size < (int)(h->param.rc.i_vbv_max_bitrate / rc->fps) ) @@ -413,8 +429,59 @@ void x264_ratecontrol_init_reconfigurabl so if the stream starts as CBR, keep it CBR. */ if( rc->b_vbv_min_rate ) h->param.rc.i_vbv_max_bitrate = h->param.rc.i_bitrate; - rc->buffer_rate = h->param.rc.i_vbv_max_bitrate * 1000. / rc->fps; - rc->buffer_size = h->param.rc.i_vbv_buffer_size * 1000.; + + int vbv_buffer_size = h->param.rc.i_vbv_buffer_size * 1000; + int vbv_max_bitrate = h->param.rc.i_vbv_max_bitrate * 1000; + + /* Init HRD */ + if( h->param.i_nal_hrd && b_init ) + { + h->sps->vui.hrd.i_cpb_cnt = 1; + h->sps->vui.hrd.b_cbr_hrd = h->param.i_nal_hrd == X264_NAL_HRD_CBR; + h->sps->vui.hrd.i_time_offset_length = 0; + + #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; + + // normalize HRD size and rate to the value / scale notation + h->sps->vui.hrd.i_bit_rate_scale = x264_clip3( x264_ctz( bitrate ) - BR_SHIFT, 0, 15 ); + h->sps->vui.hrd.i_bit_rate_value = bitrate >> ( h->sps->vui.hrd.i_bit_rate_scale + BR_SHIFT ); + h->sps->vui.hrd.i_bit_rate_unscaled = h->sps->vui.hrd.i_bit_rate_value << ( h->sps->vui.hrd.i_bit_rate_scale + BR_SHIFT ); + h->sps->vui.hrd.i_cpb_size_scale = x264_clip3( x264_ctz( bufsize ) - CPB_SHIFT, 0, 15 ); + h->sps->vui.hrd.i_cpb_size_value = bufsize >> ( h->sps->vui.hrd.i_cpb_size_scale + CPB_SHIFT ); + h->sps->vui.hrd.i_cpb_size_unscaled = h->sps->vui.hrd.i_cpb_size_value << ( h->sps->vui.hrd.i_cpb_size_scale + CPB_SHIFT ); + + #undef CPB_SHIFT + #undef BR_SHIFT + + // arbitrary + #define MAX_DURATION 0.5 + + int max_cpb_output_delay = (int64_t)h->sps->vui.i_time_scale * h->param.i_keyint_max * MAX_DURATION / h->sps->vui.i_num_units_in_tick; + int max_dpb_output_delay = (int64_t)h->sps->vui.i_time_scale * h->sps->vui.i_max_dec_frame_buffering * MAX_DURATION / h->sps->vui.i_num_units_in_tick; + int max_delay = (int)(90000.0 * (double)h->sps->vui.hrd.i_cpb_size_unscaled / h->sps->vui.hrd.i_bit_rate_unscaled + 0.5); + + h->sps->vui.hrd.i_initial_cpb_removal_delay_length = 2 + x264_clip3( 32 - x264_clz( max_delay ), 4, 22); + h->sps->vui.hrd.i_cpb_removal_delay_length = x264_clip3( 32 - x264_clz( max_cpb_output_delay ), 4, 32 ); + h->sps->vui.hrd.i_dpb_output_delay_length = x264_clip3( 32 - x264_clz( max_dpb_output_delay ), 4, 32 ); + + #undef MAX_DURATION + + vbv_buffer_size = X264_MIN( vbv_buffer_size, h->sps->vui.hrd.i_cpb_size_unscaled ); + vbv_max_bitrate = X264_MIN( vbv_max_bitrate, h->sps->vui.hrd.i_bit_rate_unscaled ); + } + else if( h->param.i_nal_hrd && !b_init ) + { + x264_log( h, X264_LOG_WARNING, "VBV parameters cannot be changed when NAL HRD is in use\n" ); + return; + } + + rc->buffer_rate = (double)vbv_max_bitrate / rc->fps; + rc->vbv_max_rate = vbv_max_bitrate; + rc->buffer_size = vbv_buffer_size; rc->single_frame_vbv = rc->buffer_rate * 1.1 > rc->buffer_size; rc->cbr_decay = 1.0 - rc->buffer_rate / rc->buffer_size * 0.5 * X264_MAX(0, 1.5 - rc->buffer_rate * rc->fps / rc->bitrate); @@ -430,15 +497,6 @@ void x264_ratecontrol_init_reconfigurabl && h->param.rc.i_vbv_max_bitrate <= h->param.rc.i_bitrate; } } - if( h->param.rc.i_rc_method == X264_RC_CRF ) - { - /* Arbitrary rescaling to make CRF somewhat similar to QP. - * Try to compensate for MB-tree's effects as well. */ - double base_cplx = h->mb.i_mb_count * (h->param.i_bframe ? 120 : 80); - double mbtree_offset = h->param.rc.b_mb_tree ? (1.0-h->param.rc.f_qcompress)*13.5 : 0; - rc->rate_factor_constant = pow( base_cplx, 1 - rc->qcompress ) - / qp2qscale( h->param.rc.f_rf_constant + mbtree_offset ); - } } int x264_ratecontrol_new( x264_t *h ) @@ -1059,7 +1117,7 @@ void x264_ratecontrol_start( x264_t *h, else { //384 * MaxMBPS * ( tr( n ) - tr( n - 1 ) ) / MinCR - rc->frame_size_maximum = 384 * 8 * (1 / rc->fps) * l->mbps / l->mincr; + rc->frame_size_maximum = 384 * 8 * ((double)h->fenc->i_cpb_duration * h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale) * l->mbps / l->mincr; } } @@ -1244,7 +1302,8 @@ void x264_ratecontrol_mb( x264_t *h, int /* avoid VBV underflow or MinCR violation */ while( (rc->qpm < h->param.rc.i_qp_max) - && ((rc->buffer_fill - b1 < rc->buffer_rate * rc->max_frame_error) || + && ((rc->buffer_fill - b1 < ((rc->vbv_max_rate * h->fenc->i_cpb_duration * + h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale)) * rc->max_frame_error) || (rc->frame_size_maximum - b1 < rc->frame_size_maximum * rc->max_frame_error))) { rc->qpm ++; @@ -1317,7 +1376,7 @@ void x264_ratecontrol_set_weights( x264_ } /* 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 *filler ) { x264_ratecontrol_t *rc = h->rc; const int *mbs = h->stat.frame.i_mb_count; @@ -1428,7 +1487,44 @@ int x264_ratecontrol_end( x264_t *h, int } } - update_vbv( h, bits ); + *filler = update_vbv( h, bits ); + + if( h->sps->vui.b_nal_hrd_parameters_present ) + { + 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; + h->fenc->hrd_timing.cpb_removal_time = rc->nrt_first_access_unit = (double)rc->initial_cpb_removal_delay / 90000; + } + else + { + h->fenc->hrd_timing.cpb_removal_time = rc->nrt_first_access_unit + + (double)h->fenc->i_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 = h->fenc->hrd_timing.cpb_removal_time; + rc->initial_cpb_removal_delay = h->initial_cpb_removal_delay; + } + + if( h->sps->vui.hrd.b_cbr_hrd ) + 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 = h->fenc->hrd_timing.cpb_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 + h->fenc->hrd_timing.cpb_final_arrival_time = rc->previous_cpb_final_arrival_time = + h->fenc->hrd_timing.cpb_initial_arrival_time + (double)(bits + X264_MAX( FILLER_OVERHEAD, *filler )*8) / h->sps->vui.hrd.i_bit_rate_unscaled; + + h->fenc->hrd_timing.dpb_output_time = (double)h->fenc->i_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"); @@ -1565,8 +1661,10 @@ static void update_predictor( predictor_ } // update VBV after encoding a frame -static void update_vbv( x264_t *h, int bits ) +static int update_vbv( x264_t *h, int bits ) { + int filler = 0; + x264_ratecontrol_t *rcc = h->rc; x264_ratecontrol_t *rct = h->thread[0]->rc; @@ -1574,14 +1672,41 @@ static void update_vbv( x264_t *h, int b update_predictor( &rct->pred[h->sh.i_type], qp2qscale(rcc->qpa_rc), rcc->last_satd, bits ); if( !rcc->b_vbv ) - return; + return filler; rct->buffer_fill_final -= 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 += rcc->buffer_rate; - rct->buffer_fill_final = X264_MIN( rct->buffer_fill_final, rcc->buffer_size ); + rct->buffer_fill_final += h->fenc->i_cpb_duration * rcc->vbv_max_rate * h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale; + + if( h->sps->vui.hrd.b_cbr_hrd && rct->buffer_fill_final >= rcc->buffer_size ) + { + filler = (rct->buffer_fill_final - rcc->buffer_size) / 8; + rct->buffer_fill_final -= X264_MAX( FILLER_OVERHEAD, filler ) * 8; + } + else + rct->buffer_fill_final = X264_MIN( rct->buffer_fill_final, rcc->buffer_size ); + + return filler; +} + +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 = h->sps->vui.hrd.i_bit_rate_unscaled; + double cpb_size = h->sps->vui.hrd.i_cpb_size_unscaled; + double cpb_fullness = 90000.0*cpb_bits/bps; + + if( cpb_bits < 0 || cpb_bits > cpb_size ) + { + x264_log( h, X264_LOG_WARNING, "CPB %s: %.0lf bits in a %.0lf-bit buffer\n", + cpb_bits < 0 ? "underflow" : "overflow", cpb_bits, cpb_size ); + } + + return x264_clip3f( cpb_fullness + 0.5, 0, 90000.0*cpb_size/bps ); // just lie if we are in a weird state } // provisionally update VBV according to the planned size of all frames currently in progress @@ -2112,6 +2237,9 @@ void x264_thread_sync_ratecontrol( x264_ COPY(expected_bits_sum); COPY(wanted_bits_window); COPY(bframe_bits); + COPY(initial_cpb_removal_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 -uNrp c/encoder/ratecontrol.h b/encoder/ratecontrol.h --- c/encoder/ratecontrol.h 2010-02-15 12:48:41 +0200 +++ b/encoder/ratecontrol.h 2010-02-26 21:45:45 +0200 @@ -39,7 +39,7 @@ int x264_ratecontrol_slice_type( x264_t 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 *filler ); 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 *); @@ -47,6 +47,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 -uNrp c/encoder/set.c b/encoder/set.c --- c/encoder/set.c 2010-02-23 13:09:14 +0200 +++ b/encoder/set.c 2010-02-26 21:49:04 +0200 @@ -28,6 +28,9 @@ #define bs_write_ue bs_write_ue_big +// Indexed by pic_struct values +static int num_clock_ts[10] = { 0, 1, 1, 1, 2, 2, 3, 3, 2, 3 }; + static void transpose( uint8_t *buf, int w ) { int i, j; @@ -179,15 +182,21 @@ void x264_sps_init( x264_sps_t *sps, int 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); + + 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; - sps->vui.b_fixed_frame_rate = !param->b_vfr_input; + sps->vui.b_fixed_frame_rate = !param->b_vfr_input || param->b_pulldown; } + sps->vui.b_vcl_hrd_parameters_present = 0; // we don't support VCL HRD + sps->vui.b_nal_hrd_parameters_present = !!param->i_nal_hrd; + sps->vui.b_pic_struct_present = !!param->b_pic_struct; + + // NOTE: HRD related parts of the SPS are initialised in x264_ratecontrol_init_reconfigurable + 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 +212,10 @@ void x264_sps_init( x264_sps_t *sps, int 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)log2f( param->analyse.i_mv_range*4-1 ) + 1; } } - void x264_sps_write( bs_t *s, x264_sps_t *sps ) { bs_realign( s ); @@ -343,9 +351,30 @@ void x264_sps_write( bs_t *s, x264_sps_t 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_hrd ); + + 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 ) { @@ -476,7 +505,7 @@ void x264_sei_recovery_point_write( x264 int payload_size; bs_realign( s ); - bs_write( s, 8, 0x06 ); // payload_type = Recovery Point + bs_write( s, 8, SEI_RECOVERY_POINT ); payload_size = bs_size_ue( recovery_frame_cnt ) + 4; bs_write( s, 8, (payload_size + 7) / 8); @@ -512,7 +541,7 @@ int x264_sei_version_write( x264_t *h, b length = strlen(version)+1+16; bs_realign( s ); - 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 ); @@ -534,6 +563,96 @@ 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_realign( s ); + 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 ); /* initial_cpb_removal_delay_offset */ + } + + if( s->i_left&7 ) + bs_write1( s, 1 ); + if( s->i_left&7 ) + bs_align_0( s ); + + bs_rbsp_trailing( s ); + bs_flush( 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; // size of (pic_struct) + payload_size += num_clock_ts[pic_struct]; + } + + bs_realign( s ); + 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-1 ); // We use index 0 for "Auto" + + // These clock timestamps are not standardised so we don't set them + // They could be time of origin, capture or alternative ideal display + for( i = 0; i < num_clock_ts[pic_struct]; i++ ) + bs_write1( s, 0 ); // clock_timestamp_flag + } + + if( s->i_left&7 ) + bs_write1( s, 1 ); + if( s->i_left&7 ) + bs_align_0( s ); + + bs_rbsp_trailing( s ); + bs_flush( s ); +} + +void x264_filler_write( x264_t *h, bs_t *s, int filler ) +{ + int i; + + bs_realign( s ); + + for( i = 0; i < filler; i++ ) + bs_write( s, 8, 0xff ); + + bs_rbsp_trailing( s ); + bs_flush( s ); +} + const x264_level_t x264_levels[] = { { 10, 1485, 99, 152064, 64, 175, 64, 64, 0, 2, 0, 0, 1 }, diff -uNrp c/encoder/set.h b/encoder/set.h --- c/encoder/set.h 2010-02-15 12:48:41 +0200 +++ b/encoder/set.h 2010-02-26 21:49:13 +0200 @@ -31,5 +31,7 @@ void x264_pps_write( bs_t *s, x264_pps_t 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 ); +void x264_filler_write( x264_t *h, bs_t *s, int filler ); #endif diff -uNrp c/encoder/slicetype.c b/encoder/slicetype.c --- c/encoder/slicetype.c 2010-02-15 12:48:41 +0200 +++ b/encoder/slicetype.c 2010-02-26 21:50:03 +0200 @@ -1326,6 +1326,27 @@ void x264_slicetype_decide( x264_t *h ) x264_weights_analyse( h, h->lookahead->next.list[bframes], h->lookahead->last_nonb, 0 ); } + int lookahead_size = h->lookahead->next.i_size; + + for( i = 0; i <= bframes; i++ ) + { + if( h->param.b_vfr_input ) + { + if( lookahead_size-- > 1 ) + h->i_last_duration = h->lookahead->next.list[i]->i_duration = h->lookahead->next.list[i+1]->i_pts - + h->lookahead->next.list[i]->i_pts; + else + h->lookahead->next.list[i]->i_duration = h->i_last_duration; + } + else + { + h->lookahead->next.list[i]->i_duration = (int64_t)h->param.i_timebase_den * h->param.i_fps_den / + h->param.i_timebase_num / h->param.i_fps_num; + } + h->lookahead->next.list[i]->i_field_cnt = h->i_disp_fields; + h->i_disp_fields += 2 * h->lookahead->next.list[i]->i_duration; + } + /* shift sequence to coded order. use a small temporary list to avoid shifting the entire next buffer around */ int i_coded = h->lookahead->next.list[0]->i_frame; @@ -1342,8 +1363,35 @@ void x264_slicetype_decide( x264_t *h ) frames[0]->i_reordered_pts = h->lookahead->next.list[0]->i_pts; memcpy( h->lookahead->next.list, frames, (bframes+1) * sizeof(x264_frame_t*) ); } + for( i = 0; i <= bframes; i++ ) - h->lookahead->next.list[i]->i_coded = i_coded++; + { + x264_frame_t *cur_frame = h->lookahead->next.list[i]; + + cur_frame->i_coded = i_coded++; + + cur_frame->i_cpb_delay = h->i_cpb_delay; + cur_frame->i_dpb_output_delay = cur_frame->i_field_cnt - h->i_coded_fields; + + // add a correction term for frame reordering + cur_frame->i_dpb_output_delay += h->sps->vui.i_num_reorder_frames*2; + + // fix possible negative dpb_output_delay because of pulldown changes and reordering + if( cur_frame->i_dpb_output_delay < 0 ) + { + cur_frame->i_cpb_delay += cur_frame->i_dpb_output_delay; + cur_frame->i_dpb_output_delay = 0; + if( i ) + h->lookahead->next.list[i-1]->i_cpb_duration += cur_frame->i_dpb_output_delay; + } + + if( h->lookahead->next.list[i]->b_keyframe ) + h->i_cpb_delay = 0; + + h->i_cpb_delay += cur_frame->i_duration * 2; + h->i_coded_fields += cur_frame->i_duration* 2; + cur_frame->i_cpb_duration = cur_frame->i_duration * 2; + } } int x264_rc_analyse_slice( x264_t *h ) diff -uNrp c/input/avs.c b/input/avs.c --- c/input/avs.c 2010-02-15 12:48:41 +0200 +++ b/input/avs.c 2010-02-26 21:50:29 +0200 @@ -263,6 +263,7 @@ static int picture_alloc( x264_picture_t pic->img.i_csp = i_csp; pic->img.i_plane = 3; pic->param = NULL; + pic->i_pic_struct = PIC_STRUCT_AUTO; return 0; } diff -uNrp c/output/flv.c b/output/flv.c --- c/output/flv.c 2010-02-15 12:48:41 +0200 +++ b/output/flv.c 2010-02-26 21:51:21 +0200 @@ -154,9 +154,9 @@ static int write_headers( hnd_t handle, 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, 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, // 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 -uNrp c/output/matroska.c b/output/matroska.c --- c/output/matroska.c 2010-02-15 12:48:41 +0200 +++ b/output/matroska.c 2010-02-26 21:51:41 +0200 @@ -107,13 +107,13 @@ static int write_headers( hnd_t handle, { 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; - - 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; + 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 *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 -uNrp c/output/mp4.c b/output/mp4.c --- c/output/mp4.c 2010-02-15 12:48:41 +0200 +++ b/output/mp4.c 2010-02-26 21:52:04 +0200 @@ -228,13 +228,13 @@ static int write_headers( hnd_t handle, 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; - - 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; + 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 *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 -uNrp c/x264.c b/x264.c --- c/x264.c 2010-02-26 21:21:08 +0200 +++ b/x264.c 2010-02-26 21:56:48 +0200 @@ -57,6 +57,7 @@ typedef struct { hnd_t hin; hnd_t hout; FILE *qpfile; + int i_pulldown; } cli_opt_t; /* i/o file operation function pointer structs */ @@ -92,10 +93,31 @@ static const char * const muxer_names[] 0 }; +static const char * const pulldown_names[] = { "", "22", "32", "64", "double", "triple", "euro", 0 }; + +typedef struct{ + int mod; + int *pattern; + int i_timebase_multiplier; +} cli_pulldown_t; + +enum pulldown_type_e +{ + X264_PULLDOWN_22 = 1, + X264_PULLDOWN_32 = 2, + X264_PULLDOWN_64 = 3, + X264_PULLDOWN_DOUBLE = 4, + X264_PULLDOWN_TRIPLE = 5, + X264_PULLDOWN_EURO = 6, +}; + +// indexed by pic_struct enum +static float frame_duration[10] = { 0.0, 1, 0.5, 0.5, 1, 1, 1.5, 1.5, 2, 3 }; + static void Help( x264_param_t *defaults, int longhelp ); static int Parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ); static int Encode( x264_param_t *param, cli_opt_t *opt ); - +static int set_pulldown( cli_pulldown_t *pulldown, int i_pulldown, x264_param_t *param ); /**************************************************************************** * main: ****************************************************************************/ @@ -332,7 +354,8 @@ static void Help( x264_param_t *defaults 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" ); @@ -474,6 +497,11 @@ static void Help( x264_param_t *defaults 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" + " - vbr, cbr. (requires vbv-bufsize; cbr not allowed in .mp4)\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" ); @@ -506,6 +534,8 @@ static void Help( x264_param_t *defaults 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" + " - 22, 32, 64, double, triple, euro\n" ); H0( "\n" ); } @@ -527,6 +557,7 @@ static void Help( x264_param_t *defaults #define OPT_DEMUXER 271 #define OPT_INDEX 272 #define OPT_INTERLACED 273 +#define OPT_PULLDOWN 274 static char short_options[] = "8A:B:b:f:hI:i:m:o:p:q:r:t:Vvw"; static struct option long_options[] = @@ -555,6 +586,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 }, @@ -661,6 +694,9 @@ static struct option long_options[] = { "colormatrix", required_argument, NULL, 0 }, { "chromaloc", required_argument, NULL, 0 }, { "force-cfr", no_argument, NULL, 0 }, + { "pic-struct", no_argument, NULL, 0 }, + { "nal-hrd", required_argument, NULL, 0 }, + { "pulldown", required_argument, NULL, OPT_PULLDOWN }, {0, 0, 0, 0} }; @@ -675,9 +711,13 @@ static int select_output( const char *mu #ifdef MP4_OUTPUT output = mp4_output; param->b_annexb = 0; - param->b_aud = 0; param->b_dts_compress = 0; param->b_repeat_headers = 0; + if( param->i_nal_hrd == X264_NAL_HRD_CBR ) + { + fprintf( stderr, "x264 [warning]: cbr nal-hrd is not compatible with mp4\n" ); + param->i_nal_hrd = X264_NAL_HRD_VBR; + } #else fprintf( stderr, "x264 [error]: not compiled with MP4 output support\n" ); return -1; @@ -687,7 +727,6 @@ static int select_output( const char *mu { output = mkv_output; param->b_annexb = 0; - param->b_aud = 0; param->b_dts_compress = 0; param->b_repeat_headers = 0; } @@ -695,7 +734,6 @@ static int select_output( const char *mu { output = flv_output; param->b_annexb = 0; - param->b_aud = 0; param->b_dts_compress = 1; param->b_repeat_headers = 0; } @@ -784,6 +822,88 @@ static int select_input( const char *dem return 0; } +static int set_pulldown( cli_pulldown_t *pulldown, int i_pulldown, x264_param_t *param ) +{ + float f_max_fps_factor = 1; + // multiplier used when repeat field frames make frame duration not an multiple of timebase_den + pulldown->i_timebase_multiplier = 1; + param->b_vfr_input = 1; + + switch( i_pulldown ) + { + case X264_PULLDOWN_22: + pulldown->mod = 1; + pulldown->pattern = malloc( pulldown->mod*sizeof(int) ); + if( !pulldown->pattern ) + return -1; + pulldown->pattern[0] = PIC_STRUCT_TOP_BOTTOM; + break; + case X264_PULLDOWN_32: + pulldown->mod = 4; + pulldown->pattern = malloc( pulldown->mod*sizeof(int) ); + if( !pulldown->pattern ) + return -1; + pulldown->pattern[0] = PIC_STRUCT_TOP_BOTTOM_TOP; + pulldown->pattern[1] = PIC_STRUCT_BOTTOM_TOP; + pulldown->pattern[2] = PIC_STRUCT_BOTTOM_TOP_BOTTOM; + pulldown->pattern[3] = PIC_STRUCT_TOP_BOTTOM; + f_max_fps_factor = 1.25; + pulldown->i_timebase_multiplier = 2; + break; + + case X264_PULLDOWN_64: + pulldown->mod = 2; + pulldown->pattern = malloc( pulldown->mod*sizeof(int) ); + if( !pulldown->pattern ) + return -1; + pulldown->pattern[0] = PIC_STRUCT_DOUBLE; + pulldown->pattern[1] = PIC_STRUCT_TRIPLE; + f_max_fps_factor = 2.5; + break; + + case X264_PULLDOWN_DOUBLE: + pulldown->mod = 1; + pulldown->pattern = malloc( pulldown->mod*sizeof(int) ); + if( !pulldown->pattern ) + return -1; + pulldown->pattern[0] = PIC_STRUCT_DOUBLE; + f_max_fps_factor = 2; + break; + + case X264_PULLDOWN_TRIPLE: + pulldown->mod = 1; + pulldown->pattern = malloc( pulldown->mod*sizeof(int) ); + if( !pulldown->pattern ) + return -1; + pulldown->pattern[0] = PIC_STRUCT_TRIPLE; + f_max_fps_factor = 3; + break; + + case X264_PULLDOWN_EURO: + pulldown->mod = 24; + pulldown->pattern = malloc( pulldown->mod*sizeof(int) ); + if( !pulldown->pattern ) + return -1; + + int j; + for( j = 0; j < 11; j++ ) + { + pulldown->pattern[j+1] = PIC_STRUCT_BOTTOM_TOP; + pulldown->pattern[j+13] = PIC_STRUCT_TOP_BOTTOM; + } + pulldown->pattern[0] = PIC_STRUCT_TOP_BOTTOM_TOP; + pulldown->pattern[12] = PIC_STRUCT_BOTTOM_TOP_BOTTOM; + f_max_fps_factor = 25.0/24.0; + pulldown->i_timebase_multiplier = 2; + break; + } + + param->i_timebase_num = param->i_fps_den; + param->i_timebase_den = param->i_fps_num * f_max_fps_factor * pulldown->i_timebase_multiplier; + + return 0; +} + /***************************************************************************** * Parse: *****************************************************************************/ @@ -953,6 +1073,16 @@ static int Parse( int argc, char **argv, case OPT_INTERLACED: b_user_interlaced = 1; goto generic_option; + case OPT_PULLDOWN: + for( i = 0; pulldown_names[i] && strcasecmp( pulldown_names[i], optarg ); ) + i++; + if( !pulldown_names[i] ) + { + fprintf( stderr, "x264 [error]: invalid pulldown '%s'\n", optarg ); + return -1; + } + opt->i_pulldown = i; + break; default: generic_option: { @@ -1198,6 +1328,7 @@ static int Encode( x264_param_t *param, { x264_t *h; x264_picture_t pic; + cli_pulldown_t pulldown; int i_frame, i_frame_total, i_frame_output; int64_t i_start, i_end; @@ -1213,6 +1344,7 @@ static int Encode( x264_param_t *param, double duration; int prev_timebase_den = param->i_timebase_den / gcd( param->i_timebase_num, param->i_timebase_den ); int dts_compress_multiplier; + int64_t pulldown_pts = 0; opt->b_progress &= param->i_log_level < X264_LOG_DEBUG; i_frame_total = input.get_frame_total( opt->hin ); @@ -1223,6 +1355,14 @@ static int Encode( x264_param_t *param, param->i_frame_total = i_frame_total; i_update_interval = i_frame_total ? x264_clip3( i_frame_total / 1000, 1, 10 ) : 10; + /* set up pulldown */ + if( opt->i_pulldown && !param->b_vfr_input ) + { + param->b_pulldown = 1; + if( set_pulldown( &pulldown, opt->i_pulldown, param ) < 0 ) + return -1; + } + if( ( h = x264_encoder_open( param ) ) == NULL ) { fprintf( stderr, "x264 [error]: x264_encoder_open failed\n" ); @@ -1282,6 +1422,14 @@ static int Encode( x264_param_t *param, if( !param->b_vfr_input ) pic.i_pts = i_frame; + + if( param->b_pulldown ) + { + pic.i_pic_struct = pulldown.pattern[ i_frame % pulldown.mod ]; + pic.i_pts = pulldown_pts; + pulldown_pts += frame_duration[pic.i_pic_struct] * pulldown.i_timebase_multiplier; + } + if( pic.i_pts <= largest_pts ) { if( param->i_log_level >= X264_LOG_WARNING ) @@ -1295,6 +1443,7 @@ static int Encode( x264_param_t *param, } pic.i_pts = largest_pts + ticks_per_frame; } + second_largest_pts = largest_pts; largest_pts = pic.i_pts; @@ -1368,5 +1517,8 @@ static int Encode( x264_param_t *param, (double) i_file * 8 / ( 1000 * duration ) ); } + if( param->b_pulldown ) + free( pulldown.pattern ); + return 0; } diff -uNrp c/x264.h b/x264.h --- c/x264.h 2010-02-24 10:20:41 +0200 +++ b/x264.h 2010-02-26 21:58:54 +0200 @@ -35,7 +35,7 @@ #include -#define X264_BUILD 88 +#define X264_BUILD 89 /* x264_t: * opaque handler for encoder */ @@ -111,6 +111,7 @@ static const char * const x264_fullrange 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_nal_hrd_names[] = { "none", "vbr", "cbr", 0 }; /* Colorspace type * legacy only; nothing other than I420 is really supported. */ @@ -148,6 +149,11 @@ static const char * const x264_colmatrix #define X264_THREADS_AUTO 0 /* Automatically select optimal number of threads */ #define X264_SYNC_LOOKAHEAD_AUTO (-1) /* Automatically select optimal lookahead thread buffer size */ +/* HRD */ +#define X264_NAL_HRD_NONE 0 +#define X264_NAL_HRD_VBR 1 +#define X264_NAL_HRD_CBR 2 + /* Zones: override ratecontrol or other options for specific sections of the video. * See x264_encoder_reconfig() for which options can be changed. * If zones overlap, whichever comes later in the list takes precedence. */ @@ -176,6 +182,8 @@ typedef struct x264_param_t int i_level_idc; int i_frame_total; /* number of frames to encode if known, else 0 */ + int i_nal_hrd; /* Use Buffering and Picture Timing SEI's to signal HRD */ + struct { /* they will be reduced to be 0 < x <= 65535 and prime */ @@ -320,6 +328,21 @@ typedef struct x264_param_t * by compressing them to be less than the second PTS. * Warning: this will change the timebase! */ + int b_tff; + + /* Pulldown: + * Set b_pulldown to use pulldown and pass the correct pic_struct with the frame + * The input timebase should be the timebase relating to the output framerate. This should be constant. + * b_pic_struct must also be on. Pulldown is (almost always) a form of VFR so b_vfr_input must be on. + * PTS must also be the PTS of the pulldowned frame + * + * Pulldown changes are not clearly defined in H.264. Therefore, it is the calling app's responsibility to manage this. + */ + + int b_pulldown; + + int b_pic_struct; + /* 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. */ @@ -436,6 +459,29 @@ int x264_param_apply_profile( x264_p /**************************************************************************** * Picture structures and functions ****************************************************************************/ + +enum pic_struct_e +{ + PIC_STRUCT_AUTO = 0, // automatically decide (default) + PIC_STRUCT_PROGRESSIVE = 1, // progressive frame + // "TOP" and "BOTTOM" are not supported in x264 (PAFF only) + PIC_STRUCT_TOP_BOTTOM = 4, // top field followed by bottom + PIC_STRUCT_BOTTOM_TOP = 5, // bottom field followed by top + PIC_STRUCT_TOP_BOTTOM_TOP = 6, // top field, bottom field, top field repeated + PIC_STRUCT_BOTTOM_TOP_BOTTOM = 7, // bottom field, top field, bottom field repeated + PIC_STRUCT_DOUBLE = 8, // double frame + PIC_STRUCT_TRIPLE = 9 // triple frame +}; + +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; /* Colorspace */ @@ -455,6 +501,9 @@ typedef struct int i_type; /* In: force quantizer for > 0 */ int i_qpplus1; + /* In: pic_struct, for pulldown/doubling/etc...used only if b_pic_timing_sei=1. + * use pic_struct_e for pic_struct inputs */ + 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; @@ -472,6 +521,8 @@ typedef struct x264_param_t *param; /* In: raw data */ x264_image_t img; + /* Out: HRD timing information. Output only when b_nal_hrd is set. */ + x264_hrd_t hrd_timing; /* private user data. libx264 doesn't touch this, not even copy it from input to output frames. */ void *opaque; @@ -503,6 +554,7 @@ enum nal_unit_type_e NAL_SPS = 7, NAL_PPS = 8, NAL_AUD = 9, + NAL_FILLER = 12, /* ref_idc == 0 for 6,9,10,11,12 */ }; enum nal_priority_e