[x265] [PATCH] Add CLI support for configuring RC at frame level
Mahesh Pittala
mahesh at multicorewareinc.com
Tue Nov 12 14:21:39 UTC 2024
Pushed 2 patches to the master and release_4.1 branches
On Tue, Nov 12, 2024 at 3:02 PM Kirithika Kalirathnam <
kirithika at multicorewareinc.com> wrote:
> 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*
> _______________________________________________
> x265-devel mailing list
> x265-devel at videolan.org
> https://mailman.videolan.org/listinfo/x265-devel
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20241112/3fab8a0b/attachment-0001.htm>
More information about the x265-devel
mailing list