changeset 12695:f34b58f2f3de

Add real-time VBV fullness based QP tuning in VBV 2 pass This commit enables real-time VBV fullness based 2nd pass QP tuning. Experimental feature.Default disabled
author Kirithika <kirithika@multicorewareinc.com>
date Wed, 09 Sep 2020 21:45:55 +0530
parents b760c0874cc6
children 49a6e30c692e
files doc/reST/cli.rst source/CMakeLists.txt source/common/param.cpp source/encoder/ratecontrol.cpp source/encoder/slicetype.cpp source/x265.h source/x265cli.cpp source/x265cli.h
diffstat 8 files changed, 93 insertions(+-), 51 deletions(-) [+]
line wrap: on
line diff
--- a/doc/reST/cli.rst	Thu Sep 03 10:28:17 2020 +0530
+++ b/doc/reST/cli.rst	Wed Sep 09 21:45:55 2020 +0530
@@ -1977,6 +1977,15 @@ Quality, rate control and rate distortio
 
    **Range of values:**  0 to 10
 
+.. option:: --vbv-live-multi-pass, --no-vbv-live-multi-pass
+
+   It enables the Qp tuning at frame level based on real time VBV Buffer fullness
+   in the ratecontrol 2nd pass of multi pass mode to reduce the VBV violations.
+   It could only be enabled with rate control stat-read encodes with VBV and ABR
+   rate control mode.
+
+   Default disabled. **Experimental feature**
+
 Quantization Options
 ====================
 
--- a/source/CMakeLists.txt	Thu Sep 03 10:28:17 2020 +0530
+++ b/source/CMakeLists.txt	Wed Sep 09 21:45:55 2020 +0530
@@ -29,7 +29,7 @@ option(NATIVE_BUILD "Target the build CP
 option(STATIC_LINK_CRT "Statically link C runtime for release builds" OFF)
 mark_as_advanced(FPROFILE_USE FPROFILE_GENERATE NATIVE_BUILD)
 # X265_BUILD must be incremented each time the public API is changed
-set(X265_BUILD 196)
+set(X265_BUILD 197)
 configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
                "${PROJECT_BINARY_DIR}/x265.def")
 configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
--- a/source/common/param.cpp	Thu Sep 03 10:28:17 2020 +0530
+++ b/source/common/param.cpp	Wed Sep 09 21:45:55 2020 +0530
@@ -288,6 +288,7 @@ void x265_param_default(x265_param* para
     param->bResetZoneConfig = 1;
     param->reconfigWindowSize = 0;
     param->decoderVbvMaxRate = 0;
+    param->bliveVBV2pass = 0;
 
     /* Video Usability Information (VUI) */
     param->vui.aspectRatioIdc = 0;
@@ -1375,6 +1376,7 @@ int x265_param_parse(x265_param* p, cons
             sscanf(value, "%d,%d,%d", &p->hmeRange[0], &p->hmeRange[1], &p->hmeRange[2]);
             p->bEnableHME = true;
         }
+        OPT("vbv-live-multi-pass") p->bliveVBV2pass = atobool(value);
         else
             return X265_PARAM_BAD_NAME;
     }
@@ -1817,6 +1819,15 @@ int x265_check_params(x265_param* param)
     CHECK(param->confWinRightOffset < 0, "Conformance Window Right Offset must be 0 or greater");
     CHECK(param->confWinBottomOffset < 0, "Conformance Window Bottom Offset must be 0 or greater");
     CHECK(param->decoderVbvMaxRate < 0, "Invalid Decoder Vbv Maxrate. Value can not be less than zero");
+    if (param->bliveVBV2pass)
+    {
+        CHECK((param->rc.bStatRead == 0), "Live VBV in multi pass option requires rate control 2 pass to be enabled");
+        if ((param->rc.vbvMaxBitrate <= 0 || param->rc.vbvBufferSize <= 0))
+        {
+            param->bliveVBV2pass = 0;
+            x265_log(param, X265_LOG_WARNING, "Live VBV enabled without VBV settings.Disabling live VBV in 2 pass\n");
+        }
+    }
     return check_failed;
 }
 
@@ -2238,6 +2249,7 @@ char *x265_param2string(x265_param* p, i
         s += sprintf(s, " scenecut-window=%d qp-delta-ref=%f qp-delta-nonref=%f", p->scenecutWindow, p->refQpDelta, p->nonRefQpDelta);
     s += sprintf(s, "conformance-window-offsets right=%d bottom=%d", p->confWinRightOffset, p->confWinBottomOffset);
     s += sprintf(s, " decoder-max-rate=%d", p->decoderVbvMaxRate);
+    BOOL(p->bliveVBV2pass, "vbv-live-multi-pass");
 #undef BOOL
     return buf;
 }
@@ -2593,6 +2605,7 @@ void x265_copy_params(x265_param* dst, x
 
     dst->confWinRightOffset = src->confWinRightOffset;
     dst->confWinBottomOffset = src->confWinBottomOffset;
+    dst->bliveVBV2pass = src->bliveVBV2pass;
 #ifdef SVT_HEVC
     memcpy(dst->svtHevcParam, src->svtHevcParam, sizeof(EB_H265_ENC_CONFIGURATION));
 #endif
--- a/source/encoder/ratecontrol.cpp	Thu Sep 03 10:28:17 2020 +0530
+++ b/source/encoder/ratecontrol.cpp	Wed Sep 09 21:45:55 2020 +0530
@@ -1755,34 +1755,32 @@ double RateControl::rateEstimateQscale(F
                      g_sliceTypeToChar[m_sliceType], g_sliceTypeToChar[rce->sliceType]);
         }
     }
-    else
+
+    if ((m_param->bliveVBV2pass && m_param->rc.rateControlMode == X265_RC_ABR) || m_isAbr)
     {
-        if (m_isAbr)
+        int pos = m_sliderPos % s_slidingWindowFrames;
+        int addPos = (pos + s_slidingWindowFrames - 1) % s_slidingWindowFrames;
+        if (m_sliderPos > s_slidingWindowFrames)
         {
-            int pos = m_sliderPos % s_slidingWindowFrames;
-            int addPos = (pos + s_slidingWindowFrames - 1) % s_slidingWindowFrames;
-            if (m_sliderPos > s_slidingWindowFrames)
-            {
-                const static double base = pow(0.5, s_slidingWindowFrames - 1);
-                m_movingAvgSum -= m_lastRemovedSatdCost * base;
-                m_movingAvgSum *= 0.5;
-                m_movingAvgSum += m_satdCostWindow[addPos];
-            }
-            else if (m_sliderPos == s_slidingWindowFrames)
-            {
-                m_movingAvgSum += m_satdCostWindow[addPos];
-            }
-            else if (m_sliderPos > 0)
-            {
-                m_movingAvgSum += m_satdCostWindow[addPos];
-                m_movingAvgSum *= 0.5;
-            }
+            const static double base = pow(0.5, s_slidingWindowFrames - 1);
+            m_movingAvgSum -= m_lastRemovedSatdCost * base;
+            m_movingAvgSum *= 0.5;
+            m_movingAvgSum += m_satdCostWindow[addPos];
+        }
+        else if (m_sliderPos == s_slidingWindowFrames)
+        {
+            m_movingAvgSum += m_satdCostWindow[addPos];
+        }
+        else if (m_sliderPos > 0)
+        {
+            m_movingAvgSum += m_satdCostWindow[addPos];
+            m_movingAvgSum *= 0.5;
+        }
 
-            rce->movingAvgSum = m_movingAvgSum;
-            m_lastRemovedSatdCost = m_satdCostWindow[pos];
-            m_satdCostWindow[pos] = rce->lastSatd;
-            m_sliderPos++;
-        }
+        rce->movingAvgSum = m_movingAvgSum;
+        m_lastRemovedSatdCost = m_satdCostWindow[pos];
+        m_satdCostWindow[pos] = rce->lastSatd;
+        m_sliderPos++;
     }
 
     if (m_sliceType == B_SLICE)
@@ -1887,7 +1885,7 @@ double RateControl::rateEstimateQscale(F
                 qScale = x265_clip3(lqmin, lqmax, qScale);
             }
 
-            if (!m_2pass)
+            if (!m_2pass || m_param->bliveVBV2pass)
             {
                 /* clip qp to permissible range after vbv-lookahead estimation to avoid possible 
                  * mispredictions by initial frame size predictors */
@@ -1955,7 +1953,9 @@ double RateControl::rateEstimateQscale(F
                 else
                     q /= zone->bitrateFactor;
             }
-            q /= x265_clip3(0.5, 2.0, (double)(abrBuffer - diff) / abrBuffer);
+            /*Existing ABR conformance check may not be valid with real time VBV*/
+            if(!m_param->bliveVBV2pass)
+                q /= x265_clip3(0.5, 2.0, (double)(abrBuffer - diff) / abrBuffer);
             if (m_expectedBitsSum > 0)
             {
                 /* Adjust quant based on the difference between
@@ -1988,24 +1988,34 @@ double RateControl::rateEstimateQscale(F
 
             if (m_isVbv)
             {
-                /* Do not overflow vbv */
-                double expectedSize = qScale2bits(rce, q);
-                double expectedVbv = m_bufferFill + m_bufferRate - expectedSize;
-                double expectedFullness = rce->expectedVbv / m_bufferSize;
-                double qmax = q * (2 - expectedFullness);
-                double sizeConstraint = 1 + expectedFullness;
-                qmax = X265_MAX(qmax, rce->newQScale);
-                if (expectedFullness < .05)
-                    qmax = lmax;
-                qmax = X265_MIN(qmax, lmax);
-                while (((expectedVbv < rce->expectedVbv/sizeConstraint) && (q < qmax)) ||
+                if (!m_param->bliveVBV2pass)
+                {
+                    /* Do not overflow vbv */
+                    double expectedSize = qScale2bits(rce, q);
+                    double expectedVbv = m_bufferFill + m_bufferRate - expectedSize;
+                    double expectedFullness = rce->expectedVbv / m_bufferSize;
+                    double qmax = q * (2 - expectedFullness);
+                    double sizeConstraint = 1 + expectedFullness;
+                    qmax = X265_MAX(qmax, rce->newQScale);
+                    if (expectedFullness < .05)
+                        qmax = lmax;
+                    qmax = X265_MIN(qmax, lmax);
+                    while (((expectedVbv < rce->expectedVbv / sizeConstraint) && (q < qmax)) ||
                         ((expectedVbv < 0) && (q < lmax)))
+                    {
+                        q *= 1.05;
+                        expectedSize = qScale2bits(rce, q);
+                        expectedVbv = m_bufferFill + m_bufferRate - expectedSize;
+                    }
+                }
+                else
                 {
-                    q *= 1.05;
-                    expectedSize = qScale2bits(rce, q);
-                    expectedVbv = m_bufferFill + m_bufferRate - expectedSize;
+                        /*  clip qp to permissible range after vbv-lookahead estimation to avoid possible
+                        * mispredictions by Rate Control pass 1 statistics analysis */
+                        q = clipQscale(curFrame, rce, q);
                 }
             }
+
             q = x265_clip3(lmin, lmax, q);
         }
         else
@@ -2379,7 +2389,7 @@ double RateControl::clipQscale(Frame* cu
                     }
                     /* Try to get the buffer not more than 80% filled, but don't set an impossible goal. */
                     targetFill = x265_clip3(m_bufferSize * (1 - 0.2 * finalDur), m_bufferSize, m_bufferFill - totalDuration * m_vbvMaxRate * 0.5);
-                    if (m_isCbr && bufferFillCur > targetFill && !m_isSceneTransition)
+                    if ((m_isCbr || m_2pass) && bufferFillCur > targetFill && !m_isSceneTransition)
                     {
                         q /= 1.01;
                         loopTerminate |= 2;
@@ -2432,7 +2442,7 @@ double RateControl::clipQscale(Frame* cu
          * lookahead vbv reduces its qscale by half its value. Be on safer side and avoid drastic 
          * qscale reductions for frames high in complexity */
         bool mispredCheck = rce->movingAvgSum && m_currentSatd >= rce->movingAvgSum && q <= q0 / 2;
-        if (!m_isCbr || (m_isAbr && mispredCheck))
+        if (!m_isCbr || ((m_isAbr || m_2pass) && mispredCheck))
             q = X265_MAX(q0, q);
 
         if (m_rateFactorMaxIncrement)
@@ -2442,7 +2452,7 @@ double RateControl::clipQscale(Frame* cu
             return x265_clip3(lmin, qmax, q);
         }
     }
-    if (m_2pass)
+    if (!curFrame && m_2pass)
     {
         double min = log(lmin);
         double max = log(lmax);
--- a/source/encoder/slicetype.cpp	Thu Sep 03 10:28:17 2020 +0530
+++ b/source/encoder/slicetype.cpp	Wed Sep 09 21:45:55 2020 +0530
@@ -1497,14 +1497,15 @@ void Lookahead::slicetypeDecide()
         }
     }
 
-    if (m_lastNonB && !m_param->rc.bStatRead &&
+    if (m_lastNonB &&
         ((m_param->bFrameAdaptive && m_param->bframes) ||
          m_param->rc.cuTree || m_param->scenecutThreshold || m_param->bHistBasedSceneCut ||
          (m_param->lookaheadDepth && m_param->rc.vbvBufferSize)))
     {
-        slicetypeAnalyse(frames, false);
+        if(!m_param->rc.bStatRead)
+            slicetypeAnalyse(frames, false);
         bool bIsVbv = m_param->rc.vbvBufferSize > 0 && m_param->rc.vbvMaxBitrate > 0;
-        if (m_param->analysisLoad && m_param->scaleFactor && bIsVbv)
+        if ((m_param->analysisLoad && m_param->scaleFactor && bIsVbv) || m_param->bliveVBV2pass)
         {
             int numFrames;
             for (numFrames = 0; numFrames < maxSearch; numFrames++)
@@ -1749,7 +1750,7 @@ void Lookahead::slicetypeDecide()
         }
     }
 
-    bool isKeyFrameAnalyse = (m_param->rc.cuTree || (m_param->rc.vbvBufferSize && m_param->lookaheadDepth)) && !m_param->rc.bStatRead;
+    bool isKeyFrameAnalyse = (m_param->rc.cuTree || (m_param->rc.vbvBufferSize && m_param->lookaheadDepth));
     if (isKeyFrameAnalyse && IS_X265_TYPE_I(m_lastNonB->sliceType))
     {
         m_inputLock.acquire();
@@ -1764,9 +1765,10 @@ void Lookahead::slicetypeDecide()
         m_inputLock.release();
 
         frames[j + 1] = NULL;
-        slicetypeAnalyse(frames, true);
+        if (!m_param->rc.bStatRead)
+            slicetypeAnalyse(frames, true);
         bool bIsVbv = m_param->rc.vbvBufferSize > 0 && m_param->rc.vbvMaxBitrate > 0;
-        if (m_param->analysisLoad && m_param->scaleFactor && bIsVbv)
+        if ((m_param->analysisLoad && m_param->scaleFactor && bIsVbv) || m_param->bliveVBV2pass)
         {
             int numFrames;
             for (numFrames = 0; numFrames < maxSearch; numFrames++)
--- a/source/x265.h	Thu Sep 03 10:28:17 2020 +0530
+++ b/source/x265.h	Wed Sep 09 21:45:55 2020 +0530
@@ -1920,6 +1920,11 @@ typedef struct x265_param
 
     /* Maxrate that could be signaled to the decoder. Default 0. API only. */
     int      decoderVbvMaxRate;
+
+    /*Enables Qp tuning with respect to real time VBV buffer fullness in rate
+    control 2 pass. Experimental.Default is disabled*/
+    int      bliveVBV2pass;
+
 } x265_param;
 
 /* x265_param_alloc:
--- a/source/x265cli.cpp	Thu Sep 03 10:28:17 2020 +0530
+++ b/source/x265cli.cpp	Wed Sep 09 21:45:55 2020 +0530
@@ -221,6 +221,7 @@ namespace X265_NS {
             "                                   - 3 : Nth pass, overwrites stats file\n");
         H0("   --[no-]multi-pass-opt-analysis   Refine analysis in 2 pass based on analysis information from pass 1\n");
         H0("   --[no-]multi-pass-opt-distortion Use distortion of CTU from pass 1 to refine qp in 2 pass\n");
+        H0("   --[no-]vbv-live-multi-pass    Enable realtime VBV in rate control 2 pass.Default %s\n", OPT(param->bliveVBV2pass));
         H0("   --stats                       Filename for stats file in multipass pass rate control. Default x265_2pass.log\n");
         H0("   --[no-]analyze-src-pics       Motion estimation uses source frame planes. Default disable\n");
         H0("   --[no-]slow-firstpass         Enable a slow first pass in a multipass rate control mode. Default %s\n", OPT(param->rc.bEnableSlowFirstPass));
--- a/source/x265cli.h	Thu Sep 03 10:28:17 2020 +0530
+++ b/source/x265cli.h	Wed Sep 09 21:45:55 2020 +0530
@@ -284,6 +284,8 @@ static const struct option long_options[
     { "no-multi-pass-opt-analysis",    no_argument, NULL, 0 },
     { "multi-pass-opt-distortion",     no_argument, NULL, 0 },
     { "no-multi-pass-opt-distortion",  no_argument, NULL, 0 },
+    { "vbv-live-multi-pass",           no_argument, NULL, 0 },
+    { "no-vbv-live-multi-pass",        no_argument, NULL, 0 },
     { "slow-firstpass",       no_argument, NULL, 0 },
     { "no-slow-firstpass",    no_argument, NULL, 0 },
     { "multi-pass-opt-rps",   no_argument, NULL, 0 },