[x265] [PATCH] ratecontrol: improve visual quality and bitrate savings in ABR

aarthi at multicorewareinc.com aarthi at multicorewareinc.com
Thu May 29 20:10:02 CEST 2014


# HG changeset patch
# User Aarthi Thirumalai
# Date 1401386381 -19800
#      Thu May 29 23:29:41 2014 +0530
# Node ID cbefd4760814f85da2f805781177a1fade437709
# Parent  e9776dfd1471ec6691276518007b725095ab6d52
ratecontrol: improve visual quality and bitrate savings in ABR.

Try to prevent ABR over-compensation after I frames by amortizing the cost over
the next few frames;

Improve ABR quality with frame parallelism - enable frame parallelism only after first
few P frames to prevent excessive qp fluctuations.

Fix initial I frame qp. when cu tree is enabled, the qp decided arbitrarily is
too low. This causes a huge qp spike in immediate P frames.Tuned cplxrSum
for intial I frame so that a more appropriate qp is chosen.

diff -r e9776dfd1471 -r cbefd4760814 source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp	Wed May 28 20:39:40 2014 -0500
+++ b/source/encoder/encoder.cpp	Thu May 29 23:29:41 2014 +0530
@@ -186,6 +186,7 @@
     }
     m_lookahead->init();
     m_encodeStartTime = x265_mdate();
+    m_totalFrameThreads = param->frameNumThreads;
 }
 
 int Encoder::getStreamHeaders(NALUnitEBSP **nalunits)
@@ -323,6 +324,20 @@
     if (flush)
         m_lookahead->flush();
 
+    if (param->rc.rateControlMode == X265_RC_ABR)
+    {
+        // delay frame parallelism for non-VBV ABR
+        if (m_pocLast == 0 && !param->rc.vbvBufferSize && !param->rc.vbvMaxBitrate)
+            param->frameNumThreads = 1;
+        else if (param->frameNumThreads != m_totalFrameThreads)
+        {
+            // re-enable frame parallelism after the first few P frames are encoded
+            uint32_t frameCnt = (uint32_t)((0.5 * param->fpsNum / param->fpsDenom) / (param->bframes + 1));
+            if (m_analyzeP.m_numPics > frameCnt)
+                param->frameNumThreads = m_totalFrameThreads;
+        }
+    }
+
     FrameEncoder *curEncoder = &m_frameEncoder[m_curEncoder];
     m_curEncoder = (m_curEncoder + 1) % param->frameNumThreads;
     int ret = 0;
diff -r e9776dfd1471 -r cbefd4760814 source/encoder/encoder.h
--- a/source/encoder/encoder.h	Wed May 28 20:39:40 2014 -0500
+++ b/source/encoder/encoder.h	Thu May 29 23:29:41 2014 +0530
@@ -90,6 +90,7 @@
     DPB*               m_dpb;
     /* frame parallelism */
     int                m_curEncoder;
+    int                m_totalFrameThreads;
 
     /* Collect statistics globally */
     EncStats           m_analyzeAll;
diff -r e9776dfd1471 -r cbefd4760814 source/encoder/ratecontrol.cpp
--- a/source/encoder/ratecontrol.cpp	Wed May 28 20:39:40 2014 -0500
+++ b/source/encoder/ratecontrol.cpp	Thu May 29 23:29:41 2014 +0530
@@ -30,6 +30,10 @@
 
 using namespace x265;
 
+/* Amortize the partial cost of I frames over the next N frames */
+const double RateControl::amortizeFraction = 0.85;
+const int RateControl::amortizeFrames = 75;
+
 /* Compute variance to derive AC energy of each block */
 static inline uint32_t acEnergyVar(TComPic *pic, uint64_t sum_ssd, int shift, int i)
 {
@@ -204,6 +208,8 @@
         qCompress = param->rc.qCompress;
 
     // validate for param->rc, maybe it is need to add a function like x265_parameters_valiate()
+    residualFrames = 0;
+    residualCost = 0;
     param->rc.rfConstant = Clip3((double)-QP_BD_OFFSET, (double)51, param->rc.rfConstant);
     param->rc.rfConstantMax = Clip3((double)-QP_BD_OFFSET, (double)51, param->rc.rfConstantMax);
     rateFactorMaxIncrement = 0;
@@ -316,7 +322,7 @@
     {
         /* Adjust the first frame in order to stabilize the quality level compared to the rest */
 #define ABR_INIT_QP_MIN (24 + QP_BD_OFFSET)
-#define ABR_INIT_QP_MAX (34 + QP_BD_OFFSET)
+#define ABR_INIT_QP_MAX (40 + QP_BD_OFFSET)
     }
     else if (param->rc.rateControlMode == X265_RC_CRF)
     {
@@ -353,9 +359,12 @@
 {
     totalBits = 0;
     framesDone = 0;
-
+    double tuneCplxFactor = 1;
+    /* 720p videos seem to be a good cutoff for cplxrSum */
+    if (param->rc.cuTree && ncu > 3600)
+        tuneCplxFactor = 2.5;
     /* estimated ratio that produces a reasonable QP for the first I-frame */
-    cplxrSum = .01 * pow(7.0e5, qCompress) * pow(ncu, 0.5);
+    cplxrSum = .01 * pow(7.0e5, qCompress) * pow(ncu, 0.5) * tuneCplxFactor;
     wantedBitsWindow = bitrate * frameDuration;
     accumPNorm = .01;
     accumPQp = (param->rc.rateControlMode == X265_RC_CRF ? CRF_INIT_QP : ABR_INIT_QP_MIN) * accumPNorm;
@@ -550,7 +559,7 @@
                 /* use framesDone instead of POC as poc count is not serial with bframes enabled */
                 double timeDone = (double)(framesDone - param->frameNumThreads + 1) * frameDuration;
                 wantedBits = timeDone * bitrate;
-                if (wantedBits > 0 && totalBits > 0)
+                if (wantedBits > 0 && totalBits > 0 && !residualFrames)
                 {
                     abrBuffer *= X265_MAX(1, sqrt(timeDone));
                     overflow = Clip3(.5, 2.0, 1.0 + (totalBits - wantedBits) / abrBuffer);
@@ -572,10 +581,13 @@
                 double lqmin = 0, lqmax = 0;
                 lqmin = lastQScaleFor[sliceType] / lstep;
                 lqmax = lastQScaleFor[sliceType] * lstep;
-                if (overflow > 1.1 && framesDone > 3)
-                    lqmax *= lstep;
-                else if (overflow < 0.9)
-                    lqmin /= lstep;
+                if (!residualFrames)
+                {
+                    if (overflow > 1.1 && framesDone > 3)
+                        lqmax *= lstep;
+                    else if (overflow < 0.9)
+                        lqmin /= lstep;
+                }
                 q = Clip3(lqmin, lqmax, q);
             }
         }
@@ -1083,6 +1095,24 @@
                 }
             }
 
+            /* amortize part of each I slice over the next several frames, up to
+             * keyint-max, to avoid over-compensating for the large I slice cost */
+            if (rce->sliceType == I_SLICE)
+            {
+                /* previous I still had a residual; roll it into the new loan */
+                if (residualFrames)
+                    bits += residualCost * residualFrames;
+
+                residualFrames = X265_MIN(amortizeFrames, param->keyframeMax);
+                residualCost = (int)((bits * amortizeFraction) / residualFrames);
+                bits -= residualCost * residualFrames;
+            }
+            else if (residualFrames)
+            {
+                bits += residualCost;
+                residualFrames--;
+            }
+
             if (rce->sliceType != B_SLICE)
                 /* The factor 1.5 is to tune up the actual bits, otherwise the cplxrSum is scaled too low
                  * to improve short term compensation for next frame. */
@@ -1111,7 +1141,7 @@
             }
         }
         updateVbv(bits, rce);
-        rce->isActive = false;
     }
+    rce->isActive = false;
     return 0;
 }
diff -r e9776dfd1471 -r cbefd4760814 source/encoder/ratecontrol.h
--- a/source/encoder/ratecontrol.h	Wed May 28 20:39:40 2014 -0500
+++ b/source/encoder/ratecontrol.h	Thu May 29 23:29:41 2014 +0530
@@ -132,6 +132,12 @@
 
 protected:
 
+    static const double amortizeFraction;
+    static const int amortizeFrames;
+
+    int residualFrames;
+    int residualCost;
+
     void init();
     double getQScale(RateControlEntry *rce, double rateFactor);
     double rateEstimateQscale(TComPic* pic, RateControlEntry *rce); // main logic for calculating QP based on ABR


More information about the x265-devel mailing list