[x265] [PATCH] rc: add CRF ratecontrol

Steve Borho steve at borho.org
Fri Nov 1 19:09:58 CET 2013


# HG changeset patch
# User idxa<idxa at sina.com>
# Date 1383123587 -28800
#      Wed Oct 30 16:59:47 2013 +0800
# Node ID 8585af48e92116fc1ce71c36661e91816739f1ae
# Parent  0d79e31728a463b676b3ed790cfcc063c6cd4055
rc: add CRF ratecontrol

1. add a parameter of "--crf" to the command line

2. modify the running branches of rateControlStart, using "if(isAbr)" instead of
"switch (cfg->param.rc.rateControlMode)", for the logic of classifying the
combination of multiple ratecontrol methods is very complex, it is not only
based on rateControlMode, so porting x264's way looks feasible.

3. add crf method into x265

diff -r 0d79e31728a4 -r 8585af48e921 source/common/common.cpp
--- a/source/common/common.cpp	Fri Nov 01 13:05:34 2013 -0500
+++ b/source/common/common.cpp	Wed Oct 30 16:59:47 2013 +0800
@@ -185,6 +185,7 @@
     param->saoLcuBasedOptimization = 1;
 
     /* Rate control options */
+    param->rc.rfConstant = 28;
     param->rc.bitrate = 0;
     param->rc.rateTolerance = 1.0;
     param->rc.qCompress = 0.6;
@@ -569,7 +570,7 @@
         x265_log(param, X265_LOG_INFO, "Rate Control                 : CQP-%d\n", param->rc.qp);
         break;
     case X265_RC_CRF:
-        x265_log(param, X265_LOG_INFO, "Rate Control                 : CRF-%d\n", param->rc.rateFactor);
+        x265_log(param, X265_LOG_INFO, "Rate Control                 : CRF-%f\n", param->rc.rfConstant);
         break;
     }
 
@@ -710,10 +711,21 @@
         p->maxNumReferences = atoi(value);
     OPT("weightp")
         p->bEnableWeightedPred = bvalue;
+    OPT("crf")
+    {
+        p->rc.rfConstant = atof(value);
+        p->rc.rateControlMode = X265_RC_CRF;
+    }
     OPT("bitrate")
+    {
         p->rc.bitrate = atoi(value);
+        p->rc.rateControlMode = X265_RC_ABR;
+    }
     OPT("qp")
+    {
         p->rc.qp = atoi(value);
+        p->rc.rateControlMode = X265_RC_CQP;
+    }
     OPT("cbqpoffs")
         p->cbQpOffset = atoi(value);
     OPT("crqpoffs")
diff -r 0d79e31728a4 -r 8585af48e921 source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp	Fri Nov 01 13:05:34 2013 -0500
+++ b/source/encoder/encoder.cpp	Wed Oct 30 16:59:47 2013 +0800
@@ -997,11 +997,6 @@
     {
         _param->bEnableAMP = false;
     }
-    // if a bitrate is specified, chose ABR.  Else default to CQP
-    if (_param->rc.bitrate)
-    {
-        _param->rc.rateControlMode = X265_RC_ABR;
-    }
 
     if (!(_param->bEnableRDOQ && _param->bEnableTransformSkip))
     {
diff -r 0d79e31728a4 -r 8585af48e921 source/encoder/ratecontrol.cpp
--- a/source/encoder/ratecontrol.cpp	Fri Nov 01 13:05:34 2013 -0500
+++ b/source/encoder/ratecontrol.cpp	Wed Oct 30 16:59:47 2013 +0800
@@ -115,9 +115,25 @@
 RateControl::RateControl(TEncCfg * _cfg)
 {
     this->cfg = _cfg;
+    ncu = (int)((cfg->param.sourceHeight * cfg->param.sourceWidth) / pow((int)16, 2.0));
+
+    // validate for cfg->param.rc, maybe it is need to add a function like x265_parameters_valiate()
+    cfg->param.rc.rfConstant = Clip3((double)-QP_BD_OFFSET, (double)51, cfg->param.rc.rfConstant);
+    if (cfg->param.rc.rateControlMode == X265_RC_CRF)
+    {
+        cfg->param.rc.qp = (int)cfg->param.rc.rfConstant + QP_BD_OFFSET;
+        cfg->param.rc.bitrate = 0;
+
+        double baseCplx = ncu * (cfg->param.bframes ? 120 : 80);
+        double mbtree_offset = 0;//added later
+        rateFactorConstant = pow(baseCplx, 1 - cfg->param.rc.qCompress) /
+                             qp2qScale(cfg->param.rc.rfConstant + mbtree_offset + QP_BD_OFFSET);
+    }
+
+    isAbr = cfg->param.rc.rateControlMode != X265_RC_CQP; // later add 2pass option
+
     bitrate = cfg->param.rc.bitrate * 1000;
     frameDuration = 1.0 / cfg->param.frameRate;
-    ncu = (int)((cfg->param.sourceHeight * cfg->param.sourceWidth) / pow((int)16, 2.0));
     lastNonBPictType = -1;
     baseQp = cfg->param.rc.qp;
     qp = baseQp;
@@ -126,6 +142,7 @@
     shortTermCplxSum = 0;
     shortTermCplxCount = 0;
     framesDone = 0;
+    lastNonBPictType = I_SLICE;
 
     if (cfg->param.rc.rateControlMode == X265_RC_ABR)
     {
@@ -137,8 +154,17 @@
         /* estimated ratio that produces a reasonable QP for the first I-frame */
         cplxrSum = .01 * pow(7.0e5, cfg->param.rc.qCompress) * pow(ncu, 0.5);
         wantedBitsWindow = bitrate * frameDuration;
-        lastNonBPictType = I_SLICE;
     }
+    else if (cfg->param.rc.rateControlMode == X265_RC_CRF)
+    {
+#define ABR_INIT_QP ((int)cfg->param.rc.rfConstant + QP_BD_OFFSET)
+        accumPNorm = .01;
+        accumPQp = ABR_INIT_QP * accumPNorm;
+        /* estimated ratio that produces a reasonable QP for the first I-frame */
+        cplxrSum = .01 * pow(7.0e5, cfg->param.rc.qCompress) * pow(ncu, 0.5);
+        wantedBitsWindow = bitrate * frameDuration;
+    }
+
     ipOffset = 6.0 * X265_LOG2(cfg->param.rc.ipFactor);
     pbOffset = 6.0 * X265_LOG2(cfg->param.rc.pbFactor);
     for (int i = 0; i < 3; i++)
@@ -164,9 +190,8 @@
     curSlice = pic->getSlice();
     sliceType = curSlice->getSliceType();
     rce->sliceType = sliceType;
-    switch (cfg->param.rc.rateControlMode)
-    {
-    case X265_RC_ABR:
+
+    if (isAbr) //ABR,CRF
     {
         lastSatd = l->getEstimatedPictureCost(pic);
         double q = qScale2qp(rateEstimateQscale(rce));
@@ -175,17 +200,10 @@
         /* copy value of lastRceq into thread local rce struct *to be used in RateControlEnd() */
         rce->qRceq = lastRceq;
         accumPQpUpdate();
-        break;
     }
-
-    case X265_RC_CQP:
+    else //CQP
+    {
         qp = qpConstant[sliceType];
-        break;
-
-    case X265_RC_CRF:
-    default:
-        assert(!"unimplemented");
-        break;
     }
 
     if (sliceType != B_SLICE)
@@ -269,20 +287,28 @@
         rce->blurredComplexity = shortTermCplxSum / shortTermCplxCount;
         rce->mvBits = 0;
         rce->sliceType = sliceType;
-        q = getQScale(rce, wantedBitsWindow / cplxrSum);
 
-        /* ABR code can potentially be counterproductive in CBR, so just don't bother.
-         * Don't run it if the frame complexity is zero either. */
-        if (lastSatd)
+        if (cfg->param.rc.rateControlMode == X265_RC_CRF)
         {
-            /* use framesDone instead of POC as poc count is not serial with bframes enabled */
-            double timeDone = (double)(framesDone - cfg->param.frameNumThreads + 1) / cfg->param.frameRate;
-            wantedBits = timeDone * bitrate;
-            if (wantedBits > 0 && totalBits > 0)
+            q = getQScale(rce, rateFactorConstant);
+        }
+        else
+        {
+            q = getQScale(rce, wantedBitsWindow / cplxrSum);
+
+            /* ABR code can potentially be counterproductive in CBR, so just don't bother.
+             * Don't run it if the frame complexity is zero either. */
+            if (lastSatd)
             {
-                abrBuffer *= X265_MAX(1, sqrt(timeDone));
-                overflow = Clip3(.5, 2.0, 1.0 + (totalBits - wantedBits) / abrBuffer);
-                q *= overflow;
+                /* use framesDone instead of POC as poc count is not serial with bframes enabled */
+                double timeDone = (double)(framesDone - cfg->param.frameNumThreads + 1) / cfg->param.frameRate;
+                wantedBits = timeDone * bitrate;
+                if (wantedBits > 0 && totalBits > 0)
+                {
+                    abrBuffer *= X265_MAX(1, sqrt(timeDone));
+                    overflow = Clip3(.5, 2.0, 1.0 + (totalBits - wantedBits) / abrBuffer);
+                    q *= overflow;
+                }
             }
         }
 
@@ -292,7 +318,7 @@
             q = qp2qScale(accumPQp / accumPNorm);
             q /= fabs(cfg->param.rc.ipFactor);
         }
-        else if (framesDone>0)
+        else if (framesDone > 0)
         {
             if (cfg->param.rc.rateControlMode != X265_RC_CRF)
             {
@@ -350,7 +376,7 @@
 /* After encoding one frame, update rate control state */
 int RateControl::rateControlEnd(int64_t bits, RateControlEntry* rce)
 {
-    if (cfg->param.rc.rateControlMode == X265_RC_ABR)
+    if (isAbr)
     {
         if (rce->sliceType != B_SLICE)
             /* The factor 1.5 is to tune up the actual bits, otherwise the cplxrSum is scaled too low
diff -r 0d79e31728a4 -r 8585af48e921 source/encoder/ratecontrol.h
--- a/source/encoder/ratecontrol.h	Fri Nov 01 13:05:34 2013 -0500
+++ b/source/encoder/ratecontrol.h	Wed Oct 30 16:59:47 2013 +0800
@@ -55,6 +55,9 @@
     int baseQp;               /* CQP base QP */
     double frameDuration;     /* current frame duration in seconds */
     double bitrate;
+    double rateFactorConstant;
+    bool   isAbr;
+
     int    lastSatd;
     int    qpConstant[3];
     double cplxrSum;          /* sum of bits*qscale/rceq */
diff -r 0d79e31728a4 -r 8585af48e921 source/x265.cpp
--- a/source/x265.cpp	Fri Nov 01 13:05:34 2013 -0500
+++ b/source/x265.cpp	Wed Oct 30 16:59:47 2013 +0800
@@ -117,6 +117,7 @@
     { "ref",            required_argument, NULL, 0 },
     { "no-weightp",           no_argument, NULL, 0 },
     { "weightp",              no_argument, NULL, 'w' },
+    { "crf",            required_argument, NULL, 0 },
     { "bitrate",        required_argument, NULL, 0 },
     { "qp",             required_argument, NULL, 'q' },
     { "cbqpoffs",       required_argument, NULL, 0 },
@@ -307,6 +308,7 @@
     H0("-w/--[no-]weightp                Enable weighted prediction in P slices. Default %s\n", OPT(param->bEnableWeightedPred));
     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("-q/--qp                          Base QP for CQP mode. Default %d\n", param->rc.qp);
     H0("   --cbqpoffs                    Chroma Cb QP Offset. Default %d\n", param->cbQpOffset);
     H0("   --crqpoffs                    Chroma Cr QP Offset. Default %d\n", param->crQpOffset);
diff -r 0d79e31728a4 -r 8585af48e921 source/x265.h
--- a/source/x265.h	Fri Nov 01 13:05:34 2013 -0500
+++ b/source/x265.h	Wed Oct 30 16:59:47 2013 +0800
@@ -329,7 +329,7 @@
         int       qpStep;
         int       rateControlMode;             ///<Values corresponding to RcMethod
         int       qp;                          ///< Constant QP base value
-        int       rateFactor;                  ///< Constant rate factor (CRF)
+        double    rfConstant;                  ///< Constant rate factor (CRF)
         int       aqMode;                      ///< Adaptive QP (AQ)
         double    aqStrength;
     } rc;


More information about the x265-devel mailing list