As usual, the spec is so insanely obfuscated that it's impossible to get things right the first time.
/* hrd */
int initial_cpb_removal_delay;
+ int initial_cpb_removal_delay_offset;
int64_t first_pts;
/* Current MB DCT coeffs */
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 )
{
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);
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 */
/* ---------------------- Update encoder state ------------------------- */
/* update rc */
- x264_emms();
int filler = 0;
if( x264_ratecontrol_end( h, frame_size * 8, &filler ) < 0 )
return -1;
/* 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;
};
{
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
// 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
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;
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
}
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
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
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 );
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 );
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
}
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
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 )