[x265] [PATCH] rc: use a sliding window to calculate moving avg SatdCost for ABR Reset logic
Deepthi Nandakumar
deepthi at multicorewareinc.com
Sun Oct 5 16:08:10 CEST 2014
On Sun, Oct 5, 2014 at 5:08 PM, <aarthi at multicorewareinc.com> wrote:
> # HG changeset patch
> # User Aarthi Thirumalai
> # Date 1412460518 -19800
> # Sun Oct 05 03:38:38 2014 +0530
> # Node ID 41cb94e538b800d8792fac48ceb9f8bdf2a9f627
> # Parent b6d49505b179cb509aa76f3a065192f0b4926579
> rc: use a sliding window to calculate moving avg SatdCost for ABR Reset
> logic
>
> contains improvements for detection of scene changes within Rate Control
> to stabilize qp and prevent
> extreme spikes in bitrate. Removes the blockiness or distortions in the
> frames that a streak of low-detailed
> frames and prevents vbv from overreacting at the points of scene cuts.
>
> diff -r b6d49505b179 -r 41cb94e538b8 source/encoder/ratecontrol.cpp
> --- a/source/encoder/ratecontrol.cpp Thu Oct 02 16:47:55 2014 -0500
> +++ b/source/encoder/ratecontrol.cpp Sun Oct 05 03:38:38 2014 +0530
> @@ -40,6 +40,7 @@
> /* Amortize the partial cost of I frames over the next N frames */
> const double RateControl::s_amortizeFraction = 0.85;
> const int RateControl::s_amortizeFrames = 75;
> +const int RateControl::s_slidingWindowFrames = 20;
>
Does it make sense to set this based on the framerate? For a 60fps video,
assume 1 sec of black frames, and continually triggering reset 3 times in a
row, could this lead to VBV issues?
const char *RateControl::s_defaultStatFileName = "x265_2pass.log";
>
> namespace {
> @@ -472,6 +473,12 @@
> m_framesDone = 0;
> m_residualCost = 0;
> m_partialResidualCost = 0;
> + for (int i = 0; i < s_slidingWindowFrames; i++)
> + {
> + m_satdCostWindow[i] = 0;
> + m_encodedBitsWindow[i] = 0;
> + }
> + m_sliderPos = 0;
>
> /* 720p videos seem to be a good cutoff for cplxrSum */
> double tuneCplxFactor = (m_param->rc.cuTree && m_ncu > 3600) ? 2.5 :
> 1;
> @@ -1344,6 +1351,24 @@
> g_sliceTypeToChar[m_sliceType],
> g_sliceTypeToChar[rce->sliceType]);
> }
> }
> + else
> + {
> + if (m_isAbr)
> + {
> + double slidingWindowCplxSum = 0;
> + for (int i = 0;i < s_slidingWindowFrames; i++)
> + {
> + slidingWindowCplxSum *= 0.5;
> + if (!m_satdCostWindow[i])
> + break;
> + slidingWindowCplxSum += m_satdCostWindow[i] /
> (CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION);
> + }
> + rce->movingAvgSum = slidingWindowCplxSum;
> + m_satdCostWindow[m_sliderPos % s_slidingWindowFrames] =
> rce->lastSatd;
> + m_sliderPos++;
> + }
> + }
>
Did you mean to use m_sliderPos in this fashion? For movingAvgSum, you will
end up weighting the frames wrongly - you want the closest frames weighted
the most, and the furthest ones weighted the least.
+
> if (m_sliceType == B_SLICE)
> {
> /* B-frames don't have independent rate control, but rather get
> the
> @@ -1478,7 +1503,7 @@
> * tolerances, the bit distribution approaches that of 2pass.
> */
>
> double wantedBits, overflow = 1;
> - rce->movingAvgSum = m_shortTermCplxSum;
> +
> m_shortTermCplxSum *= 0.5;
> m_shortTermCplxCount *= 0.5;
> m_shortTermCplxSum += m_currentSatd /
> (CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION);
> @@ -1619,8 +1644,12 @@
> {
> if (!m_isAbrReset && rce->movingAvgSum > 0)
> {
> + int64_t shrtTermWantedBits = (int64_t) (X265_MIN(m_sliderPos,
> s_slidingWindowFrames) * m_bitrate * m_frameDuration);
> + int64_t shrtTermTotalBitsSum = 0;
> // Reset ABR if prev frames are blank to prevent further
> sudden overflows/ high bit rate spikes.
> - double underflow = (m_totalBits - m_wantedBitsWindow) /
> abrBuffer;
> + for (int i = 0; i < s_slidingWindowFrames ; i++)
> + shrtTermTotalBitsSum += m_encodedBitsWindow[i];
> + double underflow = (shrtTermTotalBitsSum -
> shrtTermWantedBits) / abrBuffer;
> const double epsilon = 0.0001f;
> if (underflow < epsilon && !isFrameDone)
> {
> @@ -1792,9 +1821,10 @@
> q = X265_MAX(q0 / 2, q);
> }
>
> - if (!m_isCbr)
> + if (!m_isCbr || m_isAbrReset)
> q = X265_MAX(q0, q);
>
> +
> if (m_rateFactorMaxIncrement)
> {
> double qpNoVbv = x265_qScale2qp(q0);
> @@ -2111,30 +2141,29 @@
> }
> }
> }
> - if (!m_isAbrReset)
> +
> + if (m_param->rc.aqMode || m_isVbv)
> {
> - if (m_param->rc.aqMode || m_isVbv)
> + if (pic->m_qpaRc)
> {
> - if (pic->m_qpaRc)
> - {
> - for (uint32_t i = 0; i < pic->getFrameHeightInCU(); i++)
> - pic->m_avgQpRc += pic->m_qpaRc[i];
> + for (uint32_t i = 0; i < pic->getFrameHeightInCU(); i++)
> + pic->m_avgQpRc += pic->m_qpaRc[i];
>
> - pic->m_avgQpRc /= (pic->getFrameHeightInCU() *
> pic->getFrameWidthInCU());
> - rce->qpaRc = pic->m_avgQpRc;
> - // copy avg RC qp to m_avgQpAq. To print out the correct
> qp when aq/cutree is disabled.
> - pic->m_avgQpAq = pic->m_avgQpRc;
> - }
> + pic->m_avgQpRc /= (pic->getFrameHeightInCU() *
> pic->getFrameWidthInCU());
> + rce->qpaRc = pic->m_avgQpRc;
> + // copy avg RC qp to m_avgQpAq. To print out the correct qp
> when aq/cutree is disabled.
> + pic->m_avgQpAq = pic->m_avgQpRc;
> + }
>
> - if (pic->m_qpaAq)
> - {
> - for (uint32_t i = 0; i < pic->getFrameHeightInCU(); i++)
> - pic->m_avgQpAq += pic->m_qpaAq[i];
> + if (pic->m_qpaAq)
> + {
> + for (uint32_t i = 0; i < pic->getFrameHeightInCU(); i++)
> + pic->m_avgQpAq += pic->m_qpaAq[i];
>
> - pic->m_avgQpAq /= (pic->getFrameHeightInCU() *
> pic->getFrameWidthInCU());
> - }
> + pic->m_avgQpAq /= (pic->getFrameHeightInCU() *
> pic->getFrameWidthInCU());
> }
> }
> +
> // Write frame stats into the stats file if 2 pass is enabled.
> if (m_param->rc.bStatWrite)
> {
> @@ -2199,6 +2228,9 @@
> }
> m_wantedBitsWindow += m_frameDuration * m_bitrate;
> m_totalBits += bits - rce->rowTotalBits;
> + int pos = m_sliderPos - m_param->frameNumThreads;
> + if (pos >= 0)
> + m_encodedBitsWindow[pos % s_slidingWindowFrames] = actualBits;
> }
>
> if (m_2pass)
> diff -r b6d49505b179 -r 41cb94e538b8 source/encoder/ratecontrol.h
> --- a/source/encoder/ratecontrol.h Thu Oct 02 16:47:55 2014 -0500
> +++ b/source/encoder/ratecontrol.h Sun Oct 05 03:38:38 2014 +0530
> @@ -173,7 +173,9 @@
> int64_t m_totalBits; /* total bits used for already encoded
> frames */
> int m_framesDone; /* # of frames passed through RateCotrol
> already */
> double m_fps;
> -
> + int64_t m_satdCostWindow[50];
> + int m_sliderPos;
> + int64_t m_encodedBitsWindow[50];
> /* a common variable on which rateControlStart, rateControlEnd and
> rateControUpdateStats waits to
> * sync the calls to these functions. For example
> * -F2:
> @@ -233,6 +235,7 @@
>
> static const double s_amortizeFraction;
> static const int s_amortizeFrames;
> + static const int s_slidingWindowFrames;
> static const char *s_defaultStatFileName;
>
> int m_residualFrames;
> _______________________________________________
> 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/20141005/8299c3db/attachment-0001.html>
More information about the x265-devel
mailing list