<div dir="ltr">From 817342031263956fba544b1aac9f29e814bb1fdf Mon Sep 17 00:00:00 2001<br>From: Kirithika <<a href="mailto:kirithika@multicorewareinc.com">kirithika@multicorewareinc.com</a>><br>Date: Fri, 9 Dec 2022 12:31:27 +0530<br>Subject: [PATCH] Add support for Segment Based Rate Control<br><br>1.Configure keyframe interval to be 2 seconds<br><br>2.Changes frame duration settings with SBRC 3.Reset RateControl (CRF/ABR) at the GOP beginning<br>---<br> source/encoder/encoder.cpp     |  10 +++<br> source/encoder/ratecontrol.cpp | 110 ++++++++++++++++++++-------------<br> source/encoder/ratecontrol.h   |   3 +<br> 3 files changed, 79 insertions(+), 44 deletions(-)<br><br>diff --git a/source/encoder/encoder.cpp b/source/encoder/encoder.cpp<br>index 64a4e231c..ad15b984a 100644<br>--- a/source/encoder/encoder.cpp<br>+++ b/source/encoder/encoder.cpp<br>@@ -4396,6 +4396,16 @@ void Encoder::configure(x265_param *p)<br>         if (m_param->searchRange != m_param->hmeRange[2])<br>             m_param->searchRange = m_param->hmeRange[2];<br>     }<br>+<br>+    if (m_param->bEnableSBRC && p->bOpenGOP)<br>+    {<br>+        x265_log(p, X265_LOG_WARNING, "Segment based RateControl requires closed gop structure. Enabling closed GOP.\n");<br>+        m_param->bOpenGOP = 0;<br>+<br>+        //Configure GOP to 2s duration<br>+        p->keyframeMax = 2 * (m_param->fpsNum / m_param->fpsDenom);<br>+        p->keyframeMin = 2 * (m_param->fpsNum / m_param->fpsDenom);<br>+    }<br> }<br> <br> void Encoder::readAnalysisFile(x265_analysis_data* analysis, int curPoc, const x265_picture* picIn, int paramBytes)<br>diff --git a/source/encoder/ratecontrol.cpp b/source/encoder/ratecontrol.cpp<br>index 32399eda6..1c29e4a00 100644<br>--- a/source/encoder/ratecontrol.cpp<br>+++ b/source/encoder/ratecontrol.cpp<br>@@ -392,45 +392,49 @@ bool RateControl::initCUTreeSharedMem()<br>     return true;<br> }<br> <br>-bool RateControl::init(const SPS& sps)<br>+void RateControl::initVBV(const SPS& sps)<br> {<br>-    if (m_isVbv && !m_initVbv)<br>+    /* We don't support changing the ABR bitrate right now,<br>+ * so if the stream starts as CBR, keep it CBR. */<br>+    if (m_param->rc.vbvBufferSize < (int)(m_param->rc.vbvMaxBitrate / m_fps))<br>     {<br>-        /* We don't support changing the ABR bitrate right now,<br>-         * so if the stream starts as CBR, keep it CBR. */<br>-        if (m_param->rc.vbvBufferSize < (int)(m_param->rc.vbvMaxBitrate / m_fps))<br>-        {<br>-            m_param->rc.vbvBufferSize = (int)(m_param->rc.vbvMaxBitrate / m_fps);<br>-            x265_log(m_param, X265_LOG_WARNING, "VBV buffer size cannot be smaller than one frame, using %d kbit\n",<br>-                     m_param->rc.vbvBufferSize);<br>-        }<br>-        int vbvBufferSize = m_param->rc.vbvBufferSize * 1000;<br>-        int vbvMaxBitrate = m_param->rc.vbvMaxBitrate * 1000;<br>+        m_param->rc.vbvBufferSize = (int)(m_param->rc.vbvMaxBitrate / m_fps);<br>+        x265_log(m_param, X265_LOG_WARNING, "VBV buffer size cannot be smaller than one frame, using %d kbit\n",<br>+            m_param->rc.vbvBufferSize);<br>+    }<br>+    int vbvBufferSize = m_param->rc.vbvBufferSize * 1000;<br>+    int vbvMaxBitrate = m_param->rc.vbvMaxBitrate * 1000;<br> <br>-        if (m_param->bEmitHRDSEI && !m_param->decoderVbvMaxRate)<br>-        {<br>-            const HRDInfo* hrd = &sps.vuiParameters.hrdParameters;<br>-            vbvBufferSize = hrd->cpbSizeValue << (hrd->cpbSizeScale + CPB_SHIFT);<br>-            vbvMaxBitrate = hrd->bitRateValue << (hrd->bitRateScale + BR_SHIFT);<br>-        }<br>-        m_bufferRate = vbvMaxBitrate / m_fps;<br>-        m_vbvMaxRate = vbvMaxBitrate;<br>-        m_bufferSize = vbvBufferSize;<br>-        m_singleFrameVbv = m_bufferRate * 1.1 > m_bufferSize;<br>+    if (m_param->bEmitHRDSEI && !m_param->decoderVbvMaxRate)<br>+    {<br>+        const HRDInfo* hrd = &sps.vuiParameters.hrdParameters;<br>+        vbvBufferSize = hrd->cpbSizeValue << (hrd->cpbSizeScale + CPB_SHIFT);<br>+        vbvMaxBitrate = hrd->bitRateValue << (hrd->bitRateScale + BR_SHIFT);<br>+    }<br>+    m_bufferRate = vbvMaxBitrate / m_fps;<br>+    m_vbvMaxRate = vbvMaxBitrate;<br>+    m_bufferSize = vbvBufferSize;<br>+    m_singleFrameVbv = m_bufferRate * 1.1 > m_bufferSize;<br>+<br>+    if (m_param->rc.vbvBufferInit > 1.)<br>+        m_param->rc.vbvBufferInit = x265_clip3(0.0, 1.0, m_param->rc.vbvBufferInit / m_param->rc.vbvBufferSize);<br>+    if (m_param->vbvBufferEnd > 1.)<br>+        m_param->vbvBufferEnd = x265_clip3(0.0, 1.0, m_param->vbvBufferEnd / m_param->rc.vbvBufferSize);<br>+    if (m_param->vbvEndFrameAdjust > 1.)<br>+        m_param->vbvEndFrameAdjust = x265_clip3(0.0, 1.0, m_param->vbvEndFrameAdjust);<br>+    m_param->rc.vbvBufferInit = x265_clip3(0.0, 1.0, X265_MAX(m_param->rc.vbvBufferInit, m_bufferRate / m_bufferSize));<br>+    m_bufferFillFinal = m_bufferSize * m_param->rc.vbvBufferInit;<br>+    m_bufferFillActual = m_bufferFillFinal;<br>+    m_bufferExcess = 0;<br>+    m_minBufferFill = m_param->minVbvFullness / 100;<br>+    m_maxBufferFill = 1 - (m_param->maxVbvFullness / 100);<br>+}<br> <br>-        if (m_param->rc.vbvBufferInit > 1.)<br>-            m_param->rc.vbvBufferInit = x265_clip3(0.0, 1.0, m_param->rc.vbvBufferInit / m_param->rc.vbvBufferSize);<br>-        if (m_param->vbvBufferEnd > 1.)<br>-            m_param->vbvBufferEnd = x265_clip3(0.0, 1.0, m_param->vbvBufferEnd / m_param->rc.vbvBufferSize);<br>-        if (m_param->vbvEndFrameAdjust > 1.)<br>-            m_param->vbvEndFrameAdjust = x265_clip3(0.0, 1.0, m_param->vbvEndFrameAdjust);<br>-        m_param->rc.vbvBufferInit = x265_clip3(0.0, 1.0, X265_MAX(m_param->rc.vbvBufferInit, m_bufferRate / m_bufferSize));<br>-        m_bufferFillFinal = m_bufferSize * m_param->rc.vbvBufferInit;<br>-        m_bufferFillActual = m_bufferFillFinal;<br>-        m_bufferExcess = 0;<br>-        m_minBufferFill = m_param->minVbvFullness / 100;<br>-        m_maxBufferFill = 1 - (m_param->maxVbvFullness / 100);<br>-        m_initVbv = true;<br>+bool RateControl::init(const SPS& sps)<br>+{<br>+    if (m_isVbv)<br>+    {<br>+        initVBV(sps);<br>     }<br> <br>     if (!m_param->bResetZoneConfig && (m_relativeComplexity == NULL))<br>@@ -477,7 +481,7 @@ bool RateControl::init(const SPS& sps)<br> <br>     /* estimated ratio that produces a reasonable QP for the first I-frame */<br>     m_cplxrSum = .01 * pow(7.0e5, m_qCompress) * pow(m_ncu, 0.5) * tuneCplxFactor;<br>-    m_wantedBitsWindow = m_bitrate * m_frameDuration;<br>+    m_wantedBitsWindow = (m_param->bEnableSBRC ? (m_bitrate * ( 1.0 / m_param->keyframeMax )) :m_bitrate * m_frameDuration);<br>     m_accumPNorm = .01;<br>     m_accumPQp = (m_param->rc.rateControlMode == X265_RC_CRF ? CRF_INIT_QP : ABR_INIT_QP_MIN) * m_accumPNorm;<br> <br>@@ -1819,7 +1823,8 @@ double RateControl::tuneAbrQScaleFromFeedback(double qScale)<br>     double abrBuffer = 2 * m_rateTolerance * m_bitrate;<br>     /* use framesDone instead of POC as poc count is not serial with bframes enabled */<br>     double overflow = 1.0;<br>-    double timeDone = (double)(m_framesDone - m_param->frameNumThreads + 1) * m_frameDuration;<br>+    double duration = m_param->bEnableSBRC ? (1.0 / m_param->keyframeMax) : m_frameDuration;<br>+    double timeDone = (double)(m_framesDone - m_param->frameNumThreads + 1) * duration;<br>     double wantedBits = timeDone * m_bitrate;<br>     int64_t encodedBits = m_totalBits;<br>     if (m_param->totalFrames && m_param->totalFrames <= 2 * m_fps)<br>@@ -1829,7 +1834,7 @@ double RateControl::tuneAbrQScaleFromFeedback(double qScale)<br>     }<br> <br>     if (wantedBits > 0 && encodedBits > 0 && (!m_partialResidualFrames || <br>-        m_param->rc.bStrictCbr || m_isGrainEnabled))<br>+        m_param->rc.bStrictCbr || m_isGrainEnabled || m_param->bEnableSBRC))<br>     {<br>         abrBuffer *= X265_MAX(1, sqrt(timeDone));<br>         overflow = x265_clip3(.5, 2.0, 1.0 + (encodedBits - wantedBits) / abrBuffer);<br>@@ -2191,6 +2196,10 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)<br> <br>             if (m_param->rc.rateControlMode == X265_RC_CRF)<br>             {<br>+                if (!m_param->rc.bStatRead && m_param->bEnableSBRC)<br>+                {<br>+                    checkAndResetCRF(rce);<br>+                }<br>                 q = getQScale(rce, m_rateFactorConstant);<br>                 x265_zone* zone = getZone();<br>                 if (zone)<br>@@ -2216,7 +2225,7 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)<br>                 }<br>                 double tunedQScale = tuneAbrQScaleFromFeedback(initialQScale);<br>                 overflow = tunedQScale / initialQScale;<br>-                q = !m_partialResidualFrames? tunedQScale : initialQScale;<br>+                q = (!m_partialResidualFrames || m_param->bEnableSBRC)? tunedQScale : initialQScale;<br>                 bool isEncodeEnd = (m_param->totalFrames && <br>                     m_framesDone > 0.75 * m_param->totalFrames) ? 1 : 0;<br>                 bool isEncodeBeg = m_framesDone < (int)(m_fps + 0.5);<br>@@ -2385,15 +2394,27 @@ void RateControl::rateControlUpdateStats(RateControlEntry* rce)<br>     }<br> }<br> <br>+<br>+void RateControl::checkAndResetCRF(RateControlEntry* rce)<br>+{<br>+    if(rce->poc % m_param->keyframeMax == 0)<br>+    {<br>+        init(*m_curSlice->m_sps);<br>+        m_shortTermCplxSum = rce->lastSatd / (CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION);<br>+        m_shortTermCplxCount = 1;<br>+        rce->blurredComplexity = m_shortTermCplxSum / m_shortTermCplxCount;<br>+    }<br>+}<br>+<br> void RateControl::checkAndResetABR(RateControlEntry* rce, bool isFrameDone)<br> {<br>     double abrBuffer = 2 * m_rateTolerance * m_bitrate;<br> <br>     // Check if current Slice is a scene cut that follows low detailed/blank frames<br>-    if (rce->lastSatd > 4 * rce->movingAvgSum || rce->scenecut || rce->isFadeEnd)<br>+    if (rce->lastSatd > 4 * rce->movingAvgSum || rce->scenecut || rce->isFadeEnd || (m_param->bEnableSBRC && (rce->poc % m_param->keyframeMax == 0)))<br>     {<br>         if (!m_isAbrReset && rce->movingAvgSum > 0<br>-            && (m_isPatternPresent || !m_param->bframes))<br>+            && (m_isPatternPresent || !m_param->bframes ||(m_param->bEnableSBRC && (rce->poc % m_param->keyframeMax == 0))))<br>         {<br>             int pos = X265_MAX(m_sliderPos - m_param->frameNumThreads, 0);<br>             int64_t shrtTermWantedBits = (int64_t) (X265_MIN(pos, s_slidingWindowFrames) * m_bitrate * m_frameDuration);<br>@@ -2403,7 +2424,7 @@ void RateControl::checkAndResetABR(RateControlEntry* rce, bool isFrameDone)<br>                 shrtTermTotalBitsSum += m_encodedBitsWindow[i];<br>             double underflow = (shrtTermTotalBitsSum - shrtTermWantedBits) / abrBuffer;<br>             const double epsilon = 0.0001f;<br>-            if ((underflow < epsilon || rce->isFadeEnd) && !isFrameDone)<br>+            if ((underflow < epsilon || rce->isFadeEnd || (m_param->bEnableSBRC && (rce->poc % m_param->keyframeMax == 0))) && !isFrameDone)<br>             {<br>                 init(*m_curSlice->m_sps);<br>                 // Reduce tune complexity factor for scenes that follow blank frames<br>@@ -2413,6 +2434,7 @@ void RateControl::checkAndResetABR(RateControlEntry* rce, bool isFrameDone)<br>                 m_shortTermCplxCount = 1;<br>                 m_isAbrReset = true;<br>                 m_lastAbrResetPoc = rce->poc;<br>+                rce->blurredComplexity = m_shortTermCplxSum / m_shortTermCplxCount;<br>             }<br>         }<br>         else if (m_isAbrReset && isFrameDone)<br>@@ -2487,7 +2509,7 @@ double RateControl::clipQscale(Frame* curFrame, RateControlEntry* rce, double q)<br>                 for (int j = 0; bufferFillCur >= 0 && iter ; j++)<br>                 {<br>                     int type = curFrame->m_lowres.plannedType[j];<br>-                    if (type == X265_TYPE_AUTO || totalDuration >= 1.0)<br>+                    if (type == X265_TYPE_AUTO || totalDuration >= 1.0 || (m_param->bEnableSBRC && type == X265_TYPE_IDR))<br>                         break;<br>                     totalDuration += m_frameDuration;<br>                     double wantedFrameSize = m_vbvMaxRate * m_frameDuration;<br>@@ -3053,7 +3075,7 @@ int RateControl::rateControlEnd(Frame* curFrame, int64_t bits, RateControlEntry*<br>                 * Not perfectly accurate with B-refs, but good enough. */<br>             m_cplxrSum += (bits * x265_qp2qScale(rce->qpaRc) / (rce->qRceq * fabs(m_param->rc.pbFactor))) - (rce->rowCplxrSum);<br>         }<br>-        m_wantedBitsWindow += m_frameDuration * m_bitrate;<br>+        m_wantedBitsWindow += (m_param->bEnableSBRC ? (m_bitrate * (1.0 / m_param->keyframeMax)) : m_frameDuration * m_bitrate);<br>         m_totalBits += bits - rce->rowTotalBits;<br>         m_encodedBits += actualBits;<br>         int pos = m_sliderPos - m_param->frameNumThreads;<br>diff --git a/source/encoder/ratecontrol.h b/source/encoder/ratecontrol.h<br>index e9fd0e182..9c2fe0ed6 100644<br>--- a/source/encoder/ratecontrol.h<br>+++ b/source/encoder/ratecontrol.h<br>@@ -150,6 +150,7 @@ public:<br>     int    m_lastScenecutAwareIFrame;<br>     double m_rateTolerance;<br>     double m_frameDuration;     /* current frame duration in seconds */<br>+    double m_frameDurInGOP;     /* current frame duration when considered as a segment */<br>     double m_bitrate;<br>     double m_rateFactorConstant;<br>     double m_bufferSize;<br>@@ -255,6 +256,7 @@ public:<br>     RateControl(x265_param& p, Encoder *enc);<br>     bool init(const SPS& sps);<br>     void initHRD(SPS& sps);<br>+    void initVBV(const SPS& sps);<br>     void reconfigureRC();<br> <br>     void setFinalFrameCount(int count);<br>@@ -315,6 +317,7 @@ protected:<br>     double tuneQScaleForGrain(double rcOverflow);<br>     void   splitdeltaPOC(char deltapoc[], RateControlEntry *rce);<br>     void   splitbUsed(char deltapoc[], RateControlEntry *rce);<br>+    void   checkAndResetCRF(RateControlEntry* rce);<br> };<br> }<br> #endif // ifndef X265_RATECONTROL_H<br>-- <br>2.28.0.windows.1<br><br><div><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><i>Thanks,</i><div><i>Kirithika</i></div></div></div></div></div>