]> git.sesse.net Git - x264/commitdiff
Improve HRD accuracy
authorFiona Glaser <fiona@x264.com>
Sat, 19 Jun 2010 10:27:33 +0000 (03:27 -0700)
committerFiona Glaser <fiona@x264.com>
Fri, 25 Jun 2010 06:57:10 +0000 (23:57 -0700)
In a staggering display of brain damage, the spec requires all HRD math to be done in infinite precision despite the output being of quite limited precision.
Accordingly, convert buffer management to work in units of timescale.
These accumulating rounding errors probably didn't cause any real problems, but might in theory cause issues in very picky muxers on extremely long-running streams.

common/common.c
common/common.h
encoder/encoder.c
encoder/ratecontrol.c
encoder/ratecontrol.h

index 4fa5e4bf307ea4dd5d9e47070f6c3ccbbe3a8f8a..2a9c76e4de00155be3663d873604ef48d664f891 100644 (file)
@@ -1080,24 +1080,28 @@ void x264_free( void *p )
 /****************************************************************************
  * x264_reduce_fraction:
  ****************************************************************************/
-void x264_reduce_fraction( uint32_t *n, uint32_t *d )
-{
-    uint32_t a = *n;
-    uint32_t b = *d;
-    uint32_t c;
-    if( !a || !b )
-        return;
-    c = a % b;
-    while(c)
-    {
-        a = b;
-        b = c;
-        c = a % b;
-    }
-    *n /= b;
-    *d /= b;
+#define REDUCE_FRACTION( name, type )\
+void name( type *n, type *d )\
+{                   \
+    type a = *n;    \
+    type b = *d;    \
+    type c;         \
+    if( !a || !b )  \
+        return;     \
+    c = a % b;      \
+    while( c )      \
+    {               \
+        a = b;      \
+        b = c;      \
+        c = a % b;  \
+    }               \
+    *n /= b;        \
+    *d /= b;        \
 }
 
+REDUCE_FRACTION( x264_reduce_fraction  , uint32_t )
+REDUCE_FRACTION( x264_reduce_fraction64, uint64_t )
+
 /****************************************************************************
  * x264_slurp_file:
  ****************************************************************************/
index abb5db232cd3823245afb62718e46a678d3ca2d3..3d522eb0d90e28b64b46d515b090b382b94603b5 100644 (file)
@@ -183,6 +183,7 @@ char *x264_param2string( x264_param_t *p, int b_res );
 void x264_log( x264_t *h, int i_level, const char *psz_fmt, ... );
 
 void x264_reduce_fraction( uint32_t *n, uint32_t *d );
+void x264_reduce_fraction64( uint64_t *n, uint64_t *d );
 void x264_init_vlc_tables();
 
 static ALWAYS_INLINE pixel x264_clip_pixel( int x )
index ebc86c75381ffe22994e23da8a26400c7b9edf62..0228708d6d55c66dad0562ecf5337e0efa0d8bbe 100644 (file)
@@ -2569,8 +2569,7 @@ static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current,
     /* 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_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 ) )
index bf0d9d1cb67b34abc26a4809d5a4ec0acfcf19ec..d8ec1203c1a136d4005eb515126fe2c4614aef44 100644 (file)
@@ -91,7 +91,7 @@ struct x264_ratecontrol_t
 
     /* VBV stuff */
     double buffer_size;
-    double buffer_fill_final;   /* real buffer as of the last finished frame */
+    int64_t buffer_fill_final;
     double buffer_fill;         /* planned buffer, if all in-progress frames hit their bit budget */
     double buffer_rate;         /* # of bits added to buffer_fill after each frame */
     double vbv_max_rate;        /* # of bits added to buffer_fill per second */
@@ -157,6 +157,7 @@ struct x264_ratecontrol_t
     int initial_cpb_removal_delay_offset;
     double nrt_first_access_unit; /* nominal removal time */
     double previous_cpb_final_arrival_time;
+    uint64_t hrd_multiply_denom;
 };
 
 
@@ -463,6 +464,8 @@ void x264_ratecontrol_init_reconfigurable( x264_t *h, int b_init )
         int vbv_max_bitrate = h->param.rc.i_vbv_max_bitrate * 1000;
 
         /* Init HRD */
+        h->sps->vui.hrd.i_bit_rate_unscaled = vbv_max_bitrate;
+        h->sps->vui.hrd.i_cpb_size_unscaled = vbv_buffer_size;
         if( h->param.i_nal_hrd && b_init )
         {
             h->sps->vui.hrd.i_cpb_cnt = 1;
@@ -499,8 +502,8 @@ void x264_ratecontrol_init_reconfigurable( x264_t *h, int b_init )
 
             #undef MAX_DURATION
 
-            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 );
+            vbv_buffer_size = h->sps->vui.hrd.i_cpb_size_unscaled;
+            vbv_max_bitrate = h->sps->vui.hrd.i_bit_rate_unscaled;
         }
         else if( h->param.i_nal_hrd && !b_init )
         {
@@ -528,7 +531,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_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->buffer_fill_final = rc->buffer_size * h->param.rc.f_vbv_buffer_init * h->sps->vui.i_time_scale;
             rc->b_vbv = 1;
             rc->b_vbv_min_rate = !rc->b_2pass
                           && h->param.rc.i_rc_method == X264_RC_ABR
@@ -577,6 +580,23 @@ int x264_ratecontrol_new( x264_t *h )
 
     x264_ratecontrol_init_reconfigurable( h, 1 );
 
+    if( h->param.i_nal_hrd )
+    {
+        uint64_t denom = (uint64_t)h->sps->vui.hrd.i_bit_rate_unscaled * h->sps->vui.i_time_scale;
+        uint64_t num = 180000;
+        x264_reduce_fraction64( &num, &denom );
+        rc->hrd_multiply_denom = 180000 / num;
+
+        double bits_required = log2( 180000 / rc->hrd_multiply_denom )
+                             + log2( h->sps->vui.i_time_scale )
+                             + log2( h->sps->vui.hrd.i_cpb_size_unscaled );
+        if( bits_required >= 63 )
+        {
+            x264_log( h, X264_LOG_ERROR, "HRD with very large timescale and bufsize not supported\n" );
+            return -1;
+        }
+    }
+
     if( rc->rate_tolerance < 0.01 )
     {
         x264_log(h, X264_LOG_WARNING, "bitrate tolerance too small, using .01\n");
@@ -1722,9 +1742,10 @@ static void update_predictor( predictor_t *p, double q, double var, double bits
 static int update_vbv( x264_t *h, int bits )
 {
     int filler = 0;
-
+    int bitrate = h->sps->vui.hrd.i_bit_rate_unscaled;
     x264_ratecontrol_t *rcc = h->rc;
     x264_ratecontrol_t *rct = h->thread[0]->rc;
+    uint64_t buffer_size = (uint64_t)h->sps->vui.hrd.i_cpb_size_unscaled * h->sps->vui.i_time_scale;
 
     if( rcc->last_satd >= h->mb.i_mb_count )
         update_predictor( &rct->pred[h->sh.i_type], qp2qscale( rcc->qpa_rc ), rcc->last_satd, bits );
@@ -1732,48 +1753,48 @@ static int update_vbv( x264_t *h, int bits )
     if( !rcc->b_vbv )
         return filler;
 
-    rct->buffer_fill_final -= bits;
+    rct->buffer_fill_final -= (uint64_t)bits * h->sps->vui.i_time_scale;
 
     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 );
+        x264_log( h, X264_LOG_WARNING, "VBV underflow (frame %d, %.0f bits)\n", h->i_frame, (double)rct->buffer_fill_final / h->sps->vui.i_time_scale );
     rct->buffer_fill_final = X264_MAX( rct->buffer_fill_final, 0 );
-    rct->buffer_fill_final += rcc->buffer_rate;
+    rct->buffer_fill_final += (uint64_t)bitrate * h->sps->vui.i_num_units_in_tick * h->fenc->i_cpb_duration;
 
-    if( h->sps->vui.hrd.b_cbr_hrd && rct->buffer_fill_final > rcc->buffer_size )
+    if( h->sps->vui.hrd.b_cbr_hrd && rct->buffer_fill_final > buffer_size )
     {
-        filler = ceil( (rct->buffer_fill_final - rcc->buffer_size) / 8 );
-        rct->buffer_fill_final -= X264_MAX( (FILLER_OVERHEAD - h->param.b_annexb), filler ) * 8;
+        filler = ceil( (rct->buffer_fill_final - buffer_size) / (8. * h->sps->vui.i_time_scale) );
+        bits = X264_MAX( (FILLER_OVERHEAD - h->param.b_annexb), filler ) * 8;
+        rct->buffer_fill_final -= (uint64_t)bits * h->sps->vui.i_time_scale;
     }
     else
-        rct->buffer_fill_final = X264_MIN( rct->buffer_fill_final, rcc->buffer_size );
+        rct->buffer_fill_final = X264_MIN( rct->buffer_fill_final, buffer_size );
 
     return filler;
 }
 
-int x264_hrd_fullness( x264_t *h )
+void x264_hrd_fullness( x264_t *h )
 {
     x264_ratecontrol_t *rct = h->thread[0]->rc;
-    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;
+    uint64_t denom = (uint64_t)h->sps->vui.hrd.i_bit_rate_unscaled * h->sps->vui.i_time_scale / rct->hrd_multiply_denom;
+    uint64_t cpb_state = rct->buffer_fill_final;
+    uint64_t cpb_size = (uint64_t)h->sps->vui.hrd.i_cpb_size_unscaled * h->sps->vui.i_time_scale;
+    uint64_t multiply_factor = 180000 / rct->hrd_multiply_denom;
 
-    if( cpb_bits < 0 || cpb_bits > cpb_size )
+    if( cpb_state < 0 || cpb_state > cpb_size )
     {
          x264_log( h, X264_LOG_WARNING, "CPB %s: %.0lf bits in a %.0lf-bit buffer\n",
-                   cpb_bits < 0 ? "underflow" : "overflow", cpb_bits, cpb_size );
+                   cpb_state < 0 ? "underflow" : "overflow", (float)cpb_state/denom, (float)cpb_size/denom );
     }
 
-    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
+    h->initial_cpb_removal_delay = (multiply_factor * cpb_state + denom) / (2*denom);
+    h->initial_cpb_removal_delay_offset = (multiply_factor * cpb_size + denom) / (2*denom) - h->initial_cpb_removal_delay;
 }
 
 // provisionally update VBV according to the planned size of all frames currently in progress
 static void update_vbv_plan( x264_t *h, int overhead )
 {
     x264_ratecontrol_t *rcc = h->rc;
-    rcc->buffer_fill = h->thread[0]->rc->buffer_fill_final;
+    rcc->buffer_fill = h->thread[0]->rc->buffer_fill_final / h->sps->vui.i_time_scale;
     if( h->i_thread_frames > 1 )
     {
         int j = h->rc - h->thread[0]->rc;
index dd139eb652130b97c9f9a4c296f9ac2ae499070c..f39c0701c64a4fa7c9ca093cc7a91604ef9b94db 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 );
+void x264_hrd_fullness( x264_t *h );
 #endif