On Fri, Sep 4, 2020 at 5:44 PM Kirithika Kalirathnam < kirith...@multicorewareinc.com> wrote:
> From f1391f094c9c3c0502624532f751219d126905aa Mon Sep 17 00:00:00 2001 > From: Kirithika <kirith...@multicorewareinc.com> > Date: Fri, 28 Aug 2020 09:20:47 +0530 > Subject: [PATCH] Add real-time VBV fullness based QP tuning in VBV 2 pass > > This commit enables real-time VBV fullness based 2nd pass QP tuning. > Experimental feature.Default disabled > --- > doc/reST/cli.rst | 9 +++ > source/CMakeLists.txt | 2 +- > source/common/param.cpp | 12 ++++ > source/encoder/ratecontrol.cpp | 100 ++++++++++++++++++--------------- > source/encoder/slicetype.cpp | 14 +++-- > source/x265.h | 4 ++ > source/x265cli.cpp | 1 + > source/x265cli.h | 2 + > 8 files changed, 92 insertions(+), 52 deletions(-) > > diff --git a/doc/reST/cli.rst b/doc/reST/cli.rst > index e6c628c36..2a4b25aca 100755 > --- a/doc/reST/cli.rst > +++ b/doc/reST/cli.rst > @@ -1977,6 +1977,15 @@ Quality, rate control and rate distortion options > > **Range of values:** 0 to 10 > > +.. option:: --realtime-vbv-2pass, --no-realtime-vbv-2pass > [AM] Can we find a better alternate for the CLI option? --real-time-vbv-2-pass looks a bit long. The term 2pass is a little confusing. Technically it is multi-pass; not just 2pass. > + > + It enables the Qp tuning at frame level based on real time VBV Buffer > fullness > + in the ratecontrol second pass to reduce the VBV violations. > + It could only be enabled with rate control pass 2 encodes with VBV and > ABR > + rate control mode. > + > [AM] why not for CRF rate-control mode? > + Default disabled. **Experimental feature** > + > Quantization Options > ==================== > > diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt > index d9ed3983e..5738c861c 100755 > --- a/source/CMakeLists.txt > +++ b/source/CMakeLists.txt > @@ -29,7 +29,7 @@ option(NATIVE_BUILD "Target the build CPU" OFF) > option(STATIC_LINK_CRT "Statically link C runtime 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 196) > +set(X265_BUILD 197) > 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/param.cpp b/source/common/param.cpp > index 845c38bd2..fc0425905 100755 > --- a/source/common/param.cpp > +++ b/source/common/param.cpp > @@ -288,6 +288,7 @@ void x265_param_default(x265_param* param) > param->bResetZoneConfig = 1; > param->reconfigWindowSize = 0; > param->decoderVbvMaxRate = 0; > + param->rc.bRealtimeVBV2pass = 0; > > /* Video Usability Information (VUI) */ > param->vui.aspectRatioIdc = 0; > @@ -1375,6 +1376,7 @@ int x265_param_parse(x265_param* p, const char* > name, const char* value) > sscanf(value, "%d,%d,%d", &p->hmeRange[0], &p->hmeRange[1], > &p->hmeRange[2]); > p->bEnableHME = true; > } > + OPT("realtime-vbv-2pass") p->rc.bRealtimeVBV2pass = > atobool(value); > else > return X265_PARAM_BAD_NAME; > } > @@ -1817,6 +1819,15 @@ int x265_check_params(x265_param* param) > CHECK(param->confWinRightOffset < 0, "Conformance Window Right Offset > must be 0 or greater"); > CHECK(param->confWinBottomOffset < 0, "Conformance Window Bottom > Offset must be 0 or greater"); > CHECK(param->decoderVbvMaxRate < 0, "Invalid Decoder Vbv Maxrate. > Value can not be less than zero"); > + if (param->rc.bRealtimeVBV2pass) > + { > + CHECK((param->rc.bStatRead == 0), "Real time VBV in 2 pass option > requires rate control 2 pass to be enabled"); > + if ((param->rc.vbvMaxBitrate <= 0 || param->rc.vbvBufferSize <= > 0)) > + { > + param->rc.bRealtimeVBV2pass = 0; > + x265_log(param, X265_LOG_WARNING, "Real time VBV enabled > without VBV settings.Disabling real time VBV in 2 pass\n"); > + } > + } > return check_failed; > } > > @@ -2593,6 +2604,7 @@ void x265_copy_params(x265_param* dst, x265_param* > src) > > dst->confWinRightOffset = src->confWinRightOffset; > dst->confWinBottomOffset = src->confWinBottomOffset; > + dst->rc.bRealtimeVBV2pass = src->rc.bRealtimeVBV2pass; > #ifdef SVT_HEVC > memcpy(dst->svtHevcParam, src->svtHevcParam, > sizeof(EB_H265_ENC_CONFIGURATION)); > #endif > diff --git a/source/encoder/ratecontrol.cpp > b/source/encoder/ratecontrol.cpp > index 82d7c4f2a..ebf374759 100644 > --- a/source/encoder/ratecontrol.cpp > +++ b/source/encoder/ratecontrol.cpp > @@ -1755,34 +1755,32 @@ double RateControl::rateEstimateQscale(Frame* > curFrame, RateControlEntry *rce) > g_sliceTypeToChar[m_sliceType], > g_sliceTypeToChar[rce->sliceType]); > } > } > - else > + > + if ((m_param->rc.bRealtimeVBV2pass && m_param->rc.rateControlMode == > X265_RC_ABR) || m_isAbr) > { > - if (m_isAbr) > + int pos = m_sliderPos % s_slidingWindowFrames; > + int addPos = (pos + s_slidingWindowFrames - 1) % > s_slidingWindowFrames; > + if (m_sliderPos > s_slidingWindowFrames) > { > - int pos = m_sliderPos % s_slidingWindowFrames; > - int addPos = (pos + s_slidingWindowFrames - 1) % > s_slidingWindowFrames; > - if (m_sliderPos > s_slidingWindowFrames) > - { > - const static double base = pow(0.5, s_slidingWindowFrames > - 1); > - m_movingAvgSum -= m_lastRemovedSatdCost * base; > - m_movingAvgSum *= 0.5; > - m_movingAvgSum += m_satdCostWindow[addPos]; > - } > - else if (m_sliderPos == s_slidingWindowFrames) > - { > - m_movingAvgSum += m_satdCostWindow[addPos]; > - } > - else if (m_sliderPos > 0) > - { > - m_movingAvgSum += m_satdCostWindow[addPos]; > - m_movingAvgSum *= 0.5; > - } > - > - rce->movingAvgSum = m_movingAvgSum; > - m_lastRemovedSatdCost = m_satdCostWindow[pos]; > - m_satdCostWindow[pos] = rce->lastSatd; > - m_sliderPos++; > + const static double base = pow(0.5, s_slidingWindowFrames - > 1); > + m_movingAvgSum -= m_lastRemovedSatdCost * base; > + m_movingAvgSum *= 0.5; > + m_movingAvgSum += m_satdCostWindow[addPos]; > + } > + else if (m_sliderPos == s_slidingWindowFrames) > + { > + m_movingAvgSum += m_satdCostWindow[addPos]; > } > + else if (m_sliderPos > 0) > + { > + m_movingAvgSum += m_satdCostWindow[addPos]; > + m_movingAvgSum *= 0.5; > + } > + > + rce->movingAvgSum = m_movingAvgSum; > + m_lastRemovedSatdCost = m_satdCostWindow[pos]; > + m_satdCostWindow[pos] = rce->lastSatd; > + m_sliderPos++; > } > > if (m_sliceType == B_SLICE) > @@ -1887,7 +1885,7 @@ double RateControl::rateEstimateQscale(Frame* > curFrame, RateControlEntry *rce) > qScale = x265_clip3(lqmin, lqmax, qScale); > } > > - if (!m_2pass) > + if (!m_2pass || m_param->rc.bRealtimeVBV2pass) > { > /* clip qp to permissible range after vbv-lookahead > estimation to avoid possible > * mispredictions by initial frame size predictors */ > @@ -1955,7 +1953,9 @@ double RateControl::rateEstimateQscale(Frame* > curFrame, RateControlEntry *rce) > else > q /= zone->bitrateFactor; > } > - q /= x265_clip3(0.5, 2.0, (double)(abrBuffer - diff) / > abrBuffer); > + /*Existing ABR conformance check may not be valid with real > time VBV*/ > + if(!m_param->rc.bRealtimeVBV2pass) > + q /= x265_clip3(0.5, 2.0, (double)(abrBuffer - diff) / > abrBuffer); > if (m_expectedBitsSum > 0) > { > /* Adjust quant based on the difference between > @@ -1988,24 +1988,34 @@ double RateControl::rateEstimateQscale(Frame* > curFrame, RateControlEntry *rce) > > if (m_isVbv) > { > - /* Do not overflow vbv */ > - double expectedSize = qScale2bits(rce, q); > - double expectedVbv = m_bufferFill + m_bufferRate - > expectedSize; > - double expectedFullness = rce->expectedVbv / m_bufferSize; > - double qmax = q * (2 - expectedFullness); > - double sizeConstraint = 1 + expectedFullness; > - qmax = X265_MAX(qmax, rce->newQScale); > - if (expectedFullness < .05) > - qmax = lmax; > - qmax = X265_MIN(qmax, lmax); > - while (((expectedVbv < rce->expectedVbv/sizeConstraint) > && (q < qmax)) || > + if (!m_param->rc.bRealtimeVBV2pass) > + { > + /* Do not overflow vbv */ > + double expectedSize = qScale2bits(rce, q); > + double expectedVbv = m_bufferFill + m_bufferRate - > expectedSize; > + double expectedFullness = rce->expectedVbv / > m_bufferSize; > + double qmax = q * (2 - expectedFullness); > + double sizeConstraint = 1 + expectedFullness; > + qmax = X265_MAX(qmax, rce->newQScale); > + if (expectedFullness < .05) > + qmax = lmax; > + qmax = X265_MIN(qmax, lmax); > + while (((expectedVbv < rce->expectedVbv / > sizeConstraint) && (q < qmax)) || > ((expectedVbv < 0) && (q < lmax))) > + { > + q *= 1.05; > + expectedSize = qScale2bits(rce, q); > + expectedVbv = m_bufferFill + m_bufferRate - > expectedSize; > + } > + } > + else > { > - q *= 1.05; > - expectedSize = qScale2bits(rce, q); > - expectedVbv = m_bufferFill + m_bufferRate - > expectedSize; > + /* clip qp to permissible range after > vbv-lookahead estimation to avoid possible > + * mispredictions by Rate Control pass 1 > statistics analysis */ > + q = clipQscale(curFrame, rce, q); > } > } > + > q = x265_clip3(lmin, lmax, q); > } > else > @@ -2301,7 +2311,7 @@ double RateControl::clipQscale(Frame* curFrame, > RateControlEntry* rce, double q) > double lmin = m_lmin[rce->sliceType]; > double lmax = m_lmax[rce->sliceType]; > double q0 = q; > - if (m_isVbv && m_currentSatd > 0 && curFrame) > + if ((m_isVbv || m_param->rc.bRealtimeVBV2pass) && curFrame && > m_currentSatd > 0 ) > { > [AM] Do we need to m_param->rc.bRealtimeVBV2pass check here? The call to clipQscale() is guarded by "m_param->rc.bRealtimeVBV2pass" from what I see. > if (m_param->lookaheadDepth || m_param->rc.cuTree || > (m_param->scenecutThreshold || m_param->bHistBasedSceneCut) || > @@ -2379,7 +2389,7 @@ double RateControl::clipQscale(Frame* curFrame, > RateControlEntry* rce, double q) > } > /* Try to get the buffer not more than 80% filled, > but don't set an impossible goal. */ > targetFill = x265_clip3(m_bufferSize * (1 - 0.2 * > finalDur), m_bufferSize, m_bufferFill - totalDuration * m_vbvMaxRate * 0.5); > - if (m_isCbr && bufferFillCur > targetFill && > !m_isSceneTransition) > + if ((m_isCbr || m_2pass) && bufferFillCur > > targetFill && !m_isSceneTransition) > { > q /= 1.01; > loopTerminate |= 2; > @@ -2432,7 +2442,7 @@ double RateControl::clipQscale(Frame* curFrame, > RateControlEntry* rce, double q) > * lookahead vbv reduces its qscale by half its value. Be on > safer side and avoid drastic > * qscale reductions for frames high in complexity */ > bool mispredCheck = rce->movingAvgSum && m_currentSatd >= > rce->movingAvgSum && q <= q0 / 2; > - if (!m_isCbr || (m_isAbr && mispredCheck)) > + if (!m_isCbr || ((m_isAbr || m_2pass) && mispredCheck)) > [AM] Same question here. > q = X265_MAX(q0, q); > > if (m_rateFactorMaxIncrement) > @@ -2442,7 +2452,7 @@ double RateControl::clipQscale(Frame* curFrame, > RateControlEntry* rce, double q) > return x265_clip3(lmin, qmax, q); > } > } > - if (m_2pass) > + if (!curFrame && m_2pass) > { > double min = log(lmin); > double max = log(lmax); > diff --git a/source/encoder/slicetype.cpp b/source/encoder/slicetype.cpp > index 4e52e584c..69f3b0ad1 100644 > --- a/source/encoder/slicetype.cpp > +++ b/source/encoder/slicetype.cpp > @@ -1497,14 +1497,15 @@ void Lookahead::slicetypeDecide() > } > } > > - if (m_lastNonB && !m_param->rc.bStatRead && > + if (m_lastNonB && > ((m_param->bFrameAdaptive && m_param->bframes) || > m_param->rc.cuTree || m_param->scenecutThreshold || > m_param->bHistBasedSceneCut || > (m_param->lookaheadDepth && m_param->rc.vbvBufferSize))) > { > - slicetypeAnalyse(frames, false); > + if(!m_param->rc.bStatRead) > + slicetypeAnalyse(frames, false); > bool bIsVbv = m_param->rc.vbvBufferSize > 0 && > m_param->rc.vbvMaxBitrate > 0; > - if (m_param->analysisLoad && m_param->scaleFactor && bIsVbv) > + if ((m_param->analysisLoad && m_param->scaleFactor && bIsVbv) || > m_param->rc.bRealtimeVBV2pass) > { > int numFrames; > for (numFrames = 0; numFrames < maxSearch; numFrames++) > @@ -1749,7 +1750,7 @@ void Lookahead::slicetypeDecide() > } > } > > - bool isKeyFrameAnalyse = (m_param->rc.cuTree || > (m_param->rc.vbvBufferSize && m_param->lookaheadDepth)) && > !m_param->rc.bStatRead; > + bool isKeyFrameAnalyse = (m_param->rc.cuTree || > (m_param->rc.vbvBufferSize && m_param->lookaheadDepth)); > if (isKeyFrameAnalyse && IS_X265_TYPE_I(m_lastNonB->sliceType)) > { > m_inputLock.acquire(); > @@ -1764,9 +1765,10 @@ void Lookahead::slicetypeDecide() > m_inputLock.release(); > > frames[j + 1] = NULL; > - slicetypeAnalyse(frames, true); > + if (!m_param->rc.bStatRead) > + slicetypeAnalyse(frames, true); > bool bIsVbv = m_param->rc.vbvBufferSize > 0 && > m_param->rc.vbvMaxBitrate > 0; > - if (m_param->analysisLoad && m_param->scaleFactor && bIsVbv) > + if ((m_param->analysisLoad && m_param->scaleFactor && bIsVbv) || > m_param->rc.bRealtimeVBV2pass) > { > int numFrames; > for (numFrames = 0; numFrames < maxSearch; numFrames++) > diff --git a/source/x265.h b/source/x265.h > index 0ffa600b0..bcd1d4e58 100644 > --- a/source/x265.h > +++ b/source/x265.h > @@ -1486,6 +1486,10 @@ typedef struct x265_param > /* internally enable if tune grain is set */ > int bEnableConstVbv; > > + /*Enables Qp tuning with respect to real time VBV buffer fullness > in rate > + control 2 pass. Experimental.Default is disabled*/ > + int bRealtimeVBV2pass; > + > [AM] Always add new param options to the end of the structure. Nested structures shall break backward compatibility. > } rc; > > /*== Video Usability Information ==*/ > diff --git a/source/x265cli.cpp b/source/x265cli.cpp > index 6e8e0e661..0b17a8d3a 100755 > --- a/source/x265cli.cpp > +++ b/source/x265cli.cpp > @@ -221,6 +221,7 @@ namespace X265_NS { > " - 3 : Nth pass, > overwrites stats file\n"); > H0(" --[no-]multi-pass-opt-analysis Refine analysis in 2 pass > based on analysis information from pass 1\n"); > H0(" --[no-]multi-pass-opt-distortion Use distortion of CTU > from pass 1 to refine qp in 2 pass\n"); > + H0(" --[no-]realtime-vbv-2pass Enable realtime VBV in rate > control 2 pass.Default %s\n", OPT(param->bLossless)); > H0(" --stats Filename for stats file in > multipass pass rate control. Default x265_2pass.log\n"); > H0(" --[no-]analyze-src-pics Motion estimation uses > source frame planes. Default disable\n"); > H0(" --[no-]slow-firstpass Enable a slow first pass in > a multipass rate control mode. Default %s\n", > OPT(param->rc.bEnableSlowFirstPass)); > diff --git a/source/x265cli.h b/source/x265cli.h > index 311f06935..89840d9af 100644 > --- a/source/x265cli.h > +++ b/source/x265cli.h > @@ -284,6 +284,8 @@ static const struct option long_options[] = > { "no-multi-pass-opt-analysis", no_argument, NULL, 0 }, > { "multi-pass-opt-distortion", no_argument, NULL, 0 }, > { "no-multi-pass-opt-distortion", no_argument, NULL, 0 }, > + { "realtime-vbv-2pass", no_argument, NULL, 0 }, > + { "no-realtime-vbv-2pass", no_argument, NULL, 0 }, > [AM] Please add an entry for the new option in x265_param2string(); This will keep the info SEI updated. > { "slow-firstpass", no_argument, NULL, 0 }, > { "no-slow-firstpass", no_argument, NULL, 0 }, > { "multi-pass-opt-rps", no_argument, NULL, 0 }, > -- > 2.28.0.windows.1 > > *Thanks,* > *Kirithika* > _______________________________________________ > x265-devel mailing list > x265-devel@videolan.org > https://mailman.videolan.org/listinfo/x265-devel > -- Regards, *Aruna Matheswaran,* Video Codec Engineer, Media & AI analytics BU,
_______________________________________________ x265-devel mailing list x265-devel@videolan.org https://mailman.videolan.org/listinfo/x265-devel