From e9726b63b92d9b704a9e8cbf9665ec0621ada5bb Mon Sep 17 00:00:00 2001 From: Kieran Kunhya Date: Sat, 3 Apr 2010 14:59:59 -0700 Subject: [PATCH] Fix HRD compliance As usual, the spec is so insanely obfuscated that it's impossible to get things right the first time. --- common/common.h | 1 + encoder/encoder.c | 36 +++++++++++++++++++++++------------- encoder/ratecontrol.c | 20 +++++++++++++------- encoder/ratecontrol.h | 2 +- encoder/set.c | 16 ++++++++-------- encoder/set.h | 4 ++-- encoder/slicetype.c | 6 +++--- 7 files changed, 51 insertions(+), 34 deletions(-) diff --git a/common/common.h b/common/common.h index 8d202dcc..6abd42b6 100644 --- a/common/common.h +++ b/common/common.h @@ -473,6 +473,7 @@ struct x264_t /* hrd */ int initial_cpb_removal_delay; + int initial_cpb_removal_delay_offset; int64_t first_pts; /* Current MB DCT coeffs */ diff --git a/encoder/encoder.c b/encoder/encoder.c index d3028021..e87f228e 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -2382,17 +2382,7 @@ 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, overhead*8 ); - - 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 - (h->param.b_annexb && h->out.i_nal-1); - } + /* buffering period sei is written in x264_encoder_frame_end */ if( h->param.b_repeat_headers && h->fenc->i_frame == 0 ) { @@ -2419,7 +2409,7 @@ int x264_encoder_encode( x264_t *h, 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 ); + x264_sei_pic_timing_write( h, &h->out.bs ); if( x264_nal_end( h ) ) return -1; overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD - (h->param.b_annexb && h->out.i_nal-1); @@ -2497,6 +2487,27 @@ static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current, x264_frame_push_unused( thread_current, h->fenc ); + x264_emms(); + /* generate sei buffering period and insert it into place */ + if( h->fenc->b_keyframe && h->sps->vui.b_nal_hrd_parameters_present ) + { + h->initial_cpb_removal_delay = x264_hrd_fullness( h ); + + x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE ); + x264_sei_buffering_period_write( h, &h->out.bs ); + if( x264_nal_end( h ) ) + return -1; + /* buffering period sei must follow AUD, SPS and PPS and precede all other SEIs */ + int idx = 0; + while( h->out.nal[idx].i_type == NAL_AUD || + h->out.nal[idx].i_type == NAL_SPS || + h->out.nal[idx].i_type == NAL_PPS ) + idx++; + x264_nal_t nal_tmp = h->out.nal[h->out.i_nal-1]; + memmove( &h->out.nal[idx+1], &h->out.nal[idx], (h->out.i_nal-idx-1)*sizeof(x264_nal_t) ); + h->out.nal[idx] = nal_tmp; + } + int frame_size = x264_encoder_encapsulate_nals( h, 0 ); /* Set output picture properties */ @@ -2544,7 +2555,6 @@ static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current, /* ---------------------- Update encoder state ------------------------- */ /* update rc */ - x264_emms(); int filler = 0; if( x264_ratecontrol_end( h, frame_size * 8, &filler ) < 0 ) return -1; diff --git a/encoder/ratecontrol.c b/encoder/ratecontrol.c index 5074980f..6cc59c34 100644 --- a/encoder/ratecontrol.c +++ b/encoder/ratecontrol.c @@ -156,6 +156,7 @@ struct x264_ratecontrol_t /* hrd stuff */ int initial_cpb_removal_delay; + int initial_cpb_removal_delay_offset; double nrt_first_access_unit; /* nominal removal time */ double previous_cpb_final_arrival_time; }; @@ -497,7 +498,7 @@ void x264_ratecontrol_init_reconfigurable( x264_t *h, int b_init ) { if( h->param.rc.f_vbv_buffer_init > 1. ) h->param.rc.f_vbv_buffer_init = x264_clip3f( h->param.rc.f_vbv_buffer_init / h->param.rc.i_vbv_buffer_size, 0, 1 ); - h->param.rc.f_vbv_buffer_init = X264_MAX( h->param.rc.f_vbv_buffer_init, rc->buffer_rate / rc->buffer_size ); + h->param.rc.f_vbv_buffer_init = x264_clip3f( X264_MAX( h->param.rc.f_vbv_buffer_init, rc->buffer_rate / rc->buffer_size ), 0, 1); rc->buffer_fill_final = rc->buffer_size * h->param.rc.f_vbv_buffer_init; rc->b_vbv = 1; rc->b_vbv_min_rate = !rc->b_2pass @@ -1514,26 +1515,28 @@ int x264_ratecontrol_end( x264_t *h, int bits, int *filler ) // access unit initialises the HRD h->fenc->hrd_timing.cpb_initial_arrival_time = 0; rc->initial_cpb_removal_delay = h->initial_cpb_removal_delay; + rc->initial_cpb_removal_delay_offset = h->initial_cpb_removal_delay_offset; 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; + + double cpb_earliest_arrival_time = h->fenc->hrd_timing.cpb_removal_time - (double)rc->initial_cpb_removal_delay / 90000; 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; + rc->initial_cpb_removal_delay_offset = h->initial_cpb_removal_delay_offset; } + else + cpb_earliest_arrival_time -= (double)rc->initial_cpb_removal_delay_offset / 90000; 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 ); - } } int filler_bits = *filler ? X264_MAX( (FILLER_OVERHEAD - h->param.b_annexb), *filler )*8 : 0; // Equation C-6 @@ -1709,10 +1712,10 @@ static int update_vbv( x264_t *h, int bits ) return filler; } -int x264_hrd_fullness( x264_t *h, int overhead ) +int x264_hrd_fullness( x264_t *h ) { x264_ratecontrol_t *rct = h->thread[0]->rc; - double cpb_bits = rct->buffer_fill_final - overhead; + double cpb_bits = rct->buffer_fill_final; 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; @@ -1723,6 +1726,8 @@ int x264_hrd_fullness( x264_t *h, int overhead ) cpb_bits < 0 ? "underflow" : "overflow", cpb_bits, cpb_size ); } + h->initial_cpb_removal_delay_offset = 90000.0*(cpb_size - cpb_bits)/bps; + return x264_clip3f( cpb_fullness + 0.5, 0, 90000.0*cpb_size/bps ); // just lie if we are in a weird state } @@ -2264,6 +2269,7 @@ void x264_thread_sync_ratecontrol( x264_t *cur, x264_t *prev, x264_t *next ) COPY(wanted_bits_window); COPY(bframe_bits); COPY(initial_cpb_removal_delay); + COPY(initial_cpb_removal_delay_offset); COPY(nrt_first_access_unit); COPY(previous_cpb_final_arrival_time); #undef COPY diff --git a/encoder/ratecontrol.h b/encoder/ratecontrol.h index f070a9cc..181ab238 100644 --- a/encoder/ratecontrol.h +++ b/encoder/ratecontrol.h @@ -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 ); +int x264_hrd_fullness( x264_t *h ); #endif diff --git a/encoder/set.c b/encoder/set.c index b759dfb7..660db8de 100644 --- a/encoder/set.c +++ b/encoder/set.c @@ -578,7 +578,7 @@ fail: return -1; } -void x264_sei_buffering_period_write( x264_t *h, bs_t *s, int initial_cpb_removal_delay ) +void x264_sei_buffering_period_write( x264_t *h, bs_t *s ) { x264_sps_t *sps = h->sps; bs_realign( s ); @@ -588,15 +588,15 @@ void x264_sei_buffering_period_write( x264_t *h, bs_t *s, int initial_cpb_remova 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 */ + bs_write( s, sps->vui.hrd.i_initial_cpb_removal_delay_length, h->initial_cpb_removal_delay ); + bs_write( s, sps->vui.hrd.i_initial_cpb_removal_delay_length, h->initial_cpb_removal_delay_offset ); } x264_sei_write( s, p_start ); 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 ) +void x264_sei_pic_timing_write( x264_t *h, bs_t *s ) { x264_sps_t *sps = h->sps; bs_realign( s ); @@ -604,17 +604,17 @@ void x264_sei_pic_timing_write( x264_t *h, bs_t *s, int cpb_removal_delay, int d 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 ); + bs_write( s, sps->vui.hrd.i_cpb_removal_delay_length, h->fenc->i_cpb_delay ); + bs_write( s, sps->vui.hrd.i_dpb_output_delay_length, h->fenc->i_dpb_output_delay ); } if( sps->vui.b_pic_struct_present ) { - bs_write( s, 4, pic_struct-1 ); // We use index 0 for "Auto" + bs_write( s, 4, h->fenc->i_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( int i = 0; i < num_clock_ts[pic_struct]; i++ ) + for( int i = 0; i < num_clock_ts[h->fenc->i_pic_struct]; i++ ) bs_write1( s, 0 ); // clock_timestamp_flag } diff --git a/encoder/set.h b/encoder/set.h index b3ed2348..cda37c46 100644 --- a/encoder/set.h +++ b/encoder/set.h @@ -31,8 +31,8 @@ void x264_pps_write( bs_t *s, x264_pps_t *pps ); void x264_sei_recovery_point_write( x264_t *h, bs_t *s, int recovery_frame_cnt ); int x264_sei_version_write( x264_t *h, bs_t *s ); int x264_validate_levels( x264_t *h, int verbose ); -void x264_sei_buffering_period_write( x264_t *h, bs_t *s, int initial_cpb_removal_delay ); -void x264_sei_pic_timing_write( x264_t *h, bs_t *s, int cpb_removal_delay, int dpb_output_delay, int pic_struct ); +void x264_sei_buffering_period_write( x264_t *h, bs_t *s ); +void x264_sei_pic_timing_write( x264_t *h, bs_t *s ); void x264_filler_write( x264_t *h, bs_t *s, int filler ); #endif diff --git a/encoder/slicetype.c b/encoder/slicetype.c index 14db2349..dac02298 100644 --- a/encoder/slicetype.c +++ b/encoder/slicetype.c @@ -847,10 +847,10 @@ static void x264_vbv_lookahead( x264_t *h, x264_mb_analysis_t *a, x264_frame_t * cur_nonb++; int next_nonb = keyframe ? last_nonb : cur_nonb; - if( frames[0]->i_coded_fields_lookahead >= 0 ) + if( frames[cur_nonb]->i_coded_fields_lookahead >= 0 ) { - h->i_coded_fields_lookahead = frames[0]->i_coded_fields_lookahead; - h->i_cpb_delay_lookahead = frames[0]->i_cpb_delay_lookahead; + h->i_coded_fields_lookahead = frames[cur_nonb]->i_coded_fields_lookahead; + h->i_cpb_delay_lookahead = frames[cur_nonb]->i_cpb_delay_lookahead; } while( cur_nonb < num_frames ) -- 2.40.0