[x265] [PATCH] 1.add parameter "vbv-maxrate" "vbv-bufsize" "vbv-init" into cmd line

Steve Borho steve at borho.org
Mon Nov 11 03:39:14 CET 2013


# HG changeset patch
# User Xun Xu, PPLive Corporation<xunxu at pptv.com>
# Date 1383907281 -28800
#      Fri Nov 08 18:41:21 2013 +0800
# Node ID ea264e7350c3c37e9d11fe818917d9947c32dce4
# Parent  9d74638c3640679d09264b793afdf3ffc58a9107
1.add parameter "vbv-maxrate" "vbv-bufsize" "vbv-init" into cmd line
2.implement vbv methods, and this patch doesn't include vbv algorithm based on lookahead

future work,
1.vbv-lookahead
2.CU level ratecontrol

diff -r 9d74638c3640 -r ea264e7350c3 source/common/common.cpp
--- a/source/common/common.cpp	Sat Nov 09 20:14:24 2013 -0600
+++ b/source/common/common.cpp	Fri Nov 08 18:41:21 2013 +0800
@@ -186,6 +186,9 @@
     param->saoLcuBasedOptimization = 1;
 
     /* Rate control options */
+    param->rc.vbvMaxBitrate = 0;
+    param->rc.vbvBufferSize = 0;
+    param->rc.vbvBufferInit = 0.9;
     param->rc.rfConstant = 28;
     param->rc.bitrate = 0;
     param->rc.rateTolerance = 1.0;
@@ -701,6 +704,12 @@
     OPT("psnr") p->bEnablePsnr = bvalue;
     OPT("hash") p->decodedPictureHashSEI = atoi(value);
     OPT("aq-mode") p->rc.aqMode = atoi(value);
+    OPT("vbv-maxrate")
+        p->rc.vbvMaxBitrate = atoi(value);
+    OPT("vbv-bufsize")
+        p->rc.vbvBufferSize = atoi(value);
+    OPT("vbv-init")
+        p->rc.vbvBufferInit = atof(value);
     OPT("crf")
     {
         p->rc.rfConstant = atof(value);
diff -r 9d74638c3640 -r ea264e7350c3 source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp	Sat Nov 09 20:14:24 2013 -0600
+++ b/source/encoder/encoder.cpp	Fri Nov 08 18:41:21 2013 +0800
@@ -182,6 +182,24 @@
     return m_frameEncoder->getStreamHeaders(nalunits);
 }
 
+void Encoder::updateVbvPlan(RateControl* rc) 
+{
+    int encIdx, curIdx;
+    curIdx = (m_curEncoder + param.frameNumThreads - 1) % param.frameNumThreads;
+    encIdx = (curIdx + 1) % param.frameNumThreads;
+    while (encIdx!=curIdx)
+    {
+        FrameEncoder *encoder = &m_frameEncoder[encIdx];
+        double bits;
+        bits = encoder->m_rce.frameSizePlanned; 
+        rc->bufferFill -= bits;
+        rc->bufferFill = X265_MAX(rc->bufferFill, 0);
+        rc->bufferFill += encoder->m_rce.bufferRate;
+        rc->bufferFill = X265_MIN(rc->bufferFill, rc->bufferSize);
+        encIdx = (encIdx + 1)% param.frameNumThreads;
+    }
+}
+
 /**
  \param   flush               force encoder to encode a frame
  \param   pic_in              input original YUV picture or NULL
@@ -305,7 +323,7 @@
         m_dpb->prepareEncode(fenc);
 
         // set slice QP
-        m_rateControl->rateControlStart(fenc, m_lookahead, &(curEncoder->m_rce));
+        m_rateControl->rateControlStart(fenc, m_lookahead, &(curEncoder->m_rce),this); 
 
         // Allow FrameEncoder::compressFrame() to start in a worker thread
         curEncoder->m_enable.trigger();
diff -r 9d74638c3640 -r ea264e7350c3 source/encoder/encoder.h
--- a/source/encoder/encoder.h	Sat Nov 09 20:14:24 2013 -0600
+++ b/source/encoder/encoder.h	Fri Nov 08 18:41:21 2013 +0800
@@ -134,6 +134,9 @@
 
     int  extractNalData(NALUnitEBSP **nalunits);
 
+    //for rateControl
+    void updateVbvPlan(RateControl* rc);
+
 protected:
 
     uint64_t calculateHashAndPSNR(TComPic* pic, NALUnitEBSP **nalunits); // Returns total number of bits for encoded pic
diff -r 9d74638c3640 -r ea264e7350c3 source/encoder/frameencoder.cpp
--- a/source/encoder/frameencoder.cpp	Sat Nov 09 20:14:24 2013 -0600
+++ b/source/encoder/frameencoder.cpp	Fri Nov 08 18:41:21 2013 +0800
@@ -64,6 +64,7 @@
 
     m_nalCount = 0;
     m_totalTime = 0;
+    memset(&m_rce, 0, sizeof(RateControlEntry));
 }
 
 void FrameEncoder::setThreadPool(ThreadPool *p)
diff -r 9d74638c3640 -r ea264e7350c3 source/encoder/ratecontrol.cpp
--- a/source/encoder/ratecontrol.cpp	Sat Nov 09 20:14:24 2013 -0600
+++ b/source/encoder/ratecontrol.cpp	Fri Nov 08 18:41:21 2013 +0800
@@ -3,6 +3,7 @@
  *
  * Authors: Sumalatha Polureddy <sumalatha at multicorewareinc.com>
  *          Aarthi Priya Thirumalai <aarthi at multicorewareinc.com>
+ *          Xun Xu, PPLive Corporation <xunxu at pptv.com>
  *
  * 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
@@ -160,6 +161,77 @@
     framesDone = 0;
     lastNonBPictType = I_SLICE;
 
+    //vbv initialization
+    cfg->param.rc.vbvBufferSize = Clip3(0, 2000000, cfg->param.rc.vbvBufferSize);
+    cfg->param.rc.vbvMaxBitrate = Clip3(0, 2000000, cfg->param.rc.vbvMaxBitrate);
+    cfg->param.rc.vbvBufferInit = Clip3(0.0, 2000000.0, cfg->param.rc.vbvBufferInit);
+
+    if(cfg->param.rc.vbvBufferSize)
+    {
+        if (cfg->param.rc.rateControlMode == X265_RC_CQP)
+        {
+            x265_log(&cfg->param, X265_LOG_WARNING, "VBV is incompatible with constant QP, ignored.\n" );
+            cfg->param.rc.vbvBufferSize = 0;
+            cfg->param.rc.vbvMaxBitrate = 0;
+        }
+        else if (cfg->param.rc.vbvMaxBitrate == 0)
+        {
+            if (cfg->param.rc.rateControlMode == X265_RC_ABR)
+            {
+                x265_log(&cfg->param, X265_LOG_WARNING, "VBV maxrate unspecified, assuming CBR\n");
+                cfg->param.rc.vbvMaxBitrate = cfg->param.rc.bitrate;
+            }
+            else
+            {
+                x265_log(&cfg->param, X265_LOG_WARNING, "VBV bufsize set but maxrate unspecified, ignored\n");
+                cfg->param.rc.vbvBufferSize = 0;
+            }
+        }
+        else if(cfg->param.rc.vbvMaxBitrate < cfg->param.rc.bitrate &&
+                 cfg->param.rc.rateControlMode == X265_RC_ABR)
+        {
+            x265_log(&cfg->param, X265_LOG_WARNING, "max bitrate less than average bitrate, assuming CBR\n");
+            cfg->param.rc.bitrate = cfg->param.rc.vbvMaxBitrate;
+        }
+    }
+    else if(cfg->param.rc.vbvMaxBitrate)
+    {
+        x265_log(&cfg->param, X265_LOG_WARNING, "VBV maxrate specified, but no bufsize, ignored\n" );
+        cfg->param.rc.vbvMaxBitrate = 0;
+    }
+
+    isVbv = cfg->param.rc.vbvMaxBitrate > 0 && cfg->param.rc.vbvBufferSize > 0;
+    fps = cfg->param.frameRate;
+    if (isVbv)
+    {
+        if(cfg->param.rc.vbvBufferSize < (int)(cfg->param.rc.vbvMaxBitrate/fps))
+        {
+            cfg->param.rc.vbvBufferSize = (int)(cfg->param.rc.vbvMaxBitrate/fps);
+            x265_log(&cfg->param, X265_LOG_WARNING, "VBV buffer size cannot be smaller than one frame, using %d kbit\n",
+                    cfg->param.rc.vbvBufferSize);
+        }
+        int vbvBufferSize = cfg->param.rc.vbvBufferSize * 1000;
+        int vbvMaxBitrate = cfg->param.rc.vbvMaxBitrate * 1000;
+
+        bufferRate = vbvMaxBitrate / fps;
+        vbvMaxRate = vbvMaxBitrate;
+        bufferSize = vbvBufferSize;
+        singleFrameVbv = bufferRate * 1.1 > bufferSize;
+        bufferFillFinal = bufferSize*cfg->param.rc.vbvBufferInit;	   
+    }
+
+    for(int i = 0; i < 5; i++ )
+    {
+        pred[i].coeff = 2.0;
+        pred[i].count = 1.0;
+        pred[i].decay = 0.5;
+        pred[i].offset = 0.0;
+    }
+    predBfromP = pred[0];
+    bframes = cfg->param.bframes;
+    bframeBits = 0;
+    leadingNoBSatd = 0;
+
     if (cfg->param.rc.rateControlMode == X265_RC_ABR)
     {
         /* Adjust the first frame in order to stabilize the quality level compared to the rest */
@@ -201,12 +273,26 @@
     lstep = pow(2, cfg->param.rc.qpStep / 6.0);
 }
 
-void RateControl::rateControlStart(TComPic* pic, Lookahead *l, RateControlEntry* rce)
+void RateControl::rateControlStart(TComPic* pic, Lookahead *l, RateControlEntry* rce, Encoder* enc)
 {
     curSlice = pic->getSlice();
     sliceType = curSlice->getSliceType();
     rce->sliceType = sliceType;
 
+    if( sliceType != B_SLICE )
+    {
+        bframes = pic->m_lowres.leadingBframes;
+    }
+    else //sliceType == B_SLICE
+        rce->bframes = bframes;
+    
+    rce->bLastMiniGopBFrame = pic->m_lowres.bLastMiniGopBFrame;
+    rce->bufferRate = bufferRate;
+    rce->poc = curSlice->getPOC();
+
+    if (isVbv)
+        updateVbvPlan(enc);
+
     if (isAbr) //ABR,CRF
     {
         lastSatd = l->getEstimatedPictureCost(pic);
@@ -215,6 +301,7 @@
         rce->qpaRc = q;
         /* copy value of lastRceq into thread local rce struct *to be used in RateControlEnd() */
         rce->qRceq = lastRceq;
+        rce->lastSatd = lastSatd;
         accumPQpUpdate();
     }
     else //CQP
@@ -276,6 +363,8 @@
         else
             q += pbOffset;
 
+        rce->frameSizePlanned = predictSize(&predBfromP, qp2qScale(q), leadingNoBSatd);
+
         return qp2qScale(q);
     }
     else
@@ -362,15 +451,109 @@
         double lmin1 = lmin[sliceType];
         double lmax1 = lmax[sliceType];
         q = Clip3(lmin1, lmax1, q);
+
+        q = clipQscale(q);
+
         lastQScaleFor[sliceType] = q;
 
         if (curSlice->getPOC() == 0)
             lastQScaleFor[P_SLICE] = q * fabs(cfg->param.rc.ipFactor);
 
+        rce->frameSizePlanned = predictSize(&pred[sliceType], q, lastSatd);
+
         return q;
     }
 }
 
+
+void RateControl::updateVbvPlan(Encoder* enc)
+{
+    bufferFill = bufferFillFinal;
+    enc->updateVbvPlan(this);
+}
+
+
+double RateControl::predictSize( Predictor *p, double q, double var)
+{
+    return (p->coeff*var + p->offset) / (q*p->count);
+}
+
+double RateControl::clipQscale(double q)
+{
+
+    double lmin1 = lmin[sliceType];
+    double lmax1 = lmax[sliceType];
+    double q0 = q;
+
+    // B-frames are not directly subject to VBV,
+    // since they are controlled by the P-frames' QPs. 
+    if( isVbv && lastSatd > 0 )
+    {
+        //if (lookahead){} //for lookahead
+        //else
+        {
+            if ((sliceType == P_SLICE ||
+                (sliceType == I_SLICE && lastNonBPictType == I_SLICE)) &&
+                bufferFill/bufferSize < 0.5)
+            {
+                q /= Clip3(0.5, 1.0, 2.0*bufferFill/bufferSize);
+            }
+
+            // Now a hard threshold to make sure the frame fits in VBV.
+            // This one is mostly for I-frames.
+            double bits = predictSize(&pred[sliceType], q, lastSatd);
+
+            // For small VBVs, allow the frame to use up the entire VBV. 
+            double maxFillFactor;
+            maxFillFactor = bufferSize >= 5*bufferRate ? 2 : 1;
+            // For single-frame VBVs, request that the frame use up the entire VBV. 
+            double minFillFactor = singleFrameVbv ? 1 : 2;
+
+            
+            for( int iterations = 0; iterations < 10; iterations++ )
+            {
+                double qf = 1.0;
+                if (bits > bufferFill/maxFillFactor)
+                    qf = Clip3(0.2, 1.0, bufferFill/(maxFillFactor*bits));
+                q /= qf;
+                bits *= qf;
+                if (bits < bufferRate/minFillFactor)
+                    q *= bits*minFillFactor/bufferRate;
+                bits = predictSize(&pred[sliceType], q, lastSatd);
+            }
+
+            q = X265_MAX(q0, q);
+        }
+
+        // Check B-frame complexity, and use up any bits that would
+        // overflow before the next P-frame.
+        if(sliceType == P_SLICE)
+        {
+            int nb = bframes;
+            double bits = predictSize(&pred[sliceType], q, lastSatd);
+            double pbbits = bits;
+            double bbits = predictSize(&predBfromP, q*cfg->param.rc.pbFactor, lastSatd);
+            double space;
+            if (bbits > bufferRate)
+                nb = 0;
+                pbbits = nb*bbits;
+
+            space = bufferFill + (1+nb)*bufferRate - bufferSize;
+            if (pbbits < space)
+            {
+                q *= X265_MAX(pbbits/space, bits/(0.5*bufferSize));
+            }
+            q = X265_MAX(q0-5, q);
+        }
+        q = X265_MAX(q0, q);
+    }
+
+    if (lmin1 == lmax1)
+        return lmin1;
+
+    return Clip3(lmin1, lmax1, q);
+}
+
 /* modify the bitrate curve from pass1 for one frame */
 double RateControl::getQScale(RateControlEntry *rce, double rateFactor)
 {
@@ -389,6 +572,48 @@
     return q;
 }
 
+void RateControl::updatePredictor(Predictor *p, double q, double var, double bits)
+{
+    const double range = 1.5;
+    if(var < 10)
+        return;
+    double old_coeff = p->coeff / p->count;
+    double new_coeff = bits*q / var;
+    double new_coeff_clipped = Clip3(old_coeff/range, old_coeff*range, new_coeff);
+    double new_offset = bits*q - new_coeff_clipped * var;
+    if( new_offset >= 0 )
+        new_coeff = new_coeff_clipped;
+    else
+        new_offset = 0;
+    p->count  *= p->decay;
+    p->coeff  *= p->decay;
+    p->offset *= p->decay;
+    p->count  ++;
+    p->coeff  += new_coeff;
+    p->offset += new_offset;
+}
+
+void RateControl::updateVbv(int64_t bits, RateControlEntry* rce)
+{
+    int mbCount = (int)((cfg->param.sourceHeight * cfg->param.sourceWidth) / pow((int)16, 2.0));
+
+    if  (rce->lastSatd >= mbCount)
+        updatePredictor(&pred[rce->sliceType], qp2qScale(rce->qpaRc), rce->lastSatd, (double)bits);
+
+    if (!isVbv)
+        return;
+
+    bufferFillFinal -= bits;
+
+    if (bufferFillFinal<0)
+        x265_log(&cfg->param, X265_LOG_WARNING, "poc:%d, VBV underflow (%.0f bits)\n", rce->poc, bufferFillFinal);
+
+    bufferFillFinal = X265_MAX(bufferFillFinal, 0);
+    bufferFillFinal += bufferRate;
+    bufferFillFinal = X265_MIN(bufferFillFinal, bufferSize);
+
+}
+
 /* After encoding one frame, update rate control state */
 int RateControl::rateControlEnd(int64_t bits, RateControlEntry* rce)
 {
@@ -405,8 +630,23 @@
             cplxrSum += bits * qp2qScale(rce->qpaRc) / (rce->qRceq * fabs(cfg->param.rc.pbFactor));
         }
         wantedBitsWindow += frameDuration * bitrate;
-        rce = NULL;
     }
     totalBits += bits;
+
+    if(isVbv)
+    {
+        if (rce->sliceType == B_SLICE)
+        {
+            bframeBits += (int)bits;
+            if (rce->bLastMiniGopBFrame)
+            {
+                if (rce->bframes != 0)
+                    updatePredictor(&predBfromP, qp2qScale(rce->qpaRc), (double)rce->lastSatd, (double)bframeBits/rce->bframes);
+                bframeBits = 0;
+            }
+        }
+    }
+
+    updateVbv(bits, rce);
     return 0;
 }
diff -r 9d74638c3640 -r ea264e7350c3 source/encoder/ratecontrol.h
--- a/source/encoder/ratecontrol.h	Sat Nov 09 20:14:24 2013 -0600
+++ b/source/encoder/ratecontrol.h	Fri Nov 08 18:41:21 2013 +0800
@@ -3,6 +3,7 @@
  *
  * Authors: Sumalatha Polureddy <sumalatha at multicorewareinc.com>
  *          Aarthi Priya Thirumalai <aarthi at multicorewareinc.com>
+ *          Xun Xu, PPLive Corporation <xunxu at pptv.com>
  *
  * 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
@@ -26,6 +27,7 @@
 #define X265_RATECONTROL_H
 
 #include "TLibCommon/CommonDef.h"
+#include "encoder.h"
 
 namespace x265 {
 // encoder namespace
@@ -42,6 +44,21 @@
     double blurredComplexity;
     double qpaRc;
     double qRceq;
+
+    int lastSatd;
+    bool bLastMiniGopBFrame; 
+    double frameSizePlanned; 
+    double bufferRate;
+    int bframes;
+    int poc;
+};
+
+struct Predictor
+{
+    double coeff;
+    double count;
+    double decay;
+    double offset;
 };
 
 struct RateControl
@@ -58,6 +75,20 @@
     double rateFactorConstant;
     bool   isAbr;
 
+    double bufferSize;
+    double bufferFillFinal;	 /* real buffer as of the last finished frame */
+    double bufferFill;       /* planned buffer, if all in-progress frames hit their bit budget */
+    double bufferRate;       /* # of bits added to buffer_fill after each frame */
+    double vbvMaxRate; //kbit
+    bool singleFrameVbv;
+    bool isVbv;
+    double fps;
+    Predictor pred[5];
+    Predictor predBfromP;
+    int bframes;
+    int bframeBits;
+    double leadingNoBSatd;
+
     int    lastSatd;
     int    qpConstant[3];
     double cplxrSum;          /* sum of bits*qscale/rceq */
@@ -79,7 +110,7 @@
     RateControl(TEncCfg * _cfg);
 
     // to be called for each frame to process RateControl and set QP
-    void rateControlStart(TComPic* pic, Lookahead *, RateControlEntry* rce);
+    void rateControlStart(TComPic* pic, Lookahead *, RateControlEntry* rce, Encoder* enc);
     void calcAdaptiveQuantFrame(TComPic *pic);
     int rateControlEnd(int64_t bits, RateControlEntry* rce);
 
@@ -89,6 +120,12 @@
     double rateEstimateQscale(RateControlEntry *rce); // main logic for calculating QP based on ABR
     void accumPQpUpdate();
     double acEnergyCu(TComPic* pic, uint32_t block_x, uint32_t block_y);
+
+    void updateVbv(int64_t bits, RateControlEntry* rce); 
+    void updatePredictor(Predictor *p, double q, double var, double bits); 
+    double clipQscale(double q); 
+    void updateVbvPlan(Encoder* enc); 
+    double predictSize( Predictor *p, double q, double var);
 };
 }
 
diff -r 9d74638c3640 -r ea264e7350c3 source/x265.cpp
--- a/source/x265.cpp	Sat Nov 09 20:14:24 2013 -0600
+++ b/source/x265.cpp	Fri Nov 08 18:41:21 2013 +0800
@@ -120,6 +120,9 @@
     { "no-weightp",           no_argument, NULL, 0 },
     { "weightp",              no_argument, NULL, 'w' },
     { "crf",            required_argument, NULL, 0 },
+    { "vbv-maxrate",    required_argument, NULL, 0 },
+    { "vbv-bufsize",    required_argument, NULL, 0 },
+    { "vbv-init",       required_argument, NULL, 0 },
     { "bitrate",        required_argument, NULL, 0 },
     { "qp",             required_argument, NULL, 'q' },
     { "aq-mode",        required_argument, NULL, 0 },
@@ -313,6 +316,9 @@
     H0("\nQP, rate control and rate distortion options:\n");
     H0("   --bitrate                     Target bitrate (kbps), implies ABR. Default %d\n", param->rc.bitrate);
     H0("   --crf                         Quality-based VBR (0-51). Default %f\n", param->rc.rfConstant);
+    H0("   --vbv-maxrate                 Max local bitrate (kbit/s). Default %d\n", param->rc.vbvMaxBitrate);
+    H0("   --vbv-bufsize                 Set size of the VBV buffer (kbit). Default %d\n", param->rc.vbvBufferSize);
+    H0("   --vbv-init                    Initial VBV buffer occupancy. Default %f\n", param->rc.vbvBufferInit);
     H0("-q/--qp                          Base QP for CQP mode. Default %d\n", param->rc.qp);
     H0("   --aq-mode                     Mode for Adaptive Quantization - 0:none 1:aqVariance Default %d\n", param->rc.aqMode);
     H0("   --cbqpoffs                    Chroma Cb QP Offset. Default %d\n", param->cbQpOffset);
diff -r 9d74638c3640 -r ea264e7350c3 source/x265.h
--- a/source/x265.h	Sat Nov 09 20:14:24 2013 -0600
+++ b/source/x265.h	Fri Nov 08 18:41:21 2013 +0800
@@ -374,6 +374,9 @@
         double    rfConstant;                  ///< Constant rate factor (CRF)
         int       aqMode;                      ///< Adaptive QP (AQ)
         double    aqStrength;
+        int       vbvMaxBitrate; 
+        int       vbvBufferSize;
+        double    vbvBufferInit;
     } rc;
 } x265_param;
 


More information about the x265-devel mailing list