[x265] [PATCH] Modify the crf 2 pass
Mahesh Pittala
mahesh at multicorewareinc.com
Tue Aug 10 05:22:18 UTC 2021
Hi Alex,
a. The software that uses the x265 library would trigger the re-encode
b. For the CRF+VBV 2pass run, it will drop performance on the re-encoding.
It will not have an impact on the others
c. By default, it is not triggered. And we are not planning to add a
command-line option as it just re-encodes some selected gops instead of all
the frames. And it will work in a CRF or CRF+VBV mode. Without crf, it will
not work
Thanks,
Mahesh
On Tue, Aug 10, 2021 at 1:55 AM Alex Giladi <alex.giladi at gmail.com> wrote:
> Thank you, Mahesh!
> Can you please explain (a) what triggers the re-encode, (b) what is
> the impact on run time, (c) is it triggered by default or are there
> plans to add a command-line option and, lastly, why doesn't it work in
> a VBV or CRF+VBV mode?
> Best,
> Alex.
>
> On Sun, Aug 8, 2021 at 10:37 PM Mahesh Pittala
> <mahesh at multicorewareinc.com> wrote:
> >
> > Hi Alex,
> >
> > This patch is to re-encode some separated gops to improve the
> compression efficiency.
> >
> > Thanks,
> > Mahesh
> >
> > On Fri, Aug 6, 2021 at 6:30 AM Alex Giladi <alex.giladi at gmail.com>
> wrote:
> >>
> >> Hi Mahesh,
> >> What is the actual modification of crf in the patch?
> >> Best,
> >> Alex
> >>
> >>
> >> On Thu, Aug 5, 2021, 2:15 AM Mahesh Pittala <
> mahesh at multicorewareinc.com> wrote:
> >>>
> >>> From 5065d31b4bfe36e05c94dd79db119cd1fd23aa23 Mon Sep 17 00:00:00 2001
> >>> From: lwWang <liwei at multicorewareinc.com>
> >>> Date: Fri, 25 Jun 2021 17:17:03 +0800
> >>> Subject: [PATCH] Modify the crf 2 pass
> >>>
> >>> Signed-off-by: lwWang <liwei at multicorewareinc.com>
> >>> ---
> >>> source/common/frame.h | 1 +
> >>> source/encoder/encoder.cpp | 1 +
> >>> source/encoder/ratecontrol.cpp | 235 ++++++++++++++++-----------------
> >>> source/encoder/ratecontrol.h | 3 +
> >>> 4 files changed, 119 insertions(+), 121 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/encoder/encoder.cpp b/source/encoder/encoder.cpp
> >>> index 003011554..227933e8a 100644
> >>> --- a/source/encoder/encoder.cpp
> >>> +++ b/source/encoder/encoder.cpp
> >>> @@ -2216,6 +2216,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 e9ceccc24..28345a1e9 100644
> >>> --- a/source/encoder/ratecontrol.cpp
> >>> +++ b/source/encoder/ratecontrol.cpp
> >>> @@ -1116,123 +1116,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;
> >>> }
> >>>
> >>> - m_start = X265_MAX(m_start, endIndex - fps);
> >>> + if (m_param->rc.rateControlMode != X265_RC_CRF)
> >>> + {
> >>> + return true;
> >>> + }
> >>> +
> >>> + if (m_isQpModified)
> >>> + {
> >>> + 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;
> >>> }
> >>> @@ -1505,15 +1488,34 @@ 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;
> >>> + int index = m_encOrder[rce->poc];
> >>> + index++;
> >>> + double totalDuration = m_frameDuration;
> >>> + for (int j = 0; totalDuration < 1.0; 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
> >>> @@ -2019,7 +2021,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))
> >>> {
> >>> /* clip qp to permissible range after vbv-lookahead
> estimation to avoid possible
> >>> * mispredictions by initial frame size predictors */
> >>> @@ -2056,7 +2058,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)
> >>> {
> >>> double lmin = m_lmin[m_sliceType];
> >>> double lmax = m_lmax[m_sliceType];
> >>> @@ -2947,7 +2949,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)
> >>> {
> >>> double avgQpRc = 0;
> >>> /* determine avg QP decided by VBV rate control */
> >>> @@ -2981,16 +2983,7 @@ 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 (fabs(curEncData.m_avgQpRc - rce->qpPrev) > 0.1)
> >>> - {
> >>> - qpRef = rce->qpPrev;
> >>> - is2passCrfChange = true;
> >>> - }
> >>> - }
> >>> - if (is2passCrfChange || fabs(qpRef - rce->qpNoVbv) > 0.5)
> >>> + if (fabs(qpRef - rce->qpNoVbv) > 0.5)
> >>> {
> >>> double crfFactor = rce->qRceq /x265_qp2qScale(qpRef);
> >>> double baseCplx = m_ncu * (m_param->bframes ? 120 : 80);
> >>> diff --git a/source/encoder/ratecontrol.h
> b/source/encoder/ratecontrol.h
> >>> index a0b555f8a..666025823 100644
> >>> --- a/source/encoder/ratecontrol.h
> >>> +++ b/source/encoder/ratecontrol.h
> >>> @@ -74,6 +74,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 */
> >>> @@ -88,6 +89,8 @@ struct RateControlEntry
> >>> double rowCplxrSum;
> >>> double qpNoVbv;
> >>> double bufferFill;
> >>> + double bufferFillFinal;
> >>> + double bufferFillActual;
> >>> double targetFill;
> >>> bool vbvEndAdj;
> >>> double frameDuration;
> >>> --
> >>> 2.22.0.windows.1
> >>>
> >>> _______________________________________________
> >>> x265-devel mailing list
> >>> x265-devel at videolan.org
> >>> https://mailman.videolan.org/listinfo/x265-devel
> >>
> >> _______________________________________________
> >> x265-devel mailing list
> >> x265-devel at videolan.org
> >> https://mailman.videolan.org/listinfo/x265-devel
> >
> > _______________________________________________
> > x265-devel mailing list
> > x265-devel at videolan.org
> > https://mailman.videolan.org/listinfo/x265-devel
> _______________________________________________
> 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/20210810/fb8438b1/attachment-0001.html>
More information about the x265-devel
mailing list