Add ability to adjust ratecontrol parameters on the fly
authorJason Garrett-Glaser <darkshikari@gmail.com>
Fri, 29 Jan 2010 10:40:41 +0000 (02:40 -0800)
committerJason Garrett-Glaser <darkshikari@gmail.com>
Sun, 14 Feb 2010 06:43:36 +0000 (22:43 -0800)
encoder_reconfig and x264_picture_t->param can now be used to change ratecontrol parameters.
This is extraordinarily useful in certain streaming situations where the encoder needs to adapt the bitrate to network circumstances.

What can be changed:
1) CRF can be adjusted if in CRF mode.
2) VBV maxrate and bufsize can be adjusted if in VBV mode.
3) Bitrate can be adjusted if in CBR mode.
However, x264 cannot switch between modes and cannot change bitrate in ABR mode.

Also fix a bug where x264_picture_t->param reconfig method would not always be frame-exact.

Commit sponsored by SayMama video calling.

encoder/encoder.c
encoder/ratecontrol.c
encoder/ratecontrol.h
x264.h

index d873cd0..008d0f2 100644 (file)
@@ -507,6 +507,39 @@ static int x264_validate_parameters( x264_t *h )
     }
     h->param.rc.i_qp_max = x264_clip3( h->param.rc.i_qp_max, 0, 51 );
     h->param.rc.i_qp_min = x264_clip3( h->param.rc.i_qp_min, 0, h->param.rc.i_qp_max );
+    if( h->param.rc.i_vbv_buffer_size )
+    {
+        if( h->param.rc.i_rc_method == X264_RC_CQP )
+        {
+            x264_log( h, X264_LOG_WARNING, "VBV is incompatible with constant QP, ignored.\n" );
+            h->param.rc.i_vbv_max_bitrate = 0;
+            h->param.rc.i_vbv_buffer_size = 0;
+        }
+        else if( h->param.rc.i_vbv_max_bitrate == 0 )
+        {
+            if( h->param.rc.i_rc_method == X264_RC_ABR )
+            {
+                x264_log( h, X264_LOG_WARNING, "VBV maxrate unspecified, assuming CBR\n" );
+                h->param.rc.i_vbv_max_bitrate = h->param.rc.i_bitrate;
+            }
+            else
+            {
+                x264_log( h, X264_LOG_WARNING, "VBV bufsize set but maxrate unspecified, ignored\n" );
+                h->param.rc.i_vbv_buffer_size = 0;
+            }
+        }
+        else if( h->param.rc.i_vbv_max_bitrate < h->param.rc.i_bitrate &&
+                 h->param.rc.i_rc_method == X264_RC_ABR )
+        {
+            x264_log( h, X264_LOG_WARNING, "max bitrate less than average bitrate, assuming CBR\n" );
+            h->param.rc.i_vbv_max_bitrate = h->param.rc.i_bitrate;
+        }
+    }
+    else if( h->param.rc.i_vbv_max_bitrate )
+    {
+        x264_log( h, X264_LOG_WARNING, "VBV maxrate specified, but no bufsize, ignored\n" );
+        h->param.rc.i_vbv_max_bitrate = 0;
+    }
 
     int max_slices = (h->param.i_height+((16<<h->param.b_interlaced)-1))/(16<<h->param.b_interlaced);
     if( h->param.b_sliced_threads )
@@ -1071,7 +1104,7 @@ fail:
  ****************************************************************************/
 int x264_encoder_reconfig( x264_t *h, x264_param_t *param )
 {
-    h = h->thread[h->i_thread_phase];
+    h = h->thread[h->thread[0]->i_thread_phase];
     x264_set_aspect_ratio( h, param, 0 );
 #define COPY(var) h->param.var = param->var
     COPY( i_frame_reference ); // but never uses more refs than initially specified
@@ -1110,11 +1143,30 @@ int x264_encoder_reconfig( x264_t *h, x264_param_t *param )
     COPY( i_slice_max_size );
     COPY( i_slice_max_mbs );
     COPY( i_slice_count );
+    /* VBV can't be turned on if it wasn't on to begin with */
+    if( h->param.rc.i_vbv_max_bitrate > 0 && h->param.rc.i_vbv_buffer_size > 0 &&
+          param->rc.i_vbv_max_bitrate > 0 &&   param->rc.i_vbv_buffer_size > 0 )
+    {
+        COPY( rc.i_vbv_max_bitrate );
+        COPY( rc.i_vbv_buffer_size );
+        COPY( rc.i_bitrate );
+    }
+    COPY( rc.f_rf_constant );
 #undef COPY
 
     mbcmp_init( h );
 
-    return x264_validate_parameters( h );
+    int ret = x264_validate_parameters( h );
+
+    /* Supported reconfiguration options (1-pass only):
+     * vbv-maxrate
+     * vbv-bufsize
+     * crf
+     * bitrate (CBR only) */
+    if( !ret )
+        x264_ratecontrol_init_reconfigurable( h, 0 );
+
+    return ret;
 }
 
 /****************************************************************************
index 63b3be6..52196e7 100644 (file)
@@ -388,6 +388,53 @@ static char *x264_strcat_filename( char *input, char *suffix )
     return output;
 }
 
+void x264_ratecontrol_init_reconfigurable( x264_t *h, int b_init )
+{
+    x264_ratecontrol_t *rc = h->rc;
+    if( !b_init && rc->b_2pass )
+        return;
+
+    if( h->param.rc.i_vbv_max_bitrate > 0 && h->param.rc.i_vbv_buffer_size > 0 )
+    {
+        if( h->param.rc.i_vbv_buffer_size < (int)(h->param.rc.i_vbv_max_bitrate / rc->fps) )
+        {
+            h->param.rc.i_vbv_buffer_size = h->param.rc.i_vbv_max_bitrate / rc->fps;
+            x264_log( h, X264_LOG_WARNING, "VBV buffer size cannot be smaller than one frame, using %d kbit\n",
+                      h->param.rc.i_vbv_buffer_size );
+        }
+
+        /* We don't support changing the ABR bitrate right now,
+           so if the stream starts as CBR, keep it CBR. */
+        if( rc->b_vbv_min_rate )
+            h->param.rc.i_vbv_max_bitrate = h->param.rc.i_bitrate;
+        rc->buffer_rate = h->param.rc.i_vbv_max_bitrate * 1000. / rc->fps;
+        rc->buffer_size = h->param.rc.i_vbv_buffer_size * 1000.;
+        rc->single_frame_vbv = rc->buffer_rate * 1.1 > rc->buffer_size;
+        rc->cbr_decay = 1.0 - rc->buffer_rate / rc->buffer_size
+                      * 0.5 * X264_MAX(0, 1.5 - rc->buffer_rate * rc->fps / rc->bitrate);
+        if( 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 );
+            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
+                          && h->param.rc.i_rc_method == X264_RC_ABR
+                          && h->param.rc.i_vbv_max_bitrate <= h->param.rc.i_bitrate;
+        }
+    }
+    if( h->param.rc.i_rc_method == X264_RC_CRF )
+    {
+        /* Arbitrary rescaling to make CRF somewhat similar to QP.
+         * Try to compensate for MB-tree's effects as well. */
+        double base_cplx = h->mb.i_mb_count * (h->param.i_bframe ? 120 : 80);
+        double mbtree_offset = h->param.rc.b_mb_tree ? (1.0-h->param.rc.f_qcompress)*13.5 : 0;
+        rc->rate_factor_constant = pow( base_cplx, 1 - rc->qcompress )
+                                 / qp2qscale( h->param.rc.f_rf_constant + mbtree_offset );
+    }
+}
+
 int x264_ratecontrol_new( x264_t *h )
 {
     x264_ratecontrol_t *rc;
@@ -426,60 +473,10 @@ int x264_ratecontrol_new( x264_t *h )
         x264_log(h, X264_LOG_ERROR, "constant rate-factor is incompatible with 2pass.\n");
         return -1;
     }
-    if( h->param.rc.i_vbv_buffer_size )
-    {
-        if( h->param.rc.i_rc_method == X264_RC_CQP )
-        {
-            x264_log(h, X264_LOG_WARNING, "VBV is incompatible with constant QP, ignored.\n");
-            h->param.rc.i_vbv_max_bitrate = 0;
-            h->param.rc.i_vbv_buffer_size = 0;
-        }
-        else if( h->param.rc.i_vbv_max_bitrate == 0 )
-        {
-            if( h->param.rc.i_rc_method == X264_RC_ABR )
-            {
-                x264_log( h, X264_LOG_INFO, "VBV maxrate unspecified, assuming CBR\n" );
-                h->param.rc.i_vbv_max_bitrate = h->param.rc.i_bitrate;
-            }
-            else
-            {
-                x264_log( h, X264_LOG_INFO, "VBV bufsize set but maxrate unspecified, ignored\n" );
-                h->param.rc.i_vbv_buffer_size = 0;
-            }
-        }
-    }
-    if( h->param.rc.i_vbv_max_bitrate < h->param.rc.i_bitrate &&
-        h->param.rc.i_vbv_max_bitrate > 0)
-        x264_log(h, X264_LOG_WARNING, "max bitrate less than average bitrate, ignored.\n");
-    else if( h->param.rc.i_vbv_max_bitrate > 0 &&
-             h->param.rc.i_vbv_buffer_size > 0 )
-    {
-        if( h->param.rc.i_vbv_buffer_size < (int)(h->param.rc.i_vbv_max_bitrate / rc->fps) )
-        {
-            h->param.rc.i_vbv_buffer_size = h->param.rc.i_vbv_max_bitrate / rc->fps;
-            x264_log( h, X264_LOG_WARNING, "VBV buffer size cannot be smaller than one frame, using %d kbit\n",
-                      h->param.rc.i_vbv_buffer_size );
-        }
-        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 );
-        rc->buffer_rate = h->param.rc.i_vbv_max_bitrate * 1000. / rc->fps;
-        rc->buffer_size = h->param.rc.i_vbv_buffer_size * 1000.;
-        rc->single_frame_vbv = rc->buffer_rate * 1.1 > rc->buffer_size;
-        h->param.rc.f_vbv_buffer_init = X264_MAX( h->param.rc.f_vbv_buffer_init, rc->buffer_rate / rc->buffer_size );
-        rc->buffer_fill_final = rc->buffer_size * h->param.rc.f_vbv_buffer_init;
-        rc->cbr_decay = 1.0 - rc->buffer_rate / rc->buffer_size
-                      * 0.5 * X264_MAX(0, 1.5 - rc->buffer_rate * rc->fps / rc->bitrate);
-        rc->b_vbv = 1;
-        rc->b_vbv_min_rate = !rc->b_2pass
-                          && h->param.rc.i_rc_method == X264_RC_ABR
-                          && h->param.rc.i_vbv_max_bitrate <= h->param.rc.i_bitrate;
-    }
-    else if( h->param.rc.i_vbv_max_bitrate )
-    {
-        x264_log(h, X264_LOG_WARNING, "VBV maxrate specified, but no bufsize.\n");
-        h->param.rc.i_vbv_max_bitrate = 0;
-    }
-    if(rc->rate_tolerance < 0.01)
+
+    x264_ratecontrol_init_reconfigurable( h, 1 );
+
+    if( rc->rate_tolerance < 0.01 )
     {
         x264_log(h, X264_LOG_WARNING, "bitrate tolerance too small, using .01\n");
         rc->rate_tolerance = 0.01;
@@ -499,16 +496,6 @@ int x264_ratecontrol_new( x264_t *h )
         rc->last_non_b_pict_type = SLICE_TYPE_I;
     }
 
-    if( h->param.rc.i_rc_method == X264_RC_CRF )
-    {
-        /* Arbitrary rescaling to make CRF somewhat similar to QP.
-         * Try to compensate for MB-tree's effects as well. */
-        double base_cplx = h->mb.i_mb_count * (h->param.i_bframe ? 120 : 80);
-        double mbtree_offset = h->param.rc.b_mb_tree ? (1.0-h->param.rc.f_qcompress)*13.5 : 0;
-        rc->rate_factor_constant = pow( base_cplx, 1 - rc->qcompress )
-                                 / qp2qscale( h->param.rc.f_rf_constant + mbtree_offset );
-    }
-
     rc->ip_offset = 6.0 * log(h->param.rc.f_ip_factor) / log(2.0);
     rc->pb_offset = 6.0 * log(h->param.rc.f_pb_factor) / log(2.0);
     rc->qp_constant[SLICE_TYPE_P] = h->param.rc.i_qp_constant;
@@ -1577,15 +1564,15 @@ static void update_vbv( x264_t *h, int bits )
     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 );
     rct->buffer_fill_final = X264_MAX( rct->buffer_fill_final, 0 );
-    rct->buffer_fill_final += rct->buffer_rate;
-    rct->buffer_fill_final = X264_MIN( rct->buffer_fill_final, rct->buffer_size );
+    rct->buffer_fill_final += rcc->buffer_rate;
+    rct->buffer_fill_final = X264_MIN( rct->buffer_fill_final, rcc->buffer_size );
 }
 
 // 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 - overhead;
+    rcc->buffer_fill = h->thread[0]->rc->buffer_fill_final;
     if( h->i_thread_frames > 1 )
     {
         int j = h->rc - h->thread[0]->rc;
@@ -1603,6 +1590,8 @@ static void update_vbv_plan( x264_t *h, int overhead )
             rcc->buffer_fill = X264_MIN( rcc->buffer_fill, rcc->buffer_size );
         }
     }
+    rcc->buffer_fill = X264_MIN( rcc->buffer_fill, rcc->buffer_size );
+    rcc->buffer_fill -= overhead;
 }
 
 // apply VBV constraints and clip qscale to between lmin and lmax
@@ -2027,8 +2016,7 @@ void x264_thread_sync_ratecontrol( x264_t *cur, x264_t *prev, x264_t *next )
 #define COPY(var) memcpy(&cur->rc->var, &prev->rc->var, sizeof(cur->rc->var))
         /* these vars are updated in x264_ratecontrol_start()
          * so copy them from the context that most recently started (prev)
-         * to the context that's about to start (cur).
-         */
+         * to the context that's about to start (cur). */
         COPY(accum_p_qp);
         COPY(accum_p_norm);
         COPY(last_satd);
@@ -2040,6 +2028,14 @@ void x264_thread_sync_ratecontrol( x264_t *cur, x264_t *prev, x264_t *next )
         COPY(bframes);
         COPY(prev_zone);
         COPY(qpbuf_pos);
+        /* these vars can be updated by x264_ratecontrol_init_reconfigurable */
+        COPY(buffer_rate);
+        COPY(buffer_size);
+        COPY(single_frame_vbv);
+        COPY(cbr_decay);
+        COPY(b_vbv_min_rate);
+        COPY(rate_factor_constant);
+        COPY(bitrate);
 #undef COPY
     }
     if( cur != next )
@@ -2047,8 +2043,7 @@ void x264_thread_sync_ratecontrol( x264_t *cur, x264_t *prev, x264_t *next )
 #define COPY(var) next->rc->var = cur->rc->var
         /* these vars are updated in x264_ratecontrol_end()
          * so copy them from the context that most recently ended (cur)
-         * to the context that's about to end (next)
-         */
+         * to the context that's about to end (next) */
         COPY(cplxr_sum);
         COPY(expected_bits_sum);
         COPY(wanted_bits_window);
index 5a8d088..2767866 100644 (file)
@@ -27,6 +27,8 @@
 int  x264_ratecontrol_new   ( x264_t * );
 void x264_ratecontrol_delete( x264_t * );
 
+void x264_ratecontrol_init_reconfigurable( x264_t *h, int b_init );
+
 void x264_adaptive_quant_frame( x264_t *h, x264_frame_t *frame );
 void x264_adaptive_quant( x264_t * );
 int  x264_macroblock_tree_read( x264_t *h, x264_frame_t *frame );
diff --git a/x264.h b/x264.h
index 2550864..e7d19b7 100644 (file)
--- a/x264.h
+++ b/x264.h
@@ -35,7 +35,7 @@
 
 #include <stdarg.h>
 
-#define X264_BUILD 84
+#define X264_BUILD 85
 
 /* x264_t:
  *      opaque handler for encoder */
@@ -480,11 +480,12 @@ typedef struct
 x264_t *x264_encoder_open( x264_param_t * );
 
 /* x264_encoder_reconfig:
- *      analysis-related parameters from x264_param_t are copied.
+ *      various parameters from x264_param_t are copied.
  *      this takes effect immediately, on whichever frame is encoded next;
  *      due to delay, this may not be the next frame passed to encoder_encode.
  *      if the change should apply to some particular frame, use x264_picture_t->param instead.
- *      returns 0 on success, negative on parameter validation error. */
+ *      returns 0 on success, negative on parameter validation error.
+ *      not all parameters can be changed; see the actual function for a detailed breakdown. */
 int     x264_encoder_reconfig( x264_t *, x264_param_t * );
 /* x264_encoder_parameters:
  *      copies the current internal set of parameters to the pointer provided