openh264 帧级码率控制源码分析

openh264 码率控制结构

关于 openh264 码率控制整体结构,可以参考:openh264 码率控制原理框架

openh264 帧级码率控制介绍

  1. 函数关系图:从图可以看出,帧级码控的核心函数就是WelsRcPictureInitGomWelsRcPictureInfoUpdateGom函数。
    在这里插入图片描述

帧级码率控制核心函数介绍

  1. WelsRcPictureInitGom函数
  • 功能:负责设置编码周期的初始参数,包括码率、帧率、量化参数等,以确保视频编码过程中码率的稳定性和视频质量的一致性。通过调整这些参数,编码器可以在保持视频质量的同时,控制输出视频的比特率。
  • 原理过程
  • 函数声明:
    • WelsRcPictureInitGom 是函数的名称,用于初始化编码周期的率控制参数。
    • 参数 pEncCtx 是指向编码器上下文的指针。
    • 参数 uiTimeStamp 是当前帧的时间戳。
  • 局部变量声明:
    • pWelsSvcRc 指向当前依赖层的率控制结构体。
    • kiSliceNum 是当前编码层最大切片数量。
  • 连续跳帧计数器重置:
    • pWelsSvcRc->iContinualSkipFrames 重置为0,这可能用于跟踪连续跳过的帧数。
  • IDR帧的特殊处理:
    • 如果编码类型是I帧(关键帧),并且IDR帧计数器 iIdrNum 为0(表示编码器刚初始化),则调用 RcInitRefreshParameter 来初始化刷新参数。
  • 码率和帧率更新判断:
    • 如果需要,调用 RcJudgeBitrateFpsUpdate 判断并使用 RcUpdateBitrateFps 更新码率和帧率。
  • 时间层0的特殊处理:
    • 如果时间ID为0,则调用 RcUpdateTemporalZero 更新时间层0的参数。
  • 基于时间戳的码率决定:
    • 如果RC模式是时间戳模式,则调用 RcDecideTargetBitsTimestamp 根据时间戳决定目标比特数,并更新最后的时间戳。
    • 如果不是时间戳模式,则调用 RcDecideTargetBits 决定目标比特数。
  • 全局优化移动平均量化参数(GOM QP)的启用与禁用:
    • 如果切片数大于1,或者在码率模式下且编码类型为I帧,则禁用GOM QP。
    • 否则,启用GOM QP。
  • 全局量化参数计算:
    • 根据帧类型(I帧或非I帧),调用 RcCalculateIdrQpRcCalculatePictureQp 计算全局量化参数。
  • 切片信息初始化:
    • 调用 RcInitSliceInformation 初始化切片信息。
  • GOM参数初始化:
    • 调用 RcInitGomParameters 初始化GOM参数。
  • 源码
void  WelsRcPictureInitGom (sWelsEncCtx* pEncCtx, long long uiTimeStamp) {
  SWelsSvcRc* pWelsSvcRc           = &pEncCtx->pWelsSvcRc[pEncCtx->uiDependencyId];
  const int32_t kiSliceNum         = pEncCtx->pCurDqLayer->iMaxSliceNum;
  pWelsSvcRc->iContinualSkipFrames = 0;

  if (pEncCtx->eSliceType == I_SLICE) {
    if (0 == pWelsSvcRc->iIdrNum) { //iIdrNum == 0 means encoder has been initialed
      RcInitRefreshParameter (pEncCtx);
    }
  }
  if (RcJudgeBitrateFpsUpdate (pEncCtx)) {
    RcUpdateBitrateFps (pEncCtx);
  }
  if (pEncCtx->uiTemporalId == 0) {
    RcUpdateTemporalZero (pEncCtx);
  }
  if (pEncCtx->pSvcParam->iRCMode == RC_TIMESTAMP_MODE) {
    RcDecideTargetBitsTimestamp (pEncCtx);
    pWelsSvcRc->uiLastTimeStamp = uiTimeStamp;
  } else {
    RcDecideTargetBits (pEncCtx);
  }
  //turn off GOM QP when slicenum is larger 1
  if ((kiSliceNum > 1) || ((pEncCtx->pSvcParam->iRCMode == RC_BITRATE_MODE)
                           && (pEncCtx->eSliceType == I_SLICE))) {
    pWelsSvcRc->bEnableGomQp = false;
  } else
    pWelsSvcRc->bEnableGomQp = true;

  //decide globe_qp
  if (pEncCtx->eSliceType == I_SLICE) {
    RcCalculateIdrQp (pEncCtx);
  } else {
    RcCalculatePictureQp (pEncCtx);
  }
  RcInitSliceInformation (pEncCtx);
  RcInitGomParameters (pEncCtx);
} 
  1. WelsRcPictureInfoUpdateGom函数
  • 功能:负责在编码一帧后更新相关的率控制参数。通过这些更新,编码器可以动态调整编码参数,以确保在达到目标码率的同时,尽可能保持视频质量。此外,跳帧和填充的计算有助于在编码过程中进一步控制码率和视频文件的大小。
  • 原理过程
  • 函数声明:
    • WelsRcPictureInfoUpdateGom 是函数的名称,用于更新编码周期中帧的信息。
    • 参数 pEncCtx 是指向编码器上下文的指针。
    • 参数 iLayerSize 是当前编码层的大小,以字节为单位。
  • 局部变量声明:
    • pWelsSvcRc 指向当前依赖层的率控制结构体。
    • iCodedBits 是编码后帧的比特数,通过对 iLayerSize 左移3位(相当于乘以8)计算得出。
  • 更新编码后的量化参数和比特数:
    • 调用 RcUpdatePictureQpBits 函数,使用 pEncCtx 和 iCodedBits 更新编码帧的量化参数和比特数。
  • 更新帧复杂度:
    • 如果编码类型是P帧(前向预测帧),调用 RcUpdateFrameComplexity 更新帧的复杂度。
    • 如果编码类型是I帧(关键帧),调用 RcUpdateIntraComplexity 更新帧的复杂度。
  • 更新剩余比特数:
    • pWelsSvcRc->iRemainingBits 减去当前帧的比特数 pWelsSvcRc->iFrameDqBits,更新剩余的比特数。
  • 跳帧判断:
    • 如果启用了帧跳过功能(bEnableFrameSkip 为真),并且当前依赖层是最后一个空间层,则调用 RcVBufferCalculationSkip 函数进行跳帧计算。
  • 填充判断:
    • 如果设置了填充标志(iPaddingFlag),调用 RcVBufferCalculationPadding 函数进行填充计算。
  • 更新编码帧计数器:
    • pWelsSvcRc->iFrameCodedInVGop 增加1,表示在当前视频组(Video Group of Pictures, VGop)中又编码了一帧。
  • 源码
void  WelsRcPictureInfoUpdateGom (sWelsEncCtx* pEncCtx, int32_t iLayerSize) {
  SWelsSvcRc* pWelsSvcRc = &pEncCtx->pWelsSvcRc[pEncCtx->uiDependencyId];
  int32_t iCodedBits = (iLayerSize << 3);

  RcUpdatePictureQpBits (pEncCtx, iCodedBits);

  if (pEncCtx->eSliceType == P_SLICE) {
    RcUpdateFrameComplexity (pEncCtx);
  } else {
    RcUpdateIntraComplexity (pEncCtx);
  }
  pWelsSvcRc->iRemainingBits -= pWelsSvcRc->iFrameDqBits;

  if (pEncCtx->pSvcParam->bEnableFrameSkip /*&&
      pEncCtx->uiDependencyId == pEncCtx->pSvcParam->iSpatialLayerNum - 1*/) {
    RcVBufferCalculationSkip (pEncCtx);
  }

  if (pEncCtx->pSvcParam->iPaddingFlag)
    RcVBufferCalculationPadding (pEncCtx);
  pWelsSvcRc->iFrameCodedInVGop++;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码流怪侠

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

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

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

打赏作者

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

抵扣说明:

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

余额充值