RcCalculateIdrQp函数
功能
在码控中,当eSliceType为I_SLICE时 计算 IDR 帧的帧级量化参数QP 值。
原理过程
- 初始化变量:
- dBpp:初始化为0,用来存储比特率每像素(bits per pixel)的值。
- i:一个整型循环变量。
- 注释说明:
- 提供了不同分辨率和帧率下的目标比特率每像素值(bpp)和对应的量化参数(QP)示例。
- 定义数组:
- dBppArray:一个二维数组,存储不同分辨率下的比特率每像素值的阈值。
- dInitialQPArray:一个二维数组,存储不同分辨率下对应的初始量化参数。
- iQpRangeArray:一个二维数组,存储量化参数的范围。
- 计算帧复杂度:
- 根据编码参数的用途类型(iUsageType),可能使用不同的方法来计算帧复杂度(iFrameComplexity)。
- 获取编码器的比特率控制和空间层配置:
- pWelsSvcRc:指向比特率控制结构体的指针。
- pDLayerParam:指向空间层配置的指针。
- pDLayerParamInternal:指向空间层内部配置的指针。
- 计算比特率每像素值(dBpp):
- 如果输出帧率、视频宽度和高度都大于0,则根据空间层的比特率、输出帧率、视频宽度和高度计算dBpp。
- 如果上述条件不满足,则将dBpp设置为0.1,这可能是一个默认值或者最小值。
- 分辨率与索引关联:
- 根据视频的分辨率(宽度乘以高度)来设置iBppIndex。这个索引将用于选择适当的比特率每像素(bpp)阈值和初始量化参数。
- iBppIndex为0时,对应90p视频(160x90像素)。
- iBppIndex为1时,对应180p视频(320x180像素)。
- iBppIndex为2时,对应360p视频(640x360像素)。
- 如果分辨率大于360p,则iBppIndex默认为3。
- 搜索合适的bpp阈值:
- 使用一个循环来搜索dBppArray中不超过当前dBpp值的最大阈值。循环中的i变量从0开始,直到找到合适的阈值或达到数组的最后一个元素。
- 确定量化参数范围:
- 根据找到的阈值索引i和iBppIndex,从iQpRangeArray中获取量化参数的最大值iMaxQp和最小值iMinQp。
- 量化参数范围的调整:
- 使用WELS_CLIP3函数来确保iMinQp和iMaxQp在编码器设定的最小量化参数pWelsSvcRc->iMinQp和最大量化参数pWelsSvcRc->iMaxQp之间。
- 检查是否为第一个IDR帧:
- 如果pWelsSvcRc->iIdrNum等于0,表示当前正在处理的是序列的第一个IDR帧。
- 设置第一个IDR帧的初始量化参数:
- 对于第一个IDR帧,使用dInitialQPArray数组中对应iBppIndex和索引i的值作为初始量化参数iInitialQp。
- 处理非第一个IDR帧:
- 如果当前IDR帧不是序列中的第一个IDR帧,则根据前一个IDR帧的复杂度来计算量化参数。
- 调整内部分块复杂度:
- 如果iNumberMbFrame(帧的宏块数)不等于iIntraMbCount(帧内的内部分块数),则重新计算iIntraComplexity,即内部分块的复杂度。
- 计算复杂度比率:
- 使用
WELS_DIV_ROUND64
函数计算iFrameComplexity(当前帧的复杂度)与iIntraComplxMean(平均内部分块复杂度)的比率iCmplxRatio,并且乘以一个常数INT_MULTIPLY。- 限制复杂度比率的范围:
- 使用
WELS_CLIP3
函数将iCmplxRatio限制在INT_MULTIPLY - FRAME_CMPLX_RATIO_RANGE和INT_MULTIPLY + FRAME_CMPLX_RATIO_RANGE之间。- 计算量化步长:
- 根据iIntraComplexity、iCmplxRatio和目标比特数iTargetBits计算量化步长iQStep,使用
WELS_DIV_ROUND
函数。- 将量化步长转换为量化参数:
- 使用
RcConvertQStep2Qp
函数将量化步长iQStep转换为量化参数iInitialQp。- 限制初始量化参数:
- pWelsSvcRc->iInitialQp =
WELS_CLIP3
(pWelsSvcRc->iInitialQp, iMinQp, iMaxQp);- 使用
WELS_CLIP3
函数确保iInitialQp(初始量化参数)在预定义的最小量化参数iMinQp和最大量化参数iMaxQp之间。这是为了确保量化参数不会超出设定的范围。- 设置全局量化参数:
- pEncCtx->iGlobalQp = pWelsSvcRc->iInitialQp;
- 将初始量化参数赋值给编码上下文pEncCtx中的全局量化参数iGlobalQp。这个全局参数会被编码过程中的其他部分使用。
- 转换量化参数为量化步长:
- pWelsSvcRc->iQStep =
RcConvertQp2QStep
(pEncCtx->iGlobalQp);- 将量化参数转换为量化步长iQStep。量化步长是比特率控制算法中的一个中间参数,用于进一步的计算。
- 记录最后计算的量化参数:
- pWelsSvcRc->iLastCalculatedQScale = pEncCtx->iGlobalQp;
- 将当前的全局量化参数保存为最后计算的量化参数iLastCalculatedQScale。用于跟踪和调整编码过程中的量化参数。
- 设置帧的最小和最大量化参数:
- pWelsSvcRc->iMinFrameQp = WELS_CLIP3 (pEncCtx->iGlobalQp - DELTA_QP_BGD_THD, iMinQp, iMaxQp);
- pWelsSvcRc->iMaxFrameQp = WELS_CLIP3 (pEncCtx->iGlobalQp + DELTA_QP_BGD_THD, iMinQp, iMaxQp);
- 分别计算帧的最小量化参数iMinFrameQp和最大量化参数iMaxFrameQp。它们是基于全局量化参数iGlobalQp减去或加上一个阈值DELTA_QP_BGD_THD来计算的,然后使用
WELS_CLIP3
函数确保结果在iMinQp和iMaxQp的范围内。
原理流程图
源码
void RcCalculateIdrQp (sWelsEncCtx* pEncCtx) {double dBpp = 0;int32_t i;//64k@6fps for 90p: bpp 0.74 QP:24
//192k@12fps for 180p: bpp 0.28 QP:26
//512k@24fps for 360p: bpp 0.09 QP:30
//1500k@30fps for 720p: bpp 0.05 QP:32double dBppArray[4][3] = {{0.5, 0.75, 1.0}, {0.2, 0.3, 0.4}, {0.05, 0.09, 0.13}, {0.03, 0.06, 0.1}};int32_t dInitialQPArray[4][4] = {{28, 26, 24, 22}, {30, 28, 26, 24}, {32, 30, 28, 26}, {34, 32, 30, 28}};int32_t iBppIndex = 0;int32_t iQpRangeArray[4][2] = {{37, 25}, {36, 24}, {35, 23}, {34, 22}};int64_t iFrameComplexity = pEncCtx->pVaa->sComplexityAnalysisParam.iFrameComplexity;if (pEncCtx->pSvcParam->iUsageType == SCREEN_CONTENT_REAL_TIME) {SVAAFrameInfoExt* pVaa = static_cast<SVAAFrameInfoExt*> (pEncCtx->pVaa);iFrameComplexity = pVaa->sComplexityScreenParam.iFrameComplexity;}SWelsSvcRc* pWelsSvcRc = &pEncCtx->pWelsSvcRc[pEncCtx->uiDependencyId];SSpatialLayerConfig* pDLayerParam = &pEncCtx->pSvcParam->sSpatialLayers[pEncCtx->uiDependencyId];SSpatialLayerInternal* pDLayerParamInternal = &pEncCtx->pSvcParam->sDependencyLayers[pEncCtx->uiDependencyId];if (pDLayerParamInternal->fOutputFrameRate > EPSN && pDLayerParam->iVideoWidth && pDLayerParam->iVideoHeight)dBpp = (double) (pDLayerParam->iSpatialBitrate) / (double) (pDLayerParamInternal->fOutputFrameRate *pDLayerParam->iVideoWidth *pDLayerParam->iVideoHeight);elsedBpp = 0.1;
//Area*2if (pDLayerParam->iVideoWidth * pDLayerParam->iVideoHeight <= 28800) // 90p video:160*90iBppIndex = 0;else if (pDLayerParam->iVideoWidth * pDLayerParam->iVideoHeight <= 115200) // 180p video:320*180iBppIndex = 1;else if (pDLayerParam->iVideoWidth * pDLayerParam->iVideoHeight <= 460800) // 360p video:640*360iBppIndex = 2;elseiBppIndex = 3;//Searchfor (i = 0; i < 3; i++) {if (dBpp <= dBppArray[iBppIndex][i])break;}int32_t iMaxQp = iQpRangeArray[i][0];int32_t iMinQp = iQpRangeArray[i][1];iMinQp = WELS_CLIP3 (iMinQp, pWelsSvcRc->iMinQp, pWelsSvcRc->iMaxQp);iMaxQp = WELS_CLIP3 (iMaxQp, pWelsSvcRc->iMinQp, pWelsSvcRc->iMaxQp);if (0 == pWelsSvcRc->iIdrNum) { //the first IDR framepWelsSvcRc->iInitialQp = dInitialQPArray[iBppIndex][i];} else {//obtain the idr qp using previous idr complexityif (pWelsSvcRc->iNumberMbFrame != pWelsSvcRc->iIntraMbCount) {pWelsSvcRc->iIntraComplexity = pWelsSvcRc->iIntraComplexity * pWelsSvcRc->iNumberMbFrame /pWelsSvcRc->iIntraMbCount;}int64_t iCmplxRatio = WELS_DIV_ROUND64 (iFrameComplexity * INT_MULTIPLY,pWelsSvcRc->iIntraComplxMean);iCmplxRatio = WELS_CLIP3 (iCmplxRatio, INT_MULTIPLY - FRAME_CMPLX_RATIO_RANGE, INT_MULTIPLY + FRAME_CMPLX_RATIO_RANGE);pWelsSvcRc->iQStep = WELS_DIV_ROUND ((pWelsSvcRc->iIntraComplexity * iCmplxRatio),(pWelsSvcRc->iTargetBits * INT_MULTIPLY));pWelsSvcRc->iInitialQp = RcConvertQStep2Qp (pWelsSvcRc->iQStep);}pWelsSvcRc->iInitialQp = WELS_CLIP3 (pWelsSvcRc->iInitialQp, iMinQp, iMaxQp);pEncCtx->iGlobalQp = pWelsSvcRc->iInitialQp;pWelsSvcRc->iQStep = RcConvertQp2QStep (pEncCtx->iGlobalQp);pWelsSvcRc->iLastCalculatedQScale = pEncCtx->iGlobalQp;pWelsSvcRc->iMinFrameQp = WELS_CLIP3 (pEncCtx->iGlobalQp - DELTA_QP_BGD_THD, iMinQp, iMaxQp);pWelsSvcRc->iMaxFrameQp = WELS_CLIP3 (pEncCtx->iGlobalQp + DELTA_QP_BGD_THD, iMinQp, iMaxQp);}