[x265] [PATCH] rc: use a sliding window to calculate moving avg SatdCost for ABR Reset logic

Aarthi Priya Thirumalai aarthi at multicorewareinc.com
Sun Oct 5 17:47:32 CEST 2014


On Oct 5, 2014 7:38 PM, "Deepthi Nandakumar" <deepthi at multicorewareinc.com>
wrote:
>
>
>
> 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?

It won't trigger reset three times in this case..only the frame that ends
this streak after 60frames will be identified as the scene change.
But if we have a large window, we cannot identify a scene change with
smaller sequence of  blank frames. As the moving avg within the window may
be high already.
>
>>  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.

Yes, you are right. The logic as it is, will be correct for weighing the
frames when the first window is filling up.but once that's done and the
window begins to slide, weighing order is messed up.
I will fix this and start the order for weighing the average satd cost
from frame : sliderPos (this will be the oldest frame in the window) if the
sliderPos > windowSize . else the order start from 0 .

>
>> +
>>      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
>
>
>
> _______________________________________________
> 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/57dd2172/attachment.html>


More information about the x265-devel mailing list