[x265] [PATCH] Support encoding only the focused frames in the crf 2pass

Dakshinya T R S dakshinya at multicorewareinc.com
Wed Oct 6 05:52:38 UTC 2021


>From c2a289d66e51e47f3e0495ca13e9c8729685f280 Mon Sep 17 00:00:00 2001
From: lwWang <liwei at multicorewareinc.com>
Date: Mon, 27 Sep 2021 11:56:43 +0800
Subject: [PATCH] Support encoding only the focused frames in the crf 2pass

---
 doc/reST/cli.rst               |   8 ++
 source/CMakeLists.txt          |   2 +-
 source/common/frame.h          |   1 +
 source/common/framedata.cpp    |   2 +-
 source/common/param.cpp        |   1 +
 source/encoder/encoder.cpp     |   6 +
 source/encoder/ratecontrol.cpp | 235 +++++++++++++++++----------------
 source/encoder/ratecontrol.h   |   3 +
 source/x265.h                  |   3 +
 9 files changed, 147 insertions(+), 114 deletions(-)

diff --git a/doc/reST/cli.rst b/doc/reST/cli.rst
index b24fa72a9..e19774c68 100755
--- a/doc/reST/cli.rst
+++ b/doc/reST/cli.rst
@@ -2059,6 +2059,14 @@ Quality, rate control and rate distortion options
    rate control mode.

    Default disabled. **Experimental feature**
+
+
+.. option:: bEncFocusedFramesOnly
+
+ Used to trigger encoding of selective GOPs; Disabled by default.
+
+ **API ONLY**
+

 Quantization Options
 ====================
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 23af32111..62afd3610 100755
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -29,7 +29,7 @@ option(NATIVE_BUILD "Target the build CPU" OFF)
 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 201)
+set(X265_BUILD 202)
 configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
                "${PROJECT_BINARY_DIR}/x265.def")
 configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
diff --git a/source/common/frame.h b/source/common/frame.h
index dc5bbacf7..ac1185e81 100644
--- a/source/common/frame.h
+++ b/source/common/frame.h
@@ -70,6 +70,7 @@ struct RcStats
     double   count[4];
     double   offset[4];
     double   bufferFillFinal;
+    int64_t  currentSatd;
 };

 class Frame
diff --git a/source/common/framedata.cpp b/source/common/framedata.cpp
index c77b017a8..70af8a248 100644
--- a/source/common/framedata.cpp
+++ b/source/common/framedata.cpp
@@ -62,7 +62,7 @@ bool FrameData::create(const x265_param& param, const
SPS& sps, int csp)
     }
     else
         return false;
-    CHECKED_MALLOC_ZERO(m_cuStat, RCStatCU, sps.numCUsInFrame);
+    CHECKED_MALLOC_ZERO(m_cuStat, RCStatCU, sps.numCUsInFrame + 1);
     CHECKED_MALLOC(m_rowStat, RCStatRow, sps.numCuInHeight);
     reinit(sps);

diff --git a/source/common/param.cpp b/source/common/param.cpp
index 6751e0aa7..2c1583d93 100755
--- a/source/common/param.cpp
+++ b/source/common/param.cpp
@@ -282,6 +282,7 @@ void x265_param_default(x265_param* param)
     param->rc.bStatRead = 0;
     param->rc.bStatWrite = 0;
     param->rc.statFileName = NULL;
+    param->rc.bEncFocusedFramesOnly = 0;
     param->rc.complexityBlur = 20;
     param->rc.qblur = 0.5;
     param->rc.zoneCount = 0;
diff --git a/source/encoder/encoder.cpp b/source/encoder/encoder.cpp
index dee8fd85d..56f0e5433 100644
--- a/source/encoder/encoder.cpp
+++ b/source/encoder/encoder.cpp
@@ -2249,6 +2249,7 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
                 outFrame->m_rcData->iCuCount =
outFrame->m_encData->m_frameStats.percent8x8Intra * m_rateControl->m_ncu;
                 outFrame->m_rcData->pCuCount =
outFrame->m_encData->m_frameStats.percent8x8Inter * m_rateControl->m_ncu;
                 outFrame->m_rcData->skipCuCount =
outFrame->m_encData->m_frameStats.percent8x8Skip  * m_rateControl->m_ncu;
+                outFrame->m_rcData->currentSatd =
curEncoder->m_rce.coeffBits;
             }

             /* Allow this frame to be recycled if no frame encoders are
using it for reference */
@@ -4018,6 +4019,11 @@ void Encoder::configure(x265_param *p)
         p->rc.bStatRead = 0;
     }

+    if (!p->rc.bStatRead || p->rc.rateControlMode != X265_RC_CRF)
+    {
+        p->rc.bEncFocusedFramesOnly = 0;
+    }
+
     /* some options make no sense if others are disabled */
     p->bSaoNonDeblocked &= p->bEnableSAO;
     p->bEnableTSkipFast &= p->bEnableTransformSkip;
diff --git a/source/encoder/ratecontrol.cpp b/source/encoder/ratecontrol.cpp
index a4756de39..6c59d0c64 100644
--- a/source/encoder/ratecontrol.cpp
+++ b/source/encoder/ratecontrol.cpp
@@ -1002,123 +1002,101 @@ bool RateControl::initPass2()
     uint64_t allAvailableBits = uint64_t(m_param->rc.bitrate * 1000. *
m_numEntries * m_frameDuration);
     int startIndex, framesCount, endIndex;
     int fps = X265_MIN(m_param->keyframeMax, (int)(m_fps + 0.5));
+    int distance = fps << 1;
+    distance = distance > m_param->keyframeMax ? (m_param->keyframeMax <<
1) : m_param->keyframeMax;
     startIndex = endIndex = framesCount = 0;
-    int diffQp = 0;
     double targetBits = 0;
     double expectedBits = 0;
-    for (startIndex = m_start, endIndex = m_start; endIndex <
m_numEntries; endIndex++)
+    double targetBits2 = 0;
+    double expectedBits2 = 0;
+    double cpxSum = 0;
+    double cpxSum2 = 0;
+
+    if (m_param->rc.rateControlMode == X265_RC_ABR)
     {
-        allConstBits += m_rce2Pass[endIndex].miscBits;
-        allCodedBits += m_rce2Pass[endIndex].coeffBits +
m_rce2Pass[endIndex].mvBits;
-        if (m_param->rc.rateControlMode == X265_RC_CRF)
+        for (startIndex = m_start, endIndex = m_start; endIndex <
m_numEntries; endIndex++)
         {
-            framesCount = endIndex - startIndex + 1;
-            diffQp += int (m_rce2Pass[endIndex].qpaRc -
m_rce2Pass[endIndex].qpNoVbv);
-            if (framesCount > fps)
-                diffQp -= int (m_rce2Pass[endIndex - fps].qpaRc -
m_rce2Pass[endIndex - fps].qpNoVbv);
-            if (framesCount >= fps)
-            {
-                if (diffQp >= 1)
-                {
-                    if (!m_isQpModified && endIndex > fps)
-                    {
-                        double factor = 2;
-                        double step = 0;
-                        if (endIndex + fps >= m_numEntries)
-                        {
-                            m_start = endIndex - (endIndex % fps);
-                            return true;
-                        }
-                        for (int start = endIndex + 1; start <= endIndex +
fps && start < m_numEntries; start++)
-                        {
-                            RateControlEntry *rce = &m_rce2Pass[start];
-                            targetBits += qScale2bits(rce,
x265_qp2qScale(rce->qpNoVbv));
-                            expectedBits += qScale2bits(rce, rce->qScale);
-                        }
-                        if (expectedBits < 0.95 * targetBits)
-                        {
-                            m_isQpModified = true;
-                            m_isGopReEncoded = true;
-                            while (endIndex + fps < m_numEntries)
-                            {
-                                step = pow(2, factor / 6.0);
-                                expectedBits = 0;
-                                for (int start = endIndex + 1; start <=
endIndex + fps; start++)
-                                {
-                                    RateControlEntry *rce =
&m_rce2Pass[start];
-                                    rce->newQScale = rce->qScale / step;
-                                    X265_CHECK(rce->newQScale >= 0, "new
Qscale is negative\n");
-                                    expectedBits += qScale2bits(rce,
rce->newQScale);
-                                    rce->newQp =
x265_qScale2qp(rce->newQScale);
-                                }
-                                if (expectedBits >= targetBits && step > 1)
-                                    factor *= 0.90;
-                                else
-                                    break;
-                            }
-
-                            if (m_isVbv && endIndex + fps < m_numEntries)
-                                if (!vbv2Pass((uint64_t)targetBits,
endIndex + fps, endIndex + 1))
-                                    return false;
-
-                            targetBits = 0;
-                            expectedBits = 0;
-
-                            for (int start = endIndex - fps + 1; start <=
endIndex; start++)
-                            {
-                                RateControlEntry *rce = &m_rce2Pass[start];
-                                targetBits += qScale2bits(rce,
x265_qp2qScale(rce->qpNoVbv));
-                            }
-                            while (1)
-                            {
-                                step = pow(2, factor / 6.0);
-                                expectedBits = 0;
-                                for (int start = endIndex - fps + 1; start
<= endIndex; start++)
-                                {
-                                    RateControlEntry *rce =
&m_rce2Pass[start];
-                                    rce->newQScale = rce->qScale * step;
-                                    X265_CHECK(rce->newQScale >= 0, "new
Qscale is negative\n");
-                                    expectedBits += qScale2bits(rce,
rce->newQScale);
-                                    rce->newQp =
x265_qScale2qp(rce->newQScale);
-                                }
-                                if (expectedBits > targetBits && step > 1)
-                                    factor *= 1.1;
-                                else
-                                     break;
-                            }
-                            if (m_isVbv)
-                                if (!vbv2Pass((uint64_t)targetBits,
endIndex, endIndex - fps + 1))
-                                    return false;
-                            diffQp = 0;
-                            m_reencode = endIndex - fps + 1;
-                            endIndex = endIndex + fps;
-                            startIndex = endIndex + 1;
-                            m_start = startIndex;
-                            targetBits = expectedBits = 0;
-                        }
-                        else
-                            targetBits = expectedBits = 0;
-                    }
-                }
-                else
-                    m_isQpModified = false;
-            }
+            allConstBits += m_rce2Pass[endIndex].miscBits;
+            allCodedBits += m_rce2Pass[endIndex].coeffBits +
m_rce2Pass[endIndex].mvBits;
         }
-    }

-    if (m_param->rc.rateControlMode == X265_RC_ABR)
-    {
         if (allAvailableBits < allConstBits)
         {
             x265_log(m_param, X265_LOG_ERROR, "requested bitrate is too
low. estimated minimum is %d kbps\n",
-                     (int)(allConstBits * m_fps / framesCount * 1000.));
+                (int)(allConstBits * m_fps / framesCount * 1000.));
             return false;
         }
         if (!analyseABR2Pass(allAvailableBits))
             return false;
+
+        return true;
+    }
+
+    if (m_isQpModified)
+    {
+        return true;
+    }
+
+    if (m_start + (fps << 1) > m_numEntries)
+    {
+        return true;
+    }
+
+    for (startIndex = m_start, endIndex = m_numEntries - 1; startIndex <
endIndex; startIndex++, endIndex--)
+    {
+        cpxSum += m_rce2Pass[startIndex].qScale /
m_rce2Pass[startIndex].coeffBits;
+        cpxSum2 += m_rce2Pass[endIndex].qScale /
m_rce2Pass[endIndex].coeffBits;
+
+        RateControlEntry *rce = &m_rce2Pass[startIndex];
+        targetBits += qScale2bits(rce, x265_qp2qScale(rce->qpNoVbv));
+        expectedBits += qScale2bits(rce, rce->qScale);
+
+        rce = &m_rce2Pass[endIndex];
+        targetBits2 += qScale2bits(rce, x265_qp2qScale(rce->qpNoVbv));
+        expectedBits2 += qScale2bits(rce, rce->qScale);
+    }
+
+    if (expectedBits < 0.95 * targetBits || expectedBits2 < 0.95 *
targetBits2)
+    {
+        if (cpxSum / cpxSum2 < 0.95 || cpxSum2 / cpxSum < 0.95)
+        {
+            m_isQpModified = true;
+            m_isGopReEncoded = true;
+
+            m_shortTermCplxSum = 0;
+            m_shortTermCplxCount = 0;
+            m_framesDone = m_start;
+
+            for (startIndex = m_start; startIndex < m_numEntries;
startIndex++)
+            {
+                m_shortTermCplxSum *= 0.5;
+                m_shortTermCplxCount *= 0.5;
+                m_shortTermCplxSum += m_rce2Pass[startIndex].currentSatd /
(CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION);
+                m_shortTermCplxCount++;
+            }
+
+            m_bufferFill = m_rce2Pass[m_start - 1].bufferFill;
+            m_bufferFillFinal = m_rce2Pass[m_start - 1].bufferFillFinal;
+            m_bufferFillActual = m_rce2Pass[m_start - 1].bufferFillActual;
+
+            m_reencode = m_start;
+            m_start = m_numEntries;
+        }
+        else
+        {
+
+            m_isQpModified = false;
+            m_isGopReEncoded = false;
+        }
     }
+    else
+    {

-    m_start = X265_MAX(m_start, endIndex - fps);
+        m_isQpModified = false;
+        m_isGopReEncoded = false;
+    }
+
+    m_start = X265_MAX(m_start, m_numEntries - distance +
m_param->keyframeMax);

     return true;
 }
@@ -1391,15 +1369,47 @@ int RateControl::rateControlStart(Frame* curFrame,
RateControlEntry* rce, Encode
             rce->frameSizeMaximum *= m_param->maxAUSizeFactor;
         }
     }
+
+    ///< regenerate the qp
     if (!m_isAbr && m_2pass && m_param->rc.rateControlMode == X265_RC_CRF)
     {
-        rce->qpPrev = x265_qScale2qp(rce->qScale);
-        rce->qScale = rce->newQScale;
-        rce->qpaRc = curEncData.m_avgQpRc = curEncData.m_avgQpAq =
x265_qScale2qp(rce->newQScale);
-        m_qp = int(rce->qpaRc + 0.5);
-        rce->frameSizePlanned = qScale2bits(rce, rce->qScale);
-        m_framesDone++;
-        return m_qp;
+        if (!m_param->rc.bEncFocusedFramesOnly)
+        {
+            rce->qpPrev = x265_qScale2qp(rce->qScale);
+            rce->qScale = rce->newQScale;
+            rce->qpaRc = curEncData.m_avgQpRc = curEncData.m_avgQpAq =
x265_qScale2qp(rce->newQScale);
+            m_qp = int(rce->qpaRc + 0.5);
+            rce->frameSizePlanned = qScale2bits(rce, rce->qScale);
+            m_framesDone++;
+            return m_qp;
+        }
+        else
+        {
+            int index = m_encOrder[rce->poc];
+            index++;
+            double totalDuration = m_frameDuration;
+            for (int j = 0; totalDuration < 1.0 && index < m_numEntries;
j++)
+            {
+                switch (m_rce2Pass[index].sliceType)
+                {
+                case B_SLICE:
+                    curFrame->m_lowres.plannedType[j] =
m_rce2Pass[index].keptAsRef ? X265_TYPE_BREF : X265_TYPE_B;
+                    break;
+                case P_SLICE:
+                    curFrame->m_lowres.plannedType[j] = X265_TYPE_P;
+                    break;
+                case I_SLICE:
+                    curFrame->m_lowres.plannedType[j] = m_param->bOpenGOP
? X265_TYPE_I : X265_TYPE_IDR;
+                    break;
+                default:
+                    break;
+                }
+
+                curFrame->m_lowres.plannedSatd[j] =
m_rce2Pass[index].currentSatd;
+                totalDuration += m_frameDuration;
+                index++;
+            }
+        }
     }

     if (m_isAbr || m_2pass) // ABR,CRF
@@ -1890,7 +1900,7 @@ double RateControl::rateEstimateQscale(Frame*
curFrame, RateControlEntry *rce)
                 qScale = x265_clip3(lqmin, lqmax, qScale);
             }

-            if (!m_2pass || m_param->bliveVBV2pass)
+            if (!m_2pass || m_param->bliveVBV2pass || (m_2pass &&
m_param->rc.rateControlMode == X265_RC_CRF &&
m_param->rc.bEncFocusedFramesOnly))
             {
                 /* clip qp to permissible range after vbv-lookahead
estimation to avoid possible
                  * mispredictions by initial frame size predictors */
@@ -1927,7 +1937,7 @@ double RateControl::rateEstimateQscale(Frame*
curFrame, RateControlEntry *rce)
     else
     {
         double abrBuffer = 2 * m_rateTolerance * m_bitrate;
-        if (m_2pass)
+        if (m_2pass && (m_param->rc.rateControlMode != X265_RC_CRF ||
!m_param->rc.bEncFocusedFramesOnly))
         {
             double lmin = m_lmin[m_sliceType];
             double lmax = m_lmax[m_sliceType];
@@ -2828,7 +2838,7 @@ int RateControl::rateControlEnd(Frame* curFrame,
int64_t bits, RateControlEntry*

     if (m_param->rc.aqMode || m_isVbv || m_param->bAQMotion ||
bEnableDistOffset)
     {
-        if (m_isVbv && !(m_2pass && m_param->rc.rateControlMode ==
X265_RC_CRF))
+        if (m_isVbv && !(m_2pass && m_param->rc.rateControlMode ==
X265_RC_CRF && !m_param->rc.bEncFocusedFramesOnly))
         {
             double avgQpRc = 0;
             /* determine avg QP decided by VBV rate control */
@@ -2862,8 +2872,9 @@ int RateControl::rateControlEnd(Frame* curFrame,
int64_t bits, RateControlEntry*
     if (m_param->rc.rateControlMode == X265_RC_CRF)
     {
         double crfVal, qpRef = curEncData.m_avgQpRc;
+
         bool is2passCrfChange = false;
-        if (m_2pass)
+        if (m_2pass && !m_param->rc.bEncFocusedFramesOnly)
         {
             if (fabs(curEncData.m_avgQpRc - rce->qpPrev) > 0.1)
             {
diff --git a/source/encoder/ratecontrol.h b/source/encoder/ratecontrol.h
index 996465eeb..204bd71e1 100644
--- a/source/encoder/ratecontrol.h
+++ b/source/encoder/ratecontrol.h
@@ -73,6 +73,7 @@ struct RateControlEntry
     Predictor  rowPreds[3][2];
     Predictor* rowPred[2];

+    int64_t currentSatd;
     int64_t lastSatd;      /* Contains the picture cost of the previous
frame, required for resetAbr and VBV */
     int64_t leadingNoBSatd;
     int64_t rowTotalBits;  /* update cplxrsum and totalbits at the end of
2 rows */
@@ -87,6 +88,8 @@ struct RateControlEntry
     double  rowCplxrSum;
     double  qpNoVbv;
     double  bufferFill;
+    double  bufferFillFinal;
+    double  bufferFillActual;
     double  targetFill;
     bool    vbvEndAdj;
     double  frameDuration;
diff --git a/source/x265.h b/source/x265.h
index 3f3133536..6bb893c98 100644
--- a/source/x265.h
+++ b/source/x265.h
@@ -1443,6 +1443,9 @@ typedef struct x265_param
          * encoder will default to using x265_2pass.log */
         const char* statFileName;

+        /* if only the focused frames would be re-encode or not */
+        int       bEncFocusedFramesOnly;
+
         /* temporally blur quants */
         double    qblur;

-- 
2.23.0.windows.1
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20211006/7c084d28/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Support-encoding-only-the-focused-frames-in-the-crf2pass_v2.diff
Type: application/octet-stream
Size: 18618 bytes
Desc: not available
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20211006/7c084d28/attachment-0001.obj>


More information about the x265-devel mailing list