openh264 码率控制结构
关于 openh264 码率控制整体结构,可以参考:openh264 码率控制原理框架。
openh264 帧级码率控制介绍
- 函数关系图:从图可以看出,帧级码控的核心函数就是
WelsRcPictureInitGom
、WelsRcPictureInfoUpdateGom
函数。
帧级码率控制核心函数介绍
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帧),调用
RcCalculateIdrQp
或RcCalculatePictureQp
计算全局量化参数。- 切片信息初始化:
- 调用
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);
}
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++;
}