[x264-devel] Re: [PATCH] 2-pass ratecontrol

Loren Merritt lorenm at u.washington.edu
Mon Aug 23 05:11:09 CEST 2004


On Sun, 22 Aug 2004, Loren Merritt wrote:

> Mostly borrowed from libavcodec.
>
> There is not much theoretical basis behind my choice of defaults for
> rc_eq, qcompress, qblur, and ip_factor.

Forgot to svn add eval.c

--Loren Merritt
-------------- next part --------------
Index: encoder/encoder.c
===================================================================
--- encoder/encoder.c	(revision 38)
+++ encoder/encoder.c	(working copy)
@@ -440,7 +440,8 @@
     x264_csp_init( h->param.cpu, h->param.i_csp, &h->csp );
 
     /* rate control */
-    x264_ratecontrol_new( h );
+    if( x264_ratecontrol_new( h ) < 0 )
+        return NULL;
 
     h->i_last_intra_size = 0;
     h->i_last_inter_size = 0;
@@ -709,14 +710,13 @@
     }
 }
 
-static inline void x264_slice_write( x264_t *h, int i_nal_type, int i_nal_ref_idc, int i_mb_count[18] )
+static inline void x264_slice_write( x264_t *h, int i_nal_type, int i_nal_ref_idc )
 {
     int i_skip;
     int mb_xy;
-    int i;
 
     /* Init stats */
-    for( i = 0; i < 17; i++ ) i_mb_count[i] = 0;
+    memset(&h->frame_stat, 0, sizeof(h->frame_stat));
 
     /* Slice */
     x264_nal_start( h, i_nal_type, i_nal_ref_idc );
@@ -725,7 +725,7 @@
     x264_slice_header_write( &h->out.bs, &h->sh, i_nal_ref_idc );
     if( h->param.b_cabac )
     {
-        /* alignement needed */
+        /* alignment needed */
         bs_align_1( &h->out.bs );
 
         /* init cabac */
@@ -806,7 +806,7 @@
         /* save cache */
         x264_macroblock_cache_save( h );
 
-        i_mb_count[h->mb.i_type]++;
+        h->frame_stat.i_mb_count[h->mb.i_type]++;
 
         x264_ratecontrol_mb(h, bs_pos(&h->out.bs) - mb_spos);
     }
@@ -841,6 +841,11 @@
     }
 
     x264_nal_end( h );
+
+    h->frame_stat.i_misc_bits = bs_pos(&h->out.bs)
+                              - h->frame_stat.i_itex_bits
+                              - h->frame_stat.i_ptex_bits
+                              - h->frame_stat.i_mv_bits;
 }
 
 /****************************************************************************
@@ -869,7 +874,7 @@
 
     int   i_global_qp;
 
-    int i_mb_count[18];
+    int *i_mb_count = h->frame_stat.i_mb_count;
 
     /* no data out */
     *pi_nal = 0;
@@ -886,8 +891,23 @@
 
         x264_frame_copy_picture( h, fenc, pic );
 
+        fenc->i_presentation_num = h->i_input_frame;
+        h->i_input_frame ++;
+
         /* 2: get its type */
-        if( ( h->frames.i_last_i + 1 >= h->param.i_iframe && h->frames.i_last_idr + 1 >= h->param.i_idrframe ) ||
+        if( h->param.i_pass & 2 )
+        {
+            /* XXX: trusts that the first pass used compatible B and IDR frequencies */
+            fenc->i_type = x264_ratecontrol_slice_type( h, fenc->i_presentation_num );
+            if( fenc->i_type == X264_TYPE_I && h->frames.next[0] == NULL
+                && h->frames.i_last_idr + 1 >= h->param.i_idrframe )
+            {
+                fenc->i_type = X264_TYPE_IDR;
+                h->i_poc       = 0;
+                h->i_frame_num = 0;
+            }
+        }
+        else if( ( h->frames.i_last_i + 1 >= h->param.i_iframe && h->frames.i_last_idr + 1 >= h->param.i_idrframe ) ||
             pic->i_type == X264_TYPE_IDR )
         {
             /* IDR */
@@ -1088,7 +1108,7 @@
     }
 
     /* Write the slice */
-    x264_slice_write( h, i_nal_type, i_nal_ref_idc, i_mb_count );
+    x264_slice_write( h, i_nal_type, i_nal_ref_idc );
 
     /* XXX: this scene cut won't work with B frame (it may never create IDR -> bad) */
     if( i_slice_type != SLICE_TYPE_I)
@@ -1190,6 +1210,8 @@
 
     TIMER_STOP( i_mtime_encode_frame );
 
+    x264_ratecontrol_write_stats( h, i_slice_type, i_global_qp );
+
     /* ---------------------- Compute/Print statistics --------------------- */
     /* Slice stat */
     h->stat.i_slice_count[i_slice_type]++;
Index: encoder/ratecontrol.h
===================================================================
--- encoder/ratecontrol.h	(revision 38)
+++ encoder/ratecontrol.h	(working copy)
@@ -27,10 +27,17 @@
 int  x264_ratecontrol_new   ( x264_t * );
 void x264_ratecontrol_delete( x264_t * );
 
+int  x264_ratecontrol_slice_type( x264_t *, int frame_num );
 void x264_ratecontrol_start( x264_t *, int i_slice_type );
 void x264_ratecontrol_mb( x264_t *, int bits );
 int  x264_ratecontrol_qp( x264_t * );
 void x264_ratecontrol_end( x264_t *, int bits );
+void x264_ratecontrol_write_stats( x264_t *, int slice_type, double qp );
 
+double x264_eval(char *s, double *const_value, const char **const_name,
+                 double (**func1)(void *, double), const char **func1_name,
+                 double (**func2)(void *, double, double), char **func2_name,
+                 void *opaque);
+
 #endif
 
Index: encoder/eval.c
===================================================================
--- encoder/eval.c	(revision 0)
+++ encoder/eval.c	(revision 0)
@@ -0,0 +1,254 @@
+/*
+ * simple arithmetic expression evaluator
+ *
+ * Copyright (c) 2002 Michael Niedermayer <michaelni at gmx.at>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/**
+ * @file eval.c
+ * simple arithmetic expression evaluator.
+ *
+ * see http://joe.hotchkiss.com/programming/eval/eval.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#ifndef NAN
+  #define NAN 0
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#define STACK_SIZE 100
+
+typedef struct Parser{
+    double stack[STACK_SIZE];
+    int stack_index;
+    char *s;
+    double *const_value;
+    const char **const_name;          // NULL terminated
+    double (**func1)(void *, double a); // NULL terminated
+    const char **func1_name;          // NULL terminated
+    double (**func2)(void *, double a, double b); // NULL terminated
+    char **func2_name;          // NULL terminated
+    void *opaque;
+} Parser;
+
+static void evalExpression(Parser *p);
+
+static void push(Parser *p, double d){
+    if(p->stack_index+1>= STACK_SIZE){
+        fprintf(stderr, "stack overflow in the parser\n");
+        return;
+    }
+    p->stack[ p->stack_index++ ]= d;
+//printf("push %f\n", d); fflush(stdout);
+}
+
+static double pop(Parser *p){
+    if(p->stack_index<=0){
+        fprintf(stderr, "stack underflow in the parser\n");
+        return NAN;
+    }
+//printf("pop\n"); fflush(stdout);
+    return p->stack[ --p->stack_index ];
+}
+
+static int strmatch(const char *s, const char *prefix){
+    int i;
+    for(i=0; prefix[i]; i++){
+        if(prefix[i] != s[i]) return 0;
+    }
+    return 1;
+}
+
+static void evalPrimary(Parser *p){
+    double d, d2=NAN;
+    char *next= p->s;
+    int i;
+
+    /* number */
+    d= strtod(p->s, &next);
+    if(next != p->s){
+        push(p, d);
+        p->s= next;
+        return;
+    }
+
+    /* named constants */
+    for(i=0; p->const_name[i]; i++){
+        if(strmatch(p->s, p->const_name[i])){
+            push(p, p->const_value[i]);
+            p->s+= strlen(p->const_name[i]);
+            return;
+        }
+    }
+
+    p->s= strchr(p->s, '(');
+    if(p->s==NULL){
+        fprintf(stderr, "Parser: missing ( in \"%s\"\n", next);
+        return;
+    }
+    p->s++; // "("
+    evalExpression(p);
+    d= pop(p);
+    if(p->s[0]== ','){
+        p->s++; // ","
+        evalExpression(p);
+        d2= pop(p);
+    }
+    if(p->s[0] != ')'){
+        fprintf(stderr, "Parser: missing ) in \"%s\"\n", next);
+        return;
+    }
+    p->s++; // ")"
+
+         if( strmatch(next, "sinh"  ) ) d= sinh(d);
+    else if( strmatch(next, "cosh"  ) ) d= cosh(d);
+    else if( strmatch(next, "tanh"  ) ) d= tanh(d);
+    else if( strmatch(next, "sin"   ) ) d= sin(d);
+    else if( strmatch(next, "cos"   ) ) d= cos(d);
+    else if( strmatch(next, "tan"   ) ) d= tan(d);
+    else if( strmatch(next, "exp"   ) ) d= exp(d);
+    else if( strmatch(next, "log"   ) ) d= log(d);
+    else if( strmatch(next, "squish") ) d= 1/(1+exp(4*d));
+    else if( strmatch(next, "gauss" ) ) d= exp(-d*d/2)/sqrt(2*M_PI);
+    else if( strmatch(next, "abs"   ) ) d= fabs(d);
+    else if( strmatch(next, "max"   ) ) d= d > d2 ? d : d2;
+    else if( strmatch(next, "min"   ) ) d= d < d2 ? d : d2;
+    else if( strmatch(next, "gt"    ) ) d= d > d2 ? 1.0 : 0.0;
+    else if( strmatch(next, "gte"    ) ) d= d >= d2 ? 1.0 : 0.0;
+    else if( strmatch(next, "lt"    ) ) d= d > d2 ? 0.0 : 1.0;
+    else if( strmatch(next, "lte"    ) ) d= d >= d2 ? 0.0 : 1.0;
+    else if( strmatch(next, "eq"    ) ) d= d == d2 ? 1.0 : 0.0;
+//    else if( strmatch(next, "l1"    ) ) d= 1 + d2*(d - 1);
+//    else if( strmatch(next, "sq01"  ) ) d= (d >= 0.0 && d <=1.0) ? 1.0 : 0.0;
+    else{
+        int error=1;
+        for(i=0; p->func1_name && p->func1_name[i]; i++){
+            if(strmatch(next, p->func1_name[i])){
+                d= p->func1[i](p->opaque, d);
+                error=0;
+                break;
+            }
+        }
+
+        for(i=0; p->func2_name && p->func2_name[i]; i++){
+            if(strmatch(next, p->func2_name[i])){
+                d= p->func2[i](p->opaque, d, d2);
+                error=0;
+                break;
+            }
+        }
+
+        if(error){
+            fprintf(stderr, "Parser: unknown function in \"%s\"\n", next);
+            return;
+        }
+    }
+
+    push(p, d);
+}
+
+static void evalPow(Parser *p){
+    int neg= 0;
+    if(p->s[0]=='+') p->s++;
+
+    if(p->s[0]=='-'){
+        neg= 1;
+        p->s++;
+    }
+
+    if(p->s[0]=='('){
+        p->s++;;
+        evalExpression(p);
+
+        if(p->s[0]!=')')
+            fprintf(stderr, "Parser: missing )\n");
+        p->s++;
+    }else{
+        evalPrimary(p);
+    }
+
+    if(neg) push(p, -pop(p));
+}
+
+static void evalFactor(Parser *p){
+    evalPow(p);
+    while(p->s[0]=='^'){
+        double d;
+
+        p->s++;
+        evalPow(p);
+        d= pop(p);
+        push(p, pow(pop(p), d));
+    }
+}
+
+static void evalTerm(Parser *p){
+    evalFactor(p);
+    while(p->s[0]=='*' || p->s[0]=='/'){
+        int inv= p->s[0]=='/';
+        double d;
+
+        p->s++;
+        evalFactor(p);
+        d= pop(p);
+        if(inv) d= 1.0/d;
+        push(p, d * pop(p));
+    }
+}
+
+static void evalExpression(Parser *p){
+    evalTerm(p);
+    while(p->s[0]=='+' || p->s[0]=='-'){
+        int sign= p->s[0]=='-';
+        double d;
+
+        p->s++;
+        evalTerm(p);
+        d= pop(p);
+        if(sign) d= -d;
+        push(p, d + pop(p));
+    }
+}
+
+double x264_eval(char *s, double *const_value, const char **const_name,
+                 double (**func1)(void *, double), const char **func1_name,
+                 double (**func2)(void *, double, double), char **func2_name,
+                 void *opaque){
+    Parser p;
+
+    p.stack_index=0;
+    p.s= s;
+    p.const_value= const_value;
+    p.const_name = const_name;
+    p.func1      = func1;
+    p.func1_name = func1_name;
+    p.func2      = func2;
+    p.func2_name = func2_name;
+    p.opaque     = opaque;
+
+    evalExpression(&p);
+    return pop(&p);
+}
Index: encoder/cavlc.c
===================================================================
--- encoder/cavlc.c	(revision 38)
+++ encoder/cavlc.c	(working copy)
@@ -265,6 +265,8 @@
     const int i_mb_type = h->mb.i_type;
     int i_mb_i_offset;
     int i;
+    int mb_pos = bs_pos(s);
+    int mb_pos2;
 
     switch( h->sh.i_type )
     {
@@ -632,6 +634,10 @@
         return;
     }
 
+    mb_pos2 = bs_pos(s);
+    h->frame_stat.i_mv_bits += mb_pos2 - mb_pos;
+    mb_pos = mb_pos2;
+
     /* Coded block patern */
     if( i_mb_type == I_4x4 )
     {
@@ -684,4 +690,13 @@
             }
         }
     }
+
+    if(IS_INTRA(i_mb_type))
+    {
+        h->frame_stat.i_itex_bits += bs_pos(s) - mb_pos;
+    }
+    else
+    {
+        h->frame_stat.i_ptex_bits += bs_pos(s) - mb_pos;
+    }
 }
Index: encoder/cabac.c
===================================================================
--- encoder/cabac.c	(revision 38)
+++ encoder/cabac.c	(working copy)
@@ -962,6 +962,8 @@
 {
     const int i_mb_type = h->mb.i_type;
     int i;
+    int mb_pos = bs_pos(s);
+    int mb_pos2;
 
     /* Write the MB type */
     x264_cabac_mb_type( h );
@@ -1141,6 +1143,10 @@
         }
     }
 
+    mb_pos2 = bs_pos(s);
+    h->frame_stat.i_mv_bits += mb_pos2 - mb_pos;
+    mb_pos = mb_pos2;
+
     if( i_mb_type != I_16x16 )
     {
         x264_cabac_mb_cbp_luma( h );
@@ -1190,5 +1196,14 @@
             }
         }
     }
+
+    if(IS_INTRA(i_mb_type))
+    {
+        h->frame_stat.i_itex_bits += bs_pos(s) - mb_pos;
+    }
+    else
+    {
+        h->frame_stat.i_ptex_bits += bs_pos(s) - mb_pos;
+    }
 }
 
Index: encoder/ratecontrol.c
===================================================================
--- encoder/ratecontrol.c	(revision 38)
+++ encoder/ratecontrol.c	(working copy)
@@ -5,6 +5,8 @@
  * $Id: ratecontrol.c,v 1.1 2004/06/03 19:27:08 fenrir Exp $
  *
  * Authors: M?ns Rullg?rd <mru at mru.ath.cx>
+ * 2 pass code: Michael Niedermayer <michaelni at gmx.at>
+ *              Loren Merritt
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,31 +25,53 @@
 
 #define _ISOC99_SOURCE
 
+#undef NDEBUG // always check asserts, the speed effect is far too small to disable them
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <math.h>
 #include <limits.h>
+#include <assert.h>
 
 #include "../core/common.h"
 #include "../core/cpu.h"
+#include "../core/macroblock.h"
 #include "ratecontrol.h"
 
 #ifdef SYS_MACOSX
 #define exp2f(x) ( (float) exp2( (x) ) )
 #endif
 
+typedef struct ratecontrol_entry_t {
+    int pict_type;
+    float qscale;
+    int mv_bits;
+    int i_tex_bits;
+    int p_tex_bits;
+    int misc_bits;
+    uint64_t expected_bits;
+    int new_pict_type;
+    float new_qscale;
+    int new_qp;
+    int i_count;
+    int p_count;
+    int s_count;
+    int f_code;
+    int b_code;
+} ratecontrol_entry_t;
+
 struct x264_ratecontrol_t
 {
     /* constants */
-    float fps;
+    double fps;
     int gop_size;
     int bitrate;
-    int nmb;                    /* number of MBs */
+    int nmb;                    /* number of macroblocks in a frame */
     int buffer_size;
     int rcbufrate;
     int init_qp;
 
+    /* 1 pass stuff */
     int gop_qp;
     int buffer_fullness;
     int frames;                 /* frames in current gop */
@@ -67,13 +91,65 @@
     int nzcoeffs;               /* # of 0-quantized coefficients */
     int ncoeffs;                /* total # of coefficients */
     int overhead;
+
+    /* 2 pass stuff */
+    FILE *stats_file;
+    int num_entries;            /* number of ratecontrol_entry_ts */
+    ratecontrol_entry_t *entry;
+    double last_qscale;
+    double last_qscale_for[5];  /* last qscale for a specific pict type, used for max_diff & ipb factor stuff  */
+    int last_non_b_pict_type;
+    double lmin[5];                /* min qscale by frame type */
+    double lmax[5];
+    double i_cplx_sum[5];       /* estimated total texture bits in intra MBs at qscale=1 */
+    double p_cplx_sum[5];
+    double mv_bits_sum[5];
+    int frame_count[5];         /* number of frames of each type */
 };
 
+
+static int init_pass2(x264_t *);
+static float rate_estimate_qscale(x264_t *h, int pict_type);
+
+/* Terminology:
+ * qp = h.264's quantizer
+ * qscale = an arbitrary linear scale, mappable to qp
+ */
+
+static inline double qp2qscale(double qp){
+    return pow(2.0, qp / 6.0);
+}
+
+static inline double qscale2qp(double qscale){
+    return 6.0 * log(qscale) / log(2.0);
+}
+
+static inline double qscale2bits(ratecontrol_entry_t *rce, double qscale){
+    if(qscale<=0.0){
+        fprintf(stderr, "qscale<=0.0\n");
+        qscale = 0.1;
+    }
+    return (double)(rce->i_tex_bits + rce->p_tex_bits + 1) * rce->qscale / qscale;
+ }
+
+static inline double bits2qscale(ratecontrol_entry_t *rce, double bits){
+    if(bits<0.9){
+        fprintf(stderr, "bits<0.9\n");
+        bits = 1.0;
+    }
+    return rce->qscale * (double)(rce->i_tex_bits + rce->p_tex_bits + 1) / bits;
+}
+
+
 int x264_ratecontrol_new( x264_t *h )
 {
     x264_ratecontrol_t *rc = x264_malloc( sizeof( x264_ratecontrol_t ) );
     float bpp;
+    int i;
 
+    x264_cpu_restore( h->param.cpu );
+
+    h->rc = rc;
     memset(rc, 0, sizeof(*rc));
 
     /* FIXME: use integers */
@@ -125,14 +201,112 @@
     x264_log(h, X264_LOG_DEBUG, "%f fps, %i bps, bufsize %i\n",
              rc->fps, rc->bitrate, rc->buffer_size);
 
-    h->rc = rc;
 
+    for(i=0; i<5; i++){
+        rc->last_qscale_for[i] = qp2qscale(26);
+        rc->lmin[i] = qp2qscale(h->param.i_qp_min);
+        rc->lmax[i] = qp2qscale(h->param.i_qp_max);
+    }
+#if 0 // FIXME: do we want to assign lmin/lmax based on ip_factor, or leave them all the same?
+    rc->lmin[SLICE_TYPE_I] /= fabs(h->param.f_ip_factor);
+    rc->lmax[SLICE_TYPE_I] /= fabs(h->param.f_ip_factor);
+    rc->lmin[SLICE_TYPE_B] *= fabs(h->param.f_pb_factor);
+    rc->lmax[SLICE_TYPE_B] *= fabs(h->param.f_pb_factor);
+#endif
+
+    if(h->param.i_pass & 2){
+        int stats_size;
+        char *p, *stats_in;
+        FILE *stats_file;
+
+        /* read 1st pass stats */
+        assert(h->param.s_2pass_file_in);
+        stats_file = fopen(h->param.s_2pass_file_in, "r");
+        if(!stats_file){
+            x264_log(h, X264_LOG_ERROR, "ratecontrol_init: can't open stats file\n");
+            return -1;
+        }
+        // FIXME: error checking
+        fseek(stats_file, 0, SEEK_END);
+        stats_size = ftell(stats_file);
+        fseek(stats_file, 0, SEEK_SET);
+        stats_in = x264_malloc(stats_size+10);
+        fread(stats_in, 1, stats_size, stats_file);
+        fclose(stats_file);
+
+        /* find number of pics */
+        p = stats_in;
+        for(i=-1; p; i++){
+            p = strchr(p+1, ';');
+        }
+        i += h->param.i_bframe;
+        rc->entry = (ratecontrol_entry_t*) x264_malloc(i*sizeof(ratecontrol_entry_t));
+        memset(rc->entry, 0, i*sizeof(ratecontrol_entry_t));
+        rc->num_entries= i;
+
+        /* init all to skipped p frames */
+        for(i=0; i<rc->num_entries; i++){
+            ratecontrol_entry_t *rce = &rc->entry[i];
+            rce->pict_type = rce->new_pict_type = SLICE_TYPE_P;
+            rce->qscale = rce->new_qscale = qp2qscale(20);
+            rce->misc_bits = rc->nmb + 10;
+            rce->new_qp = 0;
+        }
+
+        /* read stats */
+        p = stats_in;
+        for(i=0; i < rc->num_entries - h->param.i_bframe; i++){
+            ratecontrol_entry_t *rce;
+            int picture_number;
+            int e;
+            char *next;
+            float qp;
+
+            next= strchr(p, ';');
+            if(next){
+                (*next)=0; //sscanf is unbelievably slow on looong strings
+                next++;
+            }
+            e = sscanf(p, " in:%d ", &picture_number);
+
+            assert(picture_number >= 0);
+            assert(picture_number < rc->num_entries);
+            rce = &rc->entry[picture_number];
+
+            e += sscanf(p, " in:%*d out:%*d type:%d q:%f itex:%d ptex:%d mv:%d misc:%d imb:%d pmb:%d smb:%d",
+                   &rce->pict_type, &qp, &rce->i_tex_bits, &rce->p_tex_bits,
+                   &rce->mv_bits, &rce->misc_bits, &rce->i_count, &rce->p_count, &rce->s_count);
+            if(e != 10){
+                x264_log(h, X264_LOG_ERROR, "statistics are damaged at line %d, parser out=%d\n", i, e);
+                return -1;
+            }
+            rce->qscale = qp2qscale(qp);
+            p = next;
+        }
+
+        x264_free(stats_in);
+
+        if(init_pass2(h) < 0) return -1;
+    }
+
+    if(h->param.i_pass & 1){
+        assert(h->param.s_2pass_file_out);
+        h->rc->stats_file = fopen(h->param.s_2pass_file_out, "w");
+        if(!h->rc->stats_file){
+            x264_log(h, X264_LOG_ERROR, "ratecontrol_init: can't open stats file\n");
+            return -1;
+        }
+    }
+
     return 0;
+    return 0;
 }
 
 void x264_ratecontrol_delete( x264_t *h )
 {
     x264_ratecontrol_t *rc = h->rc;
+    if(rc->stats_file) fclose(rc->stats_file);
+    if(rc->entry) x264_free(rc->entry);
     x264_free( rc );
 }
 
@@ -151,6 +325,15 @@
 
     x264_cpu_restore( h->param.cpu );
 
+    if(h->param.i_pass & 2){
+        int frame = h->fenc->i_presentation_num;
+        assert(frame >= 0 && frame < rc->num_entries);
+        ratecontrol_entry_t *rce = &h->rc->entry[frame];
+        rce->new_qscale = rate_estimate_qscale(h, i_slice_type);
+        rc->qpm = rc->qp = rce->new_qp = (int)(qscale2qp(rce->new_qscale) + 0.5);
+        return;
+    }
+
     rc->slice_type = i_slice_type;
 
     switch(i_slice_type){
@@ -269,7 +452,7 @@
     int dqp;
     int i;
 
-    if( !h->param.b_cbr )
+    if(!h->param.b_cbr || (h->param.i_pass & 2))
         return;
 
     x264_cpu_restore( h->param.cpu );
@@ -319,7 +502,7 @@
 {
     x264_ratecontrol_t *rc = h->rc;
 
-    if(!h->param.b_cbr)
+    if(!h->param.b_cbr || (h->param.i_pass & 2))
         return;
 
     rc->buffer_fullness += rc->rcbufrate - bits;
@@ -352,3 +535,313 @@
     rc->frames++;
     rc->mb = 0;
 }
+
+
+/*****************************************************
+ * 2 pass functions
+ ****************************************************/
+
+void x264_ratecontrol_write_stats(x264_t *h, int slice_type, double qp){
+    if(!(h->param.i_pass & 1))
+        return;
+    assert(h->rc->stats_file);
+    fprintf(h->rc->stats_file,
+            "in:%d out:%d type:%d q:%.3f itex:%d ptex:%d mv:%d misc:%d imb:%d pmb:%d smb:%d;\n",
+            h->fenc->i_presentation_num, h->i_frame-1,
+            slice_type, qp,
+            h->frame_stat.i_itex_bits, h->frame_stat.i_ptex_bits,
+            h->frame_stat.i_mv_bits, h->frame_stat.i_misc_bits,
+            h->frame_stat.i_mb_count[I_4x4] + h->frame_stat.i_mb_count[I_16x16],
+            h->frame_stat.i_mb_count[P_L0]  + h->frame_stat.i_mb_count[P_8x8],
+            h->frame_stat.i_mb_count[P_SKIP]);
+}
+
+/**
+ * modifies the bitrate curve from pass1 for one frame
+ */
+static double get_qscale(x264_t *h, ratecontrol_entry_t *rce, double rate_factor){
+    x264_ratecontrol_t *rcc= h->rc;
+    double bits;
+    //double q, avg_cplx;
+    const int pict_type = rce->new_pict_type;
+
+    x264_cpu_restore( h->param.cpu );
+
+    //avg_cplx = (rcc->i_cplx_sum[pict_type] + rcc->p_cplx_sum[pict_type]) / rcc->frame_count[pict_type];
+
+    double const_values[]={
+        rce->i_tex_bits * rce->qscale,
+        rce->p_tex_bits * rce->qscale,
+        (rce->i_tex_bits + rce->p_tex_bits) * rce->qscale,
+        rce->mv_bits / rcc->nmb,
+        (double)rce->i_count / rcc->nmb,
+        (double)rce->p_count / rcc->nmb,
+        (double)rce->s_count / rcc->nmb,
+        rce->pict_type == SLICE_TYPE_I,
+        rce->pict_type == SLICE_TYPE_P,
+        rce->pict_type == SLICE_TYPE_B,
+        h->param.f_qcompress,
+        rcc->i_cplx_sum[SLICE_TYPE_I] / rcc->frame_count[SLICE_TYPE_I],
+        rcc->i_cplx_sum[SLICE_TYPE_P] / rcc->frame_count[SLICE_TYPE_P],
+        rcc->p_cplx_sum[SLICE_TYPE_P] / rcc->frame_count[SLICE_TYPE_P],
+        rcc->p_cplx_sum[SLICE_TYPE_B] / rcc->frame_count[SLICE_TYPE_B],
+        (rcc->i_cplx_sum[pict_type] + rcc->p_cplx_sum[pict_type]) / rcc->frame_count[pict_type],
+        0
+    };
+    static const char *const_names[]={
+        "iTex",
+        "pTex",
+        "tex",
+        "mv",
+        "iCount",
+        "pCount",
+        "sCount",
+        "isI",
+        "isP",
+        "isB",
+        "qComp",
+        "avgIITex",
+        "avgPITex",
+        "avgPPTex",
+        "avgBPTex",
+        "avgTex",
+        NULL
+    };
+    static double (*func1[])(void *, double)={
+        (void *)bits2qscale,
+        (void *)qscale2bits,
+        NULL
+    };
+    static const char *func1_names[]={
+        "bits2qp",
+        "qp2bits",
+        NULL
+    };
+
+    bits = x264_eval((char*)h->param.s_rc_eq, const_values, const_names, func1, func1_names, NULL, NULL, rce);
+
+    bits *= rate_factor;
+    if(bits<0.0) bits=0.0;
+    bits += 1.0; //avoid 1/0 issues
+
+    /* I/B difference */
+    if     (pict_type==SLICE_TYPE_I && h->param.f_ip_factor > 0)
+        bits *= h->param.f_ip_factor;
+    else if(pict_type==SLICE_TYPE_B && h->param.f_pb_factor > 0)
+        bits /= h->param.f_pb_factor;
+
+    return bits2qscale(rce, bits);
+}
+
+static double get_diff_limited_q(x264_t *h, ratecontrol_entry_t *rce, double q){
+    x264_ratecontrol_t *rcc = h->rc;
+    const int pict_type = rce->new_pict_type;
+
+    // force I/B quants as a function of P quants
+    const double last_p_q    = rcc->last_qscale_for[SLICE_TYPE_P];
+    const double last_non_b_q= rcc->last_qscale_for[rcc->last_non_b_pict_type];
+    if     (pict_type==SLICE_TYPE_I && h->param.f_ip_factor < 0)
+        q = last_p_q     / -h->param.f_ip_factor;
+    else if(pict_type==SLICE_TYPE_B && h->param.f_pb_factor < 0)
+        q = last_non_b_q * -h->param.f_pb_factor;
+
+    /* last qscale / qdiff stuff */
+    if(rcc->last_non_b_pict_type==pict_type || pict_type!=SLICE_TYPE_I)
+    {
+        double last_q = rcc->last_qscale_for[pict_type];
+        const double max_qscale = qp2qscale(qscale2qp(last_q) + h->param.i_qp_step);
+        const double min_qscale = qp2qscale(qscale2qp(last_q) - h->param.i_qp_step);
+
+        if     (q > max_qscale) q = max_qscale;
+        else if(q < min_qscale) q = min_qscale;
+    }
+
+    rcc->last_qscale_for[pict_type] = q; //Note we can't do that after blurring
+    if(pict_type!=SLICE_TYPE_B)
+        rcc->last_non_b_pict_type = pict_type;
+    return q;
+}
+
+static double modify_qscale(x264_t *h, ratecontrol_entry_t *rce, double q){
+    x264_ratecontrol_t *rcc = h->rc;
+    const int pict_type = rce->new_pict_type;
+    double lmin = rcc->lmin[pict_type];
+    double lmax = rcc->lmax[pict_type];
+
+    if(lmin==lmax /* || !h->param.b_qsquish */){
+        if     (q<lmin) q = lmin;
+        else if(q>lmax) q = lmax;
+    }else{
+        double min2 = log(lmin);
+        double max2 = log(lmax);
+
+        q = log(q);
+        q = (q - min2)/(max2-min2) - 0.5;
+        q *= -4.0;
+        q = 1.0/(1.0 + exp(q));
+        q = q*(max2-min2) + min2;
+
+        q = exp(q);
+    }
+    return q;
+}
+
+// update qscale for 1 frame based on actual bits used so far
+static float rate_estimate_qscale(x264_t *h, int pict_type)
+{
+    float q;
+    float br_compensation;
+    double diff;
+    int picture_number = h->fenc->i_presentation_num;
+    x264_ratecontrol_t *rcc = h->rc;
+    ratecontrol_entry_t *rce;
+    double lmin = rcc->lmin[pict_type];
+    double lmax = rcc->lmax[pict_type];
+    int64_t wanted_bits;
+    int64_t total_bits = 8*(h->stat.i_slice_size[SLICE_TYPE_I]
+                          + h->stat.i_slice_size[SLICE_TYPE_P]
+                          + h->stat.i_slice_size[SLICE_TYPE_B]);
+
+//printf("input_pic_num:%d pic_num:%d frame_rate:%d\n", s->input_picture_number, s->picture_number, s->frame_rate);
+
+    rce = &rcc->entry[picture_number];
+
+    if(pict_type!=SLICE_TYPE_I)
+        assert(pict_type == rce->new_pict_type);
+
+    wanted_bits = rce->expected_bits;
+
+    diff = total_bits - wanted_bits;
+    br_compensation = (rcc->buffer_size - diff) / rcc->buffer_size;
+    if(br_compensation<=0.0) br_compensation=0.001;
+
+    q = rce->new_qscale / br_compensation;
+
+    if     (q<lmin) q=lmin;
+    else if(q>lmax) q=lmax;
+
+    rcc->last_qscale = q;
+    return q;
+}
+
+static int init_pass2(x264_t *h)
+{
+    x264_ratecontrol_t *rcc = h->rc;
+    uint64_t all_const_bits = 0;
+    uint64_t all_available_bits = (uint64_t)(h->param.i_bitrate * 1000 * (double)rcc->num_entries / rcc->fps);
+    double rate_factor, step, step_mult;
+    double qblur = h->param.f_qblur;
+    const int filter_size = (int)(qblur*4) | 1;
+    double expected_bits;
+    double *qscale, *blurred_qscale;
+    int i;
+
+    /* find total/average complexity & const_bits */
+    for(i=0; i<rcc->num_entries; i++){
+        ratecontrol_entry_t *rce = &rcc->entry[i];
+        rce->new_pict_type = rce->pict_type;
+        all_const_bits += rce->mv_bits + rce->misc_bits;
+        rcc->i_cplx_sum[rce->new_pict_type] += rce->i_tex_bits * rce->qscale;
+        rcc->p_cplx_sum[rce->new_pict_type] += rce->p_tex_bits * rce->qscale;
+        rcc->mv_bits_sum[rce->new_pict_type] += rce->mv_bits;
+        rcc->frame_count[rce->new_pict_type] ++;
+    }
+
+    if(all_available_bits < all_const_bits){
+        x264_log(h, X264_LOG_ERROR, "requested bitrate is too low. estimated minimum is %d kbps\n",
+                 (int)(all_const_bits * rcc->fps / (rcc->num_entries * 1000)));
+        return -1;
+    }
+
+    qscale = x264_malloc(sizeof(double)*rcc->num_entries);
+    if(filter_size > 1)
+        blurred_qscale = x264_malloc(sizeof(double)*rcc->num_entries);
+    else
+        blurred_qscale = qscale;
+
+    expected_bits = 0;
+    for(i=0; i<rcc->num_entries; i++)
+        expected_bits += qscale2bits(&rcc->entry[i], get_qscale(h, &rcc->entry[i], 1.0));
+    step_mult = all_available_bits / expected_bits;
+
+    rate_factor = 0;
+    for(step = 1E4 * step_mult; step > 1E-7 * step_mult; step*=0.5){
+        expected_bits = 0;
+        rate_factor += step;
+
+        /* find qscale */
+        for(i=0; i<rcc->num_entries; i++){
+            qscale[i] = get_qscale(h, &rcc->entry[i], rate_factor);
+        }
+
+        /* fixed I/B QP relative to P mode */
+        for(i=rcc->num_entries-1; i>=0; i--){
+            qscale[i] = get_diff_limited_q(h, &rcc->entry[i], qscale[i]);
+            assert(qscale[i] >= 0);
+        }
+
+        /* smooth curve */
+        if(filter_size > 1){
+            assert(filter_size%2==1);
+            for(i=0; i<rcc->num_entries; i++){
+                ratecontrol_entry_t *rce = &rcc->entry[i];
+                const int pict_type = rce->new_pict_type;
+                int j;
+                double q=0.0, sum=0.0;
+
+                for(j=0; j<filter_size; j++){
+                    int index = i+j-filter_size/2;
+                    double d = index-i;
+                    double coeff = qblur==0 ? 1.0 : exp(-d*d/(qblur*qblur));
+                    if(index < 0 || index >= rcc->num_entries) continue;
+                    if(pict_type != rcc->entry[index].new_pict_type) continue;
+                    q += qscale[index] * coeff;
+                    sum += coeff;
+                }
+                blurred_qscale[i] = q/sum;
+            }
+        }
+
+        /* find expected bits */
+        for(i=0; i<rcc->num_entries; i++){
+            ratecontrol_entry_t *rce = &rcc->entry[i];
+            double bits;
+            rce->new_qscale = modify_qscale(h, rce, blurred_qscale[i]);
+            assert(rce->new_qscale >= 0);
+            bits = qscale2bits(rce, rce->new_qscale) + rce->mv_bits + rce->misc_bits;
+
+            rce->expected_bits = expected_bits;
+            expected_bits += bits;
+        }
+
+//printf("expected:%f available:%d factor:%f\n", expected_bits, (int)all_available_bits, rate_factor);
+        if(expected_bits > all_available_bits) rate_factor -= step;
+    }
+
+    x264_free(qscale);
+    if(filter_size > 1)
+        x264_free(blurred_qscale);
+
+    if(fabs(expected_bits/all_available_bits - 1.0) > 0.01 ){
+        x264_log(h, X264_LOG_ERROR, "Error: 2pass curve failed to converge\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+int x264_ratecontrol_slice_type(x264_t *h, int frame_num)
+{
+    if(h->param.i_pass & 2){
+        assert(frame_num < h->rc->num_entries);
+        switch(h->rc->entry[frame_num].new_pict_type){
+            case SLICE_TYPE_I: return X264_TYPE_I;
+            default:
+            case SLICE_TYPE_P: return X264_TYPE_P;
+            case SLICE_TYPE_B: return X264_TYPE_B;
+        }
+    }else{
+        return X264_TYPE_AUTO;
+    }
+}
+
Index: x264.c
===================================================================
--- x264.c	(revision 38)
+++ x264.c	(working copy)
@@ -120,6 +120,11 @@
              "      --rcbuf <integer>       Size of VBV buffer\n"
              "      --rcinitbuf <integer>   Initial VBV buffer occupancy\n"
              "\n"
+             "  -p, --pass <1|2>            Enable 2 pass ratecontrol\n"
+             "      --stats <string>        Filename for 2 pass stats\n"
+             "      --rceq <string>         Ratecontrol equation\n"
+             "      --qcomp <float>         0.0 => CBR, 1.0 => CQP, 0.6 => default\n"
+             "\n"
              "  -A, --analyse <string>      Analyse options:\n"
              "                                  - i4x4\n"
              "                                  - psub16x16,psub8x8\n"
@@ -161,6 +166,9 @@
 #define OPT_PBRATIO 261
 #define OPT_RCBUF 262
 #define OPT_RCIBUF 263
+#define OPT_RCSTATS 264
+#define OPT_RCEQ 265
+#define OPT_QCOMP 266
         static struct option long_options[] =
         {
             { "help",    no_argument,       NULL, 'h' },
@@ -185,12 +193,16 @@
             { "rcinitbuf",required_argument, NULL, OPT_RCIBUF },
             { "ipratio", required_argument, NULL, OPT_IPRATIO },
             { "pbratio", required_argument, NULL, OPT_PBRATIO },
+            { "pass",    required_argument, NULL, 'p' },
+            { "stats",   required_argument, NULL, OPT_RCSTATS },
+            { "rceq",    required_argument, NULL, OPT_RCEQ },
+            { "qcomp",   required_argument, NULL, OPT_QCOMP },
             {0, 0, 0, 0}
         };
 
         int c;
 
-        c = getopt_long( argc, argv, "hi:I:b:r:cxB:q:no:s:A:",
+        c = getopt_long( argc, argv, "hi:I:b:r:cxB:q:no:s:A:p:",
                          long_options, &long_options_index);
 
         if( c == -1 )
@@ -297,6 +309,19 @@
             case OPT_PBRATIO:
                 param->f_pb_factor = atoi(optarg);
                 break;
+            case 'p':
+                param->i_pass = atoi(optarg);
+                break;
+            case OPT_RCSTATS:
+                param->s_2pass_file_in = optarg;
+                param->s_2pass_file_out = optarg;
+                break;
+            case OPT_RCEQ:
+                param->s_rc_eq = optarg;
+                break;
+            case OPT_QCOMP:
+                param->f_qcompress = atof(optarg);
+                break;
             default:
                 fprintf( stderr, "unknown option (%c)\n", optopt );
                 return -1;
Index: core/frame.h
===================================================================
--- core/frame.h	(revision 38)
+++ core/frame.h	(working copy)
@@ -31,6 +31,7 @@
     int     i_type;
     int     i_qpplus1;
     int64_t i_pts;
+    int     i_presentation_num;
 
     /* YUV buffer */
     int     i_plane;
Index: core/common.c
===================================================================
--- core/common.c	(revision 38)
+++ core/common.c	(working copy)
@@ -80,6 +80,13 @@
     param->f_ip_factor = 2.0;
     param->f_pb_factor = 2.0;
 
+    param->i_pass = 0;
+    param->s_2pass_file_out = "x264_2pass.log";
+    param->s_2pass_file_in = "x264_2pass.log";
+    param->s_rc_eq = "(tex^qComp)*(avgTex^(1-qComp))";
+    param->f_qcompress = 0.6;
+    param->f_qblur = 0.5;
+
     /* Log */
     param->pf_log = x264_log_default;
     param->p_log_private = NULL;
Index: core/common.h
===================================================================
--- core/common.h	(revision 38)
+++ core/common.h	(working copy)
@@ -191,6 +191,7 @@
     /* frame number/poc */
     int             i_frame;
     int             i_poc;
+    int             i_input_frame;
 
     int             i_frame_offset; /* decoding only */
     int             i_frame_num;    /* decoding only */
@@ -348,6 +349,15 @@
         int   i_mb_count[5][18];
     } stat;
 
+    struct
+    {
+        int i_itex_bits;
+        int i_ptex_bits;
+        int i_mv_bits;
+        int i_misc_bits;
+        int i_mb_count[18];
+    } frame_stat;
+
     /* CPU functions dependants */
     x264_predict_t      predict_16x16[4+3];
     x264_predict_t      predict_8x8[4+3];
Index: x264.h
===================================================================
--- x264.h	(revision 38)
+++ x264.h	(working copy)
@@ -125,6 +125,13 @@
     float       f_ip_factor;
     float       f_pb_factor;
 
+    int         i_pass;         /* 1 => write stats, 2 => use those stats, 3 => both */
+    const char* s_2pass_file_out;
+    const char* s_2pass_file_in;
+    const char* s_rc_eq;        /* rate control equation */
+    float       f_qcompress;    /* 0.0 => cbr, 1.0 => constant qp */
+    float       f_qblur;        /* temporally blur quants */
+
     /* Log */
     void        (*pf_log)( void *, int i_level, const char *psz, va_list );
     void        *p_log_private;
Index: Jamfile
===================================================================
--- Jamfile	(revision 38)
+++ Jamfile	(working copy)
@@ -24,7 +24,7 @@
             core/common.c core/mdate.c core/csp.c
             encoder/analyse.c encoder/me.c encoder/ratecontrol.c
             encoder/set.c encoder/macroblock.c encoder/cabac.c encoder/cavlc.c
-            encoder/encoder.c ;
+            encoder/encoder.c encoder/eval.c ;
 
 SOURCES_X86 = core/i386/cpu.asm ;
 SOURCES_MMX = core/i386/mc-c.c core/i386/dct-c.c core/i386/predict.c core/i386/dct.asm core/i386/pixel.asm core/i386/mc.asm ;


More information about the x264-devel mailing list