[x265] [PATCH] rc: fix Rate Control for grainy content

aarthi at multicorewareinc.com aarthi at multicorewareinc.com
Fri Jan 29 12:19:09 CET 2016


# HG changeset patch
# User Aarthi Thirumalai
# Date 1452501832 -19800
#      Mon Jan 11 14:13:52 2016 +0530
# Node ID 2e414814c585867eda65b8c7ada434f05533d50a
# Parent  f548abe8eae8fb75513a85d1b09233e706c7b5ba
rc: fix Rate Control for grainy content

optimize params for tune grain, reduce frequent qp fluctions to prevent grain loss

diff -r f548abe8eae8 -r 2e414814c585 source/common/param.cpp
--- a/source/common/param.cpp	Wed Jan 20 18:27:42 2016 +0530
+++ b/source/common/param.cpp	Mon Jan 11 14:13:52 2016 +0530
@@ -222,6 +222,7 @@
     param->rc.zones = NULL;
     param->rc.bEnableSlowFirstPass = 0;
     param->rc.bStrictCbr = 0;
+    param->rc.bEnableGrain = 0;
 
     /* Video Usability Information (VUI) */
     param->vui.aspectRatioIdc = 0;
@@ -454,16 +455,12 @@
         }
         else if (!strcmp(tune, "grain"))
         {
-            param->deblockingFilterBetaOffset = -2;
-            param->deblockingFilterTCOffset = -2;
-            param->bIntraInBFrames = 0;
-            param->rdoqLevel = 2;
-            param->psyRdoq = 10.0;
-            param->psyRd = 0.5;
             param->rc.ipFactor = 1.1;
-            param->rc.pbFactor = 1.1;
-            param->rc.aqStrength = 0.3;
-            param->rc.qCompress = 0.8;
+            param->rc.pbFactor = 1.0;
+            param->rc.cuTree = 0;
+            param->rc.aqMode = 0;
+            param->rc.qpStep = 1.0;
+            param->rc.bEnableGrain = 1;
         }
         else
             return -1;
diff -r f548abe8eae8 -r 2e414814c585 source/encoder/ratecontrol.cpp
--- a/source/encoder/ratecontrol.cpp	Wed Jan 20 18:27:42 2016 +0530
+++ b/source/encoder/ratecontrol.cpp	Mon Jan 11 14:13:52 2016 +0530
@@ -190,6 +190,8 @@
     m_numEntries = 0;
     m_isSceneTransition = false;
     m_lastPredictorReset = 0;
+    m_avgPFrameQp = 0;
+    m_isFirstMiniGop = false;
     if (m_param->rc.rateControlMode == X265_RC_CRF)
     {
         m_param->rc.qp = (int)m_param->rc.rfConstant;
@@ -288,9 +290,13 @@
     m_ipOffset = 6.0 * X265_LOG2(m_param->rc.ipFactor);
     m_pbOffset = 6.0 * X265_LOG2(m_param->rc.pbFactor);
 
+    for (int i = 0; i < QP_MAX_MAX; i++)
+        m_qpToEncodedBits[i] = 0;
+
     /* Adjust the first frame in order to stabilize the quality level compared to the rest */
 #define ABR_INIT_QP_MIN (24)
 #define ABR_INIT_QP_MAX (40)
+#define ABR_INIT_QP_GRAIN_MAX (32)
 #define ABR_SCENECUT_INIT_QP_MIN (12)
 #define CRF_INIT_QP (int)m_param->rc.rfConstant
     for (int i = 0; i < 3; i++)
@@ -361,6 +367,7 @@
         m_amortizeFraction = 0.85;
         m_amortizeFrames = m_param->totalFrames / 2;
     }
+
     for (int i = 0; i < s_slidingWindowFrames; i++)
     {
         m_satdCostWindow[i] = 0;
@@ -370,15 +377,19 @@
     m_isPatternPresent = false;
     m_numBframesInPattern = 0;
 
+        m_isGrainEnabled = false;
+        if(m_param->rc.bEnableGrain) // tune for grainy content OR equal p-b frame sizes
+        m_isGrainEnabled = true;
+
     /* 720p videos seem to be a good cutoff for cplxrSum */
-    double tuneCplxFactor = (m_param->rc.cuTree && m_ncu > 3600) ? 2.5 : 1;
-
+    double tuneCplxFactor = (m_ncu > 3600 && m_param->rc.cuTree) ? 2.5 : m_isGrainEnabled ? 1.9 : 1;
     /* estimated ratio that produces a reasonable QP for the first I-frame */
     m_cplxrSum = .01 * pow(7.0e5, m_qCompress) * pow(m_ncu, 0.5) * tuneCplxFactor;
     m_wantedBitsWindow = m_bitrate * m_frameDuration;
     m_accumPNorm = .01;
     m_accumPQp = (m_param->rc.rateControlMode == X265_RC_CRF ? CRF_INIT_QP : ABR_INIT_QP_MIN) * m_accumPNorm;
 
+
     /* Frame Predictors used in vbv */
     initFramePredictors();
     if (!m_statFileOut && (m_param->rc.bStatWrite || m_param->rc.bStatRead))
@@ -1049,12 +1060,12 @@
     }
     m_pred[0].coeff = m_pred[3].coeff = 0.75;
     m_pred[0].coeffMin = m_pred[3].coeffMin = 0.75 / 4;
-    if (m_param->rc.qCompress >= 0.8) // when tuned for grain 
+    if (m_isGrainEnabled) // when tuned for grain 
     {
         m_pred[1].coeffMin = 0.75 / 4;
         m_pred[1].coeff = 0.75;
-        m_pred[0].coeff = m_pred[3].coeff = 0.5;
-        m_pred[0].coeffMin = m_pred[3].coeffMin = 0.5 / 4;
+        m_pred[0].coeff = m_pred[3].coeff = 0.75;
+        m_pred[0].coeffMin = m_pred[3].coeffMin = 0.75 / 4;
     }
 }
 
@@ -1089,10 +1100,12 @@
     }
     rce->isActive = true;
     bool isRefFrameScenecut = m_sliceType!= I_SLICE && m_curSlice->m_refFrameList[0][0]->m_lowres.bScenecut;
+    m_isFirstMiniGop = m_sliceType == I_SLICE ? true : m_isFirstMiniGop;
     if (curFrame->m_lowres.bScenecut)
     {
         m_isSceneTransition = true;
         m_lastPredictorReset = rce->encodeOrder;
+
         initFramePredictors();
     }
     else if (m_sliceType != B_SLICE && !isRefFrameScenecut)
@@ -1197,7 +1210,7 @@
         double q = x265_qScale2qp(rateEstimateQscale(curFrame, rce));
         q = x265_clip3((double)QP_MIN, (double)QP_MAX_MAX, q);
         m_qp = int(q + 0.5);
-        rce->qpaRc = curEncData.m_avgQpRc = curEncData.m_avgQpAq = q;
+        rce->qpaRc = curEncData.m_avgQpRc = curEncData.m_avgQpAq = int(q + 0.5);
         /* copy value of lastRceq into thread local rce struct *to be used in RateControlEnd() */
         rce->qRceq = m_lastRceq;
         accumPQpUpdate();
@@ -1436,8 +1449,6 @@
 double RateControl::tuneAbrQScaleFromFeedback(double qScale)
 {
     double abrBuffer = 2 * m_rateTolerance * m_bitrate;
-    if (m_currentSatd)
-    {
         /* use framesDone instead of POC as poc count is not serial with bframes enabled */
         double overflow = 1.0;
         double timeDone = (double)(m_framesDone - m_param->frameNumThreads + 1) * m_frameDuration;
@@ -1450,16 +1461,31 @@
         }
 
         if (wantedBits > 0 && encodedBits > 0 && (!m_partialResidualFrames || 
-            m_param->rc.bStrictCbr))
+            m_param->rc.bStrictCbr || m_isGrainEnabled))
         {
             abrBuffer *= X265_MAX(1, sqrt(timeDone));
             overflow = x265_clip3(.5, 2.0, 1.0 + (encodedBits - wantedBits) / abrBuffer);
             qScale *= overflow;
         }
-    }
     return qScale;
 }
 
+double RateControl::tuneQScaleForGrain(double rcOverflow)
+{
+    double qpstepBy1 = pow(2, 0.5 / 6.0);
+    double qScaleAvg = x265_qp2qScale(m_avgPFrameQp);
+    double  q = m_lastQScaleFor[P_SLICE];
+    int curQp = int (x265_qScale2qp(m_lastQScaleFor[P_SLICE]) + 0.5);
+    double curBitrate = m_qpToEncodedBits[curQp] * int(m_fps + 0.5);
+    int newQp = rcOverflow > 1 ? curQp + 1 : curQp - 1 ;
+    double projectedBitrate =  int(m_fps + 0.5) * m_qpToEncodedBits[newQp];
+    if (curBitrate > 0 && projectedBitrate > 0)
+        q =  abs(projectedBitrate - m_bitrate) < abs (curBitrate - m_bitrate) ? x265_qp2qScale(newQp) : m_lastQScaleFor[P_SLICE];
+    else
+        q = rcOverflow > 1 ? qScaleAvg * qpstepBy1 : rcOverflow < 1 ?  qScaleAvg / qpstepBy1 : m_lastQScaleFor[P_SLICE];
+    return q;
+}
+
 double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
 {
     double q;
@@ -1525,6 +1551,7 @@
                 q0 = q1;
             }
         }
+
         if (prevRefSlice->m_sliceType == B_SLICE && IS_REFERENCED(m_curSlice->m_refFrameList[0][0]))
             q0 -= m_pbOffset / 2;
         if (nextRefSlice->m_sliceType == B_SLICE && IS_REFERENCED(m_curSlice->m_refFrameList[1][0]))
@@ -1535,7 +1562,9 @@
             q = q1;
         else if (i1)
             q = q0;
-        else
+        else if(m_isGrainEnabled && !m_2pass)
+                q = q1;
+            else
             q = (q0 * dt1 + q1 * dt0) / (dt0 + dt1);
 
         if (IS_REFERENCED(curFrame))
@@ -1543,7 +1572,7 @@
         else
             q += m_pbOffset;
 
-        /* Set a min qp at scenechanges and transitions */
+                /* Set a min qp at scenechanges and transitions */
         if (m_isSceneTransition)
         {
             q = X265_MAX(ABR_SCENECUT_INIT_QP_MIN, q);
@@ -1553,11 +1582,28 @@
         double qScale = x265_qp2qScale(q);
         rce->qpNoVbv = q;
         double lmin = 0, lmax = 0;
+        if (m_isGrainEnabled && m_isFirstMiniGop)
+        {
+            lmin = m_lastQScaleFor[P_SLICE] / m_lstep;
+            lmax = m_lastQScaleFor[P_SLICE] * m_lstep;
+            double tunedQscale = tuneAbrQScaleFromFeedback(qScale);
+            double overflow = tunedQscale / qScale;
+            if (!m_isAbrReset)
+                qScale = x265_clip3(lmin, lmax, qScale);
+            m_avgPFrameQp = m_avgPFrameQp == 0 ? rce->qpNoVbv : m_avgPFrameQp;
+            if (overflow != 1)
+            {
+                qScale = tuneQScaleForGrain(overflow);
+                q = x265_qScale2qp(qScale);
+            }
+            rce->qpNoVbv = q;
+        }
         if (m_isVbv)
         {
             lmin = m_lastQScaleFor[P_SLICE] / m_lstep;
             lmax = m_lastQScaleFor[P_SLICE] * m_lstep;
-            if (m_isCbr)
+
+            if (m_isCbr && !m_isGrainEnabled)
             {
                 qScale = tuneAbrQScaleFromFeedback(qScale);
                 if (!m_isAbrReset)
@@ -1581,7 +1627,10 @@
             rce->frameSizePlanned = X265_MIN(rce->frameSizePlanned, rce->frameSizeMaximum);
             rce->frameSizeEstimated = rce->frameSizePlanned;
         }
+
         rce->newQScale = qScale;
+        if(rce->bLastMiniGopBFrame)
+            m_isFirstMiniGop = false;
         return qScale;
     }
     else
@@ -1675,8 +1724,17 @@
                 if (!m_param->rc.bStatRead)
                     checkAndResetABR(rce, false);
                 double initialQScale = getQScale(rce, m_wantedBitsWindow / m_cplxrSum);
-                q = tuneAbrQScaleFromFeedback(initialQScale);
-                overflow = q / initialQScale;
+                double tunedQScale = tuneAbrQScaleFromFeedback(initialQScale);
+                overflow = tunedQScale / initialQScale;
+                q = !m_partialResidualFrames? tunedQScale : initialQScale;
+                bool isEncodeEnd = (m_param->totalFrames && 
+                    m_framesDone > 0.75 * m_param->totalFrames) ? 1 : 0;
+                bool isEncodeBeg = m_framesDone < (int)(m_fps + 0.5);
+                if(m_isGrainEnabled && m_sliceType!= I_SLICE && !isEncodeEnd &&
+                    ((overflow < 1.05 && overflow > 0.95) || isEncodeBeg))
+                {
+                    q = tuneQScaleForGrain(overflow);
+                }
             }
             if (m_sliceType == I_SLICE && m_param->keyframeMax > 1
                 && m_lastNonBPictType != I_SLICE && !m_isAbrReset)
@@ -1684,6 +1742,7 @@
                 if (!m_param->rc.bStrictCbr)
                     q = x265_qp2qScale(m_accumPQp / m_accumPNorm);
                 q /= fabs(m_param->rc.ipFactor);
+                m_avgPFrameQp = 0;
             }
             else if (m_framesDone > 0)
             {
@@ -1708,8 +1767,18 @@
             else if (m_framesDone == 0 && !m_isVbv && m_param->rc.rateControlMode == X265_RC_ABR)
             {
                 /* for ABR alone, clip the first I frame qp */
-                lqmax = x265_qp2qScale(ABR_INIT_QP_MAX) * m_lstep;
-                q = X265_MIN(lqmax, q);
+                if (m_isGrainEnabled)
+                {
+                    /* to maintain grain uniformity, set I frame qp in a fixed range */
+                    lqmax = x265_qp2qScale(ABR_INIT_QP_GRAIN_MAX) * (m_lstep * m_lstep);
+                    lqmin = x265_qp2qScale(ABR_INIT_QP_GRAIN_MAX) / (m_lstep * m_lstep);
+                    q = x265_clip3(lqmin, lqmax, q);
+                }
+                else
+                {
+                    lqmax = x265_qp2qScale(ABR_INIT_QP_MAX) * m_lstep;
+                    q = X265_MIN(lqmax, q);
+                }
             }
             q = x265_clip3(MIN_QPSCALE, MAX_MAX_QPSCALE, q);
             /* Set a min qp at scenechanges and transitions */
@@ -1720,6 +1789,11 @@
                m_lastQScaleFor[P_SLICE] = X265_MAX(minScenecutQscale, m_lastQScaleFor[P_SLICE]);
             }
             rce->qpNoVbv = x265_qScale2qp(q);
+            if(m_sliceType == P_SLICE)
+            {
+                m_avgPFrameQp = m_avgPFrameQp == 0 ? rce->qpNoVbv : m_avgPFrameQp;
+                m_avgPFrameQp = (m_avgPFrameQp + rce->qpNoVbv) / 2;
+            }
             q = clipQscale(curFrame, rce, q);
             /*  clip qp to permissible range after vbv-lookahead estimation to avoid possible
              * mispredictions by initial frame size predictors, after each scenecut */
@@ -2426,6 +2500,11 @@
         int pos = m_sliderPos - m_param->frameNumThreads;
         if (pos >= 0)
             m_encodedBitsWindow[pos % s_slidingWindowFrames] = actualBits;
+        if(rce->sliceType != I_SLICE)
+        {
+        int qp = int (rce->qpaRc + 0.5);
+        m_qpToEncodedBits[qp] =  m_qpToEncodedBits[qp] == 0 ? actualBits : (m_qpToEncodedBits[qp] + actualBits) * 0.5;
+        }
     }
 
     if (m_2pass)
diff -r f548abe8eae8 -r 2e414814c585 source/encoder/ratecontrol.h
--- a/source/encoder/ratecontrol.h	Wed Jan 20 18:27:42 2016 +0530
+++ b/source/encoder/ratecontrol.h	Mon Jan 11 14:13:52 2016 +0530
@@ -126,7 +126,7 @@
     bool   m_isVbv;
     bool   m_isCbr;
     bool   m_singleFrameVbv;
-
+    bool   m_isGrainEnabled;
     bool   m_isAbrReset;
     int    m_lastAbrResetPoc;
 
@@ -141,7 +141,8 @@
     double m_vbvMaxRate;       /* in kbps */
     double m_rateFactorMaxIncrement; /* Don't allow RF above (CRF + this value). */
     double m_rateFactorMaxDecrement; /* don't allow RF below (this value). */
-
+    double m_avgPFrameQp;
+    bool   m_isFirstMiniGop;
     Predictor m_pred[4];       /* Slice predictors to preidct bits for each Slice type - I,P,Bref and B */
     int64_t m_leadingNoBSatd;
     int     m_predType;       /* Type of slice predictors to be used - depends on the slice type */
@@ -178,7 +179,7 @@
     bool    m_isPatternPresent;
     bool    m_isSceneTransition;
     int     m_lastPredictorReset;
-
+    double m_qpToEncodedBits[QP_MAX_MAX];
     /* a common variable on which rateControlStart, rateControlEnd and rateControUpdateStats waits to
      * sync the calls to these functions. For example
      * -F2:
@@ -269,6 +270,7 @@
     bool   vbv2Pass(uint64_t allAvailableBits, int frameCount, int startPos);
     bool   findUnderflow(double *fills, int *t0, int *t1, int over, int framesCount);
     bool   fixUnderflow(int t0, int t1, double adjustment, double qscaleMin, double qscaleMax);
+    double tuneQScaleForGrain(double rcOverflow);
 };
 }
 #endif // ifndef X265_RATECONTROL_H


More information about the x265-devel mailing list