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 <mah...@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.gil...@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 <mah...@multicorewareinc.com> >> wrote: >>> >>> From 5065d31b4bfe36e05c94dd79db119cd1fd23aa23 Mon Sep 17 00:00:00 2001 >>> From: lwWang <li...@multicorewareinc.com> >>> Date: Fri, 25 Jun 2021 17:17:03 +0800 >>> Subject: [PATCH] Modify the crf 2 pass >>> >>> Signed-off-by: lwWang <li...@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@videolan.org >>> https://mailman.videolan.org/listinfo/x265-devel >> >> _______________________________________________ >> x265-devel mailing list >> x265-devel@videolan.org >> https://mailman.videolan.org/listinfo/x265-devel > > _______________________________________________ > x265-devel mailing list > x265-devel@videolan.org > https://mailman.videolan.org/listinfo/x265-devel _______________________________________________ x265-devel mailing list x265-devel@videolan.org https://mailman.videolan.org/listinfo/x265-devel