[x265] [PATCH] Add CLI support for configuring RC at frame level

Kirithika Kalirathnam kirithika at multicorewareinc.com
Tue Nov 12 09:32:41 UTC 2024


>From 3b5391aed1d50348a1c00744221913bddcb3f1fb Mon Sep 17 00:00:00 2001
From: Kirithika <kirithika at multicorewareinc.com>
Date: Fri, 8 Nov 2024 09:10:20 +0530
Subject: [PATCH] Add CLI support for configuring RC at frame level

This patch also does cleanup and docs update
---
 doc/reST/cli.rst               |  9 +++-
 source/CMakeLists.txt          |  2 +-
 source/common/frame.h          |  4 +-
 source/common/param.cpp        |  5 ++
 source/encoder/api.cpp         | 22 +++++++--
 source/encoder/encoder.cpp     | 84 +++++++++++++++-------------------
 source/encoder/ratecontrol.cpp | 73 +++++++++++++----------------
 source/x265.h                  |  5 ++
 source/x265cli.cpp             |  3 +-
 source/x265cli.h               |  2 +
 10 files changed, 114 insertions(+), 95 deletions(-)

diff --git a/doc/reST/cli.rst b/doc/reST/cli.rst
index 4d4774613..b5a04a136 100755
--- a/doc/reST/cli.rst
+++ b/doc/reST/cli.rst
@@ -2102,7 +2102,14 @@ Quality, rate control and rate distortion options
  Used to trigger encoding of selective GOPs; Disabled by default.

  **API ONLY**
-
+
+.. option:: --frame-rc, --no-frame-rc
+
+    This option allows configuring Rate control parameter of the chosen
Rate Control
+    mode(CRF or QP or Bitrate) at frame level.
+    This option is recommended to be enabled only when planning to invoke
the API function
+    x265_encoder_reconfig() to configure Rate control parameter value for
each frame.
+    Default: disabled.

 Quantization Options
 ====================
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 14f6ccc49..9e5af81e4 100755
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -31,7 +31,7 @@ option(NATIVE_BUILD "Target the build CPU" OFF)
 option(STATIC_LINK_CRT "Statically link C and C++ runtimes 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 214)
+set(X265_BUILD 215)
 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 035c0655c..704f19422 100644
--- a/source/common/frame.h
+++ b/source/common/frame.h
@@ -175,10 +175,10 @@ public:
     Frame*                 m_nextSubDPB;           // PicList doubly
linked list pointers
     Frame*                 m_prevSubDPB;

-    /*Target bitrate*/
+    /*Target bitrate for this picture*/
     int64_t                m_targetBitrate;
     /* target CRF for this picture.*/
-    int                    m_targetCrf;
+    double                 m_targetCrf;
     /* target QP for this picture.*/
     int                    m_targetQp;

diff --git a/source/common/param.cpp b/source/common/param.cpp
index 85dd5df98..dd8a12ef4 100755
--- a/source/common/param.cpp
+++ b/source/common/param.cpp
@@ -444,6 +444,8 @@ void x265_param_default(x265_param* param)

     /* SCC */
     param->bEnableSCC = 0;
+
+    param->bConfigRCFrame = 0;
 }

 int x265_param_default_preset(x265_param* param, const char* preset, const
char* tune)
@@ -1517,6 +1519,7 @@ int x265_param_parse(x265_param* p, const char* name,
const char* value)
                 p->bEnableWeightedPred = false;
         }
 #endif
+        OPT("frame-rc") p->bConfigRCFrame = atobool(value);
         else
             return X265_PARAM_BAD_NAME;
     }
@@ -2472,6 +2475,7 @@ char *x265_param2string(x265_param* p, int padx, int
pady)
     s += snprintf(s, bufSize - (s - buf), "scc=%d", p->bEnableSCC);
 #endif
     BOOL(p->bEnableSBRC, "sbrc");
+    BOOL(p->bConfigRCFrame, "frame-rc");
 #undef BOOL
     return buf;
 }
@@ -3020,6 +3024,7 @@ void x265_copy_params(x265_param* dst, x265_param*
src)
     if (src->aomFilmGrain)
         dst->aomFilmGrain = src->aomFilmGrain;
     dst->bEnableSBRC = src->bEnableSBRC;
+    dst->bConfigRCFrame = src->bConfigRCFrame;
 }

 #ifdef SVT_HEVC
diff --git a/source/encoder/api.cpp b/source/encoder/api.cpp
index 5024314f6..5b6e959c0 100644
--- a/source/encoder/api.cpp
+++ b/source/encoder/api.cpp
@@ -358,7 +358,7 @@ int x265_encoder_reconfig(x265_encoder* enc,
x265_param* param_in)
         }
         if (!isReconfigureRc)
             encoder->m_reconfigure = true;
-        else if (encoder->m_reconfigureRc)
+        else if (encoder->m_reconfigureRc ||
encoder->m_latestParam->bConfigRCFrame)
         {
             VPS saveVPS;
             memcpy(&saveVPS.ptl, &encoder->m_vps.ptl, sizeof(saveVPS.ptl));
@@ -1408,7 +1408,15 @@ FILE* x265_csvlog_open(const x265_param* param)
 #if ENABLE_LIBVMAF
                     fprintf(csvfp, ", VMAF Frame Score");
 #endif
-                    fprintf(csvfp, ", Target bitrate");
+                    if (param->bConfigRCFrame)
+                    {
+                        if (param->rc.rateControlMode == X265_RC_ABR)
+                            fprintf(csvfp, ", Target bitrate");
+                        else if (param->rc.rateControlMode == X265_RC_CRF)
+                            fprintf(csvfp, ", Target CRF");
+                        else if (param->rc.rateControlMode == X265_RC_CQP)
+                            fprintf(csvfp, ", Target QP");
+                    }
                 }
                 fprintf(csvfp, "\n");
             }
@@ -1536,7 +1544,15 @@ void x265_csvlog_frame(const x265_param* param,
const x265_picture* pic)
 #if ENABLE_LIBVMAF
         fprintf(param->csvfpt, ", %lf", frameStats->vmafFrameScore);
 #endif
-        fprintf(param->csvfpt, ", %I64d", frameStats->currTrBitrate);
+        if (param->bConfigRCFrame)
+        {
+            if(param->rc.rateControlMode == X265_RC_ABR)
+                fprintf(param->csvfpt, ", %ld",
(long)frameStats->currTrBitrate);
+            else if (param->rc.rateControlMode == X265_RC_CRF)
+                fprintf(param->csvfpt, ", %f", frameStats->currTrCRF);
+            else if (param->rc.rateControlMode == X265_RC_CQP)
+                fprintf(param->csvfpt, ", %d", frameStats->currTrQP);
+        }
     }
     fprintf(param->csvfpt, "\n");
     fflush(stderr);
diff --git a/source/encoder/encoder.cpp b/source/encoder/encoder.cpp
index 17f14c561..1fbaf3228 100644
--- a/source/encoder/encoder.cpp
+++ b/source/encoder/encoder.cpp
@@ -1522,7 +1522,7 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
                 inputPic[view] = pic_in + view;
         }

-        x265_param* p = (m_reconfigure || m_reconfigureRc) ? m_latestParam
: m_param;
+        x265_param* p = (m_reconfigure || m_reconfigureRc ||
m_param->bConfigRCFrame) ? m_latestParam : m_param;
         Frame* inFrame[MAX_LAYERS];
         for (int layer = 0; layer < m_param->numLayers; layer++)
         {
@@ -1664,8 +1664,17 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
             }

             inFrame[layer]->m_forceqp = inputPic[0]->forceqp;
-            inFrame[layer]->m_param = (m_reconfigure || m_reconfigureRc) ?
m_latestParam : m_param;
+            inFrame[layer]->m_param = (m_reconfigure || m_reconfigureRc ||
m_param->bConfigRCFrame) ? m_latestParam : m_param;
             inFrame[layer]->m_picStruct = inputPic[0]->picStruct;
+
+            /*Copy reconfigured RC parameters to frame*/
+            if (m_param->rc.rateControlMode == X265_RC_ABR)
+                inFrame[layer]->m_targetBitrate =
inFrame[layer]->m_param->rc.bitrate;
+            else if (m_param->rc.rateControlMode == X265_RC_CRF)
+                inFrame[layer]->m_targetCrf =
inFrame[layer]->m_param->rc.rfConstant;
+            else if (m_param->rc.rateControlMode == X265_RC_CQP)
+                inFrame[layer]->m_targetQp =
inFrame[layer]->m_param->rc.qp;
+
             if (m_param->bField && m_param->interlaceMode)
                 inFrame[layer]->m_fieldNum = inputPic[0]->fieldNum;

@@ -1802,26 +1811,8 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
             }
         }

-        if (m_reconfigureRc)
-        {
-            if (m_param->rc.rateControlMode == X265_RC_ABR)
-                inFrame[0]->m_targetBitrate = m_latestParam->rc.bitrate;
-            else if (m_param->rc.rateControlMode == X265_RC_CRF)
-                inFrame[0]->m_targetCrf =
(int)m_latestParam->rc.rfConstant;
-            else if (m_param->rc.rateControlMode == X265_RC_CQP)
-                inFrame[0]->m_targetQp = m_latestParam->rc.qp;
+        if (m_reconfigureRc || m_param->bConfigRCFrame)
             inFrame[0]->m_reconfigureRc = true;
-            m_reconfigureRc = false;
-        }
-        else
-        {
-            if (m_param->rc.rateControlMode == X265_RC_ABR)
-                inFrame[0]->m_targetBitrate = m_latestParam->rc.bitrate;
-            else if (m_param->rc.rateControlMode == X265_RC_CRF)
-                inFrame[0]->m_targetCrf =
(int)m_latestParam->rc.rfConstant;
-            else if (m_param->rc.rateControlMode == X265_RC_CQP)
-                inFrame[0]->m_targetQp = m_latestParam->rc.qp;
-        }

         if (m_param->bEnableTemporalFilter)
         {
@@ -2005,16 +1996,6 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
                         if (m_param->bUseAnalysisFile)
                             x265_free_analysis_data(m_param,
&pic_out[sLayer].analysisData);
                     }
-                    if (sLayer == 0)
-                    {
-                        if (outFrame->m_targetBitrate &&
m_param->rc.rateControlMode == X265_RC_ABR)
-                            pic_out[sLayer].frameData.currTrBitrate =
outFrame->m_targetBitrate;
-                        else
-                            pic_out[sLayer].frameData.currTrBitrate = 0;
-                    }
-                    else
-                        x265_log(m_param, X265_LOG_WARNING, "Frame wise
bitrate reconfigure are not supported for enhancement layers\n");
-
                 }
                 if (m_param->rc.bStatWrite &&
(m_param->analysisMultiPassRefine || m_param->analysisMultiPassDistortion))
                 {
@@ -2230,26 +2211,30 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)

             if (frameEnc[0]->m_reconfigureRc)
             {
-                m_rateControl->m_bRcReConfig = true;
-                if (m_param->rc.rateControlMode == X265_RC_ABR)
+                if (m_reconfigureRc)
+                    x265_copy_params(m_param, m_latestParam);
+                else if (m_param->bConfigRCFrame)
                 {
-                    m_param->rc.bitrate =
(int)frameEnc[0]->m_targetBitrate;
-                    m_rateControl->m_param->rc.bitrate =
(int)frameEnc[0]->m_targetBitrate;
-                }
-                else if (m_param->rc.rateControlMode == X265_RC_CRF)
-                {
-                    m_param->rc.rfConstant =
(double)frameEnc[0]->m_targetCrf;
-                    m_rateControl->m_param->rc.rfConstant =
frameEnc[0]->m_targetCrf;
-                }
-                else if (m_param->rc.rateControlMode == X265_RC_CQP)
-                {
-                    m_param->rc.qp = frameEnc[0]->m_targetQp;
-                    m_rateControl->m_param->rc.qp =
frameEnc[0]->m_targetQp;
+                    m_rateControl->m_bRcReConfig = true;
+                    if (m_param->rc.rateControlMode == X265_RC_ABR)
+                    {
+                        m_param->rc.bitrate =
(int)frameEnc[0]->m_targetBitrate;
+                        m_rateControl->m_param->rc.bitrate =
(int)frameEnc[0]->m_targetBitrate;
+                    }
+                    else if (m_param->rc.rateControlMode == X265_RC_CRF)
+                    {
+                        m_param->rc.rfConstant =
(double)frameEnc[0]->m_targetCrf;
+                        m_rateControl->m_param->rc.rfConstant =
frameEnc[0]->m_targetCrf;
+                    }
+                    else if (m_param->rc.rateControlMode == X265_RC_CQP)
+                    {
+                        m_param->rc.qp = frameEnc[0]->m_targetQp;
+                        m_rateControl->m_param->rc.qp =
frameEnc[0]->m_targetQp;
+                    }
                 }
                 m_rateControl->reconfigureRC();
                 m_reconfigureRc = false;
             }
-
             if (frameEnc[0]->m_reconfigureRc && !m_reconfigureRc)
                 frameEnc[0]->m_reconfigureRc = false;
             if (curEncoder->m_reconfigure)
@@ -2522,6 +2507,10 @@ int Encoder::reconfigureParam(x265_param* encParam,
x265_param* param)
         encParam->rc.rfConstant = param->rc.rfConstant;
         m_reconfigureRc |= encParam->rc.qp != param->rc.qp;
         encParam->rc.qp = param->rc.qp;
+
+        /*Allow encoder to reconfigure RC for each input frame*/
+        if (encParam->bConfigRCFrame)
+            m_reconfigureRc = false;
     }
     else
     {
@@ -3204,6 +3193,9 @@ void Encoder::finishFrameStats(Frame* curFrame,
FrameEncoder *curEncoder, x265_f
                     }
                 }
             }
+            frameStats->currTrBitrate = curFrame->m_targetBitrate;
+            frameStats->currTrCRF = curFrame->m_targetCrf;
+            frameStats->currTrQP = curFrame->m_targetQp;
         }

         if (m_param->csvLogLevel >= 1)
diff --git a/source/encoder/ratecontrol.cpp b/source/encoder/ratecontrol.cpp
index 300e27534..d1f335e30 100644
--- a/source/encoder/ratecontrol.cpp
+++ b/source/encoder/ratecontrol.cpp
@@ -1935,11 +1935,13 @@ double RateControl::rateEstimateQscale(Frame*
curFrame, RateControlEntry *rce)
         if (m_param->rc.rateControlMode == X265_RC_ABR)
             m_bitrate = (double)curFrame->m_targetBitrate * 1000;
         else if (m_param->rc.rateControlMode == X265_RC_CRF)
-            m_param->rc.rfConstant = (double)curFrame->m_targetCrf;
-        else if (m_param->rc.rateControlMode == X265_RC_CQP)
-            m_param->rc.qp = curFrame->m_targetQp;
+        {
+            double mbtree_offset = m_param->rc.cuTree ? (1.0 -
m_param->rc.qCompress) * 13.5 : 0;
+            double qComp = (m_param->rc.cuTree && !m_param->rc.hevcAq) ?
0.99 : m_param->rc.qCompress;
+            m_rateFactorConstant = pow(m_currentSatd, 1.0 - qComp) /
+                x265_qp2qScale(curFrame->m_targetCrf + mbtree_offset);
+        }
     }
-
     if ((m_param->bliveVBV2pass && m_param->rc.rateControlMode ==
X265_RC_ABR) || m_isAbr)
     {
         int pos = m_sliderPos % s_slidingWindowFrames;
@@ -2021,11 +2023,6 @@ double RateControl::rateEstimateQscale(Frame*
curFrame, RateControlEntry *rce)

         if (m_bRcReConfig && m_param->rc.rateControlMode == X265_RC_CRF)
         {
-            double rfConstant = m_param->rc.rfConstant;
-            double mbtree_offset = m_param->rc.cuTree ? (1.0 -
m_param->rc.qCompress) * 13.5 : 0;
-            double qComp = (m_param->rc.cuTree && !m_param->rc.hevcAq) ?
0.99 : m_param->rc.qCompress;
-            m_rateFactorConstant = pow(m_currentSatd, 1.0 - qComp) /
-                x265_qp2qScale(rfConstant + mbtree_offset);
             double qScale = getQScale(rce, m_rateFactorConstant);
             q = x265_qScale2qp(qScale);
         }
@@ -2277,14 +2274,6 @@ double RateControl::rateEstimateQscale(Frame*
curFrame, RateControlEntry *rce)
                     m_rateFactorConstant = pow(m_currentSatd, 1.0 - qComp)
/
                         x265_qp2qScale(rfConstant + mbtree_offset);
                 }
-                if (m_bRcReConfig)
-                {
-                    double rfConstant = m_param->rc.rfConstant;
-                    double mbtree_offset = m_param->rc.cuTree ? (1.0 -
m_param->rc.qCompress) * 13.5 : 0;
-                    double qComp = (m_param->rc.cuTree &&
!m_param->rc.hevcAq) ? 0.99 : m_param->rc.qCompress;
-                    m_rateFactorConstant = pow(m_currentSatd, 1.0 - qComp)
/
-                        x265_qp2qScale(rfConstant + mbtree_offset);
-                }
                 q = getQScale(rce, m_rateFactorConstant);
                 x265_zone* zone = getZone();
                 if (zone)
@@ -2596,34 +2585,36 @@ double RateControl::tuneQscaleForSBRC(Frame*
curFrame, double q)
 double RateControl::tuneQscaleToUpdatedBitrate(Frame* curFrame, double q)
 {
     int depth = 18;
-
-    for (int iterations = 0; iterations < 100; iterations++)
+    if (m_isVbv && m_currentSatd > 0 && curFrame)
     {
-        int i;
-        double frameBitsTotal = m_fps * predictSize(&m_pred[m_predType],
q, (double)m_currentSatd);
-        for (i = 0; i < depth; i++)
+        for (int iterations = 0; iterations < 100; iterations++)
         {
-            int type = curFrame->m_lowres.plannedType[i];
-            if (type == X265_TYPE_AUTO)
+            int i;
+            double frameBitsTotal = m_fps *
predictSize(&m_pred[m_predType], q, (double)m_currentSatd);
+            for (i = 0; i < depth; i++)
+            {
+                int type = curFrame->m_lowres.plannedType[i];
+                if (type == X265_TYPE_AUTO)
+                    break;
+                int64_t satd = curFrame->m_lowres.plannedSatd[i] >>
(X265_DEPTH - 8);
+                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;
+                int predType =
getPredictorType(curFrame->m_lowres.plannedType[i], type);
+                double curBits = m_fps * predictSize(&m_pred[predType], q,
(double)satd);
+                frameBitsTotal += curBits;
+            }
+            frameBitsTotal /= i;
+            double allowedSize = (double)(curFrame->m_targetBitrate *
1000);
+            if (frameBitsTotal >= 1.1 * allowedSize)
+                q = q * 1.1;
+            else if (frameBitsTotal >= 1.05 * allowedSize)
+                q = q * 1.05;
+            else if (frameBitsTotal <= 0.9 * allowedSize)
+                q = q / 1.1;
+            else if (frameBitsTotal <= 0.95 * allowedSize)
+                q = q / 1.05;
+            else
                 break;
-            int64_t satd = curFrame->m_lowres.plannedSatd[i] >>
(X265_DEPTH - 8);
-            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;
-            int predType =
getPredictorType(curFrame->m_lowres.plannedType[i], type);
-            double curBits = m_fps * predictSize(&m_pred[predType], q,
(double)satd);
-            frameBitsTotal += curBits;
         }
-        frameBitsTotal /= i;
-        double allowedSize = (double)(curFrame->m_targetBitrate * 1000);
-        if (frameBitsTotal >= 1.1 * allowedSize)
-            q = q * 1.1;
-        else if (frameBitsTotal >= 1.05 * allowedSize)
-            q = q * 1.05;
-        else if (frameBitsTotal <= 0.9 * allowedSize)
-            q = q / 1.1;
-        else if (frameBitsTotal <= 0.95 * allowedSize)
-            q = q / 1.05;
-        else
-            break;
     }
     return q;
 }
diff --git a/source/x265.h b/source/x265.h
index 31060b1ee..c4fb1a54d 100644
--- a/source/x265.h
+++ b/source/x265.h
@@ -316,6 +316,8 @@ typedef struct x265_frame_stats
     double           unclippedBufferFillFinal;
     uint8_t          tLayer;
     int64_t          currTrBitrate;
+    double           currTrCRF;
+    int              currTrQP;
 } x265_frame_stats;

 typedef struct x265_ctu_info_t
@@ -2338,6 +2340,9 @@ typedef struct x265_param

     /*Screen Content Coding*/
     int     bEnableSCC;
+
+    /*Frame level RateControl Configuration*/
+    int     bConfigRCFrame;
 } x265_param;

 /* x265_param_alloc:
diff --git a/source/x265cli.cpp b/source/x265cli.cpp
index 3993543eb..a7d5f492a 100755
--- a/source/x265cli.cpp
+++ b/source/x265cli.cpp
@@ -434,7 +434,8 @@ namespace X265_NS {
         H0("\nSEI Message Options\n");
         H0("   --film-grain <filename>       File containing Film Grain
Characteristics to be written as a SEI Message\n");
         H0("   --aom-film-grain <filename>   File containing Aom Film
Grain Characteristics to be written as a SEI Message\n");
-
+        H0("   --[no-]frame-rc              Enable configuring Rate
Control parameters(QP, CRF or Bitrate) at frame level.Default 0\n"
+           "                                Enable this option only when
planning to invoke the API function x265_encoder_reconfig to configure Rate
Control parameters\n");
 #undef OPT
 #undef H0
 #undef H1
diff --git a/source/x265cli.h b/source/x265cli.h
index aaa385cb4..86fd14caf 100644
--- a/source/x265cli.h
+++ b/source/x265cli.h
@@ -396,6 +396,8 @@ static const struct option long_options[] =
     { "scenecut-qp-config", required_argument, NULL, 0 },
     { "film-grain", required_argument, NULL, 0 },
     { "aom-film-grain", required_argument, NULL, 0 },
+    { "frame-rc",no_argument, NULL, 0 },
+    { "no-frame-rc",no_argument, NULL, 0 },
     { 0, 0, 0, 0 },
     { 0, 0, 0, 0 },
     { 0, 0, 0, 0 },
-- 
2.28.0.windows.1

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


More information about the x265-devel mailing list