Index: common/bs.h =================================================================== --- common/bs.h (revision 662) +++ common/bs.h (working copy) @@ -227,6 +227,16 @@ s->p++; } } +static inline void bs_align_10( bs_t *s ) +{ + if( s->i_left != 8 ) + { + *s->p <<= s->i_left; + *s->p |= 1 << (s->i_left - 1); + s->i_left = 8; + s->p++; + } +} static inline void bs_align( bs_t *s ) { bs_align_0( s ); @@ -271,7 +281,8 @@ } i_size += i_size0_255[tmp]; - bs_write( s, 2 * i_size - 1, val ); + bs_write( s, i_size - 1, 0 ); + bs_write( s, i_size, val ); } } Index: common/common.c =================================================================== --- common/common.c (revision 662) +++ common/common.c (working copy) @@ -140,6 +140,7 @@ param->b_repeat_headers = 1; param->b_aud = 0; + param->b_nal_hrd = 0; } static int parse_enum( const char *arg, const char * const *names, int *dst ) @@ -523,7 +524,9 @@ p->analyse.b_ssim = atobool(value); OPT("aud") p->b_aud = atobool(value); - OPT("sps-id") + OPT("nal-hrd") + p->b_nal_hrd = atobool(value); + OPT("sps-id") p->i_sps_id = atoi(value); OPT("global-header") p->b_repeat_headers = !atobool(value); Index: common/common.h =================================================================== --- common/common.h (revision 662) +++ common/common.h (working copy) @@ -321,6 +321,9 @@ int i_poc_lsb; /* decoding only */ int i_poc; /* decoding only */ + int i_cpb_delay; /* Equal to number of fields preceding this field + * since last buffering_period SEI */ + int i_thread_num; /* threads only */ int i_nal_type; /* threads only */ int i_nal_ref_idc; /* threads only */ Index: common/set.h =================================================================== --- common/set.h (revision 662) +++ common/set.h (working copy) @@ -124,6 +124,18 @@ int i_num_reorder_frames; int i_max_dec_frame_buffering; + int b_nal_hrd_parameters_present; + struct + { + int i_bit_rate; + int i_cpb_size; + int b_cbr; + + int i_initial_cpb_removal_delay_length; + int i_cpb_removal_delay_length; + int i_dpb_output_delay_length; + } nal_hrd_parameters; + /* FIXME to complete */ } vui; Index: encoder/encoder.c =================================================================== --- encoder/encoder.c (revision 662) +++ encoder/encoder.c (working copy) @@ -382,6 +382,15 @@ } } + if( h->param.b_nal_hrd ) + { + if( h->param.rc.i_vbv_max_bitrate == 0 || h->param.rc.i_vbv_buffer_size == 0 ) + { + x264_log( h, X264_LOG_WARNING, "NAL HRD parameters require VBV max bitrate and buffer size to be specified\n" ); + h->param.b_nal_hrd = 0; + } + } + if( h->param.rc.i_rc_method < 0 || h->param.rc.i_rc_method > 2 ) { x264_log( h, X264_LOG_ERROR, "no ratecontrol method specified\n" ); @@ -571,6 +580,8 @@ /* Create a copy of param */ memcpy( &h->param, param, sizeof( x264_param_t ) ); + h->param.b_nal_hrd = 1; + if( x264_validate_parameters( h ) < 0 ) { x264_free( h ); @@ -625,6 +636,7 @@ h->i_frame = 0; h->i_frame_num = 0; h->i_idr_pic_id = 0; + h->i_cpb_delay = 0; h->sps = &h->sps_array[0]; x264_sps_init( h->sps, h->param.i_sps_id, &h->param ); @@ -1476,6 +1488,28 @@ x264_nal_end( h ); } + if( h->param.b_nal_hrd ) + { + int cpb_removal_delay, dpb_output_delay; + + cpb_removal_delay = h->i_cpb_delay; + + x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); + + if( i_nal_type == NAL_SLICE_IDR ) + { + x264_sei_buffering_period_write( &h->param, &h->out.bs, h->sps ); + h->i_cpb_delay = 0; + } + + dpb_output_delay = h->fenc->i_poc + (h->sps->vui.i_num_reorder_frames * 2) - h->i_cpb_delay; + x264_sei_picture_timing_write( &h->out.bs, h->sps, cpb_removal_delay, dpb_output_delay); + + h->i_cpb_delay += h->sh.b_field_pic ? 1 : 2; + + x264_nal_end( h ); + } + /* Write frame */ if( h->param.i_threads > 1 ) { Index: encoder/set.c =================================================================== --- encoder/set.c (revision 662) +++ encoder/set.c (working copy) @@ -209,6 +209,25 @@ sps->vui.i_log2_max_mv_length_horizontal = sps->vui.i_log2_max_mv_length_vertical = (int)(log(param->analyse.i_mv_range*4-1)/log(2)) + 1; } + + sps->vui.b_nal_hrd_parameters_present = param->b_nal_hrd; + if( sps->vui.b_nal_hrd_parameters_present ) + { + sps->vui.nal_hrd_parameters.i_bit_rate = param->rc.i_vbv_max_bitrate * 1000; + sps->vui.nal_hrd_parameters.i_cpb_size = param->rc.i_vbv_buffer_size * 1000; + + sps->vui.nal_hrd_parameters.i_initial_cpb_removal_delay_length = 0; + while( (1 << ++sps->vui.nal_hrd_parameters.i_initial_cpb_removal_delay_length) < + (param->rc.i_vbv_buffer_size * 90000.0 / param->rc.i_vbv_max_bitrate) ); + + sps->vui.nal_hrd_parameters.i_cpb_removal_delay_length = 0; + while( (1 << ++sps->vui.nal_hrd_parameters.i_cpb_removal_delay_length) < + (param->i_keyint_max * 2) ); + + sps->vui.nal_hrd_parameters.i_dpb_output_delay_length = 0; + while( (1 << ++sps->vui.nal_hrd_parameters.i_dpb_output_delay_length) < + ((sps->vui.i_num_reorder_frames + sps->i_num_ref_frames) * 2) ); + } } @@ -346,8 +365,47 @@ bs_write1( s, sps->vui.b_fixed_frame_rate ); } - bs_write1( s, 0 ); /* nal_hrd_parameters_present_flag */ + bs_write1( s, sps->vui.b_nal_hrd_parameters_present ); + if( sps->vui.b_nal_hrd_parameters_present ) + { + int bit_rate_scale = 0, cpb_size_scale = 0; + int bit_rate_tmp, cpb_size_tmp; + + bs_write_ue( s, 0 ); /* cpb_cnt_minus1 */ + + bit_rate_tmp = ( sps->vui.nal_hrd_parameters.i_bit_rate + 63 ) >> 6; + while( ( bit_rate_tmp & 1) == 0 ) + { + bit_rate_tmp >>= 1; + bit_rate_scale++; + if (bit_rate_scale == 15) + break; + } + + cpb_size_tmp = ( sps->vui.nal_hrd_parameters.i_cpb_size + 15 ) >> 4; + while( ( cpb_size_tmp & 1) == 0 ) + { + cpb_size_tmp >>= 1; + cpb_size_scale++; + if (cpb_size_scale == 15) + break; + } + + bs_write( s, 4, bit_rate_scale ); + bs_write( s, 4, cpb_size_scale ); + bs_write_ue( s, bit_rate_tmp - 1 ); + bs_write_ue( s, cpb_size_tmp - 1 ); + bs_write1( s, 0 ); + + bs_write( s, 5, sps->vui.nal_hrd_parameters.i_initial_cpb_removal_delay_length - 1 ); + bs_write( s, 5, sps->vui.nal_hrd_parameters.i_cpb_removal_delay_length - 1 ); + bs_write( s, 5, sps->vui.nal_hrd_parameters.i_dpb_output_delay_length - 1 ); + bs_write( s, 5, 0 ); /* time_offset_length */ + } + bs_write1( s, 0 ); /* vcl_hrd_parameters_present_flag */ + if( sps->vui.b_nal_hrd_parameters_present ) /* or VCL HRD */ + bs_write1( s, 0); /* low_delay_hrd_flag */ bs_write1( s, 0 ); /* pic_struct_present_flag */ bs_write1( s, sps->vui.b_bitstream_restriction ); if( sps->vui.b_bitstream_restriction ) @@ -503,6 +561,64 @@ bs_rbsp_trailing( s ); } +void x264_sei_buffering_period_write( x264_param_t *param, bs_t *s, x264_sps_t *sps ) +{ + int payload_size; + + bs_write( s, 8, 0x0 ); // payload_type = buffering_period + + payload_size = bs_size_ue( sps->i_id ); + if( sps->vui.b_nal_hrd_parameters_present ) + payload_size += sps->vui.nal_hrd_parameters.i_initial_cpb_removal_delay_length * 2; + + bs_write( s, 8, (payload_size + 7) / 8); + + bs_write_ue( s, sps->i_id ); + + if ( sps->vui.b_nal_hrd_parameters_present ) + { + /* NOTE: This should be a loop for each CPB, but we currently only support one */ + + /* FIXME: I have no idea how you're actually supposed to calculate + * initial_cpb_removal_delay */ + bs_write( s, sps->vui.nal_hrd_parameters.i_initial_cpb_removal_delay_length, + param->rc.f_vbv_buffer_init * param->rc.i_vbv_buffer_size * 90000 / param->rc.i_vbv_max_bitrate); + bs_write( s, sps->vui.nal_hrd_parameters.i_initial_cpb_removal_delay_length, 0); + } + + /* VCL HRD parameters should go here */ + + bs_align_10( s ); +} + +void x264_sei_picture_timing_write( bs_t *s, x264_sps_t *sps, int cpb_removal_delay, int dpb_output_delay ) +{ + int payload_size; + + bs_write( s, 8, 0x1 ); // payload_type = pic_timing + + payload_size = 0; + if( sps->vui.b_nal_hrd_parameters_present ) + payload_size += sps->vui.nal_hrd_parameters.i_cpb_removal_delay_length + + sps->vui.nal_hrd_parameters.i_dpb_output_delay_length; + + bs_write( s, 8, (payload_size + 7) / 8); + + if( sps->vui.b_nal_hrd_parameters_present ) + { + /* NOTE: This should be a loop for each CPB, but we currently only support one */ + + bs_write( s, sps->vui.nal_hrd_parameters.i_cpb_removal_delay_length, + cpb_removal_delay); + bs_write( s, sps->vui.nal_hrd_parameters.i_dpb_output_delay_length, + dpb_output_delay); + } + + /* VCL HRD parameters should go here */ + + bs_align_10( s ); +} + const x264_level_t x264_levels[] = { { 10, 1485, 99, 152064, 64, 175, 64, 64, 0, 0, 0, 1 }, Index: encoder/set.h =================================================================== --- encoder/set.h (revision 662) +++ encoder/set.h (working copy) @@ -29,6 +29,8 @@ void x264_pps_init( x264_pps_t *pps, int i_id, x264_param_t *param, x264_sps_t *sps ); void x264_pps_write( bs_t *s, x264_pps_t *pps ); void x264_sei_version_write( x264_t *h, bs_t *s ); +void x264_sei_buffering_period_write( x264_param_t *param, bs_t *s, x264_sps_t *sps ); +void x264_sei_picture_timing_write( bs_t *s, x264_sps_t *sps, int cpb_removal_delay, int dpb_output_delay ); void x264_validate_levels( x264_t *h ); #endif Index: x264.c =================================================================== --- x264.c (revision 662) +++ x264.c (working copy) @@ -320,6 +320,7 @@ H1( " --visualize Show MB types overlayed on the encoded video\n" ); H1( " --sps-id Set SPS and PPS id numbers [%d]\n", defaults->i_sps_id ); H1( " --aud Use access unit delimiters\n" ); + H1( " --nal-hrd Use NAL HRD parameters\n" ); H0( "\n" ); } @@ -445,6 +446,7 @@ { "visualize",no_argument, NULL, OPT_VISUALIZE }, { "sps-id", required_argument, NULL, 0 }, { "aud", no_argument, NULL, 0 }, + { "nal-hrd", no_argument, NULL, 0 }, { "nr", required_argument, NULL, 0 }, { "cqm", required_argument, NULL, 0 }, { "cqmfile", required_argument, NULL, 0 }, Index: x264.h =================================================================== --- x264.h (revision 662) +++ x264.h (working copy) @@ -271,6 +271,7 @@ int b_aud; /* generate access unit delimiters */ int b_repeat_headers; /* put SPS/PPS before each keyframe */ int i_sps_id; /* SPS and PPS id number */ + int b_nal_hrd; /* Add NAL HRD parameters to bitstream */ } x264_param_t; typedef struct {