[x265] [PATCH] Add real-time VBV fullness based QP tuning in VBV 2 pass
Aruna Matheswaran
aruna at multicorewareinc.com
Mon Sep 7 14:49:02 CEST 2020
On Fri, Sep 4, 2020 at 5:44 PM Kirithika Kalirathnam <
kirithika at multicorewareinc.com> wrote:
> From f1391f094c9c3c0502624532f751219d126905aa Mon Sep 17 00:00:00 2001
> From: Kirithika <kirithika at 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 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/20200907/1a9fac1a/attachment-0001.html>
More information about the x265-devel
mailing list