系列文章目录
【x264编码器】章节1——x264编码流程及基于x264的编码器demo
【x264编码器】章节2——x264的lookahead流程分析
【x265编码器】章节2——编码流程及基于x265的编码器demo
目录
1.构造函数RateControl::RateControl
3.启动码率控制RateControl::rateControlStart
4.估计量化参数RateControl::rateEstimateQscale
5.当前帧编码码控结束RateControl::rateControlEnd
一、码率控制说明
码率控制的流程图如下图所示,其中的红色的模块即为码率控制模块:
完整的x265的流程如下:
二、码率控制模型
B帧的QP不同于I/P帧的QP计算,而是由前后参考帧的QP经过偏移得到,详细过程可以查看RateControl::rateEstimateQscale。
1.R-Q模型说明
x265码率控制R-Q模型可以表示为如下:
(公式1)
(公式2)
double RateControl::predictSize(Predictor *p, double q, double var)
{
return (p->coeff * var + p->offset) / (q * p->count);
}
double x265_qScale2qp(double qScale)
{
return 12.0 + 6.0 * (double)X265_LOG2(qScale / 0.85);
}
其中Complex表示当前帧的编码复杂度,x265中用lookahead模块计算的SATD作为衡量当前帧的复杂度,c为模型参数,qscale是率失真优化中的拉格朗日乘子,R为编码当前帧需要的比特数。
(公式3)
其中
(公式4)
上述公式3对应的代码如下:
double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
{ //无关代码省略
m_shortTermCplxSum *= 0.5;//计算短期复杂性和短期复杂性计数的更新
m_shortTermCplxCount *= 0.5;
m_shortTermCplxSum += m_currentSatd / (CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION);//短期复杂性是根据当前帧的SATD(Sum of Absolute Transformed Differences)值和帧间隔计算得到的
m_shortTermCplxCount++;
/* coeffBits to be used in 2-pass 将当前帧的SATD值赋给rce->coeffBits,用于后续的2-pass过程*/
rce->coeffBits = (int)m_currentSatd;
//编码复杂度的计算方式
rce->blurredComplexity = m_shortTermCplxSum / m_shortTermCplxCount;//计算模糊复杂性(blurredComplexity),即短期复杂性与短期复杂性计数的比值
//无关代码省略
//如果使用的是CRF(恒定质量)模式
if (m_param->rc.rateControlMode == X265_RC_CRF)
{ //检查并重置CRF相关的参数,m_rateFactorConstant为固定值
q = getQScale(rce, m_rateFactorConstant);//根据rce和m_rateFactorConstant计算量化参数(q)
}
else
{ //检查并重置ABR相关的参数,m_wantedBitsWindow / m_cplxrSum即为公式中右边的部分
double initialQScale = getQScale(rce, m_wantedBitsWindow / m_cplxrSum);
//无关代码省略
}
//m_wantedBitsWindow和m_cplxrSum获取的位置,省略多余代码
int RateControl::rateControlEnd(Frame* curFrame, int64_t bits, RateControlEntry* rce, int *filler)
{
if (rce->sliceType != B_SLICE)
{ //如果不为B帧,则按以下公式更新m_cplxrSum
/* 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. */
m_cplxrSum += (bits * x265_qp2qScale(rce->qpaRc) / rce->qRceq) - (rce->rowCplxrSum);
}
else
{ //取决于B帧的QP是从下面的P帧偏移的
/* 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. */
m_cplxrSum += (bits * x265_qp2qScale(rce->qpaRc) / (rce->qRceq * fabs(m_param->rc.pbFactor))) - (rce->rowCplxrSum);
}//更新 m_wantedBitsWindow,默认使用增加 m_frameDuration * m_bitrate
m_wantedBitsWindow += (m_param->bEnableSBRC ? (m_bitrate * (1.0 / m_param->keyframeMax)) : m_frameDuration * m_bitrate);
}
2.CRF模式(Constant Rate Factor恒定码率系数)
对于CRF模式而言,右边这部分是是固定值m_rateFactorConstant,在初始化的时候确定,跟分辨率,qCompress这个参数是外部可设参数,默认值0.6,因此QP的选择只跟编码复杂度有关,跟码率无关,编码复杂度越高,QP值越高,因此可以一定程度上说CRF模式是恒定质量的编码模式。
从公式3上可以得出:
1.当qCompress=1,此时QP选择跟编码复杂度无关,即固定QP,CQP;
2.当qCompress=0,此时QP跟编码复杂度成正比,分配给复杂高和复杂度低帧的QP相同,行为接近CBR;
对于CRF而言,m_rateFactorConstant由下面代码赋值;
RateControl::RateControl(x265_param& p, Encoder *top)
{ //省略代码
double baseCplx = m_ncu * (m_param->bframes ? 120 : 80);
double mbtree_offset = m_param->rc.cuTree ? (1.0 - m_param->rc.qCompress) * 13.5 : 0;
m_rateFactorConstant = pow(baseCplx, 1 - m_qCompress) /
x265_qp2qScale(m_param->rc.rfConstant + mbtree_offset);
}
3.ABR模式(Average Bitrate平均码率)
对于CRF模式而言,经过上述的调整即可,而对于ABR模式而言,上面对于qscale的处理属于第一次调整,qscale还会经过下面第二次调整:
(公式5)
(公式6)
overflow的范围是0.5-2之间,利用overflow对总目标比特和编码时间比特之间的差距,进行调整,其中代表前一帧编码为止所产生的实际比特数之和,
为前一帧为止目标比特数之和,
是平静比特率缓存区,初始值是两倍目标比特率和瞬时容忍度(通常为1)的乘积,对应的代码如下:
double RateControl::tuneAbrQScaleFromFeedback(double qScale)
{
double abrBuffer = 2 * m_rateTolerance * m_bitrate;
/* use framesDone instead of POC as poc count is not serial with bframes enabled */
double overflow = 1.0;
double duration = m_param->bEnableSBRC ? (1.0 / m_param->keyframeMax) : m_frameDuration;
double timeDone = (double)(m_framesDone - m_param->frameNumThreads + 1) * duration;
double wantedBits = timeDone * m_bitrate;
int64_t encodedBits = m_totalBits;
if (m_param->totalFrames && m_param->totalFrames <= 2 * m_fps)
{
abrBuffer = m_param->totalFrames * (m_bitrate / m_fps);
encodedBits = m_encodedBits;
}
if (wantedBits > 0 && encodedBits > 0 && (!m_partialResidualFrames ||
m_param->rc.bStrictCbr || m_isGrainEnabled || m_param->bEnableSBRC))
{
abrBuffer *= X265_MAX(1, sqrt(timeDone));
overflow = x265_clip3(.5, 2.0, 1.0 + (encodedBits - wantedBits) / abrBuffer);
qScale *= overflow;
}
return qScale;
}
4.CBR模式(Constant Bitrate恒定码率)
CBR模式,是在ABR模式的基础上再做限制,当允许的最大码率等于平均码率,m_param->rc.vbvMaxBitrate == m_param->rc.bitrate,此时即CBR模式
//伪代码
double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
{
//第一次调整
double initialQScale = getQScale(rce, m_wantedBitsWindow / m_cplxrSum);
//第二次调整
double tunedQScale = tuneAbrQScaleFromFeedback(initialQScale);
//第三次调整
q = clipQscale(curFrame, rce, q);
}
三、代码模块分析
1.构造函数RateControl::RateControl
RateControl类的构造函数,它负责初始化RateControl类的成员变量,并根据参数设置进行一些计算和验证:
RateControl::RateControl(x265_param& p, Encoder *top)
{
m_param = &p;
m_top = top;//计算低分辨率CU(Coding Unit)的宽度和高度:根据输入源视频的宽度和高度,计算低分辨率CU的宽度和高度。这个计算用于后续的码率控制过程
int lowresCuWidth = ((m_param->sourceWidth / 2) + X265_LOWRES_CU_SIZE - 1) >> X265_LOWRES_CU_BITS;
int lowresCuHeight = ((m_param->sourceHeight / 2) + X265_LOWRES_CU_SIZE - 1) >> X265_LOWRES_CU_BITS;
m_ncu = lowresCuWidth * lowresCuHeight;
//设置qCompress参数:根据参数设置,确定是否启用qCompress参数。如果rc.cuTree为真且rc.hevcAq为假,则设置qCompress为1,否则使用参数rc.qCompress的值
m_qCompress = (m_param->rc.cuTree && !m_param->rc.hevcAq) ? 1 : m_param->rc.qCompress;
//初始化其他成员变量:初始化一系列用于码率控制的成员变量,包括帧率、码率因子的最大增量和最大减量、帧计数等等
// validate for param->rc, maybe it is need to add a function like x265_parameters_valiate()
m_zoneBufferIdx = 0;
m_residualFrames = 0;
m_partialResidualFrames = 0;
m_residualCost = 0;
m_partialResidualCost = 0;
m_rateFactorMaxIncrement = 0;
m_rateFactorMaxDecrement = 0;
m_fps = (double)m_param->fpsNum / m_param->fpsDenom;
m_startEndOrder.set(0);
m_bTerminated = false;
m_finalFrameCount = 0;
m_numEntries = 0;
m_isSceneTransition = false;
m_lastPredictorReset = 0;
m_avgPFrameQp = 0;
m_isFirstMiniGop = false;
m_lastScenecut = -1;
m_lastScenecutAwareIFrame = -1;
if (m_param->rc.rateControlMode == X265_RC_CRF)
{//如果码率控制模式为X265_RC_CRF(恒定质量因子模式),则根据参数设置调整qp(量化参数)和bitrate(比特率)的值。计算并设置码率因子常数
m_param->rc.qp = (int)m_param->rc.rfConstant;
m_param->rc.bitrate = 0;
double baseCplx = m_ncu * (m_param->bframes ? 120 : 80);
double mbtree_offset = m_param->rc.cuTree ? (1.0 - m_param->rc.qCompress) * 13.5 : 0;
m_rateFactorConstant = pow(baseCplx, 1 - m_qCompress) /
x265_qp2qScale(m_param->rc.rfConstant + mbtree_offset);
if (m_param->rc.rfConstantMax)
{ //设置码率因子的最大增量和最大减量:根据参数设置,确定码率因子的最大增量和最大减量。如果最大增量小于等于0,则发出警告并将最大增量设置为0
m_rateFactorMaxIncrement = m_param->rc.rfConstantMax - m_param->rc.rfConstant;
if (m_rateFactorMaxIncrement <= 0)
{
x265_log(m_param, X265_LOG_WARNING, "CRF max must be greater than CRF\n");
m_rateFactorMaxIncrement = 0;
}
}
if (m_param->rc.rfConstantMin)
m_rateFactorMaxDecrement = m_param->rc.rfConstant - m_param->rc.rfConstantMin;
}//设置码率控制模式和统计读取标志:根据参数设置,确定是否启用自适应比特率(Abr)和双通道模式(2pass),并相应地设置m_isAbr和m_2pass的值
m_isAbr = m_param->rc.rateControlMode != X265_RC_CQP && !m_param->rc.bStatRead;
m_2pass = m_param->rc.rateControlMode != X265_RC_CQP && m_param->rc.bStatRead;
m_bitrate = m_param->rc.bitrate * 1000;//设置比特率和帧时长:将参数中的比特率转换为以比特为单位,并将其赋值给m_bitrate。计算帧时长(frameDuration)作为帧率的倒数
m_frameDuration = (double)m_param->fpsDenom / m_param->fpsNum;
m_qp = m_param->rc.qp;//设置量化参数(qp):将参数中的qp值赋值给m_qp
m_lastRceq = 1; /* handles the cmplxrsum when the previous frame cost is zero */
m_shortTermCplxSum = 0;
m_shortTermCplxCount = 0;
m_lastNonBPictType = I_SLICE;
m_isAbrReset = false;
m_lastAbrResetPoc = -1;
m_statFileOut = NULL;
m_cutreeStatFileOut = m_cutreeStatFileIn = NULL;
m_cutreeShrMem = NULL;
m_rce2Pass = NULL;
m_encOrder = NULL;
m_lastBsliceSatdCost = 0;
m_movingAvgSum = 0.0;
m_isNextGop = false;
m_relativeComplexity = NULL;
// vbv initialization 初始化vbv参数:对码率控制中的vbv缓冲区大小、最大比特率、缓冲区初始化和缓冲区末尾进行限制和初始化
m_param->rc.vbvBufferSize = x265_clip3(0, 2000000, m_param->rc.vbvBufferSize);
m_param->rc.vbvMaxBitrate = x265_clip3(0, 2000000, m_param->rc.vbvMaxBitrate);
m_param->rc.vbvBufferInit = x265_clip3(0.0, 2000000.0, m_param->rc.vbvBufferInit);
m_param->vbvBufferEnd = x265_clip3(0.0, 2000000.0, m_param->vbvBufferEnd);
m_initVbv = false;
m_singleFrameVbv = 0;
m_rateTolerance = 1.0;
if (m_param->rc.vbvBufferSize)
{
if (m_param->rc.rateControlMode == X265_RC_CQP)
{
x265_log(m_param, X265_LOG_WARNING, "VBV is incompatible with constant QP, ignored.\n");
m_param->rc.vbvBufferSize = 0;
m_param->rc.vbvMaxBitrate = 0;
}
else if (m_param->rc.vbvMaxBitrate == 0)
{
if (m_param->rc.rateControlMode == X265_RC_ABR)
{
x265_log(m_param, X265_LOG_WARNING, "VBV maxrate unspecified, assuming CBR\n");
m_param->rc.vbvMaxBitrate = m_param->rc.bitrate;
}
else
{
x265_log(m_param, X265_LOG_WARNING, "VBV bufsize set but maxrate unspecified, ignored\n");
m_param->rc.vbvBufferSize = 0;
}
}
else if (m_param->rc.vbvMaxBitrate < m_param->rc.bitrate &&
m_param->rc.rateControlMode == X265_RC_ABR)
{
x265_log(m_param, X265_LOG_WARNING, "max bitrate less than average bitrate, assuming CBR\n");
m_param->rc.bitrate = m_param->rc.vbvMaxBitrate;
}
}
else if (m_param->rc.vbvMaxBitrate)
{
x265_log(m_param, X265_LOG_WARNING, "VBV maxrate specified, but no bufsize, ignored\n");
m_param->rc.vbvMaxBitrate = 0;
}
m_isVbv = m_param->rc.vbvMaxBitrate > 0 && m_param->rc.vbvBufferSize > 0;
if (m_param->vbvBufferEnd && !m_isVbv)
{
x265_log(m_param, X265_LOG_WARNING, "vbv-end requires VBV parameters, ignored\n");
m_param->vbvBufferEnd = 0;
}
if (m_param->bEmitHRDSEI && !m_isVbv)
{
x265_log(m_param, X265_LOG_WARNING, "NAL HRD parameters require VBV parameters, ignored\n");
m_param->bEmitHRDSEI = 0;
}
m_isCbr = m_param->rc.rateControlMode == X265_RC_ABR && m_isVbv && m_param->rc.vbvMaxBitrate <= m_param->rc.bitrate;
if (m_param->rc.bStrictCbr && !m_isCbr)
{
x265_log(m_param, X265_LOG_WARNING, "strict CBR set without CBR mode, ignored\n");
m_param->rc.bStrictCbr = 0;
}
if(m_param->rc.bStrictCbr)
m_rateTolerance = 0.7;
m_bframeBits = 0;
m_leadingNoBSatd = 0;
m_ipOffset = 6.0 * X265_LOG2(m_param->rc.ipFactor);//I和P帧QP offset
m_pbOffset = 6.0 * X265_LOG2(m_param->rc.pbFactor);//P和B帧QP offset
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 (37)
#define ABR_INIT_QP_GRAIN_MAX (33)
#define ABR_SCENECUT_INIT_QP_MIN (12)
#define CRF_INIT_QP (int)m_param->rc.rfConstant
for (int i = 0; i < 3; i++)
{ //根据不同的率控模式(ABR或CRF),设置m_lastQScaleFor、m_lmin和m_lmax数组的初始值
m_lastQScaleFor[i] = x265_qp2qScale(m_param->rc.rateControlMode == X265_RC_CRF ? CRF_INIT_QP : ABR_INIT_QP_MIN);
m_lmin[i] = x265_qp2qScale(m_param->rc.qpMin);
m_lmax[i] = x265_qp2qScale(m_param->rc.qpMax);
}
if (m_param->rc.rateControlMode == X265_RC_CQP)
{
if (m_qp && !m_param->bLossless)
{
m_qpConstant[P_SLICE] = m_qp;
m_qpConstant[I_SLICE] = x265_clip3(QP_MIN, QP_MAX_MAX, (int)(m_qp - m_ipOffset + 0.5));
m_qpConstant[B_SLICE] = x265_clip3(QP_MIN, QP_MAX_MAX, (int)(m_qp + m_pbOffset + 0.5));
}
else
{
m_qpConstant[P_SLICE] = m_qpConstant[I_SLICE] = m_qpConstant[B_SLICE] = m_qp;
}
}
/* qpstep - value set as encoder specific */
m_lstep = pow(2, m_param->rc.qpStep / 6.0);
for (int i = 0; i < 2; i++)
m_cuTreeStats.qpBuffer[i] = NULL;
}
2.初始化RateControl::init
用于初始化码率控制相关的参数和变量:
bool RateControl::init(const SPS& sps)
{ //如果启用VBV(可变比特率),并且未进行VBV初始化(m_initVbv为false)或者启用了SBRC(子带率控制),则调用initVBV函数进行VBV的初始化
if (m_isVbv && (!m_initVbv || m_param->bEnableSBRC))
initVBV(sps);
//如果不需要重置区域配置(bResetZoneConfig为false)且relativeComplexity数组为空,则分配内存并初始化relativeComplexity数组
if (!m_param->bResetZoneConfig && (m_relativeComplexity == NULL))
{
m_relativeComplexity = X265_MALLOC(double, m_param->reconfigWindowSize);
if (m_relativeComplexity == NULL)
{
x265_log(m_param, X265_LOG_ERROR, "Failed to allocate memory for m_relativeComplexity\n");
return false;
}
}
//初始化各种计数器和变量,如totalBits、framesDone、residualCost等
m_totalBits = 0;
m_encodedBits = 0;
m_framesDone = 0;
m_residualCost = 0;
m_partialResidualCost = 0;
m_amortizeFraction = 0.85;
m_amortizeFrames = 75;//根据参数设置amortizeFraction和amortizeFrames的值
if (m_param->totalFrames && m_param->totalFrames <= 2 * m_fps && m_param->rc.bStrictCbr) /* Strict CBR segment encode */
{
m_amortizeFraction = 0.85;
m_amortizeFrames = m_param->totalFrames / 2;
}
//初始化滑动窗口相关的数组和变量,如satdCostWindow、encodedBitsWindow、sliderPos等
for (int i = 0; i < s_slidingWindowFrames; i++)
{
m_satdCostWindow[i] = 0;
m_encodedBitsWindow[i] = 0;
}
m_sliderPos = 0;
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;
for (int i = 0; i < 3; i++)//设置lastQScaleFor数组的初始值
m_lastQScaleFor[i] = x265_qp2qScale(m_param->rc.rateControlMode == X265_RC_CRF ? CRF_INIT_QP : ABR_INIT_QP_MIN);
m_avgPFrameQp = 0 ;
/* 720p videos seem to be a good cutoff for cplxrSum */
double tuneCplxFactor = (m_ncu > 3600 && m_param->rc.cuTree && !m_param->rc.hevcAq) ? 2.5 : m_param->rc.hevcAq ? 1.5 : m_isGrainEnabled ? 1.9 : 1.0;
/* 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_param->bEnableSBRC ? (m_bitrate * ( 1.0 / m_param->keyframeMax )) :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))
{ //2-pass 才会进来
//省略
}
return true;
}
3.启动码率控制RateControl::rateControlStart
用于在编码过程中启动比特率控制,并进行一些初始化和配置:
int RateControl::rateControlStart(Frame* curFrame, RateControlEntry* rce, Encoder* enc)
{
int orderValue = m_startEndOrder.get();
int startOrdinal = rce->encodeOrder * 2;//获取当前编码帧的顺序值(encodeOrder)和起始顺序值(startOrdinal)
//使用循环等待直到orderValue大于等于startOrdinal或m_bTerminated标志为true
while (orderValue < startOrdinal && !m_bTerminated)
orderValue = m_startEndOrder.waitForChange(orderValue);
//如果curFrame为空,则表示编码器正在执行刷新操作,执行相应的处理,并增加m_startEndOrder的值,然后返回0
if (!curFrame)
{
// faked rateControlStart calls when the encoder is flushing
m_startEndOrder.incr();
return 0;
}
//获取当前帧的编码数据(curEncData)和切片类型(sliceType),并将切片类型存储到rce->sliceType中
FrameData& curEncData = *curFrame->m_encData;
m_curSlice = curEncData.m_slice;
m_sliceType = m_curSlice->m_sliceType;
rce->sliceType = m_sliceType;
if (!m_2pass)//如果不是2pass模式,则将rce->keptAsRef设置为当前帧是否作为引用帧的标志
rce->keptAsRef = IS_REFERENCED(curFrame);
m_predType = getPredictorType(curFrame->m_lowres.sliceType, m_sliceType);//确定预测类型(m_predType)
rce->poc = m_curSlice->m_poc;//将rce->poc设置为当前切片的POC
if (!m_param->bResetZoneConfig && (rce->encodeOrder % m_param->reconfigWindowSize == 0))
{ //默认不进来,计算区域缓冲索引(m_zoneBufferIdx)
int index = m_zoneBufferIdx % m_param->rc.zonefileCount;
int read = m_top->zoneReadCount[index].get();
int write = m_top->zoneWriteCount[index].get();
if (write <= read)
write = m_top->zoneWriteCount[index].waitForChange(write);
m_zoneBufferIdx++;
//循环遍历区域配置列表(m_param->rc.zones),找到与当前帧的encodeOrder匹配的区域
for (int i = 0; i < m_param->rc.zonefileCount; i++)
{
if (m_param->rc.zones[i].startFrame == rce->encodeOrder)
{ //将bitrate、vbvMaxBitrate和relativeComplexity等参数从区域配置中复制到当前的比特率控制参数(m_param->rc)中
m_param->rc.bitrate = m_param->rc.zones[i].zoneParam->rc.bitrate;
m_param->rc.vbvMaxBitrate = m_param->rc.zones[i].zoneParam->rc.vbvMaxBitrate;
memcpy(m_relativeComplexity, m_param->rc.zones[i].relativeComplexity, sizeof(double) * m_param->reconfigWindowSize);
reconfigureRC();//调用reconfigureRC函数重新配置比特率控制
m_isCbr = 1; /* Always vbvmaxrate == bitrate here*///设置m_isCbr为1,表示使用恒定比特率控制(vbvmaxrate == bitrate)
m_top->zoneReadCount[i].incr();//增加相应区域的读取计数
}
}
}
//如果需要重置区域配置(m_param->bResetZoneConfig)
if (m_param->bResetZoneConfig)
{
/* change ratecontrol stats for next zone if specified */
for (int i = 0; i < m_param->rc.zonefileCount; i++)
{ //遍历区域配置列表(m_param->rc.zones),找到与当前帧的encodeOrder匹配的区域
if (m_param->rc.zones[i].startFrame == curFrame->m_encodeOrder)
{ // 将区域配置的参数(m_param->rc.zones[i].zoneParam)复制到当前的比特率控制参数(m_param->rc)中
m_param = m_param->rc.zones[i].zoneParam;
reconfigureRC();//调用reconfigureRC函数重新配置比特率控制
if (!m_param->bNoResetZoneConfig)//如果m_param->bNoResetZoneConfig为false,则调用init函数初始化比特率控制
init(*m_curSlice->m_sps);
}
}
}
if (m_param->rc.bStatRead)//一般2pass进来
{
X265_CHECK(rce->poc >= 0 && rce->poc < m_numEntries, "bad encode ordinal\n");
int index = m_encOrder[rce->poc];
copyRceData(rce, &m_rce2Pass[index]);
}
rce->isActive = true;
if (!m_param->rc.bStatRead)
rce->scenecut = false;
rce->isFadeEnd = curFrame->m_lowres.bIsFadeEnd;
bool isRefFrameScenecut = m_sliceType!= I_SLICE && m_curSlice->m_refFrameList[0][0]->m_lowres.bScenecut;
m_isFirstMiniGop = m_sliceType == I_SLICE ? true : m_isFirstMiniGop;//如果当前切片类型是I_SLICE,则将m_isFirstMiniGop设置为true;否则保持不变
if (curFrame->m_lowres.bScenecut)//表示当前帧是场景切换帧
{
m_isSceneTransition = true;
rce->scenecut = true;
m_lastPredictorReset = rce->encodeOrder;//将m_lastPredictorReset设置为rce->encodeOrder,用于记录上一次预测器重置的编码顺序
//调用initFramePredictors函数初始化帧预测
initFramePredictors();
}
else if (m_sliceType != B_SLICE && !isRefFrameScenecut)
m_isSceneTransition = false;
if (rce->encodeOrder < m_lastPredictorReset + m_param->frameNumThreads)
{
rce->rowPreds[0][0].count = 0;
}
rce->bLastMiniGopBFrame = curFrame->m_lowres.bLastMiniGopBFrame;
rce->bufferRate = m_bufferRate;
rce->rowCplxrSum = 0.0;
rce->rowTotalBits = 0;
if (m_isVbv)//如果使用了vbv
{ //如果rce->rowPreds[0][0].count为0,表示还没有进行过行预测
if (rce->rowPreds[0][0].count == 0)
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 2; j++)
{
rce->rowPreds[i][j].coeffMin = 0.25 / 4;
rce->rowPreds[i][j].coeff = 0.25;
rce->rowPreds[i][j].count = 1.0;
rce->rowPreds[i][j].decay = 0.5;
rce->rowPreds[i][j].offset = 0.0;
}
}
}
rce->rowPred[0] = &rce->rowPreds[m_sliceType][0];
rce->rowPred[1] = &rce->rowPreds[m_sliceType][1];
m_predictedBits = m_totalBits;
updateVbvPlan(enc);
rce->bufferFill = m_bufferFill;
rce->vbvEndAdj = false;
if (m_param->vbvBufferEnd && rce->encodeOrder >= m_param->vbvEndFrameAdjust * m_param->totalFrames)
{
rce->vbvEndAdj = true;
rce->targetFill = 0;
}
//根据视频编码参数集(VPS)中的最小压缩比(minCrForLevel)设置变量mincr的值
int mincr = enc->m_vps.ptl.minCrForLevel;
/* Profiles above Main10 don't require maxAU size check, so just set the maximum to a large value. */
if (enc->m_vps.ptl.profileIdc > Profile::MAIN10 || enc->m_vps.ptl.levelIdc == Level::NONE)
rce->frameSizeMaximum = 1e9;
else
{
/* The spec has a special case for the first frame. */
if (curFrame->m_lowres.bKeyframe)
{
/* 1.5 * (Max( PicSizeInSamplesY, fR * MaxLumaSr) + MaxLumaSr * (AuCpbRemovalTime[ 0 ] -AuNominalRemovalTime[ 0 ])) ? MinCr */
double fr = 1. / 300;
int picSizeInSamplesY = m_param->sourceWidth * m_param->sourceHeight;
rce->frameSizeMaximum = 8 * 1.5 * X265_MAX(picSizeInSamplesY, fr * enc->m_vps.ptl.maxLumaSrForLevel) / mincr;
}
else
{
/* 1.5 * MaxLumaSr * (AuCpbRemovalTime[ n ] - AuCpbRemovalTime[ n - 1 ]) / MinCr */
rce->frameSizeMaximum = 8 * 1.5 * enc->m_vps.ptl.maxLumaSrForLevel * m_frameDuration / mincr;
}
rce->frameSizeMaximum *= m_param->maxAUSizeFactor;
}
}
///< regenerate the qp
if (!m_isAbr && m_2pass && m_param->rc.rateControlMode == X265_RC_CRF)
{
if (!m_param->rc.bEncFocusedFramesOnly)
{
rce->qpPrev = x265_qScale2qp(rce->qScale);
if (m_param->bEnableSceneCutAwareQp)
{
double lqmin = m_lmin[m_sliceType];
double lqmax = m_lmax[m_sliceType];
if (m_param->bEnableSceneCutAwareQp & FORWARD)
rce->newQScale = forwardMasking(curFrame, rce->newQScale);
if (m_param->bEnableSceneCutAwareQp & BACKWARD)
rce->newQScale = backwardMasking(curFrame, rce->newQScale);
rce->newQScale = x265_clip3(lqmin, lqmax, rce->newQScale);
}
rce->qScale = rce->newQScale;
rce->qpaRc = curEncData.m_avgQpRc = curEncData.m_avgQpAq = x265_qScale2qp(rce->newQScale);
m_qp = int(rce->qpaRc + 0.5);
rce->frameSizePlanned = qScale2bits(rce, rce->qScale);
m_framesDone++;
return m_qp;
}
else
{
int index = m_encOrder[rce->poc];
index++;
double totalDuration = m_frameDuration;
for (int j = 0; totalDuration < 1.0 && index < m_numEntries; j++)
{
switch (m_rce2Pass[index].sliceType)
{
case B_SLICE:
curFrame->m_lowres.plannedType[j] = m_rce2Pass[index].keptAsRef ? X265_TYPE_BREF : X265_TYPE_B;
break;
case P_SLICE:
curFrame->m_lowres.plannedType[j] = X265_TYPE_P;
break;
case I_SLICE:
curFrame->m_lowres.plannedType[j] = m_param->bOpenGOP ? X265_TYPE_I : X265_TYPE_IDR;
break;
default:
break;
}
curFrame->m_lowres.plannedSatd[j] = m_rce2Pass[index].currentSatd;
totalDuration += m_frameDuration;
index++;
}
}
}
if (m_isAbr || m_2pass) //表示处于 ABR,CRF 模式
{
if (m_isAbr || m_isVbv)
{
m_currentSatd = curFrame->m_lowres.satdCost >> (X265_DEPTH - 8);
/* Update rce for use in rate control VBV later */
rce->lastSatd = m_currentSatd;//更新rce->lastSatd的值为当前的低分辨率satd
X265_CHECK(rce->lastSatd, "satdcost cannot be zero\n");
/* Detect a pattern for B frames with same SATDcost to identify a series of static frames
* and the P frame at the end of the series marks a possible case for ABR reset logic */
if (m_param->bframes)
{
if (m_sliceType != B_SLICE && m_numBframesInPattern > m_param->bframes)
{
m_isPatternPresent = true;
}
else if (m_sliceType == B_SLICE && !IS_REFERENCED(curFrame))
{
if (m_currentSatd != m_lastBsliceSatdCost && !rce->bLastMiniGopBFrame)
{
m_isPatternPresent = false;
m_lastBsliceSatdCost = m_currentSatd;
m_numBframesInPattern = 0;
}//检测B帧的模式,如果连续多个B帧的低分辨率场景复杂度相同,则认为存在一个静态帧序列,并且该序列的最后一个P帧可能需要进行ABR重置
else if (m_currentSatd == m_lastBsliceSatdCost)
m_numBframesInPattern++;
}
}
if (rce->isFadeEnd)
m_isPatternPresent = true;
}
/* For a scenecut that occurs within the mini-gop, enable scene transition
* switch until the next mini-gop to ensure a min qp for all the frames within
* the scene-transition mini-gop */
//根据当前帧的码率估计量(rateEstimateQscale)计算相应的量化参数(qp)
double q = x265_qScale2qp(rateEstimateQscale(curFrame, rce));
q = x265_clip3((double)m_param->rc.qpMin, (double)m_param->rc.qpMax, q);//将qp限制在参数rc.qpMin和rc.qpMax之间
m_qp = int(q + 0.5);//将qp的整数部分赋值给m_qp
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;//将 m_lastRceq 的值复制到线程本地的 rce 结构中,以在 RateControlEnd() 中使用
accumPQpUpdate();//执行累积P/QP更新
curFrame->m_rcData->cumulativePQp = m_accumPQp;
curFrame->m_rcData->cumulativePNorm = m_accumPNorm;
for (int i = 0; i < 3; i++)
curFrame->m_rcData->lastQScaleFor[i] = m_lastQScaleFor[i];//将上一个帧的量化参数 m_lastQScaleFor 的值复制到当前帧的 curFrame->m_rcData->lastQScaleFor 数组中
curFrame->m_rcData->shortTermCplxSum = m_shortTermCplxSum;//将短期复杂度的总和赋值
curFrame->m_rcData->shortTermCplxCount = m_shortTermCplxCount;//将短期复杂度的计数赋值
}
else // CQP :Constant QP 模式
{ //如果当前帧的切片类型是 B 帧(B_SLICE)且被引用(IS_REFERENCED(curFrame) 成立),则将量化参数 m_qp 设置为 P 帧(P_SLICE)和 B 帧之间的平均值
if (m_sliceType == B_SLICE && IS_REFERENCED(curFrame))
m_qp = (m_qpConstant[B_SLICE] + m_qpConstant[P_SLICE]) / 2;
else//否则,将量化参数 m_qp 设置为与切片类型相对应的常量 m_qpConstant
m_qp = m_qpConstant[m_sliceType];
curEncData.m_avgQpAq = curEncData.m_avgQpRc = m_qp;//将 m_qp 的值赋给 curEncData.m_avgQpAq 和 curEncData.m_avgQpRc
//获取当前帧的区域(zone)
x265_zone* zone = getZone();
if (zone)
{ //如果区域强制指定了一个特定的 QP(zone->bForceQp 成立),则将 m_qp 加上 zone->qp 减去 P 帧的常量 m_qpConstant[P_SLICE]
if (zone->bForceQp)
m_qp += zone->qp - m_qpConstant[P_SLICE];
else//否则,将 m_qp 减去(int 类型的)(6.0 * X265_LOG2(zone->bitrateFactor))
m_qp -= (int)(6.0 * X265_LOG2(zone->bitrateFactor));
}
}
if (m_sliceType != B_SLICE)
{//将上一个非 B 图像类型(m_lastNonBPictType)设为当前切片类型
m_lastNonBPictType = m_sliceType;
m_leadingNoBSatd = m_currentSatd;//将当前(m_currentSatd)赋给前导非 B (m_leadingNoBSatd)
}//将前导非 B (m_leadingNoBSatd)赋给 rce->leadingNoBSatd
rce->leadingNoBSatd = m_leadingNoBSatd;
if (curFrame->m_forceqp)//如果当前帧设置了强制 QP
{//将强制 QP 的值加上 0.5 并转换为整数,然后减去 1,并将结果赋给 m_qp
m_qp = (int32_t)(curFrame->m_forceqp + 0.5) - 1;
m_qp = x265_clip3(m_param->rc.qpMin, m_param->rc.qpMax, m_qp);//使用 x265_clip3 函数将 m_qp 限制在参数 rc.qpMin 和 rc.qpMax 之间
rce->qpaRc = curEncData.m_avgQpRc = curEncData.m_avgQpAq = m_qp;
if (m_isAbr || m_2pass)
{
rce->qpNoVbv = rce->qpaRc;
m_lastQScaleFor[m_sliceType] = x265_qp2qScale(rce->qpaRc);//将 m_sliceType 对应的最后一个量化参数(m_lastQScaleFor[m_sliceType])设置为将 rce->qpaRc 转换为 qScale 的结果
if (rce->poc == 0)//将 P 帧(P_SLICE)对应的最后一个量化参数(m_lastQScaleFor[P_SLICE])设置为 m_lastQScaleFor[m_sliceType] 乘以 m_param->rc.ipFactor 的绝对值
m_lastQScaleFor[P_SLICE] = m_lastQScaleFor[m_sliceType] * fabs(m_param->rc.ipFactor);
rce->frameSizePlanned = predictSize(&m_pred[m_predType], m_qp, (double)m_currentSatd);
}
}
m_framesDone++;
return m_qp;
}
4.估计量化参数RateControl::rateEstimateQscale
返回量化参数(q)作为函数的输出:
double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
{
double q;
if (m_2pass)
{
if (m_sliceType != rce->sliceType)
{
x265_log(m_param, X265_LOG_ERROR, "slice=%c but 2pass stats say %c\n",
g_sliceTypeToChar[m_sliceType], g_sliceTypeToChar[rce->sliceType]);
}
}
if ((m_param->bliveVBV2pass && m_param->rc.rateControlMode == X265_RC_ABR) || m_isAbr)
{ //ABR模式
int pos = m_sliderPos % s_slidingWindowFrames;//窗口大小s_slidingWindowFrames=20
int addPos = (pos + s_slidingWindowFrames - 1) % s_slidingWindowFrames;
if (m_sliderPos > s_slidingWindowFrames)
{
const static double base = pow(0.5, s_slidingWindowFrames - 1);
m_movingAvgSum -= m_lastRemovedSatdCost * base;
m_movingAvgSum *= 0.5;
m_movingAvgSum += m_satdCostWindow[addPos];
}
else if (m_sliderPos == s_slidingWindowFrames)
{
m_movingAvgSum += m_satdCostWindow[addPos];
}
else if (m_sliderPos > 0)
{
m_movingAvgSum += m_satdCostWindow[addPos];
m_movingAvgSum *= 0.5;
}
rce->movingAvgSum = m_movingAvgSum;
m_lastRemovedSatdCost = m_satdCostWindow[pos];
m_satdCostWindow[pos] = rce->lastSatd;
m_sliderPos++;
}
if (m_sliceType == B_SLICE)
{ //B帧没有独立的速率控制,而是获得两个相邻P帧的平均QP+偏移
/* B-frames don't have independent rate control, but rather get the
* average QP of the two adjacent P-frames + an offset *///获取前一个参考帧和后一个参考帧的相关信息,包括平均量化参数(avgQpRc)和帧类型(I_SLICE、P_SLICE或B_SLICE)
Slice* prevRefSlice = m_curSlice->m_refFrameList[0][0]->m_encData->m_slice;
Slice* nextRefSlice = m_curSlice->m_refFrameList[1][0]->m_encData->m_slice;
double q0 = m_curSlice->m_refFrameList[0][0]->m_encData->m_avgQpRc;
double q1 = m_curSlice->m_refFrameList[1][0]->m_encData->m_avgQpRc;
bool i0 = prevRefSlice->m_sliceType == I_SLICE;
bool i1 = nextRefSlice->m_sliceType == I_SLICE;
int dt0 = abs(m_curSlice->m_poc - prevRefSlice->m_poc);
int dt1 = abs(m_curSlice->m_poc - nextRefSlice->m_poc);
//如果ABR已重置,则跳过在Scenect之前获取参考帧
// Skip taking a reference frame before the Scenecut if ABR has been reset.
if (m_lastAbrResetPoc >= 0)
{//检查前一个参考帧是否为P_SLICE(P帧)且其POC(显示顺序计数)小于最近的ABR重置POC(m_lastAbrResetPoc)。如果是,则将前一个参考帧的相关信息替换为后一个参考帧的信息
if (prevRefSlice->m_sliceType == P_SLICE && prevRefSlice->m_poc < m_lastAbrResetPoc)
{
i0 = i1;
dt0 = dt1;
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]))
q1 -= m_pbOffset / 2;
if (i0 && i1)//两个参考帧都为I帧
q = (q0 + q1) / 2 + m_ipOffset;//将I帧的QP换成对应的p帧的QP需要+ip_offset
else if (i0)
q = q1;//只有i0为I帧,则用q1的,即另一个P帧参考帧的qp,此时不需要再+ip_offset
else if (i1)
q = q0;//同理用另一个P帧的
else if(m_isGrainEnabled && !m_2pass)
q = q1;
else//两个参考帧都为P帧,则按时域距离加权
q = (q0 * dt1 + q1 * dt0) / (dt0 + dt1);
//如果当前帧被引用,则将量化参数增加pbOffset的一半;否则将量化参数增加pbOffset
if (IS_REFERENCED(curFrame))//上面仅是转换成了对应P帧的qp,因为当前帧为B帧,还要再转换为对应B帧的qp,加qp_offset
q += m_pbOffset / 2;//当前帧编码为被参考的B帧,则只加一半的pb_offset,即质量设定在非参考的B帧和P帧之间
else
q += m_pbOffset;
/* Set a min qp at scenechanges and transitions 在场景变化和转换时设置最小qp*/
if (m_isSceneTransition)
{//表示当前帧为场景切换帧,则将量化参数限制在ABR_SCENECUT_INIT_QP_MIN和前一个P帧的最后量化参数之间
q = X265_MAX(ABR_SCENECUT_INIT_QP_MIN, q);
double minScenecutQscale =x265_qp2qScale(ABR_SCENECUT_INIT_QP_MIN);
m_lastQScaleFor[P_SLICE] = X265_MAX(minScenecutQscale, m_lastQScaleFor[P_SLICE]);
}//将量化参数转换为qScale,并将其赋值给rce->qpNoVbv
double qScale = x265_qp2qScale(q);
rce->qpNoVbv = q;
double lmin = 0, lmax = 0;
if (m_isGrainEnabled && m_isFirstMiniGop)
{//如果启用了Grain(噪点)且是第一个MiniGop(一组相邻的帧),则根据反馈调整量化参数,并根据溢出情况调整qScale。最后,将量化参数转换为q
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;
}
/* Scenecut Aware QP offsets*/
if (m_param->bEnableSceneCutAwareQp)
{//如果启用了Scenecut Aware QP offsets(场景切换感知的QP偏移),根据设置的偏移进行调整,并将量化参数转换为q
double lqmin = m_lmin[m_sliceType];
double lqmax = m_lmax[m_sliceType];
if (m_param->bEnableSceneCutAwareQp & FORWARD)
qScale = forwardMasking(curFrame, qScale);
if (m_param->bEnableSceneCutAwareQp & BACKWARD)
qScale = backwardMasking(curFrame, qScale);
qScale = x265_clip3(lqmin, lqmax, qScale);
q = x265_qScale2qp(qScale);
rce->qpNoVbv = q;
}
if (m_isVbv)//如果启用了Vbv(视频缓冲区),根据设置的参数进行调整
{
lmin = m_lastQScaleFor[P_SLICE] / m_lstep;
lmax = m_lastQScaleFor[P_SLICE] * m_lstep;
//如果是Cbr(恒定比特率)模式且未启用Grain,则根据反馈调整量化参数
if (m_isCbr && !m_isGrainEnabled)
{
qScale = tuneAbrQScaleFromFeedback(qScale);
if (!m_isAbrReset)
qScale = x265_clip3(lmin, lmax, qScale);
q = x265_qScale2qp(qScale);
}
//如果没有重置区域配置,则根据设置的范围进行调整
if (!m_param->bResetZoneConfig)
{
double lqmin = m_lmin[m_sliceType];
double lqmax = m_lmax[m_sliceType];
qScale = tuneQScaleForZone(rce, qScale);
qScale = x265_clip3(lqmin, lqmax, qScale);
}
//最后,根据不同的情况,将量化参数限制在可接受的范围内,并将其保存为上一个帧的量化参数
if (!m_2pass || m_param->bliveVBV2pass || (m_2pass && m_param->rc.rateControlMode == X265_RC_CRF && m_param->rc.bEncFocusedFramesOnly))
{
/* clip qp to permissible range after vbv-lookahead estimation to avoid possible
* mispredictions by initial frame size predictors */
qScale = clipQscale(curFrame, rce, qScale);
if (m_pred[m_predType].count == 1)
qScale = x265_clip3(lmin, lmax, qScale);
m_lastQScaleFor[m_sliceType] = qScale;
}
}
if (m_2pass)// 如果是2-pass模式,则根据qScale估计帧大小。否则,使用预测模型预测帧大小
rce->frameSizePlanned = qScale2bits(rce, qScale);
else
rce->frameSizePlanned = predictSize(&m_pred[m_predType], qScale, (double)m_currentSatd);
/* Limit planned size by MinCR */
if (m_isVbv)
rce->frameSizePlanned = X265_MIN(rce->frameSizePlanned, rce->frameSizeMaximum);
rce->frameSizeEstimated = rce->frameSizePlanned;
//将计算得到的qScale赋值给rce->newQScale
rce->newQScale = qScale;
if(rce->bLastMiniGopBFrame)
{
if (m_isFirstMiniGop && m_isGrainEnabled)
{//如果当前帧是最后一个MiniGop的B帧,并且启用了Grain,则更新平均P帧量化参数和最后一个P帧的qScale
m_avgPFrameQp = (m_avgPFrameQp + rce->qpNoVbv) / 2;
m_lastQScaleFor[P_SLICE] = x265_qp2qScale(m_avgPFrameQp);
}
m_isFirstMiniGop = false;
}
return qScale;
}
else//处理除了B帧之外的其他帧类型(I帧和P帧)的情况
{
double abrBuffer = 2 * m_rateTolerance * m_bitrate;
if (m_2pass && (m_param->rc.rateControlMode != X265_RC_CRF || !m_param->rc.bEncFocusedFramesOnly))
{//2pass编码
double lmin = m_lmin[m_sliceType];
double lmax = m_lmax[m_sliceType];
int64_t diff;
if (!m_isVbv)
{
m_predictedBits = m_totalBits;
if (rce->encodeOrder < m_param->frameNumThreads)
m_predictedBits += (int64_t)(rce->encodeOrder * m_bitrate / m_fps);
else
m_predictedBits += (int64_t)(m_param->frameNumThreads * m_bitrate / m_fps);
}
/* Adjust ABR buffer based on distance to the end of the video. */
if (m_numEntries > rce->encodeOrder)
{
uint64_t finalBits = m_rce2Pass[m_numEntries - 1].expectedBits;
double videoPos = (double)rce->expectedBits / finalBits;
double scaleFactor = sqrt((1 - videoPos) * m_numEntries);
abrBuffer *= 0.5 * X265_MAX(scaleFactor, 0.5);
}
diff = m_predictedBits - (int64_t)rce->expectedBits;
q = rce->newQScale;
x265_zone* zone = getZone();
if (zone)
{
if (zone->bForceQp)
q = x265_qp2qScale(zone->qp);
else
q /= zone->bitrateFactor;
}
/*Existing ABR conformance check may not be valid with real time VBV*/
if(!m_param->bliveVBV2pass)
q /= x265_clip3(0.5, 2.0, (double)(abrBuffer - diff) / abrBuffer);
if (m_expectedBitsSum > 0)
{
/* Adjust quant based on the difference between
* achieved and expected bitrate so far */
double curTime = (double)rce->encodeOrder / m_numEntries;
double w = x265_clip3(0.0, 1.0, curTime);
q *= pow((double)m_totalBits / m_expectedBitsSum, w);
}
if (m_framesDone == 0 && m_param->rc.rateControlMode == X265_RC_ABR && m_isGrainEnabled)
q = X265_MIN(x265_qp2qScale(ABR_INIT_QP_GRAIN_MAX), q);
rce->qpNoVbv = x265_qScale2qp(q);
if ((m_sliceType == I_SLICE && m_param->keyframeMax > 1
&& m_lastNonBPictType != I_SLICE && !m_isAbrReset) || (m_isNextGop && !m_framesDone))
m_avgPFrameQp = 0;
if (m_sliceType == P_SLICE)
{
m_avgPFrameQp = m_avgPFrameQp == 0 ? rce->qpNoVbv : m_avgPFrameQp;
m_avgPFrameQp = (m_avgPFrameQp + rce->qpNoVbv) / 2;
}
/* Scenecut Aware QP offsets*/
if (m_param->bEnableSceneCutAwareQp)
{
double qmin = m_lmin[m_sliceType];
double qmax = m_lmax[m_sliceType];
if (m_param->bEnableSceneCutAwareQp & FORWARD)
q = forwardMasking(curFrame, q);
if (m_param->bEnableSceneCutAwareQp & BACKWARD)
q = backwardMasking(curFrame, q);
q = x265_clip3(qmin, qmax, q);
rce->qpNoVbv = x265_qScale2qp(q);
}
if (m_isVbv)
{
if (!m_param->bliveVBV2pass)
{
/* Do not overflow vbv */
double expectedSize = qScale2bits(rce, q);
double expectedVbv = m_bufferFill + m_bufferRate - expectedSize;
double expectedFullness = rce->expectedVbv / m_bufferSize;
double qmax = q * (2 - expectedFullness);
double sizeConstraint = 1 + expectedFullness;
qmax = X265_MAX(qmax, rce->newQScale);
if (expectedFullness < .05)
qmax = lmax;
qmax = X265_MIN(qmax, lmax);
while (((expectedVbv < rce->expectedVbv / sizeConstraint) && (q < qmax)) ||
((expectedVbv < 0) && (q < lmax)))
{
q *= 1.05;
expectedSize = qScale2bits(rce, q);
expectedVbv = m_bufferFill + m_bufferRate - expectedSize;
}
}
else
{
/* clip qp to permissible range after vbv-lookahead estimation to avoid possible
* mispredictions by Rate Control pass 1 statistics analysis */
q = clipQscale(curFrame, rce, q);
}
}
q = x265_clip3(lmin, lmax, q);
}
else
{
/* 1pass ABR */
//如果量化器被应用到目前为止的所有帧,量化器将产生期望的平均比特率。然后基于当前帧相对于迄今为止的平均复杂度的复杂度来调制该量子(使用2遍RCEQ)。然后,如果到目前为止总大小与目标相差甚远,则向上或向下偏置量化器。结果:根据rate_tolerance的值,在质量和比特率精度之间存在权衡。但是在大的公差下,比特分布接近2pass的比特分布
/* Calculate the quantizer which would have produced the desired
* average bitrate if it had been applied to all frames so far.
* Then modulate that quant based on the current frame's complexity
* relative to the average complexity so far (using the 2pass RCEQ).
* Then bias the quant up or down if total size so far was far from
* the target.
* Result: Depending on the value of rate_tolerance, there is a
* trade-off between quality and bitrate precision. But at large
* tolerances, the bit distribution approaches that of 2pass. */
//将溢出值(overflow)初始化为1。溢出值用于根据当前帧的复杂性相对于平均复杂性的调整来调整量化参数
double overflow = 1;
double lqmin = m_lmin[m_sliceType];
double lqmax = m_lmax[m_sliceType];
m_shortTermCplxSum *= 0.5;//计算短期复杂性和短期复杂性计数的更新
m_shortTermCplxCount *= 0.5;
m_shortTermCplxSum += m_currentSatd / (CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION);//短期复杂性是根据当前帧的SATD(Sum of Absolute Transformed Differences)值和帧间隔计算得到的
m_shortTermCplxCount++;
/* coeffBits to be used in 2-pass 将当前帧的SATD值赋给rce->coeffBits,用于后续的2-pass过程*/
rce->coeffBits = (int)m_currentSatd;
rce->blurredComplexity = m_shortTermCplxSum / m_shortTermCplxCount;//计算模糊复杂性(blurredComplexity),即短期复杂性与短期复杂性计数的比值
rce->mvBits = 0;//将运动矢量(MV)比特数(mvBits)初始化为0
rce->sliceType = m_sliceType;
//如果使用的是CRF(恒定质量)模式
if (m_param->rc.rateControlMode == X265_RC_CRF)
{ //检查并重置CRF相关的参数
if (!m_param->rc.bStatRead && m_param->bEnableSBRC)
checkAndResetCRF(rce);
q = getQScale(rce, m_rateFactorConstant);//根据rce和m_rateFactorConstant计算量化参数(q)
x265_zone* zone = getZone();
if (zone)
{
if (zone->bForceQp)//则将q设置为对应的qp。否则,将q除以区域的bitrateFactor
q = x265_qp2qScale(zone->qp);
else
q /= zone->bitrateFactor;
}
}
else
{ //检查并重置ABR相关的参数
if (!m_param->rc.bStatRead)
checkAndResetABR(rce, false);
double initialQScale = getQScale(rce, m_wantedBitsWindow / m_cplxrSum);
x265_zone* zone = getZone();
if (zone)
{
if (zone->bForceQp)
initialQScale = x265_qp2qScale(zone->qp);
else
initialQScale /= zone->bitrateFactor;
}//根据初始的量化参数,通过调整量化参数的方法(tuneAbrQScaleFromFeedback)获得调整后的量化参数(tunedQScale)
double tunedQScale = tuneAbrQScaleFromFeedback(initialQScale);
overflow = tunedQScale / initialQScale;//计算溢出值(overflow),即调整后的量化参数与初始的量化参数之间的比值
q = (!m_partialResidualFrames || m_param->bEnableSBRC)? tunedQScale : initialQScale;//根据部分残差帧(m_partialResidualFrames)的情况,将量化参数设置为调整后的量化参数(tunedQScale)或初始的量化参数(initialQScale)
bool isEncodeEnd = (m_param->totalFrames && //根据编码进度(m_framesDone)和总帧数(m_param->totalFrames)的比较判断是否接近编码结束或开始,并根据溢出值和是否开始编码来调整量化参数
m_framesDone > 0.75 * m_param->totalFrames) ? 1 : 0;
bool isEncodeBeg = m_framesDone < (int)(m_fps + 0.5);
if (m_isGrainEnabled)
{ //如果启用了Grain(噪点)模式,并且当前帧不是I帧且不接近编码结束,并且溢出值在一定范围内或编码刚开始,则通过调整量化参数的方法(tuneQScaleForGrain)获得调整后的量化参数
if(m_sliceType!= I_SLICE && m_framesDone && !isEncodeEnd &&
((overflow < 1.05 && overflow > 0.95) || isEncodeBeg))
{
q = tuneQScaleForGrain(overflow);
}
}
}//如果满足该条件,说明当前帧是关键帧(I帧)并且满足一些其他条件,或者当前帧是下一个GOP的第一帧且还没有处理帧(m_framesDone为0)
if ((m_sliceType == I_SLICE && m_param->keyframeMax > 1
&& m_lastNonBPictType != I_SLICE && !m_isAbrReset) || (m_isNextGop && !m_framesDone))
{ //如果不是严格的恒定比特率(CBR)模式,则根据之前帧的累积P帧量化参数(m_accumPQp / m_accumPNorm)计算新的量化参数(q)
if (!m_param->rc.bStrictCbr)
q = x265_qp2qScale(m_accumPQp / m_accumPNorm);
q /= fabs(m_param->rc.ipFactor);//将q除以ipFactor的绝对值
m_avgPFrameQp = 0;//将m_avgPFrameQp重置为0
}
else if (m_framesDone > 0)
{//检查是否使用CRF(恒定质量)模式。如果不是CRF模式,则执行以下操作
if (m_param->rc.rateControlMode != X265_RC_CRF)
{//根据之前帧的量化参数和限制范围(m_lstep)计算量化参数的上下限(lqmin和lqmax)
lqmin = m_lastQScaleFor[m_sliceType] / m_lstep;
lqmax = m_lastQScaleFor[m_sliceType] * m_lstep;
if (!m_partialResidualFrames || m_isGrainEnabled)
{//如果不是部分残差帧(m_partialResidualFrames)或启用了Grain(噪点)模式(m_isGrainEnabled),则根据溢出值(overflow)和已处理的帧数(m_framesDone)来调整上下限
if (overflow > 1.1 && m_framesDone > 3)
lqmax *= m_lstep;
else if (overflow < 0.9)
lqmin /= m_lstep;
}//将量化参数(q)限制在上下限范围内
q = x265_clip3(lqmin, lqmax, q);
}
}
else if (m_qCompress != 1 && m_param->rc.rateControlMode == X265_RC_CRF)
{//否则,如果使用CRF模式
q = x265_qp2qScale(CRF_INIT_QP) / fabs(m_param->rc.ipFactor);
}
else if (m_framesDone == 0 && !m_isVbv && m_param->rc.rateControlMode == X265_RC_ABR)
{//如果还没有处理任何帧(m_framesDone == 0)并且不是使用VBV(Video Buffering Verifier)模式且使用了ABR(Average Bit Rate)模式
/* for ABR alone, clip the first I frame qp ,将量化参数的上限(lqmax)设置为初始的ABR量化参数上限ABR_INIT_QP_MAX)*/
lqmax = (m_isGrainEnabled && m_lstep) ? x265_qp2qScale(ABR_INIT_QP_GRAIN_MAX) :
x265_qp2qScale(ABR_INIT_QP_MAX);//如果启用了Grain(噪点)模式并且lstep不为零,则将量化参数的上限设置为ABR_INIT_QP_GRAIN_MAX
q = X265_MIN(lqmax, q);//将量化参数(q)限制在上限范围内
}//将量化参数(q)限制在上下限范围内
q = x265_clip3(lqmin, lqmax, q);
/* Set a min qp at scenechanges and transitions */
if (m_isSceneTransition)//如果当前帧是场景切换帧(m_isSceneTransition)
{//将量化参数(q)限制在场景切换帧所需的最小量化参数(ABR_SCENECUT_INIT_QP_MIN)和上次P帧的量化参数的较大值之间
double minScenecutQscale = x265_qp2qScale(ABR_SCENECUT_INIT_QP_MIN);
q = X265_MAX(minScenecutQscale, q);//将上次P帧的量化参数(m_lastQScaleFor[P_SLICE])限制在场景切换帧所需的最小量化参数和自身的较大值之间
m_lastQScaleFor[P_SLICE] = X265_MAX(minScenecutQscale, m_lastQScaleFor[P_SLICE]);
}//将量化参数(q)转换为无VBV的qp值,并根据当前帧类型(m_sliceType)进行处理。
rce->qpNoVbv = x265_qScale2qp(q);
if (m_sliceType == P_SLICE)
{//如果是P帧,则计算平均P帧量化参数
m_avgPFrameQp = m_avgPFrameQp == 0 ? rce->qpNoVbv : m_avgPFrameQp;
m_avgPFrameQp = (m_avgPFrameQp + rce->qpNoVbv) / 2;
}
if (!m_param->bResetZoneConfig)
{//如果没有重置区域配置(m_param->bResetZoneConfig),则根据区域的设置调整量化参数(q)
q = tuneQScaleForZone(rce, q);
q = x265_clip3(lqmin, lqmax, q);
}
/* Scenecut Aware QP offsets, 如果启用了场景切换感知的QP偏移(m_param->bEnableSceneCutAwareQp)*/
if (m_param->bEnableSceneCutAwareQp)
{
double qmin = m_lmin[m_sliceType];
double qmax = m_lmax[m_sliceType];
//根据前向(FORWARD)和/或后向(BACKWARD)掩蔽(masking)策略对量化参数(q)进行调整,将量化参数(q)限制在前向和后向掩蔽策续的上下限范围内
if (m_param->bEnableSceneCutAwareQp & FORWARD)
q = forwardMasking(curFrame, q);
if (m_param->bEnableSceneCutAwareQp & BACKWARD)
q = backwardMasking(curFrame, q);
//将量化参数(q)限制在最小和最大量化参数(qmin和qmax)范围内
q = x265_clip3(qmin, qmax, q);
rce->qpNoVbv = x265_qScale2qp(q);
}//根据当前帧、码率控制参数和量化参数(q)进行一些额外的限制
q = clipQscale(curFrame, rce, q);
if (m_2pass)
rce->frameSizePlanned = qScale2bits(rce, q);
else//根据量化参数(q)和当前帧的预测SATD值(m_currentSatd)预测帧大小
rce->frameSizePlanned = predictSize(&m_pred[m_predType], q, (double)m_currentSatd);
//如果不是第二遍编码且使用了VBV模式,并且当前帧是场景切换帧之后的帧,则将量化参数(q)限制在上下限范围内
/* clip qp to permissible range after vbv-lookahead estimation to avoid possible
* mispredictions by initial frame size predictors, after each scenecut */
bool isFrameAfterScenecut = m_sliceType!= I_SLICE && m_curSlice->m_refFrameList[0][0]->m_lowres.bScenecut;
if (!m_2pass && m_isVbv && isFrameAfterScenecut)
q = x265_clip3(lqmin, lqmax, q);
}//将当前帧类型(m_sliceType)对应的最后一个量化参数(m_lastQScaleFor)设置为当前的量化参数(q)
m_lastQScaleFor[m_sliceType] = q;
if ((m_curSlice->m_poc == 0 || m_lastQScaleFor[P_SLICE] < q) && !(m_2pass && !m_isVbv))
m_lastQScaleFor[P_SLICE] = q * fabs(m_param->rc.ipFactor);//将上一个P帧的量化参数(m_lastQScaleFor[P_SLICE])设置为当前量化参数(q)乘以ipFactor的绝对值
if (m_2pass)
rce->frameSizePlanned = qScale2bits(rce, q);
else
rce->frameSizePlanned = predictSize(&m_pred[m_predType], q, (double)m_currentSatd);
/* Always use up the whole VBV in this case. */
if (m_singleFrameVbv)//则将帧的大小(frameSizePlanned)设置为整个VBV缓冲区的大小(m_bufferRate)
rce->frameSizePlanned = m_bufferRate;
/* Limit planned size by MinCR */
if (m_isVbv)//如果启用了VBV模式(m_isVbv),则将帧的大小(frameSizePlanned)限制在最大VBV缓冲区大小(rce->frameSizeMaximum)和预测帧大小(frameSizePlanned)之间
rce->frameSizePlanned = X265_MIN(rce->frameSizePlanned, rce->frameSizeMaximum);
rce->frameSizeEstimated = rce->frameSizePlanned;
rce->newQScale = q;
return q;//返回量化参数(q)作为函数的输出
}
}
5.当前帧编码码控结束RateControl::rateControlEnd
当前帧编码码控结束,更新码控状态:
int RateControl::rateControlEnd(Frame* curFrame, int64_t bits, RateControlEntry* rce, int *filler)
{ //获取当前的编码顺序值(encodeOrder)和结束顺序值(endOrdinal)
int orderValue = m_startEndOrder.get();
int endOrdinal = (rce->encodeOrder + m_param->frameNumThreads) * 2 - 1;
while (orderValue < endOrdinal && !m_bTerminated)
{//在满足一定条件并且没有终止信号(m_bTerminated)的情况下,通过等待 m_startEndOrder 的变化来更新编码顺序值
/* no more frames are being encoded, so fake the start event if we would
* have blocked on it. Note that this does not enforce rateControlEnd()
* ordering during flush, but this has no impact on the outputs */
if (m_finalFrameCount && orderValue >= 2 * m_finalFrameCount)
break;
orderValue = m_startEndOrder.waitForChange(orderValue);//等待 m_top->m_rateControl->m_startEndOrder.incr();
}
//获取当前帧的编码数据(curEncData)和比特率(bits)
FrameData& curEncData = *curFrame->m_encData;
int64_t actualBits = bits;
Slice *slice = curEncData.m_slice;//获取当前帧的切片(slice)
bool bEnableDistOffset = m_param->analysisMultiPassDistortion && m_param->rc.bStatRead;
if (m_param->rc.aqMode || m_isVbv || m_param->bAQMotion || bEnableDistOffset)
{
if (m_isVbv && !(m_2pass && m_param->rc.rateControlMode == X265_RC_CRF && !m_param->rc.bEncFocusedFramesOnly))
{
double avgQpRc = 0;//算由 VBV 控制决定的平均 QP 值(avgQpRc)
/* determine avg QP decided by VBV rate control */
for (uint32_t i = 0; i < slice->m_sps->numCuInHeight; i++)
avgQpRc += curEncData.m_rowStat[i].sumQpRc;
//将 avgQpRc 限制在 qpMin 和 qpMax 之间,并将结果赋给 curEncData.m_avgQpRc 和 rce->qpaRc
avgQpRc /= slice->m_sps->numCUsInFrame;
curEncData.m_avgQpRc = x265_clip3((double)m_param->rc.qpMin, (double)m_param->rc.qpMax, avgQpRc);
rce->qpaRc = curEncData.m_avgQpRc;
}
if (m_param->rc.aqMode || m_param->bAQMotion || bEnableDistOffset)
{
double avgQpAq = 0;//计算经过 AQ、Cutree 和失真调整后的实际平均编码 QP 值(avgQpAq)
/* determine actual avg encoded QP, after AQ/cutree/distortion adjustments */
for (uint32_t i = 0; i < slice->m_sps->numCuInHeight; i++)
avgQpAq += curEncData.m_rowStat[i].sumQpAq;
//将 avgQpAq 除以 num4x4Partitions(每个 CU 的 4x4 子区域数)和 numCUsInFrame(帧中的 CU 数量),并将结果赋给 curEncData.m_avgQpAq
avgQpAq /= (slice->m_sps->numCUsInFrame * m_param->num4x4Partitions);
curEncData.m_avgQpAq = avgQpAq;
}
else
curEncData.m_avgQpAq = curEncData.m_avgQpRc;
}
if (m_isAbr)
{
if (m_param->rc.rateControlMode == X265_RC_ABR && !m_param->rc.bStatRead)
checkAndResetABR(rce, true);//来检查并重置 ABR 相关参数
}
if (m_param->rc.rateControlMode == X265_RC_CRF)
{
double crfVal, qpRef = curEncData.m_avgQpRc;
bool is2passCrfChange = false;
if (m_2pass && !m_param->rc.bEncFocusedFramesOnly)
{ //如果当前帧的平均 QP 值与上一帧的 QP 值之差超过 0.1
if (fabs(curEncData.m_avgQpRc - rce->qpPrev) > 0.1)
{ //将 qpRef 设置为上一帧的 QP 值(rce->qpPrev)
qpRef = rce->qpPrev;
is2passCrfChange = true;
}
}
if (is2passCrfChange || fabs(qpRef - rce->qpNoVbv) > 0.5)
{
double crfFactor = rce->qRceq /x265_qp2qScale(qpRef);
double baseCplx = m_ncu * (m_param->bframes ? 120 : 80);
double mbtree_offset = m_param->rc.cuTree ? (1.0 - m_param->rc.qCompress) * 13.5 : 0;
crfVal = x265_qScale2qp(pow(baseCplx, 1 - m_qCompress) / crfFactor) - mbtree_offset;
}
else//针对不同帧类型,进行不同的crf赋值调整,I帧、P帧、B帧,分别-m_ipOffset、0、+m_ipOffset调整
crfVal = rce->sliceType == I_SLICE ? m_param->rc.rfConstant - m_ipOffset :
(rce->sliceType == B_SLICE ? m_param->rc.rfConstant + m_pbOffset : m_param->rc.rfConstant);
curEncData.m_rateFactor = crfVal;
}
if (m_isAbr && !m_isAbrReset)
{ //在接下来的几帧中摊销每个I切片的一部分,最高可达keyint max,以避免过度补偿较大的I切片成本
/* amortize part of each I slice over the next several frames, up to
* keyint-max, to avoid over-compensating for the large I slice cost */
if (!m_param->rc.bStatWrite && !m_param->rc.bStatRead)
{
if (rce->sliceType == I_SLICE)
{ //如果之前的 I 帧仍有剩余的成本(m_residualFrames 不为零)
/* previous I still had a residual; roll it into the new loan */
if (m_residualFrames)
bits += m_residualCost * m_residualFrames;//将剩余的成本乘以剩余帧数(m_residualFrames)并加到当前帧的比特数中(bits)
m_residualFrames = X265_MIN((int)rce->amortizeFrames, m_param->keyframeMax);//将剩余帧数(rce->amortizeFrames)限制在 m_param->keyframeMax 和 int 类型的最小值之间,并赋值给 m_residualFrames
m_residualCost = (int)((bits * rce->amortizeFraction) / m_residualFrames);
bits -= m_residualCost * m_residualFrames;
}
else if (m_residualFrames)
{//将成本(m_residualCost)加到当前帧的比特数中(bits)
bits += m_residualCost;
m_residualFrames--;
}
}
if (rce->sliceType != B_SLICE)
{ //如果不为B帧,则按以下公式更新m_cplxrSum
/* 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. */
m_cplxrSum += (bits * x265_qp2qScale(rce->qpaRc) / rce->qRceq) - (rce->rowCplxrSum);
}
else
{ //取决于B帧的QP是从下面的P帧偏移的
/* 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. */
m_cplxrSum += (bits * x265_qp2qScale(rce->qpaRc) / (rce->qRceq * fabs(m_param->rc.pbFactor))) - (rce->rowCplxrSum);
}//更新 m_wantedBitsWindow,默认使用增加 m_frameDuration * m_bitrate
m_wantedBitsWindow += (m_param->bEnableSBRC ? (m_bitrate * (1.0 / m_param->keyframeMax)) : m_frameDuration * m_bitrate);
m_totalBits += bits - rce->rowTotalBits;
m_encodedBits += actualBits;
int pos = m_sliderPos - m_param->frameNumThreads;
if (pos >= 0)//根据滑动窗口的位置 m_sliderPos - m_param->frameNumThreads,更新滑动窗口中的编码比特数
m_encodedBitsWindow[pos % s_slidingWindowFrames] = actualBits;
if(rce->sliceType != I_SLICE)
{//将 rce->qpaRc 四舍五入为整数,赋值给 qp
int qp = int (rce->qpaRc + 0.5);//如果 m_qpToEncodedBits[qp] 为 0,则将 actualBits 赋值给 m_qpToEncodedBits[qp],否则,计算新的平均编码比特数
m_qpToEncodedBits[qp] = m_qpToEncodedBits[qp] == 0 ? actualBits : (m_qpToEncodedBits[qp] + actualBits) * 0.5;
}//更新当前帧的码率控制数据(curFrame->m_rcData)的相应字段
curFrame->m_rcData->wantedBitsWindow = m_wantedBitsWindow;
curFrame->m_rcData->cplxrSum = m_cplxrSum;
curFrame->m_rcData->totalBits = m_totalBits;
curFrame->m_rcData->encodedBits = m_encodedBits;
}
if (m_2pass)
{
m_expectedBitsSum += qScale2bits(rce, x265_qp2qScale(rce->newQp));
m_totalBits += bits - rce->rowTotalBits;
}
if (m_isVbv)
{
*filler = updateVbv(actualBits, rce);
curFrame->m_rcData->bufferFillFinal = m_bufferFillFinal;
for (int i = 0; i < 4; i++)
{
curFrame->m_rcData->coeff[i] = m_pred[i].coeff;
curFrame->m_rcData->count[i] = m_pred[i].count;
curFrame->m_rcData->offset[i] = m_pred[i].offset;
}
if (m_param->bEmitHRDSEI)
{
const VUI *vui = &curEncData.m_slice->m_sps->vuiParameters;
const HRDInfo *hrd = &vui->hrdParameters;
const TimingInfo *time = &vui->timingInfo;
if (!curFrame->m_poc)
{
// first access unit initializes the HRD
rce->hrdTiming->cpbInitialAT = 0;
rce->hrdTiming->cpbRemovalTime = m_nominalRemovalTime = (double)m_bufPeriodSEI.m_initialCpbRemovalDelay / 90000;
}
else
{
rce->hrdTiming->cpbRemovalTime = m_nominalRemovalTime + (double)rce->picTimingSEI->m_auCpbRemovalDelay * time->numUnitsInTick / time->timeScale;
double cpbEarliestAT = rce->hrdTiming->cpbRemovalTime - (double)m_bufPeriodSEI.m_initialCpbRemovalDelay / 90000;
if (!curFrame->m_lowres.bKeyframe)
cpbEarliestAT -= (double)m_bufPeriodSEI.m_initialCpbRemovalDelayOffset / 90000;
rce->hrdTiming->cpbInitialAT = hrd->cbrFlag ? m_prevCpbFinalAT : X265_MAX(m_prevCpbFinalAT, cpbEarliestAT);
}
int filler_bits = *filler ? (*filler - START_CODE_OVERHEAD * 8) : 0;
uint32_t cpbsizeUnscale = hrd->cpbSizeValue << (hrd->cpbSizeScale + CPB_SHIFT);
rce->hrdTiming->cpbFinalAT = m_prevCpbFinalAT = rce->hrdTiming->cpbInitialAT + (actualBits + filler_bits)/ cpbsizeUnscale;
rce->hrdTiming->dpbOutputTime = (double)rce->picTimingSEI->m_picDpbOutputDelay * time->numUnitsInTick / time->timeScale + rce->hrdTiming->cpbRemovalTime;
}
}
rce->isActive = false;
// Allow rateControlStart of next frame only when rateControlEnd of previous frame is over
m_startEndOrder.incr();//仅当上一帧的rateControlEnd结束时才允许下一帧的rateControlStart
return 0;
}
四、参考资料
x265--速率控制模块理解_x265 ratecontrolend-CSDN博客
点赞、收藏,会是我继续写作的动力!赠人玫瑰,手有余香。