[x265] [PATCH 1 of 2] rc: fix Rate Control for grainy content
aarthi at multicorewareinc.com
aarthi at multicorewareinc.com
Fri Mar 4 14:44:19 CET 2016
# HG changeset patch
# User Aarthi Thirumalai
# Date 1457070158 -19800
# Fri Mar 04 11:12:38 2016 +0530
# Node ID e31a161a481e1fb97e06efce4af5886e74308298
# Parent 9cc9920bf82be1b43efd2a3628e28a3a78ab3b2f
rc: fix Rate Control for grainy content
optimize params for tune grain, reduce frequent qp fluctions to prevent grain loss
diff -r 9cc9920bf82b -r e31a161a481e source/CMakeLists.txt
--- a/source/CMakeLists.txt Wed Mar 02 17:26:11 2016 +0530
+++ b/source/CMakeLists.txt Fri Mar 04 11:12:38 2016 +0530
@@ -30,7 +30,7 @@
mark_as_advanced(FPROFILE_USE FPROFILE_GENERATE NATIVE_BUILD)
# X265_BUILD must be incremented each time the public API is changed
-set(X265_BUILD 81)
+set(X265_BUILD 82)
configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
"${PROJECT_BINARY_DIR}/x265.def")
configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
diff -r 9cc9920bf82b -r e31a161a481e source/common/param.cpp
--- a/source/common/param.cpp Wed Mar 02 17:26:11 2016 +0530
+++ b/source/common/param.cpp Fri Mar 04 11:12:38 2016 +0530
@@ -222,6 +222,7 @@
param->rc.zones = NULL;
param->rc.bEnableSlowFirstPass = 0;
param->rc.bStrictCbr = 0;
+ param->rc.bEnableGrain = 0;
/* Video Usability Information (VUI) */
param->vui.aspectRatioIdc = 0;
@@ -454,16 +455,12 @@
}
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;
+ param->rc.bEnableGrain = 1;
}
else
return -1;
diff -r 9cc9920bf82b -r e31a161a481e source/encoder/ratecontrol.cpp
--- a/source/encoder/ratecontrol.cpp Wed Mar 02 17:26:11 2016 +0530
+++ b/source/encoder/ratecontrol.cpp Fri Mar 04 11:12:38 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;
@@ -289,9 +291,13 @@
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)
+#define ABR_INIT_QP_GRAIN_MAX (32)
#define ABR_SCENECUT_INIT_QP_MIN (12)
#define CRF_INIT_QP (int)m_param->rc.rfConstant
for (int i = 0; i < 3; i++)
@@ -362,6 +368,7 @@
m_amortizeFraction = 0.85;
m_amortizeFrames = m_param->totalFrames / 2;
}
+
for (int i = 0; i < s_slidingWindowFrames; i++)
{
m_satdCostWindow[i] = 0;
@@ -371,15 +378,19 @@
m_isPatternPresent = false;
m_numBframesInPattern = 0;
+ m_isGrainEnabled = false;
+ if(m_param->rc.bEnableGrain) // 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 ? 1.9 : 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))
@@ -1069,12 +1080,12 @@
}
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;
- m_pred[0].coeff = m_pred[3].coeff = 0.5;
- m_pred[0].coeffMin = m_pred[3].coeffMin = 0.5 / 4;
+ m_pred[0].coeff = m_pred[3].coeff = 0.75;
+ m_pred[0].coeffMin = m_pred[3].coeffMin = 0.75 / 4;
}
}
@@ -1109,10 +1120,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)
@@ -1217,6 +1230,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);
+ q = m_isGrainEnabled ? m_qp : q;
rce->qpaRc = curEncData.m_avgQpRc = curEncData.m_avgQpAq = q;
/* copy value of lastRceq into thread local rce struct *to be used in RateControlEnd() */
rce->qRceq = m_lastRceq;
@@ -1456,8 +1470,6 @@
double RateControl::tuneAbrQScaleFromFeedback(double qScale)
{
double abrBuffer = 2 * m_rateTolerance * m_bitrate;
- if (m_currentSatd)
- {
/* use framesDone instead of POC as poc count is not serial with bframes enabled */
double overflow = 1.0;
double timeDone = (double)(m_framesDone - m_param->frameNumThreads + 1) * m_frameDuration;
@@ -1470,16 +1482,31 @@
}
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);
qScale *= overflow;
}
- }
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;
@@ -1545,6 +1572,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]))
@@ -1555,7 +1583,9 @@
q = q1;
else if (i1)
q = q0;
- else
+ else if(m_isGrainEnabled && !m_2pass)
+ q = q1;
+ else
q = (q0 * dt1 + q1 * dt0) / (dt0 + dt1);
if (IS_REFERENCED(curFrame))
@@ -1563,7 +1593,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);
@@ -1573,11 +1603,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)
@@ -1601,7 +1648,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
@@ -1695,8 +1745,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)
@@ -1704,6 +1763,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)
{
@@ -1728,8 +1788,18 @@
else if (m_framesDone == 0 && !m_isVbv && m_param->rc.rateControlMode == X265_RC_ABR)
{
/* for ABR alone, clip the first I frame qp */
- lqmax = x265_qp2qScale(ABR_INIT_QP_MAX) * m_lstep;
- q = X265_MIN(lqmax, q);
+ if (m_isGrainEnabled)
+ {
+ /* to maintain grain uniformity, set I frame qp in a fixed range */
+ lqmax = x265_qp2qScale(ABR_INIT_QP_GRAIN_MAX) * (m_lstep * m_lstep);
+ lqmin = x265_qp2qScale(ABR_INIT_QP_GRAIN_MAX) / (m_lstep * m_lstep);
+ q = x265_clip3(lqmin, lqmax, q);
+ }
+ else
+ {
+ lqmax = x265_qp2qScale(ABR_INIT_QP_MAX) * m_lstep;
+ q = X265_MIN(lqmax, q);
+ }
}
q = x265_clip3(MIN_QPSCALE, MAX_MAX_QPSCALE, q);
/* Set a min qp at scenechanges and transitions */
@@ -1740,6 +1810,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 */
@@ -2446,6 +2521,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 9cc9920bf82b -r e31a161a481e source/encoder/ratecontrol.h
--- a/source/encoder/ratecontrol.h Wed Mar 02 17:26:11 2016 +0530
+++ b/source/encoder/ratecontrol.h Fri Mar 04 11:12:38 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:
@@ -274,6 +275,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
diff -r 9cc9920bf82b -r e31a161a481e source/x265.h
--- a/source/x265.h Wed Mar 02 17:26:11 2016 +0530
+++ b/source/x265.h Fri Mar 04 11:12:38 2016 +0530
@@ -1113,6 +1113,9 @@
* (QG) size. Allowed values are 64, 32, 16 provided it falls within the
* inclusuve range [maxCUSize, minCUSize]. Experimental, default: maxCUSize */
uint32_t qgSize;
+
+ /* internally enable if tune grain is set */
+ int bEnableGrain;
} rc;
/*== Video Usability Information ==*/
More information about the x265-devel
mailing list