[x265] [PATCH] rc: fix Rate Control for grainy content
aarthi at multicorewareinc.com
aarthi at multicorewareinc.com
Wed Jan 20 10:03:28 CET 2016
# HG changeset patch
# User Aarthi Thirumalai
# Date 1452501832 -19800
# Mon Jan 11 14:13:52 2016 +0530
# Node ID 46e5a95c852732058200d9a7bb34261011341cff
# Parent 808ece071d225f300feaf08709a9f5e0872edc89
rc: fix Rate Control for grainy content
optimize params for tune grain, reduce frequent qp fluctions to prevent grain loss
diff -r 808ece071d22 -r 46e5a95c8527 source/common/param.cpp
--- a/source/common/param.cpp Mon Jan 18 21:21:25 2016 +0530
+++ b/source/common/param.cpp Mon Jan 11 14:13:52 2016 +0530
@@ -454,16 +454,11 @@
}
else if (!strcmp(tune, "grain"))
{
- param->deblockingFilterBetaOffset = -2;
- param->deblockingFilterTCOffset = -2;
- param->bIntraInBFrames = 0;
- param->rdoqLevel = 2;
- param->psyRdoq = 10.0;
- param->psyRd = 0.5;
param->rc.ipFactor = 1.1;
- param->rc.pbFactor = 1.1;
- param->rc.aqStrength = 0.3;
- param->rc.qCompress = 0.8;
+ param->rc.pbFactor = 1.0;
+ param->rc.cuTree = 0;
+ param->rc.aqMode = 0;
+ param->rc.qpStep = 1.0;
}
else
return -1;
diff -r 808ece071d22 -r 46e5a95c8527 source/encoder/ratecontrol.cpp
--- a/source/encoder/ratecontrol.cpp Mon Jan 18 21:21:25 2016 +0530
+++ b/source/encoder/ratecontrol.cpp Mon Jan 11 14:13:52 2016 +0530
@@ -190,6 +190,8 @@
m_numEntries = 0;
m_isSceneTransition = false;
m_lastPredictorReset = 0;
+ m_avgPFrameQp = 0;
+ m_isFirstMiniGop = false;
if (m_param->rc.rateControlMode == X265_RC_CRF)
{
m_param->rc.qp = (int)m_param->rc.rfConstant;
@@ -288,6 +290,9 @@
m_ipOffset = 6.0 * X265_LOG2(m_param->rc.ipFactor);
m_pbOffset = 6.0 * X265_LOG2(m_param->rc.pbFactor);
+ for (int i = 0; i < QP_MAX_MAX; i++)
+ m_qpToEncodedBits[i] = 0;
+
/* Adjust the first frame in order to stabilize the quality level compared to the rest */
#define ABR_INIT_QP_MIN (24)
#define ABR_INIT_QP_MAX (40)
@@ -361,6 +366,7 @@
m_amortizeFraction = 0.85;
m_amortizeFrames = m_param->totalFrames / 2;
}
+
for (int i = 0; i < s_slidingWindowFrames; i++)
{
m_satdCostWindow[i] = 0;
@@ -370,15 +376,19 @@
m_isPatternPresent = false;
m_numBframesInPattern = 0;
+ m_isGrainEnabled = false;
+ if(m_param->rc.pbFactor <= 1.1 && !m_param->rc.cuTree) // tune for grainy content OR equal p-b frame sizes
+ m_isGrainEnabled = true;
+
/* 720p videos seem to be a good cutoff for cplxrSum */
- double tuneCplxFactor = (m_param->rc.cuTree && m_ncu > 3600) ? 2.5 : 1;
-
+ double tuneCplxFactor = (m_ncu > 3600 && m_param->rc.cuTree) ? 2.5 : m_isGrainEnabled ? 2 : 1;
/* estimated ratio that produces a reasonable QP for the first I-frame */
m_cplxrSum = .01 * pow(7.0e5, m_qCompress) * pow(m_ncu, 0.5) * tuneCplxFactor;
m_wantedBitsWindow = m_bitrate * m_frameDuration;
m_accumPNorm = .01;
m_accumPQp = (m_param->rc.rateControlMode == X265_RC_CRF ? CRF_INIT_QP : ABR_INIT_QP_MIN) * m_accumPNorm;
+
/* Frame Predictors used in vbv */
initFramePredictors();
if (!m_statFileOut && (m_param->rc.bStatWrite || m_param->rc.bStatRead))
@@ -1049,7 +1059,7 @@
}
m_pred[0].coeff = m_pred[3].coeff = 0.75;
m_pred[0].coeffMin = m_pred[3].coeffMin = 0.75 / 4;
- if (m_param->rc.qCompress >= 0.8) // when tuned for grain
+ if (m_isGrainEnabled) // when tuned for grain
{
m_pred[1].coeffMin = 0.75 / 4;
m_pred[1].coeff = 0.75;
@@ -1089,10 +1099,12 @@
}
rce->isActive = true;
bool isRefFrameScenecut = m_sliceType!= I_SLICE && m_curSlice->m_refFrameList[0][0]->m_lowres.bScenecut;
+ m_isFirstMiniGop = m_sliceType == I_SLICE ? true : m_isFirstMiniGop;
if (curFrame->m_lowres.bScenecut)
{
m_isSceneTransition = true;
m_lastPredictorReset = rce->encodeOrder;
+
initFramePredictors();
}
else if (m_sliceType != B_SLICE && !isRefFrameScenecut)
@@ -1197,7 +1209,7 @@
double q = x265_qScale2qp(rateEstimateQscale(curFrame, rce));
q = x265_clip3((double)QP_MIN, (double)QP_MAX_MAX, q);
m_qp = int(q + 0.5);
- rce->qpaRc = curEncData.m_avgQpRc = curEncData.m_avgQpAq = q;
+ rce->qpaRc = curEncData.m_avgQpRc = curEncData.m_avgQpAq = int(q + 0.5);
/* copy value of lastRceq into thread local rce struct *to be used in RateControlEnd() */
rce->qRceq = m_lastRceq;
accumPQpUpdate();
@@ -1450,7 +1462,7 @@
}
if (wantedBits > 0 && encodedBits > 0 && (!m_partialResidualFrames ||
- m_param->rc.bStrictCbr))
+ m_param->rc.bStrictCbr || m_isGrainEnabled))
{
abrBuffer *= X265_MAX(1, sqrt(timeDone));
overflow = x265_clip3(.5, 2.0, 1.0 + (encodedBits - wantedBits) / abrBuffer);
@@ -1460,6 +1472,22 @@
return qScale;
}
+double RateControl::tuneQScaleForGrain(double rcOverflow)
+{
+ double qpstepBy1 = pow(2, 0.5 / 6.0);
+ double qScaleAvg = x265_qp2qScale(m_avgPFrameQp);
+ double q = m_lastQScaleFor[P_SLICE];
+ int curQp = int (x265_qScale2qp(m_lastQScaleFor[P_SLICE]) + 0.5);
+ double curBitrate = m_qpToEncodedBits[curQp] * int(m_fps + 0.5);
+ int newQp = rcOverflow > 1 ? curQp + 1 : curQp - 1 ;
+ double projectedBitrate = int(m_fps + 0.5) * m_qpToEncodedBits[newQp];
+ if (curBitrate > 0 && projectedBitrate > 0)
+ q = abs(projectedBitrate - m_bitrate) < abs (curBitrate - m_bitrate) ? x265_qp2qScale(newQp) : m_lastQScaleFor[P_SLICE];
+ else
+ q = rcOverflow > 1 ? qScaleAvg * qpstepBy1 : rcOverflow < 1 ? qScaleAvg / qpstepBy1 : m_lastQScaleFor[P_SLICE];
+ return q;
+}
+
double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
{
double q;
@@ -1525,6 +1553,7 @@
q0 = q1;
}
}
+
if (prevRefSlice->m_sliceType == B_SLICE && IS_REFERENCED(m_curSlice->m_refFrameList[0][0]))
q0 -= m_pbOffset / 2;
if (nextRefSlice->m_sliceType == B_SLICE && IS_REFERENCED(m_curSlice->m_refFrameList[1][0]))
@@ -1535,7 +1564,9 @@
q = q1;
else if (i1)
q = q0;
- else
+ else if(m_isGrainEnabled)
+ q = q1;
+ else
q = (q0 * dt1 + q1 * dt0) / (dt0 + dt1);
if (IS_REFERENCED(curFrame))
@@ -1543,7 +1574,7 @@
else
q += m_pbOffset;
- /* Set a min qp at scenechanges and transitions */
+ /* Set a min qp at scenechanges and transitions */
if (m_isSceneTransition)
{
q = X265_MAX(ABR_SCENECUT_INIT_QP_MIN, q);
@@ -1553,11 +1584,28 @@
double qScale = x265_qp2qScale(q);
rce->qpNoVbv = q;
double lmin = 0, lmax = 0;
+ if (m_isGrainEnabled && m_isFirstMiniGop)
+ {
+ lmin = m_lastQScaleFor[P_SLICE] / m_lstep;
+ lmax = m_lastQScaleFor[P_SLICE] * m_lstep;
+ double tunedQscale = tuneAbrQScaleFromFeedback(qScale);
+ double overflow = tunedQscale / qScale;
+ if (!m_isAbrReset)
+ qScale = x265_clip3(lmin, lmax, qScale);
+ m_avgPFrameQp = m_avgPFrameQp == 0 ? rce->qpNoVbv : m_avgPFrameQp;
+ if (overflow != 1)
+ {
+ qScale = tuneQScaleForGrain(overflow);
+ q = x265_qScale2qp(qScale);
+ }
+ rce->qpNoVbv = q;
+ }
if (m_isVbv)
{
lmin = m_lastQScaleFor[P_SLICE] / m_lstep;
lmax = m_lastQScaleFor[P_SLICE] * m_lstep;
- if (m_isCbr)
+
+ if (m_isCbr && !m_isGrainEnabled)
{
qScale = tuneAbrQScaleFromFeedback(qScale);
if (!m_isAbrReset)
@@ -1581,7 +1629,10 @@
rce->frameSizePlanned = X265_MIN(rce->frameSizePlanned, rce->frameSizeMaximum);
rce->frameSizeEstimated = rce->frameSizePlanned;
}
+
rce->newQScale = qScale;
+ if(rce->bLastMiniGopBFrame)
+ m_isFirstMiniGop = false;
return qScale;
}
else
@@ -1675,8 +1726,17 @@
if (!m_param->rc.bStatRead)
checkAndResetABR(rce, false);
double initialQScale = getQScale(rce, m_wantedBitsWindow / m_cplxrSum);
- q = tuneAbrQScaleFromFeedback(initialQScale);
- overflow = q / initialQScale;
+ double tunedQScale = tuneAbrQScaleFromFeedback(initialQScale);
+ overflow = tunedQScale / initialQScale;
+ q = !m_partialResidualFrames? tunedQScale : initialQScale;
+ bool isEncodeEnd = (m_param->totalFrames &&
+ m_framesDone > 0.75 * m_param->totalFrames) ? 1 : 0;
+ bool isEncodeBeg = m_framesDone < (int)(m_fps + 0.5);
+ if(m_isGrainEnabled && m_sliceType!= I_SLICE && !isEncodeEnd &&
+ ((overflow < 1.05 && overflow > 0.95) || isEncodeBeg))
+ {
+ q = tuneQScaleForGrain(overflow);
+ }
}
if (m_sliceType == I_SLICE && m_param->keyframeMax > 1
&& m_lastNonBPictType != I_SLICE && !m_isAbrReset)
@@ -1684,6 +1744,7 @@
if (!m_param->rc.bStrictCbr)
q = x265_qp2qScale(m_accumPQp / m_accumPNorm);
q /= fabs(m_param->rc.ipFactor);
+ m_avgPFrameQp = 0;
}
else if (m_framesDone > 0)
{
@@ -1720,6 +1781,11 @@
m_lastQScaleFor[P_SLICE] = X265_MAX(minScenecutQscale, m_lastQScaleFor[P_SLICE]);
}
rce->qpNoVbv = x265_qScale2qp(q);
+ if(m_sliceType == P_SLICE)
+ {
+ m_avgPFrameQp = m_avgPFrameQp == 0 ? rce->qpNoVbv : m_avgPFrameQp;
+ m_avgPFrameQp = (m_avgPFrameQp + rce->qpNoVbv) / 2;
+ }
q = clipQscale(curFrame, rce, q);
/* clip qp to permissible range after vbv-lookahead estimation to avoid possible
* mispredictions by initial frame size predictors, after each scenecut */
@@ -2426,6 +2492,11 @@
int pos = m_sliderPos - m_param->frameNumThreads;
if (pos >= 0)
m_encodedBitsWindow[pos % s_slidingWindowFrames] = actualBits;
+ if(rce->sliceType != I_SLICE)
+ {
+ int qp = int (rce->qpaRc + 0.5);
+ m_qpToEncodedBits[qp] = m_qpToEncodedBits[qp] == 0 ? actualBits : (m_qpToEncodedBits[qp] + actualBits) * 0.5;
+ }
}
if (m_2pass)
diff -r 808ece071d22 -r 46e5a95c8527 source/encoder/ratecontrol.h
--- a/source/encoder/ratecontrol.h Mon Jan 18 21:21:25 2016 +0530
+++ b/source/encoder/ratecontrol.h Mon Jan 11 14:13:52 2016 +0530
@@ -126,7 +126,7 @@
bool m_isVbv;
bool m_isCbr;
bool m_singleFrameVbv;
-
+ bool m_isGrainEnabled;
bool m_isAbrReset;
int m_lastAbrResetPoc;
@@ -141,7 +141,8 @@
double m_vbvMaxRate; /* in kbps */
double m_rateFactorMaxIncrement; /* Don't allow RF above (CRF + this value). */
double m_rateFactorMaxDecrement; /* don't allow RF below (this value). */
-
+ double m_avgPFrameQp;
+ bool m_isFirstMiniGop;
Predictor m_pred[4]; /* Slice predictors to preidct bits for each Slice type - I,P,Bref and B */
int64_t m_leadingNoBSatd;
int m_predType; /* Type of slice predictors to be used - depends on the slice type */
@@ -178,7 +179,7 @@
bool m_isPatternPresent;
bool m_isSceneTransition;
int m_lastPredictorReset;
-
+ double m_qpToEncodedBits[QP_MAX_MAX];
/* a common variable on which rateControlStart, rateControlEnd and rateControUpdateStats waits to
* sync the calls to these functions. For example
* -F2:
@@ -269,6 +270,7 @@
bool vbv2Pass(uint64_t allAvailableBits, int frameCount, int startPos);
bool findUnderflow(double *fills, int *t0, int *t1, int over, int framesCount);
bool fixUnderflow(int t0, int t1, double adjustment, double qscaleMin, double qscaleMax);
+ double tuneQScaleForGrain(double rcOverflow);
};
}
#endif // ifndef X265_RATECONTROL_H
More information about the x265-devel
mailing list