openh264 帧级码率控制原理:RcCalculateIdrQp 函数

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:32
  double 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);
  else
    dBpp = 0.1;
//Area*2
  if (pDLayerParam->iVideoWidth * pDLayerParam->iVideoHeight <= 28800) // 90p video:160*90
    iBppIndex = 0;
  else if (pDLayerParam->iVideoWidth * pDLayerParam->iVideoHeight <= 115200) // 180p video:320*180
    iBppIndex = 1;
  else if (pDLayerParam->iVideoWidth * pDLayerParam->iVideoHeight <= 460800) // 360p video:640*360
    iBppIndex = 2;
  else
    iBppIndex = 3;

//Search
  for (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 frame
    pWelsSvcRc->iInitialQp = dInitialQPArray[iBppIndex][i];
  } else {

    //obtain the idr qp using previous idr complexity
    if (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);

}
  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Codec Conductor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值