[x265-commits] [x265] rc: initial work towards VBV rate control

Xun at videolan.org Xun at videolan.org
Sun Dec 1 21:33:48 CET 2013


details:   http://hg.videolan.org/x265/rev/8a90153de720
branches:  
changeset: 5399:8a90153de720
user:      Xun Xu, PPLive Corporation<xunxu at pptv.com>
date:      Fri Nov 08 18:41:21 2013 +0800
description:
rc: initial work towards VBV rate control

1. add parameter "vbv-maxrate" "vbv-bufsize" "vbv-init" into cmd line
2. implement vbv methods, this patch doesn't use lookahead data

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

diffstat:

 source/common/common.cpp        |    6 +
 source/encoder/encoder.cpp      |   20 +++-
 source/encoder/encoder.h        |    2 +
 source/encoder/frameencoder.cpp |    1 +
 source/encoder/ratecontrol.cpp  |  246 +++++++++++++++++++++++++++++++++++++++-
 source/encoder/ratecontrol.h    |   39 ++++++-
 source/x265.cpp                 |    6 +
 source/x265.h                   |    3 +
 8 files changed, 317 insertions(+), 6 deletions(-)

diffs (truncated from 539 to 300 lines):

diff -r 343d9ba487b2 -r 8a90153de720 source/common/common.cpp
--- a/source/common/common.cpp	Sun Dec 01 14:16:46 2013 -0600
+++ b/source/common/common.cpp	Fri Nov 08 18:41:21 2013 +0800
@@ -205,6 +205,9 @@ void x265_param_default(x265_param *para
     param->rdPenalty = 0;
 
     /* 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;
@@ -718,6 +721,9 @@ int x265_param_parse(x265_param *p, cons
     OPT("hash") p->decodedPictureHashSEI = atoi(value);
     OPT("aq-mode") p->rc.aqMode = atoi(value);
     OPT("aq-strength") p->rc.aqStrength = atof(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 343d9ba487b2 -r 8a90153de720 source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp	Sun Dec 01 14:16:46 2013 -0600
+++ b/source/encoder/encoder.cpp	Fri Nov 08 18:41:21 2013 +0800
@@ -190,6 +190,24 @@ int Encoder::getStreamHeaders(NALUnitEBS
     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
@@ -313,7 +331,7 @@ int Encoder::encode(bool flush, const x2
         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 343d9ba487b2 -r 8a90153de720 source/encoder/encoder.h
--- a/source/encoder/encoder.h	Sun Dec 01 14:16:46 2013 -0600
+++ b/source/encoder/encoder.h	Fri Nov 08 18:41:21 2013 +0800
@@ -134,6 +134,8 @@ public:
 
     int  extractNalData(NALUnitEBSP **nalunits);
 
+    void updateVbvPlan(RateControl* rc);
+
 protected:
 
     uint64_t calculateHashAndPSNR(TComPic* pic, NALUnitEBSP **nalunits); // Returns total number of bits for encoded pic
diff -r 343d9ba487b2 -r 8a90153de720 source/encoder/frameencoder.cpp
--- a/source/encoder/frameencoder.cpp	Sun Dec 01 14:16:46 2013 -0600
+++ b/source/encoder/frameencoder.cpp	Fri Nov 08 18:41:21 2013 +0800
@@ -57,6 +57,7 @@ FrameEncoder::FrameEncoder()
 
     m_nalCount = 0;
     m_totalTime = 0;
+    memset(&m_rce, 0, sizeof(RateControlEntry));
 }
 
 void FrameEncoder::setThreadPool(ThreadPool *p)
diff -r 343d9ba487b2 -r 8a90153de720 source/encoder/ratecontrol.cpp
--- a/source/encoder/ratecontrol.cpp	Sun Dec 01 14:16:46 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
@@ -23,9 +24,10 @@
  *****************************************************************************/
 
 #include "TLibCommon/TComPic.h"
+#include "TLibEncoder/TEncCfg.h"
+#include "encoder.h"
 #include "slicetype.h"
 #include "ratecontrol.h"
-#include "TLibEncoder/TEncCfg.h"
 #include <math.h>
 
 using namespace x265;
@@ -146,7 +148,7 @@ RateControl::RateControl(TEncCfg * _cfg)
         cfg->param.rc.bitrate = 0;
 
         double baseCplx = ncu * (cfg->param.bframes ? 120 : 80);
-        double mbtree_offset = 0; //added later
+        double mbtree_offset = 0; // added later
         rateFactorConstant = pow(baseCplx, 1 - cfg->param.rc.qCompress) /
             qp2qScale(cfg->param.rc.rfConstant + mbtree_offset + QP_BD_OFFSET);
     }
@@ -165,6 +167,77 @@ RateControl::RateControl(TEncCfg * _cfg)
     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 */
@@ -206,12 +279,24 @@ RateControl::RateControl(TEncCfg * _cfg)
     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)
+        rce->bframes = bframes;
+    else
+        bframes = pic->m_lowres.leadingBframes;
+    
+    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);
@@ -220,6 +305,7 @@ void RateControl::rateControlStart(TComP
         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
@@ -281,6 +367,8 @@ double RateControl::rateEstimateQscale(R
         else
             q += pbOffset;
 
+        rce->frameSizePlanned = predictSize(&predBfromP, qp2qScale(q), leadingNoBSatd);
+
         return qp2qScale(q);
     }
     else
@@ -371,15 +459,108 @@ double RateControl::rateEstimateQscale(R
         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++ )
+            {


More information about the x265-commits mailing list