[x265] [PATCH] encoding only the focused frames in the crf 2 pass
Aruna Matheswaran
aruna at multicorewareinc.com
Fri Sep 24 08:55:51 UTC 2021
On Fri, Sep 24, 2021 at 1:34 PM Dakshinya T R S <
dakshinya at multicorewareinc.com> wrote:
> From d4ac086b18c41e6d146cd70524c581138b1186ec Mon Sep 17 00:00:00 2001
> From: lwWang <liwei at multicorewareinc.com>
> Date: Wed, 15 Sep 2021 10:39:51 +0800
> Subject: [PATCH 1/2] encoding only the focused frames in the crf 2 pass
>
> ---
> source/common/frame.h | 1 +
> source/common/framedata.cpp | 2 +-
> source/common/param.cpp | 1 +
> source/encoder/encoder.cpp | 1 +
> source/encoder/ratecontrol.cpp | 240 ++++++++++++++++++---------------
> source/encoder/ratecontrol.h | 3 +
> source/x265.h | 3 +
> 7 files changed, 138 insertions(+), 113 deletions(-)
>
> diff --git a/source/common/frame.h b/source/common/frame.h
> index dc5bbacf7..ac1185e81 100644
> --- a/source/common/frame.h
> +++ b/source/common/frame.h
> @@ -70,6 +70,7 @@ struct RcStats
> double count[4];
> double offset[4];
> double bufferFillFinal;
> + int64_t currentSatd;
> };
>
> class Frame
> diff --git a/source/common/framedata.cpp b/source/common/framedata.cpp
> index c77b017a8..70af8a248 100644
> --- a/source/common/framedata.cpp
> +++ b/source/common/framedata.cpp
> @@ -62,7 +62,7 @@ bool FrameData::create(const x265_param& param, const
> SPS& sps, int csp)
> }
> else
> return false;
> - CHECKED_MALLOC_ZERO(m_cuStat, RCStatCU, sps.numCUsInFrame);
> + CHECKED_MALLOC_ZERO(m_cuStat, RCStatCU, sps.numCUsInFrame + 1);
>
[AM] What is the significance of this additional memory?
> CHECKED_MALLOC(m_rowStat, RCStatRow, sps.numCuInHeight);
> reinit(sps);
>
> diff --git a/source/common/param.cpp b/source/common/param.cpp
> index 6751e0aa7..2c1583d93 100755
> --- a/source/common/param.cpp
> +++ b/source/common/param.cpp
> @@ -282,6 +282,7 @@ void x265_param_default(x265_param* param)
> param->rc.bStatRead = 0;
> param->rc.bStatWrite = 0;
> param->rc.statFileName = NULL;
> + param->rc.bEncFocusedFramesOnly = 0;
> param->rc.complexityBlur = 20;
> param->rc.qblur = 0.5;
> param->rc.zoneCount = 0;
> diff --git a/source/encoder/encoder.cpp b/source/encoder/encoder.cpp
> index dee8fd85d..cc014a740 100644
> --- a/source/encoder/encoder.cpp
> +++ b/source/encoder/encoder.cpp
> @@ -2249,6 +2249,7 @@ int Encoder::encode(const x265_picture* pic_in,
> x265_picture* pic_out)
> outFrame->m_rcData->iCuCount =
> outFrame->m_encData->m_frameStats.percent8x8Intra * m_rateControl->m_ncu;
> outFrame->m_rcData->pCuCount =
> outFrame->m_encData->m_frameStats.percent8x8Inter * m_rateControl->m_ncu;
> outFrame->m_rcData->skipCuCount =
> outFrame->m_encData->m_frameStats.percent8x8Skip * m_rateControl->m_ncu;
> + outFrame->m_rcData->currentSatd =
> curEncoder->m_rce.coeffBits;
> }
>
> /* Allow this frame to be recycled if no frame encoders are
> using it for reference */
> diff --git a/source/encoder/ratecontrol.cpp
> b/source/encoder/ratecontrol.cpp
> index a4756de39..2c211022d 100644
> --- a/source/encoder/ratecontrol.cpp
> +++ b/source/encoder/ratecontrol.cpp
> @@ -1002,123 +1002,106 @@ bool RateControl::initPass2()
> uint64_t allAvailableBits = uint64_t(m_param->rc.bitrate * 1000. *
> m_numEntries * m_frameDuration);
> int startIndex, framesCount, endIndex;
> int fps = X265_MIN(m_param->keyframeMax, (int)(m_fps + 0.5));
> + int distance = fps << 1;
> + distance = distance > m_param->keyframeMax ? (m_param->keyframeMax <<
> 1) : m_param->keyframeMax;
> startIndex = endIndex = framesCount = 0;
> - int diffQp = 0;
> double targetBits = 0;
> double expectedBits = 0;
> - for (startIndex = m_start, endIndex = m_start; endIndex <
> m_numEntries; endIndex++)
> + double targetBits2 = 0;
> + double expectedBits2 = 0;
> + double cpxSum = 0;
> + double cpxSum2 = 0;
> +
> + if (m_param->rc.rateControlMode == X265_RC_ABR)
> {
> - allConstBits += m_rce2Pass[endIndex].miscBits;
> - allCodedBits += m_rce2Pass[endIndex].coeffBits +
> m_rce2Pass[endIndex].mvBits;
> - if (m_param->rc.rateControlMode == X265_RC_CRF)
> + for (startIndex = m_start, endIndex = m_start; endIndex <
> m_numEntries; endIndex++)
> {
> - framesCount = endIndex - startIndex + 1;
> - diffQp += int (m_rce2Pass[endIndex].qpaRc -
> m_rce2Pass[endIndex].qpNoVbv);
> - if (framesCount > fps)
> - diffQp -= int (m_rce2Pass[endIndex - fps].qpaRc -
> m_rce2Pass[endIndex - fps].qpNoVbv);
> - if (framesCount >= fps)
> - {
> - if (diffQp >= 1)
> - {
> - if (!m_isQpModified && endIndex > fps)
> - {
> - double factor = 2;
> - double step = 0;
> - if (endIndex + fps >= m_numEntries)
> - {
> - m_start = endIndex - (endIndex % fps);
> - return true;
> - }
> - for (int start = endIndex + 1; start <= endIndex
> + fps && start < m_numEntries; start++)
> - {
> - RateControlEntry *rce = &m_rce2Pass[start];
> - targetBits += qScale2bits(rce,
> x265_qp2qScale(rce->qpNoVbv));
> - expectedBits += qScale2bits(rce, rce->qScale);
> - }
> - if (expectedBits < 0.95 * targetBits)
> - {
> - m_isQpModified = true;
> - m_isGopReEncoded = true;
> - while (endIndex + fps < m_numEntries)
> - {
> - step = pow(2, factor / 6.0);
> - expectedBits = 0;
> - for (int start = endIndex + 1; start <=
> endIndex + fps; start++)
> - {
> - RateControlEntry *rce =
> &m_rce2Pass[start];
> - rce->newQScale = rce->qScale / step;
> - X265_CHECK(rce->newQScale >= 0, "new
> Qscale is negative\n");
> - expectedBits += qScale2bits(rce,
> rce->newQScale);
> - rce->newQp =
> x265_qScale2qp(rce->newQScale);
> - }
> - if (expectedBits >= targetBits && step >
> 1)
> - factor *= 0.90;
> - else
> - break;
> - }
> -
> - if (m_isVbv && endIndex + fps < m_numEntries)
> - if (!vbv2Pass((uint64_t)targetBits,
> endIndex + fps, endIndex + 1))
> - return false;
> -
> - targetBits = 0;
> - expectedBits = 0;
> -
> - for (int start = endIndex - fps + 1; start <=
> endIndex; start++)
> - {
> - RateControlEntry *rce =
> &m_rce2Pass[start];
> - targetBits += qScale2bits(rce,
> x265_qp2qScale(rce->qpNoVbv));
> - }
> - while (1)
> - {
> - step = pow(2, factor / 6.0);
> - expectedBits = 0;
> - for (int start = endIndex - fps + 1;
> start <= endIndex; start++)
> - {
> - RateControlEntry *rce =
> &m_rce2Pass[start];
> - rce->newQScale = rce->qScale * step;
> - X265_CHECK(rce->newQScale >= 0, "new
> Qscale is negative\n");
> - expectedBits += qScale2bits(rce,
> rce->newQScale);
> - rce->newQp =
> x265_qScale2qp(rce->newQScale);
> - }
> - if (expectedBits > targetBits && step > 1)
> - factor *= 1.1;
> - else
> - break;
> - }
> - if (m_isVbv)
> - if (!vbv2Pass((uint64_t)targetBits,
> endIndex, endIndex - fps + 1))
> - return false;
> - diffQp = 0;
> - m_reencode = endIndex - fps + 1;
> - endIndex = endIndex + fps;
> - startIndex = endIndex + 1;
> - m_start = startIndex;
> - targetBits = expectedBits = 0;
> - }
> - else
> - targetBits = expectedBits = 0;
> - }
> - }
> - else
> - m_isQpModified = false;
> - }
> + allConstBits += m_rce2Pass[endIndex].miscBits;
> + allCodedBits += m_rce2Pass[endIndex].coeffBits +
> m_rce2Pass[endIndex].mvBits;
> }
> - }
>
> - if (m_param->rc.rateControlMode == X265_RC_ABR)
> - {
> if (allAvailableBits < allConstBits)
> {
> x265_log(m_param, X265_LOG_ERROR, "requested bitrate is too
> low. estimated minimum is %d kbps\n",
> - (int)(allConstBits * m_fps / framesCount * 1000.));
> + (int)(allConstBits * m_fps / framesCount * 1000.));
> return false;
> }
> if (!analyseABR2Pass(allAvailableBits))
> return false;
> +
> + return true;
> + }
> +
> + if (m_param->rc.rateControlMode != X265_RC_CRF)
> + {
>
[AM] This condition will never hit. Please clarify why this is required.
> + return true;
> }
>
> - m_start = X265_MAX(m_start, endIndex - fps);
> + if (m_isQpModified)
> + {
>
[AM] This condition will always hit. Please clarify why this check is
required.
> + return true;
> + }
> +
> + if (m_start + (fps << 1) > m_numEntries)
> + {
> + return true;
> + }
> +
> + for (startIndex = m_start, endIndex = m_numEntries - 1; startIndex <
> endIndex; startIndex++, endIndex--)
> + {
> + cpxSum += m_rce2Pass[startIndex].qScale /
> m_rce2Pass[startIndex].coeffBits;
> + cpxSum2 += m_rce2Pass[endIndex].qScale /
> m_rce2Pass[endIndex].coeffBits;
> +
> + RateControlEntry *rce = &m_rce2Pass[startIndex];
> + targetBits += qScale2bits(rce, x265_qp2qScale(rce->qpNoVbv));
> + expectedBits += qScale2bits(rce, rce->qScale);
> +
> + rce = &m_rce2Pass[endIndex];
> + targetBits2 += qScale2bits(rce, x265_qp2qScale(rce->qpNoVbv));
> + expectedBits2 += qScale2bits(rce, rce->qScale);
> + }
> +
> + if (expectedBits < 0.95 * targetBits || expectedBits2 < 0.95 *
> targetBits2)
> + {
> + if (cpxSum / cpxSum2 < 0.95 || cpxSum2 / cpxSum < 0.95)
> + {
> + m_isQpModified = true;
> + m_isGopReEncoded = true;
> +
> + m_shortTermCplxSum = 0;
> + m_shortTermCplxCount = 0;
> + m_framesDone = m_start;
> +
> + for (startIndex = m_start; startIndex < m_numEntries;
> startIndex++)
> + {
> + m_shortTermCplxSum *= 0.5;
> + m_shortTermCplxCount *= 0.5;
> + m_shortTermCplxSum += m_rce2Pass[startIndex].currentSatd
> / (CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION);
> + m_shortTermCplxCount++;
> + }
> +
> + m_bufferFill = m_rce2Pass[m_start - 1].bufferFill;
> + m_bufferFillFinal = m_rce2Pass[m_start - 1].bufferFillFinal;
> + m_bufferFillActual = m_rce2Pass[m_start - 1].bufferFillActual;
> +
> + m_reencode = m_start;
> + m_start = m_numEntries;
> + }
> + else
> + {
> +
> + m_isQpModified = false;
> + m_isGopReEncoded = false;
> + }
> + }
> + else
> + {
> +
> + m_isQpModified = false;
> + m_isGopReEncoded = false;
> + }
> +
> + m_start = X265_MAX(m_start, m_numEntries - distance +
> m_param->keyframeMax);
>
> return true;
> }
> @@ -1391,15 +1374,47 @@ int RateControl::rateControlStart(Frame* curFrame,
> RateControlEntry* rce, Encode
> rce->frameSizeMaximum *= m_param->maxAUSizeFactor;
> }
> }
> +
> + ///< regenerate the qp
> if (!m_isAbr && m_2pass && m_param->rc.rateControlMode == X265_RC_CRF)
> {
> - rce->qpPrev = x265_qScale2qp(rce->qScale);
> - rce->qScale = rce->newQScale;
> - rce->qpaRc = curEncData.m_avgQpRc = curEncData.m_avgQpAq =
> x265_qScale2qp(rce->newQScale);
> - m_qp = int(rce->qpaRc + 0.5);
> - rce->frameSizePlanned = qScale2bits(rce, rce->qScale);
> - m_framesDone++;
> - return m_qp;
> + if (!m_param->rc.bEncFocusedFramesOnly)
> + {
> + rce->qpPrev = x265_qScale2qp(rce->qScale);
> + rce->qScale = rce->newQScale;
> + rce->qpaRc = curEncData.m_avgQpRc = curEncData.m_avgQpAq =
> x265_qScale2qp(rce->newQScale);
> + m_qp = int(rce->qpaRc + 0.5);
> + rce->frameSizePlanned = qScale2bits(rce, rce->qScale);
> + m_framesDone++;
> + return m_qp;
> + }
> + else
> + {
> + int index = m_encOrder[rce->poc];
> + index++;
> + double totalDuration = m_frameDuration;
> + for (int j = 0; totalDuration < 1.0 && index < m_numEntries;
> j++)
> + {
> + switch (m_rce2Pass[index].sliceType)
> + {
> + case B_SLICE:
> + curFrame->m_lowres.plannedType[j] =
> m_rce2Pass[index].keptAsRef ? X265_TYPE_BREF : X265_TYPE_B;
> + break;
> + case P_SLICE:
> + curFrame->m_lowres.plannedType[j] = X265_TYPE_P;
> + break;
> + case I_SLICE:
> + curFrame->m_lowres.plannedType[j] = m_param->bOpenGOP
> ? X265_TYPE_I : X265_TYPE_IDR;
> + break;
> + default:
> + break;
> + }
> +
> + curFrame->m_lowres.plannedSatd[j] =
> m_rce2Pass[index].currentSatd;
> + totalDuration += m_frameDuration;
> + index++;
> + }
> + }
> }
>
> if (m_isAbr || m_2pass) // ABR,CRF
> @@ -1890,7 +1905,7 @@ double RateControl::rateEstimateQscale(Frame*
> curFrame, RateControlEntry *rce)
> qScale = x265_clip3(lqmin, lqmax, qScale);
> }
>
> - if (!m_2pass || m_param->bliveVBV2pass)
> + if (!m_2pass || m_param->bliveVBV2pass || (m_2pass &&
> m_param->rc.rateControlMode == X265_RC_CRF &&
> m_param->rc.bEncFocusedFramesOnly))
> {
> /* clip qp to permissible range after vbv-lookahead
> estimation to avoid possible
> * mispredictions by initial frame size predictors */
> @@ -1927,7 +1942,7 @@ double RateControl::rateEstimateQscale(Frame*
> curFrame, RateControlEntry *rce)
> else
> {
> double abrBuffer = 2 * m_rateTolerance * m_bitrate;
> - if (m_2pass)
> + if (m_2pass && (m_param->rc.rateControlMode != X265_RC_CRF ||
> !m_param->rc.bEncFocusedFramesOnly))
> {
>
[AM] Please include a conformance check in Encoder::configure() to disable
m_param->rc.bEncFocusedFramesOnly if either bStatRead or CRF is disabled.
> double lmin = m_lmin[m_sliceType];
> double lmax = m_lmax[m_sliceType];
> @@ -2828,7 +2843,7 @@ int RateControl::rateControlEnd(Frame* curFrame,
> int64_t bits, RateControlEntry*
>
> if (m_param->rc.aqMode || m_isVbv || m_param->bAQMotion ||
> bEnableDistOffset)
> {
> - if (m_isVbv && !(m_2pass && m_param->rc.rateControlMode ==
> X265_RC_CRF))
> + if (m_isVbv && !(m_2pass && m_param->rc.rateControlMode ==
> X265_RC_CRF && !m_param->rc.bEncFocusedFramesOnly))
> {
> double avgQpRc = 0;
> /* determine avg QP decided by VBV rate control */
> @@ -2862,8 +2877,9 @@ int RateControl::rateControlEnd(Frame* curFrame,
> int64_t bits, RateControlEntry*
> if (m_param->rc.rateControlMode == X265_RC_CRF)
> {
> double crfVal, qpRef = curEncData.m_avgQpRc;
> +
> bool is2passCrfChange = false;
> - if (m_2pass)
> + if (m_2pass && !m_param->rc.bEncFocusedFramesOnly)
> {
> if (fabs(curEncData.m_avgQpRc - rce->qpPrev) > 0.1)
> {
> diff --git a/source/encoder/ratecontrol.h b/source/encoder/ratecontrol.h
> index 996465eeb..204bd71e1 100644
> --- a/source/encoder/ratecontrol.h
> +++ b/source/encoder/ratecontrol.h
> @@ -73,6 +73,7 @@ struct RateControlEntry
> Predictor rowPreds[3][2];
> Predictor* rowPred[2];
>
> + int64_t currentSatd;
> int64_t lastSatd; /* Contains the picture cost of the previous
> frame, required for resetAbr and VBV */
> int64_t leadingNoBSatd;
> int64_t rowTotalBits; /* update cplxrsum and totalbits at the end of
> 2 rows */
> @@ -87,6 +88,8 @@ struct RateControlEntry
> double rowCplxrSum;
> double qpNoVbv;
> double bufferFill;
> + double bufferFillFinal;
> + double bufferFillActual;
> double targetFill;
> bool vbvEndAdj;
> double frameDuration;
> diff --git a/source/x265.h b/source/x265.h
> index 3f3133536..6bb893c98 100644
> --- a/source/x265.h
> +++ b/source/x265.h
> @@ -1443,6 +1443,9 @@ typedef struct x265_param
> * encoder will default to using x265_2pass.log */
> const char* statFileName;
>
> + /* if only the focused frames would be re-encode or not */
> + int bEncFocusedFramesOnly;
> +
>
[AM] Update X265_BUILD, add help document, and add an entry for the new
param in info SEI.
Is this a param?API only option? If so, clarify that in the document. Else,
add a test CLI.
> /* temporally blur quants */
> double qblur;
>
> --
> 2.22.0.windows.1
>
> _______________________________________________
> x265-devel mailing list
> x265-devel at videolan.org
> https://mailman.videolan.org/listinfo/x265-devel
>
--
Regards,
*Aruna Matheswaran,*
Video Codec Engineer,
Media & AI analytics BU,
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20210924/65b039ba/attachment-0001.html>
More information about the x265-devel
mailing list