怎样制作网站教程,网站建设销售专业话术,万户网站做的怎样,网站网页价格系列文章目录 HEVC视频编解码标准简介
【x264编码器】章节1——x264编码流程及基于x264的编码器demo
【x264编码器】章节2——x264的lookahead流程分析
【x264编码器】章节3——x264的码率控制
【x264编码器】章节4——x264的帧内预测流程
【x264编码器】章节5——x264的帧…
系列文章目录 HEVC视频编解码标准简介
【x264编码器】章节1——x264编码流程及基于x264的编码器demo
【x264编码器】章节2——x264的lookahead流程分析
【x264编码器】章节3——x264的码率控制
【x264编码器】章节4——x264的帧内预测流程
【x264编码器】章节5——x264的帧间预测流程
【x264编码器】章节6——x264的变换量化
【x265编码器】章节1——lookahead模块分析
【x265编码器】章节2——编码流程及基于x265的编码器demo
【x265编码器】章节3——帧内预测流程
【x265编码器】章节4——帧间预测流程
【x265编码器】章节5——x265帧间运动估计流程
【x265编码器】章节6——x265的码率控制
【x265编码器】章节7——滤波模块
【x265编码器】章节8——变换量化模块 目录
系列文章目录
一、码率控制说明
二、码率控制模型
1.R-Q模型说明
2.CRF模式
3.ABR模式
三、代码模块分析
1.构造函数RateControl::RateControl
2.初始化RateControl::init
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);//短期复杂性是根据当前帧的SATDSum 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-frames QP is an offset from the following P-frames.* 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_bitratem_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.当qCompress1此时QP选择跟编码复杂度无关即固定QPCQP
2.当qCompress0此时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;//计算低分辨率CUCoding 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则发出警告并将最大增量设置为0m_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_qpm_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 offsetm_pbOffset 6.0 * X265_LOG2(m_param-rc.pbFactor);//P和B帧QP offsetfor (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.rfConstantfor (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 sizesm_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标志为truewhile (orderValue startOrdinal !m_bTerminated)orderValue m_startEndOrder.waitForChange(orderValue);//如果curFrame为空则表示编码器正在执行刷新操作执行相应的处理并增加m_startEndOrder的值然后返回0if (!curFrame){// faked rateControlStart calls when the encoder is flushingm_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_predTyperce-poc m_curSlice-m_poc;//将rce-poc设置为当前切片的POCif (!m_param-bResetZoneConfig (rce-encodeOrder % m_param-reconfigWindowSize 0)){ //默认不进来计算区域缓冲索引m_zoneBufferIdxint 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 bitratem_top-zoneReadCount[i].incr();//增加相应区域的读取计数}}}//如果需要重置区域配置m_param-bResetZoneConfigif (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 dont 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 qpif (!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的值为当前的低分辨率satdX265_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计算相应的量化参数qpdouble 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_qpq 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_qpConstantm_qp m_qpConstant[m_sliceType];curEncData.m_avgQpAq curEncData.m_avgQpRc m_qp;//将 m_qp 的值赋给 curEncData.m_avgQpAq 和 curEncData.m_avgQpRc//获取当前帧的区域zonex265_zone* zone getZone();if (zone){ //如果区域强制指定了一个特定的 QPzone-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-leadingNoBSatdrce-leadingNoBSatd m_leadingNoBSatd;if (curFrame-m_forceqp)//如果当前帧设置了强制 QP{//将强制 QP 的值加上 0.5 并转换为整数然后减去 1并将结果赋给 m_qpm_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_slidingWindowFrames20int 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 dont have independent rate control, but rather get the* average QP of the two adjacent P-frames an offset *///获取前一个参考帧和后一个参考帧的相关信息包括平均量化参数avgQpRc和帧类型I_SLICE、P_SLICE或B_SLICESlice* 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_SLICEP帧且其POC显示顺序计数小于最近的ABR重置POCm_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_offsetelse if (i0)q q1;//只有i0为I帧则用q1的即另一个P帧参考帧的qp此时不需要再ip_offsetelse if (i1)q q0;//同理用另一个P帧的else if(m_isGrainEnabled !m_2pass)q q1;else//两个参考帧都为P帧则按时域距离加权q (q0 * dt1 q1 * dt0) / (dt0 dt1);//如果当前帧被引用则将量化参数增加pbOffset的一半否则将量化参数增加pbOffsetif (IS_REFERENCED(curFrame))//上面仅是转换成了对应P帧的qp因为当前帧为B帧还要再转换为对应B帧的qp加qp_offsetq m_pbOffset / 2;//当前帧编码为被参考的B帧则只加一半的pb_offset即质量设定在非参考的B帧和P帧之间elseq 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-qpNoVbvdouble qScale x265_qp2qScale(q);rce-qpNoVbv q;double lmin 0, lmax 0;if (m_isGrainEnabled m_isFirstMiniGop){//如果启用了Grain噪点且是第一个MiniGop一组相邻的帧则根据反馈调整量化参数并根据溢出情况调整qScale。最后将量化参数转换为qlmin 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偏移根据设置的偏移进行调整并将量化参数转换为qdouble 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);elserce-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-newQScalerce-newQScale qScale;if(rce-bLastMiniGopBFrame){if (m_isFirstMiniGop m_isGrainEnabled){//如果当前帧是最后一个MiniGop的B帧并且启用了Grain则更新平均P帧量化参数和最后一个P帧的qScalem_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);elsem_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);elseq / 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 frames 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);//短期复杂性是根据当前帧的SATDSum 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初始化为0rce-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计算量化参数qx265_zone* zone getZone();if (zone){if (zone-bForceQp)//则将q设置为对应的qp。否则将q除以区域的bitrateFactorq x265_qp2qScale(zone-qp);elseq / 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);elseinitialQScale / zone-bitrateFactor;}//根据初始的量化参数通过调整量化参数的方法tuneAbrQScaleFromFeedback获得调整后的量化参数tunedQScaledouble tunedQScale tuneAbrQScaleFromFeedback(initialQScale);overflow tunedQScale / initialQScale;//计算溢出值overflow即调整后的量化参数与初始的量化参数之间的比值q (!m_partialResidualFrames || m_param-bEnableSBRC)? tunedQScale : initialQScale;//根据部分残差帧m_partialResidualFrames的情况将量化参数设置为调整后的量化参数tunedQScale或初始的量化参数initialQScalebool 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为0if ((m_sliceType I_SLICE m_param-keyframeMax 1 m_lastNonBPictType ! I_SLICE !m_isAbrReset) || (m_isNextGop !m_framesDone)){ //如果不是严格的恒定比特率CBR模式则根据之前帧的累积P帧量化参数m_accumPQp / m_accumPNorm计算新的量化参数qif (!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和lqmaxlqmin 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并且不是使用VBVVideo Buffering Verifier模式且使用了ABRAverage 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_MAXq 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则根据区域的设置调整量化参数qq 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设置为当前的量化参数qm_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);elserce-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_bufferRaterce-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和结束顺序值endOrdinalint 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和比特率bitsFrameData curEncData *curFrame-m_encData;int64_t actualBits bits;Slice *slice curEncData.m_slice;//获取当前帧的切片slicebool 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-qpaRcavgQpRc / 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_avgQpAqavgQpAq / (slice-m_sps-numCUsInFrame * m_param-num4x4Partitions);curEncData.m_avgQpAq avgQpAq;}elsecurEncData.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.1if (fabs(curEncData.m_avgQpRc - rce-qpPrev) 0.1){ //将 qpRef 设置为上一帧的 QP 值rce-qpPrevqpRef 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并加到当前帧的比特数中bitsm_residualFrames X265_MIN((int)rce-amortizeFrames, m_param-keyframeMax);//将剩余帧数rce-amortizeFrames限制在 m_param-keyframeMax 和 int 类型的最小值之间并赋值给 m_residualFramesm_residualCost (int)((bits * rce-amortizeFraction) / m_residualFrames);bits - m_residualCost * m_residualFrames;}else if (m_residualFrames){//将成本m_residualCost加到当前帧的比特数中bitsbits 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-frames QP is an offset from the following P-frames.* 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_bitratem_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 四舍五入为整数赋值给 qpint 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 HRDrce-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 overm_startEndOrder.incr();//仅当上一帧的rateControlEnd结束时才允许下一帧的rateControlStartreturn 0;
}
四、参考资料
x265--速率控制模块理解_x265 ratecontrolend-CSDN博客
x264和x265编码器码率控制之基本模型 - 知乎 点赞、收藏会是我继续写作的动力赠人玫瑰手有余香。 文章转载自: http://www.morning.lsfrc.cn.gov.cn.lsfrc.cn http://www.morning.ydryk.cn.gov.cn.ydryk.cn http://www.morning.dfndz.cn.gov.cn.dfndz.cn http://www.morning.dddcfr.cn.gov.cn.dddcfr.cn http://www.morning.hlxpz.cn.gov.cn.hlxpz.cn http://www.morning.jcbjy.cn.gov.cn.jcbjy.cn http://www.morning.rkkpr.cn.gov.cn.rkkpr.cn http://www.morning.vjdofuj.cn.gov.cn.vjdofuj.cn http://www.morning.fwkjp.cn.gov.cn.fwkjp.cn http://www.morning.ftcrt.cn.gov.cn.ftcrt.cn http://www.morning.nnhrp.cn.gov.cn.nnhrp.cn http://www.morning.xnqjs.cn.gov.cn.xnqjs.cn http://www.morning.mjbjq.cn.gov.cn.mjbjq.cn http://www.morning.lfbsd.cn.gov.cn.lfbsd.cn http://www.morning.ltffk.cn.gov.cn.ltffk.cn http://www.morning.jggr.cn.gov.cn.jggr.cn http://www.morning.cklgf.cn.gov.cn.cklgf.cn http://www.morning.lcxdm.cn.gov.cn.lcxdm.cn http://www.morning.lnbcx.cn.gov.cn.lnbcx.cn http://www.morning.qzbwmf.cn.gov.cn.qzbwmf.cn http://www.morning.jljiangyan.com.gov.cn.jljiangyan.com http://www.morning.qkxt.cn.gov.cn.qkxt.cn http://www.morning.hxcuvg.cn.gov.cn.hxcuvg.cn http://www.morning.zsrdp.cn.gov.cn.zsrdp.cn http://www.morning.gnbfj.cn.gov.cn.gnbfj.cn http://www.morning.ffbl.cn.gov.cn.ffbl.cn http://www.morning.tmsxn.cn.gov.cn.tmsxn.cn http://www.morning.nydtt.cn.gov.cn.nydtt.cn http://www.morning.sbrrf.cn.gov.cn.sbrrf.cn http://www.morning.lflsq.cn.gov.cn.lflsq.cn http://www.morning.prysb.cn.gov.cn.prysb.cn http://www.morning.ghyfm.cn.gov.cn.ghyfm.cn http://www.morning.hmbtb.cn.gov.cn.hmbtb.cn http://www.morning.dhqg.cn.gov.cn.dhqg.cn http://www.morning.xwlhc.cn.gov.cn.xwlhc.cn http://www.morning.wrdpj.cn.gov.cn.wrdpj.cn http://www.morning.hsjrk.cn.gov.cn.hsjrk.cn http://www.morning.tlyms.cn.gov.cn.tlyms.cn http://www.morning.kcfnp.cn.gov.cn.kcfnp.cn http://www.morning.mjtft.cn.gov.cn.mjtft.cn http://www.morning.kndst.cn.gov.cn.kndst.cn http://www.morning.nrxsl.cn.gov.cn.nrxsl.cn http://www.morning.gnkbf.cn.gov.cn.gnkbf.cn http://www.morning.ldfcb.cn.gov.cn.ldfcb.cn http://www.morning.ngpdk.cn.gov.cn.ngpdk.cn http://www.morning.mlycx.cn.gov.cn.mlycx.cn http://www.morning.lgmty.cn.gov.cn.lgmty.cn http://www.morning.pfjbn.cn.gov.cn.pfjbn.cn http://www.morning.wflsk.cn.gov.cn.wflsk.cn http://www.morning.rbrd.cn.gov.cn.rbrd.cn http://www.morning.xfrqf.cn.gov.cn.xfrqf.cn http://www.morning.jcwhk.cn.gov.cn.jcwhk.cn http://www.morning.tkxr.cn.gov.cn.tkxr.cn http://www.morning.pkrb.cn.gov.cn.pkrb.cn http://www.morning.hysqx.cn.gov.cn.hysqx.cn http://www.morning.ljbm.cn.gov.cn.ljbm.cn http://www.morning.wbqt.cn.gov.cn.wbqt.cn http://www.morning.bgqqr.cn.gov.cn.bgqqr.cn http://www.morning.jtrqn.cn.gov.cn.jtrqn.cn http://www.morning.khntd.cn.gov.cn.khntd.cn http://www.morning.njdtq.cn.gov.cn.njdtq.cn http://www.morning.pdynk.cn.gov.cn.pdynk.cn http://www.morning.mtrz.cn.gov.cn.mtrz.cn http://www.morning.xhrws.cn.gov.cn.xhrws.cn http://www.morning.dtrzw.cn.gov.cn.dtrzw.cn http://www.morning.ksqzd.cn.gov.cn.ksqzd.cn http://www.morning.zwtp.cn.gov.cn.zwtp.cn http://www.morning.gwjnm.cn.gov.cn.gwjnm.cn http://www.morning.ishoufeipin.cn.gov.cn.ishoufeipin.cn http://www.morning.prfrb.cn.gov.cn.prfrb.cn http://www.morning.jmwrj.cn.gov.cn.jmwrj.cn http://www.morning.jxpwr.cn.gov.cn.jxpwr.cn http://www.morning.bfrsr.cn.gov.cn.bfrsr.cn http://www.morning.qxrct.cn.gov.cn.qxrct.cn http://www.morning.mmjyk.cn.gov.cn.mmjyk.cn http://www.morning.gbsby.cn.gov.cn.gbsby.cn http://www.morning.tbhlc.cn.gov.cn.tbhlc.cn http://www.morning.wphfl.cn.gov.cn.wphfl.cn http://www.morning.jrgxx.cn.gov.cn.jrgxx.cn http://www.morning.mnqg.cn.gov.cn.mnqg.cn