[x265] [PATCH] vbv: lookahead

santhoshini at multicorewareinc.com santhoshini at multicorewareinc.com
Wed Feb 12 09:28:52 CET 2014


# HG changeset patch
# User Santhoshini Sekar <santhoshini at multicorewareinc.com>
# Date 1392193304 -19800
#      Wed Feb 12 13:51:44 2014 +0530
# Node ID 377667a1343362a9cc84506144f1a5a91bf5e8a4
# Parent  699f2aa335e9995e32a07181eb70bd1ed1dfb7e9
vbv: lookahead

diff -r 699f2aa335e9 -r 377667a13433 source/common/lowres.h
--- a/source/common/lowres.h	Tue Feb 11 13:06:43 2014 +0530
+++ b/source/common/lowres.h	Wed Feb 12 13:51:44 2014 +0530
@@ -121,6 +121,8 @@
     uint16_t(*lowresCosts[X265_BFRAME_MAX + 2][X265_BFRAME_MAX + 2]);
     int32_t*  lowresMvCosts[2][X265_BFRAME_MAX + 1];
     MV*       lowresMvs[2][X265_BFRAME_MAX + 1];
+    int       plannedType[X265_LOOKAHEAD_MAX+1];
+    int64_t   plannedSatd[X265_LOOKAHEAD_MAX+1];
 
     /* rate control / adaptive quant data */
     double*   qpAqOffset;      // qp Aq offset values for each Cu
diff -r 699f2aa335e9 -r 377667a13433 source/encoder/ratecontrol.cpp
--- a/source/encoder/ratecontrol.cpp	Tue Feb 11 13:06:43 2014 +0530
+++ b/source/encoder/ratecontrol.cpp	Wed Feb 12 13:51:44 2014 +0530
@@ -384,7 +384,7 @@
     {
         lastSatd = l->getEstimatedPictureCost(pic);
         rce->lastSatd = lastSatd;
-        double q = qScale2qp(rateEstimateQscale(rce));
+        double q = qScale2qp(rateEstimateQscale(pic, 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() */
@@ -416,7 +416,7 @@
         accumPQp += qp;
 }
 
-double RateControl::rateEstimateQscale(RateControlEntry *rce)
+double RateControl::rateEstimateQscale(TComPic* pic, RateControlEntry *rce)
 {
     double q;
 
@@ -558,7 +558,7 @@
         double lmax1 = lmax[sliceType];
         q = Clip3(lmin1, lmax1, q);
 
-        q = clipQscale(q);
+        q = clipQscale(pic, q);
 
         lastQScaleFor[sliceType] = q;
 
@@ -614,7 +614,7 @@
     return (p->coeff * var + p->offset) / (q * p->count);
 }
 
-double RateControl::clipQscale(double q)
+double RateControl::clipQscale(TComPic* pic, double q)
 {
     double lmin1 = lmin[sliceType];
     double lmax1 = lmax[sliceType];
@@ -624,8 +624,55 @@
     // since they are controlled by the P-frames' QPs.
     if (isVbv && lastSatd > 0)
     {
-        //if (lookahead){} //for lookahead
-        //else
+        if (cfg->param.lookaheadDepth)
+        {
+            int terminate = 0;
+
+            /* Avoid an infinite loop. */
+            for (int iterations = 0; iterations < 1000 && terminate != 3; iterations++)
+            {
+                double frameQ[3];
+                double curBits = predictSize(&pred[sliceType], q, (double)lastSatd);
+                double bufferFillCur = bufferFill - curBits;
+                double targetFill;
+                double totalDuration = 0;
+                frameQ[0] = sliceType == I_SLICE ? q * cfg->param.rc.ipFactor : q;
+                frameQ[1] = frameQ[0] * cfg->param.rc.pbFactor;
+                frameQ[2] = frameQ[0] / cfg->param.rc.ipFactor;
+
+                /* Loop over the planned future frames. */
+                for (int j = 0; bufferFillCur >= 0 && bufferFillCur <= bufferSize; j++)
+                {
+                    totalDuration += frameDuration;
+                    bufferFillCur += vbvMaxRate * frameDuration;
+                    int type = pic->m_lowres.plannedType[j];
+                    int64_t satd = pic->m_lowres.plannedSatd[j];
+                    if (type == X265_TYPE_AUTO)
+                        break;
+                    type = IS_X265_TYPE_I(type) ? I_SLICE : IS_X265_TYPE_B(type) ? B_SLICE : P_SLICE;
+                    curBits = predictSize(&pred[type], frameQ[type], (double)satd);
+                    bufferFillCur -= curBits;
+                }
+                /* Try to get the buffer at least 50% filled, but don't set an impossible goal. */
+                targetFill = X265_MIN(bufferFill + totalDuration * vbvMaxRate * 0.5, bufferSize * 0.5);
+                if (bufferFillCur < targetFill)
+                {
+                    q *= 1.01;
+                    terminate |= 1;
+                    continue;
+                }
+                /* Try to get the buffer no more than 80% filled, but don't set an impossible goal. */
+                targetFill = Clip3(bufferFill - totalDuration * vbvMaxRate * 0.5, bufferSize * 0.8, bufferSize);
+                if (vbvMinRate && bufferFillCur > targetFill)
+                {
+                    q /= 1.01;
+                    terminate |= 2;
+                    continue;
+                }
+                break;
+            }
+        }
+        else
         {
             if ((sliceType == P_SLICE ||
                  (sliceType == I_SLICE && lastNonBPictType == I_SLICE)) &&
diff -r 699f2aa335e9 -r 377667a13433 source/encoder/ratecontrol.h
--- a/source/encoder/ratecontrol.h	Tue Feb 11 13:06:43 2014 +0530
+++ b/source/encoder/ratecontrol.h	Wed Feb 12 13:51:44 2014 +0530
@@ -129,13 +129,13 @@
 protected:
 
     double getQScale(RateControlEntry *rce, double rateFactor);
-    double rateEstimateQscale(RateControlEntry *rce); // main logic for calculating QP based on ABR
+    double rateEstimateQscale(TComPic* pic, RateControlEntry *rce); // main logic for calculating QP based on ABR
     void accumPQpUpdate();
     uint32_t acEnergyCu(TComPic* pic, uint32_t block_x, uint32_t block_y);
 
     void updateVbv(int64_t bits, RateControlEntry* rce);
     void updatePredictor(Predictor *p, double q, double var, double bits);
-    double clipQscale(double q);
+    double clipQscale(TComPic* pic, double q);
     void updateVbvPlan(Encoder* enc);
     double predictSize(Predictor *p, double q, double var);
     void checkAndResetABR(RateControlEntry* rce);
diff -r 699f2aa335e9 -r 377667a13433 source/encoder/slicetype.cpp
--- a/source/encoder/slicetype.cpp	Tue Feb 11 13:06:43 2014 +0530
+++ b/source/encoder/slicetype.cpp	Wed Feb 12 13:51:44 2014 +0530
@@ -373,13 +373,57 @@
     }
 }
 
+void Lookahead::vbvLookahead(Lowres **frames, int numFrames, int keyframe)
+{
+    int prevNonB = 0, curNonB = 1, idx = 0;
+    while (curNonB < numFrames && frames[curNonB]->sliceType == X265_TYPE_B)
+        curNonB++;
+    int nextNonB = keyframe ? prevNonB : curNonB;
+
+    while (curNonB < numFrames)
+    {
+        /* P/I cost: This shouldn't include the cost of nextNonB */
+        if (nextNonB != curNonB)
+        {
+            int p0 = IS_X265_TYPE_I(frames[curNonB]->sliceType) ? curNonB : prevNonB;
+            frames[nextNonB]->plannedSatd[idx] = vbvFrameCost(frames, p0, curNonB, curNonB);
+            frames[nextNonB]->plannedType[idx] = frames[curNonB]->sliceType;
+            idx++;
+        }
+        /* Handle the B-frames: coded order */
+        for (int i = prevNonB+1; i < curNonB; i++, idx++)
+        {
+            frames[nextNonB]->plannedSatd[idx] = vbvFrameCost(frames, prevNonB, curNonB, i);
+            frames[nextNonB]->plannedType[idx] = X265_TYPE_B;
+        }
+        prevNonB = curNonB;
+        curNonB++;
+        while (curNonB <= numFrames && frames[curNonB]->sliceType == X265_TYPE_B)
+            curNonB++;
+    }
+    frames[nextNonB]->plannedType[idx] = X265_TYPE_AUTO;
+}
+
+int64_t Lookahead::vbvFrameCost(Lowres **frames, int p0, int p1, int b)
+{
+    int64_t cost = est.estimateFrameCost(frames, p0, p1, b, 0);
+    if (cfg->param.rc.aqMode)
+    {
+        if (cfg->param.rc.cuTree)
+            return frameCostRecalculate(frames, p0, p1, b);
+        else
+            return frames[b]->costEstAq[b-p0][p1-b];
+    }
+    return cost;
+}
+
 void Lookahead::slicetypeAnalyse(Lowres **frames, bool bKeyframe)
 {
     int numFrames, origNumFrames, keyintLimit, framecnt;
     int maxSearch = X265_MIN(cfg->param.lookaheadDepth, X265_LOOKAHEAD_MAX);
     int cuCount = NUM_CUS;
     int resetStart;
-
+    bool bIsVbvLookahead = cfg->param.rc.vbvBufferSize && cfg->param.lookaheadDepth;
     if (!lastNonB)
         return;
 
@@ -403,7 +447,9 @@
     keyintLimit = cfg->param.keyframeMax - frames[0]->frameNum + lastKeyframe - 1;
     origNumFrames = numFrames = X265_MIN(framecnt, keyintLimit);
 
-    if (cfg->param.bOpenGOP && numFrames < framecnt)
+    if (bIsVbvLookahead)
+        numFrames = framecnt;
+    else if (cfg->param.bOpenGOP && numFrames < framecnt)
         numFrames++;
     else if (numFrames == 0)
     {
@@ -538,6 +584,9 @@
         resetStart = X265_MIN(resetStart, j + 1);
     }
 
+    if (bIsVbvLookahead)
+        vbvLookahead(frames, numFrames, bKeyframe);
+
     /* Restore frametypes for all frames that haven't actually been decided yet. */
     for (int j = resetStart; j <= numFrames; j++)
     {
diff -r 699f2aa335e9 -r 377667a13433 source/encoder/slicetype.h
--- a/source/encoder/slicetype.h	Tue Feb 11 13:06:43 2014 +0530
+++ b/source/encoder/slicetype.h	Wed Feb 12 13:51:44 2014 +0530
@@ -151,6 +151,8 @@
     bool    scenecutInternal(Lowres **frames, int p0, int p1, bool bRealScenecut);
     void    slicetypePath(Lowres **frames, int length, char(*best_paths)[X265_LOOKAHEAD_MAX + 1]);
     int64_t slicetypePathCost(Lowres **frames, char *path, int64_t threshold);
+    int64_t vbvFrameCost(Lowres **frames, int p0, int p1, int b);
+    void    vbvLookahead(Lowres **frames, int numFrames, int keyframes);
 
     /* called by slicetypeAnalyse() to effect cuTree adjustments to adaptive
      * quant offsets */


More information about the x265-devel mailing list