[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