]> git.sesse.net Git - x264/commitdiff
Fix HRD compliance
authorKieran Kunhya <kieran@kunhya.com>
Sat, 3 Apr 2010 21:59:59 +0000 (14:59 -0700)
committerFiona Glaser <fiona@x264.com>
Tue, 6 Apr 2010 19:53:08 +0000 (12:53 -0700)
As usual, the spec is so insanely obfuscated that it's impossible to get things right the first time.

common/common.h
encoder/encoder.c
encoder/ratecontrol.c
encoder/ratecontrol.h
encoder/set.c
encoder/set.h
encoder/slicetype.c

index 8d202dccc0c495e0b7db0a956e43993506e90dc0..6abd42b6e2f73be8967a7792a4a7ea6847dfcf64 100644 (file)
@@ -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 */
index d3028021cfe12f1229435b4358377240e9968d03..e87f228e9182d3e1455168c52aa6cfb03955b72d 100644 (file)
@@ -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;
index 5074980f746f90d7e242568651d90f1178cadaf9..6cc59c34713ad2591d17ab7820a3e56b859cb0ce 100644 (file)
@@ -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
index f070a9ccb1cee90a9c9515f8e31bca1481ac2ecc..181ab23870f0a5bcbd629b68b8a5bb0b78425444 100644 (file)
@@ -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
 
index b759dfb7c374e9ae7c2a7f50ecc73c234362096f..660db8deed58acd60c72505518438d5be0897544 100644 (file)
@@ -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
     }
 
index b3ed2348dcfe4ca7abf258aec66d4c49c5ed904d..cda37c464e02c169f237f4d27bd598053a53fc5f 100644 (file)
@@ -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
index 14db2349ff9d8e3b5f3116d65b2b1b697d324518..dac02298c0fc29d4d1db1426852bfdb58e4ab3a1 100644 (file)
@@ -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 )