[x265] [PATCH] Modify the crf 2 pass
Alex Giladi
alex.giladi at gmail.com
Mon Aug 9 20:24:49 UTC 2021
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
More information about the x265-devel
mailing list