[x265] [PATCH] 1.add parameter "vbv-maxrate" "vbv-bufsize" "vbv-init" into cmd line
Steve Borho
steve at borho.org
Mon Nov 11 03:39:14 CET 2013
# HG changeset patch
# User Xun Xu, PPLive Corporation<xunxu at pptv.com>
# Date 1383907281 -28800
# Fri Nov 08 18:41:21 2013 +0800
# Node ID ea264e7350c3c37e9d11fe818917d9947c32dce4
# Parent 9d74638c3640679d09264b793afdf3ffc58a9107
1.add parameter "vbv-maxrate" "vbv-bufsize" "vbv-init" into cmd line
2.implement vbv methods, and this patch doesn't include vbv algorithm based on lookahead
future work,
1.vbv-lookahead
2.CU level ratecontrol
diff -r 9d74638c3640 -r ea264e7350c3 source/common/common.cpp
--- a/source/common/common.cpp Sat Nov 09 20:14:24 2013 -0600
+++ b/source/common/common.cpp Fri Nov 08 18:41:21 2013 +0800
@@ -186,6 +186,9 @@
param->saoLcuBasedOptimization = 1;
/* Rate control options */
+ param->rc.vbvMaxBitrate = 0;
+ param->rc.vbvBufferSize = 0;
+ param->rc.vbvBufferInit = 0.9;
param->rc.rfConstant = 28;
param->rc.bitrate = 0;
param->rc.rateTolerance = 1.0;
@@ -701,6 +704,12 @@
OPT("psnr") p->bEnablePsnr = bvalue;
OPT("hash") p->decodedPictureHashSEI = atoi(value);
OPT("aq-mode") p->rc.aqMode = atoi(value);
+ OPT("vbv-maxrate")
+ p->rc.vbvMaxBitrate = atoi(value);
+ OPT("vbv-bufsize")
+ p->rc.vbvBufferSize = atoi(value);
+ OPT("vbv-init")
+ p->rc.vbvBufferInit = atof(value);
OPT("crf")
{
p->rc.rfConstant = atof(value);
diff -r 9d74638c3640 -r ea264e7350c3 source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp Sat Nov 09 20:14:24 2013 -0600
+++ b/source/encoder/encoder.cpp Fri Nov 08 18:41:21 2013 +0800
@@ -182,6 +182,24 @@
return m_frameEncoder->getStreamHeaders(nalunits);
}
+void Encoder::updateVbvPlan(RateControl* rc)
+{
+ int encIdx, curIdx;
+ curIdx = (m_curEncoder + param.frameNumThreads - 1) % param.frameNumThreads;
+ encIdx = (curIdx + 1) % param.frameNumThreads;
+ while (encIdx!=curIdx)
+ {
+ FrameEncoder *encoder = &m_frameEncoder[encIdx];
+ double bits;
+ bits = encoder->m_rce.frameSizePlanned;
+ rc->bufferFill -= bits;
+ rc->bufferFill = X265_MAX(rc->bufferFill, 0);
+ rc->bufferFill += encoder->m_rce.bufferRate;
+ rc->bufferFill = X265_MIN(rc->bufferFill, rc->bufferSize);
+ encIdx = (encIdx + 1)% param.frameNumThreads;
+ }
+}
+
/**
\param flush force encoder to encode a frame
\param pic_in input original YUV picture or NULL
@@ -305,7 +323,7 @@
m_dpb->prepareEncode(fenc);
// set slice QP
- m_rateControl->rateControlStart(fenc, m_lookahead, &(curEncoder->m_rce));
+ m_rateControl->rateControlStart(fenc, m_lookahead, &(curEncoder->m_rce),this);
// Allow FrameEncoder::compressFrame() to start in a worker thread
curEncoder->m_enable.trigger();
diff -r 9d74638c3640 -r ea264e7350c3 source/encoder/encoder.h
--- a/source/encoder/encoder.h Sat Nov 09 20:14:24 2013 -0600
+++ b/source/encoder/encoder.h Fri Nov 08 18:41:21 2013 +0800
@@ -134,6 +134,9 @@
int extractNalData(NALUnitEBSP **nalunits);
+ //for rateControl
+ void updateVbvPlan(RateControl* rc);
+
protected:
uint64_t calculateHashAndPSNR(TComPic* pic, NALUnitEBSP **nalunits); // Returns total number of bits for encoded pic
diff -r 9d74638c3640 -r ea264e7350c3 source/encoder/frameencoder.cpp
--- a/source/encoder/frameencoder.cpp Sat Nov 09 20:14:24 2013 -0600
+++ b/source/encoder/frameencoder.cpp Fri Nov 08 18:41:21 2013 +0800
@@ -64,6 +64,7 @@
m_nalCount = 0;
m_totalTime = 0;
+ memset(&m_rce, 0, sizeof(RateControlEntry));
}
void FrameEncoder::setThreadPool(ThreadPool *p)
diff -r 9d74638c3640 -r ea264e7350c3 source/encoder/ratecontrol.cpp
--- a/source/encoder/ratecontrol.cpp Sat Nov 09 20:14:24 2013 -0600
+++ b/source/encoder/ratecontrol.cpp Fri Nov 08 18:41:21 2013 +0800
@@ -3,6 +3,7 @@
*
* Authors: Sumalatha Polureddy <sumalatha at multicorewareinc.com>
* Aarthi Priya Thirumalai <aarthi at multicorewareinc.com>
+ * Xun Xu, PPLive Corporation <xunxu at pptv.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -160,6 +161,77 @@
framesDone = 0;
lastNonBPictType = I_SLICE;
+ //vbv initialization
+ cfg->param.rc.vbvBufferSize = Clip3(0, 2000000, cfg->param.rc.vbvBufferSize);
+ cfg->param.rc.vbvMaxBitrate = Clip3(0, 2000000, cfg->param.rc.vbvMaxBitrate);
+ cfg->param.rc.vbvBufferInit = Clip3(0.0, 2000000.0, cfg->param.rc.vbvBufferInit);
+
+ if(cfg->param.rc.vbvBufferSize)
+ {
+ if (cfg->param.rc.rateControlMode == X265_RC_CQP)
+ {
+ x265_log(&cfg->param, X265_LOG_WARNING, "VBV is incompatible with constant QP, ignored.\n" );
+ cfg->param.rc.vbvBufferSize = 0;
+ cfg->param.rc.vbvMaxBitrate = 0;
+ }
+ else if (cfg->param.rc.vbvMaxBitrate == 0)
+ {
+ if (cfg->param.rc.rateControlMode == X265_RC_ABR)
+ {
+ x265_log(&cfg->param, X265_LOG_WARNING, "VBV maxrate unspecified, assuming CBR\n");
+ cfg->param.rc.vbvMaxBitrate = cfg->param.rc.bitrate;
+ }
+ else
+ {
+ x265_log(&cfg->param, X265_LOG_WARNING, "VBV bufsize set but maxrate unspecified, ignored\n");
+ cfg->param.rc.vbvBufferSize = 0;
+ }
+ }
+ else if(cfg->param.rc.vbvMaxBitrate < cfg->param.rc.bitrate &&
+ cfg->param.rc.rateControlMode == X265_RC_ABR)
+ {
+ x265_log(&cfg->param, X265_LOG_WARNING, "max bitrate less than average bitrate, assuming CBR\n");
+ cfg->param.rc.bitrate = cfg->param.rc.vbvMaxBitrate;
+ }
+ }
+ else if(cfg->param.rc.vbvMaxBitrate)
+ {
+ x265_log(&cfg->param, X265_LOG_WARNING, "VBV maxrate specified, but no bufsize, ignored\n" );
+ cfg->param.rc.vbvMaxBitrate = 0;
+ }
+
+ isVbv = cfg->param.rc.vbvMaxBitrate > 0 && cfg->param.rc.vbvBufferSize > 0;
+ fps = cfg->param.frameRate;
+ if (isVbv)
+ {
+ if(cfg->param.rc.vbvBufferSize < (int)(cfg->param.rc.vbvMaxBitrate/fps))
+ {
+ cfg->param.rc.vbvBufferSize = (int)(cfg->param.rc.vbvMaxBitrate/fps);
+ x265_log(&cfg->param, X265_LOG_WARNING, "VBV buffer size cannot be smaller than one frame, using %d kbit\n",
+ cfg->param.rc.vbvBufferSize);
+ }
+ int vbvBufferSize = cfg->param.rc.vbvBufferSize * 1000;
+ int vbvMaxBitrate = cfg->param.rc.vbvMaxBitrate * 1000;
+
+ bufferRate = vbvMaxBitrate / fps;
+ vbvMaxRate = vbvMaxBitrate;
+ bufferSize = vbvBufferSize;
+ singleFrameVbv = bufferRate * 1.1 > bufferSize;
+ bufferFillFinal = bufferSize*cfg->param.rc.vbvBufferInit;
+ }
+
+ for(int i = 0; i < 5; i++ )
+ {
+ pred[i].coeff = 2.0;
+ pred[i].count = 1.0;
+ pred[i].decay = 0.5;
+ pred[i].offset = 0.0;
+ }
+ predBfromP = pred[0];
+ bframes = cfg->param.bframes;
+ bframeBits = 0;
+ leadingNoBSatd = 0;
+
if (cfg->param.rc.rateControlMode == X265_RC_ABR)
{
/* Adjust the first frame in order to stabilize the quality level compared to the rest */
@@ -201,12 +273,26 @@
lstep = pow(2, cfg->param.rc.qpStep / 6.0);
}
-void RateControl::rateControlStart(TComPic* pic, Lookahead *l, RateControlEntry* rce)
+void RateControl::rateControlStart(TComPic* pic, Lookahead *l, RateControlEntry* rce, Encoder* enc)
{
curSlice = pic->getSlice();
sliceType = curSlice->getSliceType();
rce->sliceType = sliceType;
+ if( sliceType != B_SLICE )
+ {
+ bframes = pic->m_lowres.leadingBframes;
+ }
+ else //sliceType == B_SLICE
+ rce->bframes = bframes;
+
+ rce->bLastMiniGopBFrame = pic->m_lowres.bLastMiniGopBFrame;
+ rce->bufferRate = bufferRate;
+ rce->poc = curSlice->getPOC();
+
+ if (isVbv)
+ updateVbvPlan(enc);
+
if (isAbr) //ABR,CRF
{
lastSatd = l->getEstimatedPictureCost(pic);
@@ -215,6 +301,7 @@
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
@@ -276,6 +363,8 @@
else
q += pbOffset;
+ rce->frameSizePlanned = predictSize(&predBfromP, qp2qScale(q), leadingNoBSatd);
+
return qp2qScale(q);
}
else
@@ -362,15 +451,109 @@
double lmin1 = lmin[sliceType];
double lmax1 = lmax[sliceType];
q = Clip3(lmin1, lmax1, q);
+
+ q = clipQscale(q);
+
lastQScaleFor[sliceType] = q;
if (curSlice->getPOC() == 0)
lastQScaleFor[P_SLICE] = q * fabs(cfg->param.rc.ipFactor);
+ rce->frameSizePlanned = predictSize(&pred[sliceType], q, lastSatd);
+
return q;
}
}
+
+void RateControl::updateVbvPlan(Encoder* enc)
+{
+ bufferFill = bufferFillFinal;
+ enc->updateVbvPlan(this);
+}
+
+
+double RateControl::predictSize( Predictor *p, double q, double var)
+{
+ return (p->coeff*var + p->offset) / (q*p->count);
+}
+
+double RateControl::clipQscale(double q)
+{
+
+ double lmin1 = lmin[sliceType];
+ double lmax1 = lmax[sliceType];
+ double q0 = q;
+
+ // B-frames are not directly subject to VBV,
+ // since they are controlled by the P-frames' QPs.
+ if( isVbv && lastSatd > 0 )
+ {
+ //if (lookahead){} //for lookahead
+ //else
+ {
+ if ((sliceType == P_SLICE ||
+ (sliceType == I_SLICE && lastNonBPictType == I_SLICE)) &&
+ bufferFill/bufferSize < 0.5)
+ {
+ q /= Clip3(0.5, 1.0, 2.0*bufferFill/bufferSize);
+ }
+
+ // Now a hard threshold to make sure the frame fits in VBV.
+ // This one is mostly for I-frames.
+ double bits = predictSize(&pred[sliceType], q, lastSatd);
+
+ // For small VBVs, allow the frame to use up the entire VBV.
+ double maxFillFactor;
+ maxFillFactor = bufferSize >= 5*bufferRate ? 2 : 1;
+ // For single-frame VBVs, request that the frame use up the entire VBV.
+ double minFillFactor = singleFrameVbv ? 1 : 2;
+
+
+ for( int iterations = 0; iterations < 10; iterations++ )
+ {
+ double qf = 1.0;
+ if (bits > bufferFill/maxFillFactor)
+ qf = Clip3(0.2, 1.0, bufferFill/(maxFillFactor*bits));
+ q /= qf;
+ bits *= qf;
+ if (bits < bufferRate/minFillFactor)
+ q *= bits*minFillFactor/bufferRate;
+ bits = predictSize(&pred[sliceType], q, lastSatd);
+ }
+
+ q = X265_MAX(q0, q);
+ }
+
+ // Check B-frame complexity, and use up any bits that would
+ // overflow before the next P-frame.
+ if(sliceType == P_SLICE)
+ {
+ int nb = bframes;
+ double bits = predictSize(&pred[sliceType], q, lastSatd);
+ double pbbits = bits;
+ double bbits = predictSize(&predBfromP, q*cfg->param.rc.pbFactor, lastSatd);
+ double space;
+ if (bbits > bufferRate)
+ nb = 0;
+ pbbits = nb*bbits;
+
+ space = bufferFill + (1+nb)*bufferRate - bufferSize;
+ if (pbbits < space)
+ {
+ q *= X265_MAX(pbbits/space, bits/(0.5*bufferSize));
+ }
+ q = X265_MAX(q0-5, q);
+ }
+ q = X265_MAX(q0, q);
+ }
+
+ if (lmin1 == lmax1)
+ return lmin1;
+
+ return Clip3(lmin1, lmax1, q);
+}
+
/* modify the bitrate curve from pass1 for one frame */
double RateControl::getQScale(RateControlEntry *rce, double rateFactor)
{
@@ -389,6 +572,48 @@
return q;
}
+void RateControl::updatePredictor(Predictor *p, double q, double var, double bits)
+{
+ const double range = 1.5;
+ if(var < 10)
+ return;
+ double old_coeff = p->coeff / p->count;
+ double new_coeff = bits*q / var;
+ double new_coeff_clipped = Clip3(old_coeff/range, old_coeff*range, new_coeff);
+ double new_offset = bits*q - new_coeff_clipped * var;
+ if( new_offset >= 0 )
+ new_coeff = new_coeff_clipped;
+ else
+ new_offset = 0;
+ p->count *= p->decay;
+ p->coeff *= p->decay;
+ p->offset *= p->decay;
+ p->count ++;
+ p->coeff += new_coeff;
+ p->offset += new_offset;
+}
+
+void RateControl::updateVbv(int64_t bits, RateControlEntry* rce)
+{
+ int mbCount = (int)((cfg->param.sourceHeight * cfg->param.sourceWidth) / pow((int)16, 2.0));
+
+ if (rce->lastSatd >= mbCount)
+ updatePredictor(&pred[rce->sliceType], qp2qScale(rce->qpaRc), rce->lastSatd, (double)bits);
+
+ if (!isVbv)
+ return;
+
+ bufferFillFinal -= bits;
+
+ if (bufferFillFinal<0)
+ x265_log(&cfg->param, X265_LOG_WARNING, "poc:%d, VBV underflow (%.0f bits)\n", rce->poc, bufferFillFinal);
+
+ bufferFillFinal = X265_MAX(bufferFillFinal, 0);
+ bufferFillFinal += bufferRate;
+ bufferFillFinal = X265_MIN(bufferFillFinal, bufferSize);
+
+}
+
/* After encoding one frame, update rate control state */
int RateControl::rateControlEnd(int64_t bits, RateControlEntry* rce)
{
@@ -405,8 +630,23 @@
cplxrSum += bits * qp2qScale(rce->qpaRc) / (rce->qRceq * fabs(cfg->param.rc.pbFactor));
}
wantedBitsWindow += frameDuration * bitrate;
- rce = NULL;
}
totalBits += bits;
+
+ if(isVbv)
+ {
+ if (rce->sliceType == B_SLICE)
+ {
+ bframeBits += (int)bits;
+ if (rce->bLastMiniGopBFrame)
+ {
+ if (rce->bframes != 0)
+ updatePredictor(&predBfromP, qp2qScale(rce->qpaRc), (double)rce->lastSatd, (double)bframeBits/rce->bframes);
+ bframeBits = 0;
+ }
+ }
+ }
+
+ updateVbv(bits, rce);
return 0;
}
diff -r 9d74638c3640 -r ea264e7350c3 source/encoder/ratecontrol.h
--- a/source/encoder/ratecontrol.h Sat Nov 09 20:14:24 2013 -0600
+++ b/source/encoder/ratecontrol.h Fri Nov 08 18:41:21 2013 +0800
@@ -3,6 +3,7 @@
*
* Authors: Sumalatha Polureddy <sumalatha at multicorewareinc.com>
* Aarthi Priya Thirumalai <aarthi at multicorewareinc.com>
+ * Xun Xu, PPLive Corporation <xunxu at pptv.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,6 +27,7 @@
#define X265_RATECONTROL_H
#include "TLibCommon/CommonDef.h"
+#include "encoder.h"
namespace x265 {
// encoder namespace
@@ -42,6 +44,21 @@
double blurredComplexity;
double qpaRc;
double qRceq;
+
+ int lastSatd;
+ bool bLastMiniGopBFrame;
+ double frameSizePlanned;
+ double bufferRate;
+ int bframes;
+ int poc;
+};
+
+struct Predictor
+{
+ double coeff;
+ double count;
+ double decay;
+ double offset;
};
struct RateControl
@@ -58,6 +75,20 @@
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 */
+ double bufferRate; /* # of bits added to buffer_fill after each frame */
+ double vbvMaxRate; //kbit
+ bool singleFrameVbv;
+ bool isVbv;
+ double fps;
+ Predictor pred[5];
+ Predictor predBfromP;
+ int bframes;
+ int bframeBits;
+ double leadingNoBSatd;
+
int lastSatd;
int qpConstant[3];
double cplxrSum; /* sum of bits*qscale/rceq */
@@ -79,7 +110,7 @@
RateControl(TEncCfg * _cfg);
// to be called for each frame to process RateControl and set QP
- void rateControlStart(TComPic* pic, Lookahead *, RateControlEntry* rce);
+ void rateControlStart(TComPic* pic, Lookahead *, RateControlEntry* rce, Encoder* enc);
void calcAdaptiveQuantFrame(TComPic *pic);
int rateControlEnd(int64_t bits, RateControlEntry* rce);
@@ -89,6 +120,12 @@
double rateEstimateQscale(RateControlEntry *rce); // main logic for calculating QP based on ABR
void accumPQpUpdate();
double 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);
+ void updateVbvPlan(Encoder* enc);
+ double predictSize( Predictor *p, double q, double var);
};
}
diff -r 9d74638c3640 -r ea264e7350c3 source/x265.cpp
--- a/source/x265.cpp Sat Nov 09 20:14:24 2013 -0600
+++ b/source/x265.cpp Fri Nov 08 18:41:21 2013 +0800
@@ -120,6 +120,9 @@
{ "no-weightp", no_argument, NULL, 0 },
{ "weightp", no_argument, NULL, 'w' },
{ "crf", required_argument, NULL, 0 },
+ { "vbv-maxrate", required_argument, NULL, 0 },
+ { "vbv-bufsize", required_argument, NULL, 0 },
+ { "vbv-init", required_argument, NULL, 0 },
{ "bitrate", required_argument, NULL, 0 },
{ "qp", required_argument, NULL, 'q' },
{ "aq-mode", required_argument, NULL, 0 },
@@ -313,6 +316,9 @@
H0("\nQP, rate control and rate distortion options:\n");
H0(" --bitrate Target bitrate (kbps), implies ABR. Default %d\n", param->rc.bitrate);
H0(" --crf Quality-based VBR (0-51). Default %f\n", param->rc.rfConstant);
+ H0(" --vbv-maxrate Max local bitrate (kbit/s). Default %d\n", param->rc.vbvMaxBitrate);
+ H0(" --vbv-bufsize Set size of the VBV buffer (kbit). Default %d\n", param->rc.vbvBufferSize);
+ H0(" --vbv-init Initial VBV buffer occupancy. Default %f\n", param->rc.vbvBufferInit);
H0("-q/--qp Base QP for CQP mode. Default %d\n", param->rc.qp);
H0(" --aq-mode Mode for Adaptive Quantization - 0:none 1:aqVariance Default %d\n", param->rc.aqMode);
H0(" --cbqpoffs Chroma Cb QP Offset. Default %d\n", param->cbQpOffset);
diff -r 9d74638c3640 -r ea264e7350c3 source/x265.h
--- a/source/x265.h Sat Nov 09 20:14:24 2013 -0600
+++ b/source/x265.h Fri Nov 08 18:41:21 2013 +0800
@@ -374,6 +374,9 @@
double rfConstant; ///< Constant rate factor (CRF)
int aqMode; ///< Adaptive QP (AQ)
double aqStrength;
+ int vbvMaxBitrate;
+ int vbvBufferSize;
+ double vbvBufferInit;
} rc;
} x265_param;
More information about the x265-devel
mailing list