<div dir="ltr"><div>From 3f5c5bac03486d73ec5287f7e9138d7577d33b3c Mon Sep 17 00:00:00 2001<br>From: Karam Singh <<a href="mailto:karam.singh@multicorewareinc.com">karam.singh@multicorewareinc.com</a>><br>Date: Tue, 8 Oct 2024 12:54:35 +0530<br>Subject: [PATCH] Add support to configure Bitrate,CRF,QP at frame level<br><br>---<br> source/common/frame.cpp        |   4 ++<br> source/common/frame.h          |   7 +++<br> source/encoder/api.cpp         |   2 +<br> source/encoder/encoder.cpp     |  53 +++++++++++++++-<br> source/encoder/ratecontrol.cpp | 110 ++++++++++++++++++++++++++++++---<br> source/encoder/ratecontrol.h   |   3 +<br> source/x265.h                  |   1 +<br> 7 files changed, 169 insertions(+), 11 deletions(-)<br><br>diff --git a/source/common/frame.cpp b/source/common/frame.cpp<br>index fbecfb4e9..f497979f1 100644<br>--- a/source/common/frame.cpp<br>+++ b/source/common/frame.cpp<br>@@ -81,6 +81,10 @@ Frame::Frame()<br>     m_valid = 0;<br>     m_nextSubDPB = NULL;<br>     m_prevSubDPB = NULL;<br>+<br>+    m_targetBitrate = 0;<br>+    m_targetCrf = 0;<br>+    m_targetQp = 0;<br> }<br> <br> bool Frame::create(x265_param *param, float* quantOffsets)<br>diff --git a/source/common/frame.h b/source/common/frame.h<br>index 588fa6696..035c0655c 100644<br>--- a/source/common/frame.h<br>+++ b/source/common/frame.h<br>@@ -175,6 +175,13 @@ public:<br>     Frame*                 m_nextSubDPB;           // PicList doubly linked list pointers<br>     Frame*                 m_prevSubDPB;<br> <br>+    /*Target bitrate*/<br>+    int64_t                m_targetBitrate;<br>+    /* target CRF for this picture.*/<br>+    int                    m_targetCrf;<br>+    /* target QP for this picture.*/<br>+    int                    m_targetQp;<br>+<br>     Frame();<br> <br>     bool create(x265_param *param, float* quantOffsets);<br>diff --git a/source/encoder/api.cpp b/source/encoder/api.cpp<br>index 9d8650008..df8f9a9d1 100644<br>--- a/source/encoder/api.cpp<br>+++ b/source/encoder/api.cpp<br>@@ -1398,6 +1398,7 @@ FILE* x265_csvlog_open(const x265_param* param)<br> #if ENABLE_LIBVMAF<br>                     fprintf(csvfp, ", VMAF Frame Score");<br> #endif<br>+                    fprintf(csvfp, ", Target bitrate");<br>                 }<br>                 fprintf(csvfp, "\n");<br>             }<br>@@ -1525,6 +1526,7 @@ void x265_csvlog_frame(const x265_param* param, const x265_picture* pic)<br> #if ENABLE_LIBVMAF<br>         fprintf(param->csvfpt, ", %lf", frameStats->vmafFrameScore);<br> #endif<br>+        fprintf(param->csvfpt, ", %I64d", frameStats->currTrBitrate);<br>     }<br>     fprintf(param->csvfpt, "\n");<br>     fflush(stderr);<br>diff --git a/source/encoder/encoder.cpp b/source/encoder/encoder.cpp<br>index 09f58c62a..dc0e344f6 100644<br>--- a/source/encoder/encoder.cpp<br>+++ b/source/encoder/encoder.cpp<br>@@ -1810,7 +1810,25 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture* pic_out)<br>         }<br> <br>         if (m_reconfigureRc)<br>+        {<br>+            if (m_param->rc.rateControlMode == X265_RC_ABR)<br>+                inFrame[0]->m_targetBitrate = m_latestParam->rc.bitrate;<br>+            else if (m_param->rc.rateControlMode == X265_RC_CRF)<br>+                inFrame[0]->m_targetCrf = (int)m_latestParam->rc.rfConstant;<br>+            else if (m_param->rc.rateControlMode == X265_RC_CQP)<br>+                inFrame[0]->m_targetQp = m_latestParam->rc.qp;<br>             inFrame[0]->m_reconfigureRc = true;<br>+            m_reconfigureRc = false;<br>+        }<br>+        else<br>+        {<br>+            if (m_param->rc.rateControlMode == X265_RC_ABR)<br>+                inFrame[0]->m_targetBitrate = m_latestParam->rc.bitrate;<br>+            else if (m_param->rc.rateControlMode == X265_RC_CRF)<br>+                inFrame[0]->m_targetCrf = (int)m_latestParam->rc.rfConstant;<br>+            else if (m_param->rc.rateControlMode == X265_RC_CQP)<br>+                inFrame[0]->m_targetQp = m_latestParam->rc.qp;<br>+        }<br> <br>         if (m_param->bEnableTemporalFilter)<br>         {<br>@@ -1994,6 +2012,16 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture* pic_out)<br>                         if (m_param->bUseAnalysisFile)<br>                             x265_free_analysis_data(m_param, &pic_out[sLayer].analysisData);<br>                     }<br>+                    if (sLayer == 0)<br>+                    {<br>+                        if (outFrame->m_targetBitrate && m_param->rc.rateControlMode == X265_RC_ABR)<br>+                            pic_out[sLayer].frameData.currTrBitrate = outFrame->m_targetBitrate;<br>+                        else<br>+                            pic_out[sLayer].frameData.currTrBitrate = 0;<br>+                    }<br>+                    else<br>+                        x265_log(m_param, X265_LOG_WARNING, "Frame wise bitrate reconfigure are not supported for enhancement layers\n");<br>+<br>                 }<br>                 if (m_param->rc.bStatWrite && (m_param->analysisMultiPassRefine || m_param->analysisMultiPassDistortion))<br>                 {<br>@@ -2207,12 +2235,28 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture* pic_out)<br>                 }<br>             }<br> <br>-            if (frameEnc[0]->m_reconfigureRc && m_reconfigureRc)<br>+            if (frameEnc[0]->m_reconfigureRc)<br>             {<br>-                x265_copy_params(m_param, m_latestParam);<br>+                m_rateControl->m_bRcReConfig = true;<br>+                if (m_param->rc.rateControlMode == X265_RC_ABR)<br>+                {<br>+                    m_param->rc.bitrate = (int)frameEnc[0]->m_targetBitrate;<br>+                    m_rateControl->m_param->rc.bitrate = (int)frameEnc[0]->m_targetBitrate;<br>+                }<br>+                else if (m_param->rc.rateControlMode == X265_RC_CRF)<br>+                {<br>+                    m_param->rc.rfConstant = (double)frameEnc[0]->m_targetCrf;<br>+                    m_rateControl->m_param->rc.rfConstant = frameEnc[0]->m_targetCrf;<br>+                }<br>+                else if (m_param->rc.rateControlMode == X265_RC_CQP)<br>+                {<br>+                    m_param->rc.qp = frameEnc[0]->m_targetQp;<br>+                    m_rateControl->m_param->rc.qp = frameEnc[0]->m_targetQp;<br>+                }<br>                 m_rateControl->reconfigureRC();<br>                 m_reconfigureRc = false;<br>             }<br>+<br>             if (frameEnc[0]->m_reconfigureRc && !m_reconfigureRc)<br>                 frameEnc[0]->m_reconfigureRc = false;<br>             if (curEncoder->m_reconfigure)<br>@@ -2483,6 +2527,8 @@ int Encoder::reconfigureParam(x265_param* encParam, x265_param* param)<br>         encParam->rc.bitrate = param->rc.bitrate;<br>         m_reconfigureRc |= encParam->rc.rfConstant != param->rc.rfConstant;<br>         encParam->rc.rfConstant = param->rc.rfConstant;<br>+        m_reconfigureRc |= encParam->rc.qp != param->rc.qp;<br>+        encParam->rc.qp = param->rc.qp;<br>     }<br>     else<br>     {<br>@@ -2541,7 +2587,8 @@ bool Encoder::isReconfigureRc(x265_param* latestParam, x265_param* param_in)<br>     return (latestParam->rc.vbvMaxBitrate != param_in->rc.vbvMaxBitrate<br>         || latestParam->rc.vbvBufferSize != param_in->rc.vbvBufferSize<br>         || latestParam->rc.bitrate != param_in->rc.bitrate<br>-        || latestParam->rc.rfConstant != param_in->rc.rfConstant);<br>+        || latestParam->rc.rfConstant != param_in->rc.rfConstant<br>+        || latestParam->rc.qp != param_in->rc.qp);<br> }<br> <br> void Encoder::copyCtuInfo(x265_ctu_info_t** frameCtuInfo, int poc)<br>diff --git a/source/encoder/ratecontrol.cpp b/source/encoder/ratecontrol.cpp<br>index e46896987..ec4695125 100644<br>--- a/source/encoder/ratecontrol.cpp<br>+++ b/source/encoder/ratecontrol.cpp<br>@@ -352,6 +352,8 @@ RateControl::RateControl(x265_param& p, Encoder *top)<br>         }<br>     }<br> <br>+    m_bRcReConfig = false;<br>+<br>     /* qpstep - value set as encoder specific */<br>     m_lstep = pow(2, m_param->rc.qpStep / 6.0);<br> <br>@@ -458,6 +460,7 @@ bool RateControl::init(const SPS& sps)<br>     m_partialResidualCost = 0;<br>     m_amortizeFraction = 0.85;<br>     m_amortizeFrames = 75;<br>+    m_bRcReConfig = false;<br>     if (m_param->totalFrames && m_param->totalFrames <= 2 * m_fps && m_param->rc.bStrictCbr) /* Strict CBR segment encode */<br>     {<br>         m_amortizeFraction = 0.85;<br>@@ -1613,6 +1616,11 @@ int RateControl::rateControlStart(Frame* curFrame, RateControlEntry* rce, Encode<br>             else<br>                 m_qp -= (int)(6.0 * X265_LOG2(zone->bitrateFactor));<br>         }<br>+        if (m_bRcReConfig)<br>+        {<br>+            m_qp = curFrame->m_targetQp;<br>+            curEncData.m_avgQpAq = curEncData.m_avgQpRc = m_qp;<br>+        }<br>     }<br>     if (m_sliceType != B_SLICE)<br>     {<br>@@ -1855,10 +1863,14 @@ 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_bRcReConfig && m_param->rc.rateControlMode == X265_RC_ABR)))<br>     {<br>         abrBuffer *= X265_MAX(1, sqrt(timeDone));<br>         overflow = x265_clip3(.5, 2.0, 1.0 + (encodedBits - wantedBits) / abrBuffer);<br>+        if (m_bRcReConfig && overflow > 1.05)<br>+            qScale *= m_lstep;<br>+        if (m_bRcReConfig && overflow < 0.95)<br>+            qScale /= m_lstep;<br>         qScale *= overflow;<br>     }<br>     return qScale;<br>@@ -1918,6 +1930,16 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)<br>         }<br>     }<br> <br>+    if (m_bRcReConfig)<br>+    {<br>+        if (m_param->rc.rateControlMode == X265_RC_ABR)<br>+            m_bitrate = (double)curFrame->m_targetBitrate * 1000;<br>+        else if (m_param->rc.rateControlMode == X265_RC_CRF)<br>+            m_param->rc.rfConstant = (double)curFrame->m_targetCrf;<br>+        else if (m_param->rc.rateControlMode == X265_RC_CQP)<br>+            m_param->rc.qp = curFrame->m_targetQp;<br>+    }<br>+<br>     if ((m_param->bliveVBV2pass && m_param->rc.rateControlMode == X265_RC_ABR) || m_isAbr)<br>     {<br>         int pos = m_sliderPos % s_slidingWindowFrames;<br>@@ -1997,6 +2019,16 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)<br>             m_lastQScaleFor[P_SLICE] = X265_MAX(minScenecutQscale, m_lastQScaleFor[P_SLICE]);<br>         }<br> <br>+        if (m_bRcReConfig && m_param->rc.rateControlMode == X265_RC_CRF)<br>+        {<br>+            double rfConstant = m_param->rc.rfConstant;<br>+            double mbtree_offset = m_param->rc.cuTree ? (1.0 - m_param->rc.qCompress) * 13.5 : 0;<br>+            double qComp = (m_param->rc.cuTree && !m_param->rc.hevcAq) ? 0.99 : m_param->rc.qCompress;<br>+            m_rateFactorConstant = pow(m_currentSatd, 1.0 - qComp) /<br>+                x265_qp2qScale(rfConstant + mbtree_offset);<br>+            double qScale = getQScale(rce, m_rateFactorConstant);<br>+            q = x265_qScale2qp(qScale);<br>+        }<br>         double qScale = x265_qp2qScale(q);<br>         rce->qpNoVbv = q;<br> <br>@@ -2068,6 +2100,13 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)<br>                     qScale = x265_clip3(lmin, lmax, qScale);<br>                 m_lastQScaleFor[m_sliceType] = qScale;<br>             }<br>+<br>+            if (m_bRcReConfig && m_param->rc.rateControlMode == X265_RC_ABR)<br>+            {<br>+                qScale = tuneQscaleToUpdatedBitrate(curFrame, qScale);<br>+                rce->qpNoVbv = x265_qScale2qp(qScale);<br>+                m_lastQScaleFor[m_sliceType] = qScale;<br>+            }<br>         }<br> <br>         if (m_2pass)<br>@@ -2238,6 +2277,14 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)<br>                     m_rateFactorConstant = pow(m_currentSatd, 1.0 - qComp) /<br>                         x265_qp2qScale(rfConstant + mbtree_offset);<br>                 }<br>+                if (m_bRcReConfig)<br>+                {<br>+                    double rfConstant = m_param->rc.rfConstant;<br>+                    double mbtree_offset = m_param->rc.cuTree ? (1.0 - m_param->rc.qCompress) * 13.5 : 0;<br>+                    double qComp = (m_param->rc.cuTree && !m_param->rc.hevcAq) ? 0.99 : m_param->rc.qCompress;<br>+                    m_rateFactorConstant = pow(m_currentSatd, 1.0 - qComp) /<br>+                        x265_qp2qScale(rfConstant + mbtree_offset);<br>+                }<br>                 q = getQScale(rce, m_rateFactorConstant);<br>                 x265_zone* zone = getZone();<br>                 if (zone)<br>@@ -2263,8 +2310,8 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)<br>                 }<br>                 double tunedQScale = tuneAbrQScaleFromFeedback(initialQScale);<br>                 overflow = tunedQScale / initialQScale;<br>-                q = !m_partialResidualFrames ? tunedQScale : initialQScale;<br>-                bool isEncodeEnd = (m_param->totalFrames && <br>+                q = (!m_partialResidualFrames || m_bRcReConfig) ? 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>                 if (m_isGrainEnabled)<br>@@ -2290,7 +2337,7 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)<br>                 {<br>                     lqmin = m_lastQScaleFor[m_sliceType] / m_lstep;<br>                     lqmax = m_lastQScaleFor[m_sliceType] * m_lstep;<br>-                    if (!m_partialResidualFrames || m_isGrainEnabled)<br>+                    if (!m_partialResidualFrames || m_isGrainEnabled || m_bRcReConfig)<br>                     {<br>                         if (overflow > 1.1 && m_framesDone > 3)<br>                             lqmax *= m_lstep;<br>@@ -2350,6 +2397,12 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)<br>             }<br>             q = clipQscale(curFrame, rce, q);<br> <br>+            if (m_bRcReConfig && m_param->rc.rateControlMode == X265_RC_ABR)<br>+            {<br>+                q = tuneQscaleToUpdatedBitrate(curFrame, q);<br>+                rce->qpNoVbv = x265_qScale2qp(q);<br>+            }<br>+<br>             if (m_2pass)<br>                 rce->frameSizePlanned = qScale2bits(rce, q);<br>             else<br>@@ -2540,6 +2593,41 @@ double RateControl::tuneQscaleForSBRC(Frame* curFrame, double q)<br>     return q;<br> }<br> <br>+double RateControl::tuneQscaleToUpdatedBitrate(Frame* curFrame, double q)<br>+{<br>+    int depth = 18;<br>+<br>+    for (int iterations = 0; iterations < 100; iterations++)<br>+    {<br>+        int i;<br>+        double frameBitsTotal = m_fps * predictSize(&m_pred[m_predType], q, (double)m_currentSatd);<br>+        for (i = 0; i < depth; i++)<br>+        {<br>+            int type = curFrame->m_lowres.plannedType[i];<br>+            if (type == X265_TYPE_AUTO)<br>+                break;<br>+            int64_t satd = curFrame->m_lowres.plannedSatd[i] >> (X265_DEPTH - 8);<br>+            type = IS_X265_TYPE_I(curFrame->m_lowres.plannedType[i]) ? I_SLICE : IS_X265_TYPE_B(curFrame->m_lowres.plannedType[i]) ? B_SLICE : P_SLICE;<br>+            int predType = getPredictorType(curFrame->m_lowres.plannedType[i], type);<br>+            double curBits = m_fps * predictSize(&m_pred[predType], q, (double)satd);<br>+            frameBitsTotal += curBits;<br>+        }<br>+        frameBitsTotal /= i;<br>+        double allowedSize = (double)(curFrame->m_targetBitrate * 1000);<br>+        if (frameBitsTotal >= 1.1 * allowedSize)<br>+            q = q * 1.1;<br>+        else if (frameBitsTotal >= 1.05 * allowedSize)<br>+            q = q * 1.05;<br>+        else if (frameBitsTotal <= 0.9 * allowedSize)<br>+            q = q / 1.1;<br>+        else if (frameBitsTotal <= 0.95 * allowedSize)<br>+            q = q / 1.05;<br>+        else<br>+            break;<br>+    }<br>+    return q;<br>+}<br>+<br> double RateControl::clipQscale(Frame* curFrame, RateControlEntry* rce, double q)<br> {<br>     // B-frames are not directly subject to VBV,<br>@@ -2577,7 +2665,8 @@ double RateControl::clipQscale(Frame* curFrame, RateControlEntry* rce, double q)<br>                     if (type == X265_TYPE_AUTO || totalDuration >= 1.0)<br>                         break;<br>                     totalDuration += m_frameDuration;<br>-                    double wantedFrameSize = m_vbvMaxRate * m_frameDuration;<br>+                    double wantedFrameSize = ((m_bRcReConfig && m_param->rc.rateControlMode == X265_RC_ABR) ?<br>+                        curFrame->m_targetBitrate * 1000 : m_vbvMaxRate) * m_frameDuration;<br>                     if (bufferFillCur + wantedFrameSize <= m_bufferSize)<br>                         bufferFillCur += wantedFrameSize;<br>                     int64_t satd = curFrame->m_lowres.plannedSatd[j] >> (X265_DEPTH - 8);<br>@@ -3105,8 +3194,13 @@ int RateControl::rateControlEnd(Frame* curFrame, int64_t bits, RateControlEntry*<br>             crfVal = x265_qScale2qp(pow(baseCplx, 1 - m_qCompress) / crfFactor) - mbtree_offset;<br>         }<br>         else<br>-            crfVal = rce->sliceType == I_SLICE ? m_param->rc.rfConstant - m_ipOffset : <br>-            (rce->sliceType == B_SLICE ? m_param->rc.rfConstant + m_pbOffset : m_param->rc.rfConstant);<br>+        {<br>+            if (m_bRcReConfig)<br>+                crfVal = curFrame->m_targetCrf;<br>+            else<br>+                crfVal = rce->sliceType == I_SLICE ? (m_param->rc.rfConstant - m_ipOffset) :<br>+                (rce->sliceType == B_SLICE ? (m_param->rc.rfConstant + m_pbOffset) : m_param->rc.rfConstant);<br>+        }<br> <br>         curEncData.m_rateFactor = crfVal;<br>     }<br>@@ -3144,7 +3238,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_frameDuration * (m_bRcReConfig ? (curFrame->m_targetBitrate * 1000) : m_bitrate);<br>         m_totalBits += bits - rce->rowTotalBits;<br>         m_encodedBits += actualBits;<br>         m_encodedSegmentBits += actualBits;<br>diff --git a/source/encoder/ratecontrol.h b/source/encoder/ratecontrol.h<br>index 7becd94ee..9a6b889b8 100644<br>--- a/source/encoder/ratecontrol.h<br>+++ b/source/encoder/ratecontrol.h<br>@@ -249,6 +249,8 @@ public:<br>     RateControlEntry* m_rce2Pass;<br>     Encoder* m_top;<br> <br>+    bool    m_bRcReConfig;                /* RC-reconfigurtion */<br>+<br>     struct<br>     {<br>         uint16_t *qpBuffer[2]; /* Global buffers for converting MB-tree quantizer data. */<br>@@ -301,6 +303,7 @@ protected:<br>     double tuneAbrQScaleFromFeedback(double qScale);<br>     double tuneQScaleForZone(RateControlEntry *rce, double qScale); // Tune qScale to adhere to zone budget<br>     double tuneQscaleForSBRC(Frame* curFrame, double q); // Tune qScale to adhere to segment budget<br>+    double tuneQscaleToUpdatedBitrate(Frame* curFrame, double q); // Tune qScale according to updated bitrate<br>     void   accumPQpUpdate();<br> <br>     int    getPredictorType(int lowresSliceType, int sliceType);<br>diff --git a/source/x265.h b/source/x265.h<br>index b4dceb406..e71eaaff8 100644<br>--- a/source/x265.h<br>+++ b/source/x265.h<br>@@ -313,6 +313,7 @@ typedef struct x265_frame_stats<br>     double           bufferFillFinal;<br>     double           unclippedBufferFillFinal;<br>     uint8_t          tLayer;<br>+    int64_t          currTrBitrate;<br> } x265_frame_stats;<br> <br> typedef struct x265_ctu_info_t<br>-- <br>2.28.0.windows.1<br><br></div><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>