[x265] [PATCH] abr: reset ABR to prevent high bitrate peaks in single pass ABR

Steve Borho steve at borho.org
Wed Feb 5 18:35:11 CET 2014


On Tue, Feb 4, 2014 at 12:51 PM, Aarthi Priya Thirumalai <
aarthi at multicorewareinc.com> wrote:

> # HG changeset patch
> # User Aarthi Thirumalai
> # Date 1391539452 -19800
> #      Wed Feb 05 00:14:12 2014 +0530
> # Node ID ce4d1e450d2670d9f4b9ab9f1408decb32e4846b
> # Parent  2beb0bfb95032ecbedcda956d169f58bfd2b9386
> abr: reset ABR to prevent high bitrate peaks in single pass ABR
>
> Long series of blank frames in video followed by detailed content causes
> heavy ABR underflow and overall bitrates surges high for a long while.
> This patch detects this condition in Single pass ABR mode and resets ABR -
> to not consider history from blank frames and continue from following
> scene-cut.
>

queued


>
> diff -r 2beb0bfb9503 -r ce4d1e450d26 source/encoder/ratecontrol.cpp
> --- a/source/encoder/ratecontrol.cpp Tue Feb 04 12:17:11 2014 -0600
> +++ b/source/encoder/ratecontrol.cpp Wed Feb 05 00:14:12 2014 +0530
> @@ -239,7 +239,8 @@
>      shortTermCplxCount = 0;
>      framesDone = 0;
>      lastNonBPictType = I_SLICE;
> -
> +    isAbrReset = false;
> +    lastAbrResetPoc = -1;
>      // vbv initialization
>      cfg->param.rc.vbvBufferSize = Clip3(0, 2000000,
> cfg->param.rc.vbvBufferSize);
>      cfg->param.rc.vbvMaxBitrate = Clip3(0, 2000000,
> cfg->param.rc.vbvMaxBitrate);
> @@ -374,12 +375,12 @@
>      if (isAbr) //ABR,CRF
>      {
>          lastSatd = l->getEstimatedPictureCost(pic);
> +        rce->lastSatd = lastSatd;
>          double q = qScale2qp(rateEstimateQscale(rce));
>          qp = Clip3(MIN_QP, MAX_MAX_QP, (int)(q + 0.5));
>          rce->qpaRc = q;
>          /* copy value of lastRceq into thread local rce struct *to be
> used in RateControlEnd() */
>          rce->qRceq = lastRceq;
> -        rce->lastSatd = lastSatd;
>          accumPQpUpdate();
>      }
>      else //CQP
> @@ -424,6 +425,16 @@
>          double q0 = prevRefSlice->m_avgQpRc;
>          double q1 = nextRefSlice->m_avgQpRc;
>
> +        //Skip taking a reference frame before the Scenecut if ABR has
> been reset.
> +        if (lastAbrResetPoc >= 0 && !isVbv)
> +        {
> +            if (prevRefSlice->getSliceType() == P_SLICE &&
> prevRefSlice->getPOC() < lastAbrResetPoc)
> +            {
> +                i0 = i1;
> +                dt0 = dt1;
> +                q0 = q1;
> +            }
> +        }
>          if (prevRefSlice->getSliceType() == B_SLICE &&
> prevRefSlice->isReferenced())
>              q0 -= pbOffset / 2;
>          if (nextRefSlice->getSliceType() == B_SLICE &&
> nextRefSlice->isReferenced())
> @@ -463,6 +474,7 @@
>           * tolerances, the bit distribution approaches that of 2pass. */
>
>          double wantedBits, overflow = 1;
> +        rce->movingAvgSum = shortTermCplxSum;
>          shortTermCplxSum *= 0.5;
>          shortTermCplxCount *= 0.5;
>          shortTermCplxSum += lastSatd / (CLIP_DURATION(frameDuration) /
> BASE_FRAME_DURATION);
> @@ -478,6 +490,10 @@
>          }
>          else
>          {
> +            if (!isVbv)
> +            {
> +                checkAndResetABR(rce);
> +            }
>              q = getQScale(rce, wantedBitsWindow / cplxrSum);
>
>              /* ABR code can potentially be counterproductive in CBR, so
> just don't bother.
> @@ -497,7 +513,7 @@
>          }
>
>          if (sliceType == I_SLICE && cfg->param.keyframeMax > 1
> -            && lastNonBPictType != I_SLICE)
> +            && lastNonBPictType != I_SLICE && !isAbrReset)
>          {
>              q = qp2qScale(accumPQp / accumPNorm);
>              q /= fabs(cfg->param.rc.ipFactor);
> @@ -538,7 +554,7 @@
>
>          lastQScaleFor[sliceType] = q;
>
> -        if (curSlice->getPOC() == 0)
> +        if (curSlice->getPOC() == 0 || (isAbrReset && sliceType ==
> I_SLICE))
>              lastQScaleFor[P_SLICE] = q * fabs(cfg->param.rc.ipFactor);
>
>          rce->frameSizePlanned = predictSize(&pred[sliceType], q,
> (double)lastSatd);
> @@ -547,6 +563,38 @@
>      }
>  }
>
> +void RateControl::checkAndResetABR(RateControlEntry* rce)
> +{
> +    double abrBuffer = 2 * cfg->param.rc.rateTolerance * bitrate;
> +    // Check if current Slice is a scene cut that follows low
> detailed/blank frames
> +    if (rce->lastSatd > 4 * rce->movingAvgSum)
> +    {
> +        if (!isAbrReset && rce->movingAvgSum > 0)
> +        {
> +            // Reset ABR if prev frames are blank to prevent further
> sudden overflows/ high bit rate spikes.
> +            double underflow = 1.0 + (totalBits - wantedBitsWindow) /
> abrBuffer;
> +            if (underflow < 1 && curSlice->m_avgQpRc == 0)
> +            {
> +                totalBits = 0;
> +                framesDone = 0;
> +                cplxrSum = .01 * pow(7.0e5, qCompress) * pow(ncu, 0.5);
> +                wantedBitsWindow = bitrate * frameDuration;
> +                accumPNorm = .01;
> +                accumPQp = (ABR_INIT_QP_MIN)*accumPNorm;
> +                shortTermCplxSum = rce->lastSatd /
> (CLIP_DURATION(frameDuration) / BASE_FRAME_DURATION);
> +                shortTermCplxCount = 1;
> +                isAbrReset = true;
> +                lastAbrResetPoc = rce->poc;
> +            }
> +        }
> +       else
> +       {
> +           // Clear flag to reset ABR and continue as usual.
> +           isAbrReset = false;
> +       }
> +    }
> +}
> +
>  void RateControl::updateVbvPlan(Encoder* enc)
>  {
>      bufferFill = bufferFillFinal;
> @@ -705,19 +753,27 @@
>  {
>      if (isAbr)
>      {
> -        if (rce->sliceType != B_SLICE)
> -            /* The factor 1.5 is to tune up the actual bits, otherwise
> the cplxrSum is scaled too low
> -             * to improve short term compensation for next frame. */
> -            cplxrSum += bits * qp2qScale(rce->qpaRc) / rce->qRceq;
> -        else
> +        if (!isVbv)
>          {
> -            /* Depends on the fact that B-frame's QP is an offset from
> the following P-frame's.
> -             * Not perfectly accurate with B-refs, but good enough. */
> -            cplxrSum += bits * qp2qScale(rce->qpaRc) / (rce->qRceq *
> fabs(cfg->param.rc.pbFactor));
> +            checkAndResetABR(rce);
>          }
> -        wantedBitsWindow += frameDuration * bitrate;
> +
> +        if (!isAbrReset)
> +        {
> +            if (rce->sliceType != B_SLICE)
> +                /* The factor 1.5 is to tune up the actual bits,
> otherwise the cplxrSum is scaled too low
> +                 * to improve short term compensation for next frame. */
> +                cplxrSum += bits * qp2qScale(rce->qpaRc) / rce->qRceq;
> +            else
> +            {
> +                /* Depends on the fact that B-frame's QP is an offset
> from the following P-frame's.
> +                 * Not perfectly accurate with B-refs, but good enough. */
> +                cplxrSum += bits * qp2qScale(rce->qpaRc) / (rce->qRceq *
> fabs(cfg->param.rc.pbFactor));
> +            }
> +            wantedBitsWindow += frameDuration * bitrate;
> +            totalBits += bits;
> +        }
>      }
> -    totalBits += bits;
>
>      if (isVbv)
>      {
> @@ -731,8 +787,7 @@
>                  bframeBits = 0;
>              }
>          }
> +        updateVbv(bits, rce);
>      }
> -
> -    updateVbv(bits, rce);
>      return 0;
>  }
> diff -r 2beb0bfb9503 -r ce4d1e450d26 source/encoder/ratecontrol.h
> --- a/source/encoder/ratecontrol.h Tue Feb 04 12:17:11 2014 -0600
> +++ b/source/encoder/ratecontrol.h Wed Feb 05 00:14:12 2014 +0530
> @@ -60,6 +60,7 @@
>      double qRceq;
>      double frameSizePlanned;
>      double bufferRate;
> +    double movingAvgSum;
>  };
>
>  struct Predictor
> @@ -83,7 +84,6 @@
>      double bitrate;
>      double rateFactorConstant;
>      bool   isAbr;
> -
>      double bufferSize;
>      double bufferFillFinal;  /* real buffer as of the last finished frame
> */
>      double bufferFill;       /* planned buffer, if all in-progress frames
> hit their bit budget */
> @@ -97,7 +97,8 @@
>      int bframes;
>      int bframeBits;
>      double leadingNoBSatd;
> -
> +    bool isAbrReset;
> +    int lastAbrResetPoc;
>      int64_t lastSatd;
>      int    qpConstant[3];
>      double cplxrSum;          /* sum of bits*qscale/rceq */
> @@ -136,6 +137,7 @@
>      double clipQscale(double q);
>      void updateVbvPlan(Encoder* enc);
>      double predictSize(Predictor *p, double q, double var);
> +    void checkAndResetABR(RateControlEntry* rce);
>  };
>  }
>
>
>
>
> On Sun, Feb 2, 2014 at 6:07 PM, Aarthi Priya Thirumalai <
> aarthi at multicorewareinc.com> wrote:
>
>> # HG changeset patch
>> # User Aarthi Thirumalai
>> # Date 1391343895 -19800
>> #      Sun Feb 02 17:54:55 2014 +0530
>> # Node ID bf00f495951eb882e34225740c43540311bf51cb
>> # Parent  bb33ab0f4ef933315cedf388d70443bc97fefeea
>> abr: reset ABR to prevent high bitrate peaks in single pass ABR .
>>
>> Long series of blank frames in video followed by detailed content causes
>> heavy ABR underflow and overall bitrates surges high for a long while.
>> patch detects this condition in Single pass ABR mode and resets ABR - to
>> not consider history from blank frames and continue from following
>> scene-cut.
>>
>> diff -r bb33ab0f4ef9 -r bf00f495951e source/encoder/ratecontrol.cpp
>> --- a/source/encoder/ratecontrol.cpp Sat Feb 01 16:39:12 2014 -0600
>> +++ b/source/encoder/ratecontrol.cpp Sun Feb 02 17:54:55 2014 +0530
>> @@ -240,7 +240,7 @@
>>      shortTermCplxCount = 0;
>>      framesDone = 0;
>>      lastNonBPictType = I_SLICE;
>> -
>> +    isAbrReset = false;
>>      // vbv initialization
>>      cfg->param.rc.vbvBufferSize = Clip3(0, 2000000,
>> cfg->param.rc.vbvBufferSize);
>>      cfg->param.rc.vbvMaxBitrate = Clip3(0, 2000000,
>> cfg->param.rc.vbvMaxBitrate);
>> @@ -375,12 +375,12 @@
>>      if (isAbr) //ABR,CRF
>>      {
>>          lastSatd = l->getEstimatedPictureCost(pic);
>> +        rce->lastSatd = lastSatd;
>>          double q = qScale2qp(rateEstimateQscale(rce));
>>          qp = Clip3(MIN_QP, MAX_MAX_QP, (int)(q + 0.5));
>>          rce->qpaRc = q;
>>          /* copy value of lastRceq into thread local rce struct *to be
>> used in RateControlEnd() */
>>          rce->qRceq = lastRceq;
>> -        rce->lastSatd = lastSatd;
>>          accumPQpUpdate();
>>      }
>>      else //CQP
>> @@ -464,6 +464,7 @@
>>           * tolerances, the bit distribution approaches that of 2pass. */
>>
>>          double wantedBits, overflow = 1;
>> +        rce->movingAvgSum = shortTermCplxSum;
>>          shortTermCplxSum *= 0.5;
>>          shortTermCplxCount *= 0.5;
>>          shortTermCplxSum += lastSatd / (CLIP_DURATION(frameDuration) /
>> BASE_FRAME_DURATION);
>> @@ -479,6 +480,8 @@
>>          }
>>          else
>>          {
>> +            if (!isVbv)
>> +                checkAndResetABR(rce);
>>              q = getQScale(rce, wantedBitsWindow / cplxrSum);
>>
>>              /* ABR code can potentially be counterproductive in CBR, so
>> just don't bother.
>> @@ -498,7 +501,7 @@
>>          }
>>
>>          if (sliceType == I_SLICE && cfg->param.keyframeMax > 1
>> -            && lastNonBPictType != I_SLICE)
>> +            && lastNonBPictType != I_SLICE &&  !isAbrReset)
>>          {
>>              q = qp2qScale(accumPQp / accumPNorm);
>>              q /= fabs(cfg->param.rc.ipFactor);
>> @@ -539,7 +542,7 @@
>>
>>          lastQScaleFor[sliceType] = q;
>>
>> -        if (curSlice->getPOC() == 0)
>> +        if (curSlice->getPOC() == 0 || (isAbrReset && sliceType ==
>> I_SLICE))
>>              lastQScaleFor[P_SLICE] = q * fabs(cfg->param.rc.ipFactor);
>>
>>          rce->frameSizePlanned = predictSize(&pred[sliceType], q,
>> (double)lastSatd);
>> @@ -548,6 +551,35 @@
>>      }
>>  }
>>
>> +void RateControl::checkAndResetABR( RateControlEntry* rce )
>> +{
>> +    double abrBuffer = 2 * cfg->param.rc.rateTolerance * bitrate;
>> +    // Check if current SLice is a scene cut that follow low
>> detailed/blank frames
>> +    if (rce->lastSatd > 4 * rce->movingAvgSum)
>> +    {
>> +        if (!isAbrReset && rce->movingAvgSum > 0 )
>> +        {
>> +            // Reset ABR if prev frames are blank to prevent further
>> sudden overflows/ high bit rate spikes.
>> +            double underflow = 1.0 + (totalBits - wantedBitsWindow) /
>> abrBuffer;
>> +            if (underflow < 1 && curSlice->m_avgQpRc==0)
>> +            {
>> +                totalBits = 0;
>> +                framesDone = 0;
>> +                cplxrSum = .01 * pow(7.0e5, qCompress) * pow(ncu, 0.5);
>> +                wantedBitsWindow = bitrate * frameDuration;
>> +                accumPNorm = .01;
>> +                accumPQp = (ABR_INIT_QP_MIN)*accumPNorm;
>> +                isAbrReset = true;
>> +            }
>> +        }
>> +        // Clear flag to reset ABR and continue as usual.
>> +       else
>> +       {
>> +           isAbrReset = false;
>> +       }
>> +    }
>> +}
>> +
>>  void RateControl::updateVbvPlan(Encoder* enc)
>>  {
>>      bufferFill = bufferFillFinal;
>> @@ -706,19 +738,25 @@
>>  {
>>      if (isAbr)
>>      {
>> -        if (rce->sliceType != B_SLICE)
>> -            /* The factor 1.5 is to tune up the actual bits, otherwise
>> the cplxrSum is scaled too low
>> -             * to improve short term compensation for next frame. */
>> -            cplxrSum += bits * qp2qScale(rce->qpaRc) / rce->qRceq;
>> -        else
>> +        if (!isVbv)
>> +            checkAndResetABR(rce);
>> +
>> +        if(!isAbrReset)
>>          {
>> -            /* Depends on the fact that B-frame's QP is an offset from
>> the following P-frame's.
>> -             * Not perfectly accurate with B-refs, but good enough. */
>> -            cplxrSum += bits * qp2qScale(rce->qpaRc) / (rce->qRceq *
>> fabs(cfg->param.rc.pbFactor));
>> +            if (rce->sliceType != B_SLICE)
>> +                /* The factor 1.5 is to tune up the actual bits,
>> otherwise the cplxrSum is scaled too low
>> +                 * to improve short term compensation for next frame. */
>> +                cplxrSum += bits * qp2qScale(rce->qpaRc) / rce->qRceq;
>> +            else
>> +            {
>> +                /* Depends on the fact that B-frame's QP is an offset
>> from the following P-frame's.
>> +                 * Not perfectly accurate with B-refs, but good enough.
>> */
>> +                cplxrSum += bits * qp2qScale(rce->qpaRc) / (rce->qRceq *
>> fabs(cfg->param.rc.pbFactor));
>> +            }
>> +            wantedBitsWindow += frameDuration * bitrate;
>> +            totalBits += bits;
>>          }
>> -        wantedBitsWindow += frameDuration * bitrate;
>>      }
>> -    totalBits += bits;
>>
>>      if (isVbv)
>>      {
>> @@ -732,8 +770,9 @@
>>                  bframeBits = 0;
>>              }
>>          }
>> +        updateVbv(bits, rce);
>>      }
>>
>> -    updateVbv(bits, rce);
>> +
>>      return 0;
>>  }
>> diff -r bb33ab0f4ef9 -r bf00f495951e source/encoder/ratecontrol.h
>> --- a/source/encoder/ratecontrol.h Sat Feb 01 16:39:12 2014 -0600
>> +++ b/source/encoder/ratecontrol.h Sun Feb 02 17:54:55 2014 +0530
>> @@ -60,6 +60,7 @@
>>      double qRceq;
>>      double frameSizePlanned;
>>      double bufferRate;
>> +    double movingAvgSum;
>>  };
>>
>>  struct Predictor
>> @@ -97,7 +98,7 @@
>>      int bframes;
>>      int bframeBits;
>>      double leadingNoBSatd;
>> -
>> +    bool isAbrReset;
>>      int64_t lastSatd;
>>      int    qpConstant[3];
>>      double cplxrSum;          /* sum of bits*qscale/rceq */
>> @@ -136,6 +137,7 @@
>>      double clipQscale(double q);
>>      void updateVbvPlan(Encoder* enc);
>>      double predictSize(Predictor *p, double q, double var);
>> +    void checkAndResetABR( RateControlEntry* rce );
>>  };
>>  }
>>
>>
>>
>
> _______________________________________________
> x265-devel mailing list
> x265-devel at videolan.org
> https://mailman.videolan.org/listinfo/x265-devel
>
>


-- 
Steve Borho
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20140205/fc6e474d/attachment-0001.html>


More information about the x265-devel mailing list