[x265] [PATCH] Add support for Segment Based Rate Control

Kirithika Kalirathnam kirithika at multicorewareinc.com
Thu Dec 29 16:29:09 UTC 2022


>From 817342031263956fba544b1aac9f29e814bb1fdf Mon Sep 17 00:00:00 2001
From: Kirithika <kirithika at multicorewareinc.com>
Date: Fri, 9 Dec 2022 12:31:27 +0530
Subject: [PATCH] Add support for Segment Based Rate Control

1.Configure keyframe interval to be 2 seconds

2.Changes frame duration settings with SBRC 3.Reset RateControl (CRF/ABR)
at the GOP beginning
---
 source/encoder/encoder.cpp     |  10 +++
 source/encoder/ratecontrol.cpp | 110 ++++++++++++++++++++-------------
 source/encoder/ratecontrol.h   |   3 +
 3 files changed, 79 insertions(+), 44 deletions(-)

diff --git a/source/encoder/encoder.cpp b/source/encoder/encoder.cpp
index 64a4e231c..ad15b984a 100644
--- a/source/encoder/encoder.cpp
+++ b/source/encoder/encoder.cpp
@@ -4396,6 +4396,16 @@ void Encoder::configure(x265_param *p)
         if (m_param->searchRange != m_param->hmeRange[2])
             m_param->searchRange = m_param->hmeRange[2];
     }
+
+    if (m_param->bEnableSBRC && p->bOpenGOP)
+    {
+        x265_log(p, X265_LOG_WARNING, "Segment based RateControl requires
closed gop structure. Enabling closed GOP.\n");
+        m_param->bOpenGOP = 0;
+
+        //Configure GOP to 2s duration
+        p->keyframeMax = 2 * (m_param->fpsNum / m_param->fpsDenom);
+        p->keyframeMin = 2 * (m_param->fpsNum / m_param->fpsDenom);
+    }
 }

 void Encoder::readAnalysisFile(x265_analysis_data* analysis, int curPoc,
const x265_picture* picIn, int paramBytes)
diff --git a/source/encoder/ratecontrol.cpp b/source/encoder/ratecontrol.cpp
index 32399eda6..1c29e4a00 100644
--- a/source/encoder/ratecontrol.cpp
+++ b/source/encoder/ratecontrol.cpp
@@ -392,45 +392,49 @@ bool RateControl::initCUTreeSharedMem()
     return true;
 }

-bool RateControl::init(const SPS& sps)
+void RateControl::initVBV(const SPS& sps)
 {
-    if (m_isVbv && !m_initVbv)
+    /* We don't support changing the ABR bitrate right now,
+ * so if the stream starts as CBR, keep it CBR. */
+    if (m_param->rc.vbvBufferSize < (int)(m_param->rc.vbvMaxBitrate /
m_fps))
     {
-        /* We don't support changing the ABR bitrate right now,
-         * so if the stream starts as CBR, keep it CBR. */
-        if (m_param->rc.vbvBufferSize < (int)(m_param->rc.vbvMaxBitrate /
m_fps))
-        {
-            m_param->rc.vbvBufferSize = (int)(m_param->rc.vbvMaxBitrate /
m_fps);
-            x265_log(m_param, X265_LOG_WARNING, "VBV buffer size cannot be
smaller than one frame, using %d kbit\n",
-                     m_param->rc.vbvBufferSize);
-        }
-        int vbvBufferSize = m_param->rc.vbvBufferSize * 1000;
-        int vbvMaxBitrate = m_param->rc.vbvMaxBitrate * 1000;
+        m_param->rc.vbvBufferSize = (int)(m_param->rc.vbvMaxBitrate /
m_fps);
+        x265_log(m_param, X265_LOG_WARNING, "VBV buffer size cannot be
smaller than one frame, using %d kbit\n",
+            m_param->rc.vbvBufferSize);
+    }
+    int vbvBufferSize = m_param->rc.vbvBufferSize * 1000;
+    int vbvMaxBitrate = m_param->rc.vbvMaxBitrate * 1000;

-        if (m_param->bEmitHRDSEI && !m_param->decoderVbvMaxRate)
-        {
-            const HRDInfo* hrd = &sps.vuiParameters.hrdParameters;
-            vbvBufferSize = hrd->cpbSizeValue << (hrd->cpbSizeScale +
CPB_SHIFT);
-            vbvMaxBitrate = hrd->bitRateValue << (hrd->bitRateScale +
BR_SHIFT);
-        }
-        m_bufferRate = vbvMaxBitrate / m_fps;
-        m_vbvMaxRate = vbvMaxBitrate;
-        m_bufferSize = vbvBufferSize;
-        m_singleFrameVbv = m_bufferRate * 1.1 > m_bufferSize;
+    if (m_param->bEmitHRDSEI && !m_param->decoderVbvMaxRate)
+    {
+        const HRDInfo* hrd = &sps.vuiParameters.hrdParameters;
+        vbvBufferSize = hrd->cpbSizeValue << (hrd->cpbSizeScale +
CPB_SHIFT);
+        vbvMaxBitrate = hrd->bitRateValue << (hrd->bitRateScale +
BR_SHIFT);
+    }
+    m_bufferRate = vbvMaxBitrate / m_fps;
+    m_vbvMaxRate = vbvMaxBitrate;
+    m_bufferSize = vbvBufferSize;
+    m_singleFrameVbv = m_bufferRate * 1.1 > m_bufferSize;
+
+    if (m_param->rc.vbvBufferInit > 1.)
+        m_param->rc.vbvBufferInit = x265_clip3(0.0, 1.0,
m_param->rc.vbvBufferInit / m_param->rc.vbvBufferSize);
+    if (m_param->vbvBufferEnd > 1.)
+        m_param->vbvBufferEnd = x265_clip3(0.0, 1.0, m_param->vbvBufferEnd
/ m_param->rc.vbvBufferSize);
+    if (m_param->vbvEndFrameAdjust > 1.)
+        m_param->vbvEndFrameAdjust = x265_clip3(0.0, 1.0,
m_param->vbvEndFrameAdjust);
+    m_param->rc.vbvBufferInit = x265_clip3(0.0, 1.0,
X265_MAX(m_param->rc.vbvBufferInit, m_bufferRate / m_bufferSize));
+    m_bufferFillFinal = m_bufferSize * m_param->rc.vbvBufferInit;
+    m_bufferFillActual = m_bufferFillFinal;
+    m_bufferExcess = 0;
+    m_minBufferFill = m_param->minVbvFullness / 100;
+    m_maxBufferFill = 1 - (m_param->maxVbvFullness / 100);
+}

-        if (m_param->rc.vbvBufferInit > 1.)
-            m_param->rc.vbvBufferInit = x265_clip3(0.0, 1.0,
m_param->rc.vbvBufferInit / m_param->rc.vbvBufferSize);
-        if (m_param->vbvBufferEnd > 1.)
-            m_param->vbvBufferEnd = x265_clip3(0.0, 1.0,
m_param->vbvBufferEnd / m_param->rc.vbvBufferSize);
-        if (m_param->vbvEndFrameAdjust > 1.)
-            m_param->vbvEndFrameAdjust = x265_clip3(0.0, 1.0,
m_param->vbvEndFrameAdjust);
-        m_param->rc.vbvBufferInit = x265_clip3(0.0, 1.0,
X265_MAX(m_param->rc.vbvBufferInit, m_bufferRate / m_bufferSize));
-        m_bufferFillFinal = m_bufferSize * m_param->rc.vbvBufferInit;
-        m_bufferFillActual = m_bufferFillFinal;
-        m_bufferExcess = 0;
-        m_minBufferFill = m_param->minVbvFullness / 100;
-        m_maxBufferFill = 1 - (m_param->maxVbvFullness / 100);
-        m_initVbv = true;
+bool RateControl::init(const SPS& sps)
+{
+    if (m_isVbv)
+    {
+        initVBV(sps);
     }

     if (!m_param->bResetZoneConfig && (m_relativeComplexity == NULL))
@@ -477,7 +481,7 @@ bool RateControl::init(const SPS& sps)

     /* 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_wantedBitsWindow = (m_param->bEnableSBRC ? (m_bitrate * ( 1.0 /
m_param->keyframeMax )) :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;

@@ -1819,7 +1823,8 @@ double RateControl::tuneAbrQScaleFromFeedback(double
qScale)
     double abrBuffer = 2 * m_rateTolerance * m_bitrate;
     /* 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;
+    double duration = m_param->bEnableSBRC ? (1.0 / m_param->keyframeMax)
: m_frameDuration;
+    double timeDone = (double)(m_framesDone - m_param->frameNumThreads +
1) * duration;
     double wantedBits = timeDone * m_bitrate;
     int64_t encodedBits = m_totalBits;
     if (m_param->totalFrames && m_param->totalFrames <= 2 * m_fps)
@@ -1829,7 +1834,7 @@ double RateControl::tuneAbrQScaleFromFeedback(double
qScale)
     }

     if (wantedBits > 0 && encodedBits > 0 && (!m_partialResidualFrames ||
-        m_param->rc.bStrictCbr || m_isGrainEnabled))
+        m_param->rc.bStrictCbr || m_isGrainEnabled ||
m_param->bEnableSBRC))
     {
         abrBuffer *= X265_MAX(1, sqrt(timeDone));
         overflow = x265_clip3(.5, 2.0, 1.0 + (encodedBits - wantedBits) /
abrBuffer);
@@ -2191,6 +2196,10 @@ double RateControl::rateEstimateQscale(Frame*
curFrame, RateControlEntry *rce)

             if (m_param->rc.rateControlMode == X265_RC_CRF)
             {
+                if (!m_param->rc.bStatRead && m_param->bEnableSBRC)
+                {
+                    checkAndResetCRF(rce);
+                }
                 q = getQScale(rce, m_rateFactorConstant);
                 x265_zone* zone = getZone();
                 if (zone)
@@ -2216,7 +2225,7 @@ double RateControl::rateEstimateQscale(Frame*
curFrame, RateControlEntry *rce)
                 }
                 double tunedQScale =
tuneAbrQScaleFromFeedback(initialQScale);
                 overflow = tunedQScale / initialQScale;
-                q = !m_partialResidualFrames? tunedQScale : initialQScale;
+                q = (!m_partialResidualFrames || m_param->bEnableSBRC)?
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);
@@ -2385,15 +2394,27 @@ void
RateControl::rateControlUpdateStats(RateControlEntry* rce)
     }
 }

+
+void RateControl::checkAndResetCRF(RateControlEntry* rce)
+{
+    if(rce->poc % m_param->keyframeMax == 0)
+    {
+        init(*m_curSlice->m_sps);
+        m_shortTermCplxSum = rce->lastSatd /
(CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION);
+        m_shortTermCplxCount = 1;
+        rce->blurredComplexity = m_shortTermCplxSum / m_shortTermCplxCount;
+    }
+}
+
 void RateControl::checkAndResetABR(RateControlEntry* rce, bool isFrameDone)
 {
     double abrBuffer = 2 * m_rateTolerance * m_bitrate;

     // Check if current Slice is a scene cut that follows low
detailed/blank frames
-    if (rce->lastSatd > 4 * rce->movingAvgSum || rce->scenecut ||
rce->isFadeEnd)
+    if (rce->lastSatd > 4 * rce->movingAvgSum || rce->scenecut ||
rce->isFadeEnd || (m_param->bEnableSBRC && (rce->poc % m_param->keyframeMax
== 0)))
     {
         if (!m_isAbrReset && rce->movingAvgSum > 0
-            && (m_isPatternPresent || !m_param->bframes))
+            && (m_isPatternPresent || !m_param->bframes
||(m_param->bEnableSBRC && (rce->poc % m_param->keyframeMax == 0))))
         {
             int pos = X265_MAX(m_sliderPos - m_param->frameNumThreads, 0);
             int64_t shrtTermWantedBits = (int64_t) (X265_MIN(pos,
s_slidingWindowFrames) * m_bitrate * m_frameDuration);
@@ -2403,7 +2424,7 @@ void RateControl::checkAndResetABR(RateControlEntry*
rce, bool isFrameDone)
                 shrtTermTotalBitsSum += m_encodedBitsWindow[i];
             double underflow = (shrtTermTotalBitsSum - shrtTermWantedBits)
/ abrBuffer;
             const double epsilon = 0.0001f;
-            if ((underflow < epsilon || rce->isFadeEnd) && !isFrameDone)
+            if ((underflow < epsilon || rce->isFadeEnd ||
(m_param->bEnableSBRC && (rce->poc % m_param->keyframeMax == 0))) &&
!isFrameDone)
             {
                 init(*m_curSlice->m_sps);
                 // Reduce tune complexity factor for scenes that follow
blank frames
@@ -2413,6 +2434,7 @@ void RateControl::checkAndResetABR(RateControlEntry*
rce, bool isFrameDone)
                 m_shortTermCplxCount = 1;
                 m_isAbrReset = true;
                 m_lastAbrResetPoc = rce->poc;
+                rce->blurredComplexity = m_shortTermCplxSum /
m_shortTermCplxCount;
             }
         }
         else if (m_isAbrReset && isFrameDone)
@@ -2487,7 +2509,7 @@ double RateControl::clipQscale(Frame* curFrame,
RateControlEntry* rce, double q)
                 for (int j = 0; bufferFillCur >= 0 && iter ; j++)
                 {
                     int type = curFrame->m_lowres.plannedType[j];
-                    if (type == X265_TYPE_AUTO || totalDuration >= 1.0)
+                    if (type == X265_TYPE_AUTO || totalDuration >= 1.0 ||
(m_param->bEnableSBRC && type == X265_TYPE_IDR))
                         break;
                     totalDuration += m_frameDuration;
                     double wantedFrameSize = m_vbvMaxRate *
m_frameDuration;
@@ -3053,7 +3075,7 @@ int RateControl::rateControlEnd(Frame* curFrame,
int64_t bits, RateControlEntry*
                 * Not perfectly accurate with B-refs, but good enough. */
             m_cplxrSum += (bits * x265_qp2qScale(rce->qpaRc) / (rce->qRceq
* fabs(m_param->rc.pbFactor))) - (rce->rowCplxrSum);
         }
-        m_wantedBitsWindow += m_frameDuration * m_bitrate;
+        m_wantedBitsWindow += (m_param->bEnableSBRC ? (m_bitrate * (1.0 /
m_param->keyframeMax)) : m_frameDuration * m_bitrate);
         m_totalBits += bits - rce->rowTotalBits;
         m_encodedBits += actualBits;
         int pos = m_sliderPos - m_param->frameNumThreads;
diff --git a/source/encoder/ratecontrol.h b/source/encoder/ratecontrol.h
index e9fd0e182..9c2fe0ed6 100644
--- a/source/encoder/ratecontrol.h
+++ b/source/encoder/ratecontrol.h
@@ -150,6 +150,7 @@ public:
     int    m_lastScenecutAwareIFrame;
     double m_rateTolerance;
     double m_frameDuration;     /* current frame duration in seconds */
+    double m_frameDurInGOP;     /* current frame duration when considered
as a segment */
     double m_bitrate;
     double m_rateFactorConstant;
     double m_bufferSize;
@@ -255,6 +256,7 @@ public:
     RateControl(x265_param& p, Encoder *enc);
     bool init(const SPS& sps);
     void initHRD(SPS& sps);
+    void initVBV(const SPS& sps);
     void reconfigureRC();

     void setFinalFrameCount(int count);
@@ -315,6 +317,7 @@ protected:
     double tuneQScaleForGrain(double rcOverflow);
     void   splitdeltaPOC(char deltapoc[], RateControlEntry *rce);
     void   splitbUsed(char deltapoc[], RateControlEntry *rce);
+    void   checkAndResetCRF(RateControlEntry* rce);
 };
 }
 #endif // ifndef X265_RATECONTROL_H
-- 
2.28.0.windows.1

*Thanks,*
*Kirithika*
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20221229/6da186a2/attachment-0001.htm>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: patch2.diff
Type: application/octet-stream
Size: 13411 bytes
Desc: not available
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20221229/6da186a2/attachment-0001.obj>


More information about the x265-devel mailing list