diff -uNrp a/common/common.c b/common/common.c --- a/common/common.c 2010-02-15 12:48:41 +0200 +++ b/common/common.c 2010-02-15 12:54:15 +0200 @@ -60,6 +60,9 @@ void x264_param_default( x264_param_t param->vui.i_transfer = 2; /* undef */ param->vui.i_colmatrix = 2; /* undef */ param->vui.i_chroma_loc= 0; /* left center */ + + param->b_nal_hrd = 0; + param->i_fps_num = 25; param->i_fps_den = 1; param->i_level_idc = -1; @@ -158,6 +161,12 @@ void x264_param_default( x264_param_t param->b_aud = 0; param->b_vfr_input = 1; param->b_dts_compress = 0; + param->b_nal_hrd = 0; + param->b_cbr_hrd = 0; + param->b_tff = 1; + param->b_pic_struct = 0; + param->b_pulldown = 0; + param->i_pulldown_delay = 0; } static int parse_enum( const char *arg, const char * const *names, int *dst ) @@ -403,6 +412,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 = 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") @@ -622,6 +638,13 @@ 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") + { + p->b_cbr_hrd = !strcasecmp( value, "cbr" ); + p->b_nal_hrd = p->b_cbr_hrd || !strcasecmp( value, "vbr" ); + } + OPT("pic-struct") + p->b_pic_struct = atobool(value); else return X264_PARAM_BAD_NAME; #undef OPT @@ -697,6 +720,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; } @@ -908,6 +932,8 @@ char *x264_param2string( x264_param_t *p 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 ); @@ -960,6 +986,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=%d", p->b_nal_hrd ); return buf; } diff -uNrp a/common/common.h b/common/common.h --- a/common/common.h 2010-02-15 12:48:41 +0200 +++ b/common/common.h 2010-02-15 12:55:43 +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_ITU_T_T35 = 4, + SEI_USER_DATA_UNREGISTERED = 5, + SEI_RECOVERY_POINT = 6, +}; + typedef struct { x264_sps_t *sps; @@ -367,6 +381,12 @@ 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 */ + /* We use only one SPS and one PPS */ x264_sps_t sps_array[1]; x264_sps_t *sps; @@ -449,7 +469,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 @@ -722,7 +745,6 @@ struct x264_t int i_direct_frames[2]; /* num p-frames weighted */ int i_wpred[3]; - } stat; void *scratch_buffer; /* for any temporary storage that doesn't want repeated malloc */ diff -uNrp a/common/frame.c b/common/frame.c --- a/common/frame.c 2010-02-15 12:48:41 +0200 +++ b/common/frame.c 2010-02-15 12:56:24 +0200 @@ -73,6 +73,10 @@ 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->orig = frame; /* all 4 luma planes allocated together, since the cacheline split code @@ -225,6 +229,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 a/common/frame.h b/common/frame.h --- a/common/frame.h 2010-02-15 12:48:41 +0200 +++ b/common/frame.h 2010-02-15 12:57:05 +0200 @@ -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 */ @@ -108,6 +111,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 a/common/osdep.h b/common/osdep.h --- a/common/osdep.h 2010-02-15 12:48:41 +0200 +++ b/common/osdep.h 2010-02-15 12:58:11 +0200 @@ -221,10 +221,13 @@ 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 ) { static uint8_t lut[16] = {4,3,2,2,1,1,1,1,0,0,0,0,0,0,0,0}; + if( !x ) + return 0; // just to be consistent with __builtin_clz int y, z = (((x >> 16) - 1) >> 27) & 16; x >>= z^16; z += y = ((x - 0x100) >> 28) & 8; @@ -233,6 +236,20 @@ 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}; + if( !x ) + return 0; // just to be consistent with __builtin_ctz + 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 a/common/set.h b/common/set.h --- a/common/set.h 2010-02-15 12:48:41 +0200 +++ b/common/set.h 2010-02-15 12:58:42 +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 a/encoder/encoder.c b/encoder/encoder.c --- a/encoder/encoder.c 2010-02-15 12:48:41 +0200 +++ b/encoder/encoder.c 2010-02-15 13:08:57 +0200 @@ -39,14 +39,14 @@ //#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, x264_nal_t **pp_nal, int *pi_nal, x264_picture_t *pic_out ); +static int delta_tfi_divisor[10] = { 0, 2, 1, 1, 2, 2, 3, 3, 4, 6 }; // Number of coded/displayed fields (indexed by i_pic_struct) + /**************************************************************************** * ******************************* x264 libs ********************************** @@ -512,6 +512,35 @@ static int x264_validate_parameters( x26 h->param.rc.i_aq_mode = 0; h->param.rc.b_mb_tree = 0; } + + if( h->param.rc.i_rc_method == X264_RC_CRF && h->param.rc.b_stat_read ) + { + x264_log(h, X264_LOG_ERROR, "constant rate-factor is incompatible with 2pass.\n"); + return -1; + } + if( h->param.rc.i_vbv_buffer_size ) + { + if( h->param.rc.i_rc_method == X264_RC_CQP ) + { + x264_log(h, X264_LOG_WARNING, "VBV is incompatible with constant QP, ignored.\n"); + h->param.rc.i_vbv_max_bitrate = 0; + h->param.rc.i_vbv_buffer_size = 0; + } + else if( h->param.rc.i_vbv_max_bitrate == 0 ) + { + if( h->param.rc.i_rc_method == X264_RC_ABR ) + { + x264_log( h, X264_LOG_INFO, "VBV maxrate unspecified, assuming CBR\n" ); + h->param.rc.i_vbv_max_bitrate = h->param.rc.i_bitrate; + } + else + { + x264_log( h, X264_LOG_INFO, "VBV bufsize set but maxrate unspecified, ignored\n" ); + h->param.rc.i_vbv_buffer_size = 0; + } + } + } + h->param.rc.i_qp_max = x264_clip3( h->param.rc.i_qp_max, 0, 51 ); h->param.rc.i_qp_min = x264_clip3( h->param.rc.i_qp_min, 0, h->param.rc.i_qp_max ); if( h->param.rc.i_vbv_buffer_size ) @@ -777,6 +806,34 @@ static int x264_validate_parameters( x26 h->param.analyse.b_ssim = 0; } + if( h->param.b_pulldown && !h->param.i_pulldown_delay ) + { + x264_log( h, X264_LOG_WARNING, "Pulldown requires delay\n" ); + h->param.b_pulldown = 0; + } + + if( h->param.b_pulldown && h->param.b_vfr_input ) + { + x264_log( h, X264_LOG_WARNING, "Pulldown not compatible with variable framerate\n" ); + h->param.b_pulldown = 0; + } + + if( h->param.b_pulldown || h->param.b_interlaced ) + h->param.b_pic_struct = 1; + + if( h->param.b_nal_hrd && !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; + } + + if( h->param.b_cbr_hrd && ( 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.b_cbr_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 ); @@ -802,6 +859,8 @@ static int x264_validate_parameters( x26 BOOLIFY( rc.b_stat_write ); BOOLIFY( rc.b_stat_read ); BOOLIFY( rc.b_mb_tree ); + BOOLIFY( b_nal_hrd ); + BOOLIFY( b_cbr_hrd ); #undef BOOLIFY return 0; @@ -907,6 +966,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 ); @@ -961,7 +1021,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 = 0; x264_rdo_init(); /* init CPU functions */ @@ -1069,6 +1129,12 @@ x264_t *x264_encoder_open( x264_param_t if( x264_ratecontrol_new( h ) < 0 ) goto fail; + if( h->param.b_nal_hrd ) + { + x264_log( h, X264_LOG_INFO, "HRD bitrate: %ld bits/sec\n", h->sps->vui.hrd.i_bit_rate_unscaled ); + x264_log( h, X264_LOG_INFO, "CPB size: %lld 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 +1208,17 @@ int x264_encoder_reconfig( x264_t *h, x2 COPY( i_slice_max_size ); COPY( i_slice_max_mbs ); COPY( i_slice_count ); + + COPY( b_nal_hrd ); + COPY( b_cbr_hrd ); + COPY( b_pic_struct ); + + if( h->param.b_pulldown != param->b_pulldown ) + { + COPY( b_pulldown ); + COPY( i_pulldown_delay ); + } + /* 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 ) @@ -1211,10 +1288,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 +1304,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 size = x264_nal_encode( nal_buffer, h->param.b_annexb, &h->out.nal[i] ); h->out.nal[i].i_payload = size; @@ -1237,7 +1320,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); } /**************************************************************************** @@ -1251,11 +1334,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 ); @@ -1269,7 +1347,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; @@ -1693,7 +1778,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->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 ) { @@ -2101,6 +2193,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 ) { @@ -2148,6 +2241,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 ) + { + if( h->param.b_interlaced ) + fenc->i_pic_struct = fenc->b_top_field_first ? PIC_STRUCT_TOP_BOTTOM : PIC_STRUCT_BOTTOM_TOP; + else + fenc->i_pic_struct = PIC_STRUCT_PROGRESSIVE; + } + else if( ( fenc->i_pic_struct < PIC_STRUCT_AUTO ) || ( fenc->i_pic_struct > PIC_STRUCT_TRIPLE ) ) + fenc->i_pic_struct = PIC_STRUCT_PROGRESSIVE; + + // add number of "displayed" fields + 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 ) @@ -2296,13 +2404,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; @@ -2324,22 +2431,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 ); x264_sps_write( &h->out.bs, h->sps ); @@ -2355,6 +2451,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; @@ -2365,6 +2487,40 @@ 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 pulldown + h->dpb_output_delay += h->param.i_pulldown_delay; + + // add a correction term for frame reordering + h->dpb_output_delay += h->sps->vui.i_num_reorder_frames*2; + + // fix possible negative dpb_output_delay because of pulldown changes + if( h->dpb_output_delay < 0 ) + { + h->i_cpb_delay += h->dpb_output_delay; + h->dpb_output_delay = 0; + } + + 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]; + 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 ); @@ -2438,13 +2594,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 ) @@ -2491,9 +2641,27 @@ 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; + + if( filler ) + { + x264_nal_start( h, NAL_FILLER, NAL_PRIORITY_DISPOSABLE ); + x264_filler_write( h, &h->out.bs, filler ); + if( x264_nal_end( h ) ) + return -1; + frame_size += x264_encoder_encapsulate_nals( h, h->out.i_nal-1 ); + } + + /* 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 a/encoder/ratecontrol.c b/encoder/ratecontrol.c --- a/encoder/ratecontrol.c 2010-02-15 12:48:41 +0200 +++ b/encoder/ratecontrol.c 2010-02-15 14:46:12 +0200 @@ -148,13 +148,19 @@ 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; }; 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, int *filler_sei_size ); 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 ); @@ -409,8 +415,51 @@ 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.; +// 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.b_nal_hrd ) + { + h->sps->vui.hrd.i_cpb_cnt = 1; + h->sps->vui.hrd.b_cbr_hrd = h->param.b_cbr_hrd; + 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 + + // FIXME should we use the maximums from the levels + int max_cpb = 2*h->param.i_keyint_max + 1; + int max_dpb = 2 + 2*h->sps->vui.i_max_dec_frame_buffering + 1; + 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 ), 4, 24 ); + h->sps->vui.hrd.i_dpb_output_delay_length = x264_clip3( 32 - x264_clz( max_dpb ), 4, 24 ); + + 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 ); + } + + rc->buffer_rate = (double)vbv_max_bitrate / rc->fps; + 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); @@ -470,11 +519,11 @@ int x264_ratecontrol_new( x264_t *h ) rc->last_non_b_pict_type = -1; rc->cbr_decay = 1.0; - if( h->param.rc.i_rc_method == X264_RC_CRF && h->param.rc.b_stat_read ) - { - x264_log(h, X264_LOG_ERROR, "constant rate-factor is incompatible with 2pass.\n"); - return -1; - } +// if( h->param.rc.i_rc_method == X264_RC_CRF && h->param.rc.b_stat_read ) +// { +// x264_log(h, X264_LOG_ERROR, "constant rate-factor is incompatible with 2pass.\n"); +// return -1; +// } x264_ratecontrol_init_reconfigurable( h, 1 ); @@ -1294,11 +1343,12 @@ 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; int i; + int filler_sei_size = 0; x264_emms(); @@ -1405,7 +1455,57 @@ int x264_ratecontrol_end( x264_t *h, int } } - update_vbv( h, bits ); + *filler = update_vbv( h, bits, &filler_sei_size ); + + if( 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->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 = 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 + h->fenc->hrd_timing.cpb_final_arrival_time = rc->previous_cpb_final_arrival_time = + h->fenc->hrd_timing.cpb_initial_arrival_time + (double)(bits + filler_sei_size*8) / h->sps->vui.hrd.i_bit_rate_unscaled; + + 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 + { + // access unit is so large it cannot be removed at the nominal cpb removal time + 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"); @@ -1542,8 +1642,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_sei_size ) { + int filler = 0; + x264_ratecontrol_t *rcc = h->rc; x264_ratecontrol_t *rct = h->thread[0]->rc; @@ -1551,14 +1653,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 ); + + if( h->sps->vui.hrd.b_cbr_hrd && rct->buffer_fill_final >= rct->buffer_size ) + { + filler = (rct->buffer_fill_final - rct->buffer_size) / 8; + *filler_sei_size = filler + FILLER_OVERHEAD; + rct->buffer_fill_final -= *filler_sei_size * 8; + } + else + rct->buffer_fill_final = X264_MIN( rct->buffer_fill_final, rct->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_fullness < 0 || cpb_fullness > cpb_size ) // why? + { + 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 @@ -2078,6 +2207,10 @@ void x264_thread_sync_ratecontrol( x264_ 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 -uNrp a/encoder/ratecontrol.h b/encoder/ratecontrol.h --- a/encoder/ratecontrol.h 2010-02-15 12:48:41 +0200 +++ b/encoder/ratecontrol.h 2010-02-15 14:46:51 +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 a/encoder/set.c b/encoder/set.c --- a/encoder/set.c 2010-02-15 12:48:41 +0200 +++ b/encoder/set.c 2010-02-15 14:49:54 +0200 @@ -28,6 +28,8 @@ #define bs_write_ue bs_write_ue_big +static int num_clock_ts[10] = { 0, 1, 1, 1, 2, 2, 3, 3, 2, 3 }; // Indexed by pic_struct values + static void transpose( uint8_t *buf, int w ) { int i, j; @@ -179,15 +181,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_vcl_hrd_parameters_present = 0; // we don't support VCL HRD + sps->vui.b_nal_hrd_parameters_present = param->b_nal_hrd; + sps->vui.b_pic_struct_present = !!param->b_pic_struct; + + // NOTE: HRD related parts of the SPS are initialised in ratecontrol_new + 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 +211,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)log2( param->analyse.i_mv_range*4-1 ) + 1; } } - void x264_sps_write( bs_t *s, x264_sps_t *sps ) { bs_realign( s ); @@ -343,9 +350,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 +504,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_USER_DATA_UNREGISTERED ); payload_size = bs_size_ue( recovery_frame_cnt ) + 4; bs_write( s, 8, (payload_size + 7) / 8); @@ -512,7 +540,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 +562,95 @@ 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" + + // FIXME: is the use of num_clock_ts standardised + 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 ); + 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, 0, 0, 1 }, diff -uNrp a/encoder/set.h b/encoder/set.h --- a/encoder/set.h 2010-02-15 12:48:41 +0200 +++ b/encoder/set.h 2010-02-15 14:50:14 +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 a/input/avs.c b/input/avs.c --- a/input/avs.c 2010-02-15 12:48:41 +0200 +++ b/input/avs.c 2010-02-15 14:50:36 +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 a/output/flv.c b/output/flv.c --- a/output/flv.c 2010-02-15 12:48:41 +0200 +++ b/output/flv.c 2010-02-15 14:52:06 +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 a/output/matroska.c b/output/matroska.c --- a/output/matroska.c 2010-02-15 12:48:41 +0200 +++ b/output/matroska.c 2010-02-15 14:52:37 +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 a/output/mp4.c b/output/mp4.c --- a/output/mp4.c 2010-02-15 12:48:41 +0200 +++ b/output/mp4.c 2010-02-15 14:53: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 a/x264.c b/x264.c --- a/x264.c 2010-02-15 12:48:41 +0200 +++ b/x264.c 2010-02-15 14:57:42 +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,27 @@ static const char * const muxer_names[] 0 }; +static const char * const pulldown_names[] = { "", "32", "64", "double", "triple", "euro", 0 }; + +typedef struct{ + int mod; + int *pattern; + int i_delay; +} cli_pulldown_t; + +enum pulldown_type_e +{ + X264_PULLDOWN_32 = 1, + X264_PULLDOWN_64 = 2, + X264_PULLDOWN_DOUBLE = 3, + X264_PULLDOWN_TRIPLE = 4, + X264_PULLDOWN_EURO = 5, +}; + 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 ); /**************************************************************************** * main: ****************************************************************************/ @@ -334,7 +352,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" ); @@ -476,6 +495,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-hrd 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" ); @@ -508,6 +532,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" + " - 32, 64, double, triple, euro\n" ); H0( "\n" ); } @@ -529,6 +555,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[] = @@ -557,6 +584,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 }, @@ -663,6 +692,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} }; @@ -677,9 +709,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->b_cbr_hrd ) + { + fprintf( stderr, "x264 [warning]: cbr-hrd is not compatible with mp4\n" ); + param->b_cbr_hrd = 0; + } #else fprintf( stderr, "x264 [error]: not compiled with MP4 output support\n" ); return -1; @@ -689,7 +725,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; } @@ -697,7 +732,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; } @@ -786,6 +820,71 @@ static int select_input( const char *dem return 0; } +static int set_pulldown( cli_pulldown_t *pulldown, int i_pulldown ) +{ + switch( i_pulldown ) + { + 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; + pulldown->i_delay = 1; + 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; + pulldown->i_delay = 4; + 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; + pulldown->i_delay = 2; + break; + + case X264_PULLDOWN_TRIPLE: + pulldown->mod = 1; + pulldown->i_delay = 4; + pulldown->pattern = malloc( pulldown->mod*sizeof(int) ); + if( !pulldown->pattern ) + return -1; + pulldown->pattern[0] = PIC_STRUCT_TRIPLE; + break; + + case X264_PULLDOWN_EURO: + pulldown->mod = 24; + pulldown->i_delay = 1; + 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; + break; + } + + return 0; +} + /***************************************************************************** * Parse: *****************************************************************************/ @@ -1154,6 +1253,16 @@ psy_failure: 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: { @@ -1440,6 +1549,7 @@ static int Encode( x264_param_t *param, { x264_t *h; x264_picture_t pic; + cli_pulldown_t p_pulldown; int i_frame, i_frame_total, i_frame_output; int64_t i_start, i_end; @@ -1465,6 +1575,15 @@ 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_pulldown = 1; + if( set_pulldown( &p_pulldown, opt->i_pulldown ) < 0 ) + return -1; + param->i_pulldown_delay = p_pulldown.i_delay; + } + if( ( h = x264_encoder_open( param ) ) == NULL ) { fprintf( stderr, "x264 [error]: x264_encoder_open failed\n" ); @@ -1549,6 +1668,9 @@ static int Encode( x264_param_t *param, pic.i_qpplus1 = 0; } + if( opt->i_pulldown ) + pic.i_pic_struct = p_pulldown.pattern[ i_frame % p_pulldown.mod ]; + i_frame_size = Encode_frame( h, opt->hout, &pic, &last_pts ); if( i_frame_size < 0 ) return -1; @@ -1610,5 +1732,8 @@ static int Encode( x264_param_t *param, (double) i_file * 8 / ( 1000 * duration ) ); } + if( opt->i_pulldown ) + free(p_pulldown.pattern); + return 0; } diff -uNrp a/x264.h b/x264.h --- a/x264.h 2010-02-15 12:48:41 +0200 +++ b/x264.h 2010-02-15 14:59:14 +0200 @@ -176,6 +176,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_nal_hrd; /* Use Buffering and Picture Timing SEI's to signal HRD */ + int b_cbr_hrd; /* Use CBR-HRD mode with filler bytes */ + struct { /* they will be reduced to be 0 < x <= 65535 and prime */ @@ -320,6 +323,26 @@ typedef struct x264_param_t * by compressing them to be less than the second PTS. * Warning: this will change the timebase! */ + int b_tff; + + /* Custom Pulldown: + * Set b_pulldown to use pulldown and pass the correct pic_struct with the frame + * + * i_pulldown_delay is the number of effective extra frames added to the sequence as a result of pulldown (mandatory) + * i_pulldown_delay is added to each frame's dpb output delay + * timebase must also be appropriate for the pulldown changes that could be made + * + * Pulldown changes are not clearly defined in H.264. Therefore, it is the calling app's responsibility to manage this. + * Correct timestamps must also be input. + * + * Use the frame-exact x264_picture_t->param instead of the frame-inexact x264_encoder_reconfig to change pulldown on the fly. + */ + + int b_pulldown; + int i_pulldown_delay; + + 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. */ @@ -368,6 +391,29 @@ int x264_param_parse( x264_param_t *, co /**************************************************************************** * 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; @@ -388,6 +434,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. + * 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; @@ -405,6 +454,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; @@ -436,6 +487,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