介绍
- 功能:openh264 编码每个 slice 的核心函数
- 原型:
int32_t WelsCodeOneSlice (sWelsEncCtx* pEncCtx, SSlice* pCurSlice, const int32_t kiNalType)
- 参数:
- pEncCtx:指向编码上下文结构体sWelsEncCtx的指针,包含编码过程中所需的所有上下文信息。
- pCurSlice:指向当前要编码的切片结构体SSlice的指针。
- kiNalType:NAL单元类型,用于指定编码的NAL单元类型。
函数调用关系图
函数原理
WelsCodeOneSlice函数
- 过程:
- 获取上下文信息;
- 计算动态切片标志kiDynamicSliceFlag;
- 如果是I_SLICE,
- 设置标志bIdrFlag为 1,sScaleShift偏移为 0;
- 否则,
- 计算时域分层 ID值kuiTemporalId,
- sScaleShift偏移计算根据kuiTemporalId得到;
- 调用
WelsSliceHeaderExtInit
来初始化切片头扩展;- 如果启用了基于宏块的速率控制(pWelsSvcRc->bGomRC),则调用
GomRCInitForOneSlice
进行初始化;- 根据是否使用扩展的切片头标志,调用
g_pWelsWriteSliceHeader
数组中的相应函数来写入切片头;- 计算并设置当前片最后一个宏块的量化参数(QP)uiLastMbQp;
- 使用
g_pWelsSliceCoding
数组中的函数指针来调用相应的编码函数。这个数组根据切片是否为IDR切片和是否为动态切片来选择编码函数;- 调用
WelsWriteSliceEndSyn
来写入切片结束符号,这个操作取决于熵编码模式的标志;
- 源码:
int32_t WelsCodeOneSlice (sWelsEncCtx* pEncCtx, SSlice* pCurSlice, const int32_t kiNalType) {
SDqLayer* pCurLayer = pEncCtx->pCurDqLayer;
SWelsSvcRc* pWelsSvcRc = &pEncCtx->pWelsSvcRc[pEncCtx->uiDependencyId];
SNalUnitHeaderExt* pNalHeadExt = &pCurLayer->sLayerInfo.sNalHeaderExt;
SBitStringAux* pBs = pCurSlice->pSliceBsa;
const int32_t kiDynamicSliceFlag =
(pEncCtx->pSvcParam->sSpatialLayers[pEncCtx->uiDependencyId].sSliceArgument.uiSliceMode
== SM_SIZELIMITED_SLICE);
if (I_SLICE == pEncCtx->eSliceType) {
pNalHeadExt->bIdrFlag = 1;
pCurSlice->sScaleShift = 0;
} else {
const uint32_t kuiTemporalId = pNalHeadExt->uiTemporalId;
pCurSlice->sScaleShift = kuiTemporalId ? (kuiTemporalId - pEncCtx->pRefPic->uiTemporalId) : 0;
}
WelsSliceHeaderExtInit (pEncCtx, pCurLayer, pCurSlice);
//RomRC init slice by slice
if (pWelsSvcRc->bGomRC) {
GomRCInitForOneSlice (pCurSlice, pWelsSvcRc->iBitsPerMb);
}
g_pWelsWriteSliceHeader[pCurSlice->bSliceHeaderExtFlag] (pEncCtx, pBs, pCurLayer, pCurSlice,
pEncCtx->pFuncList->pParametersetStrategy);
pCurSlice->uiLastMbQp = pCurLayer->sLayerInfo.pPpsP->iPicInitQp + pCurSlice->sSliceHeaderExt.sSliceHeader.iSliceQpDelta;
int32_t iEncReturn = g_pWelsSliceCoding[pNalHeadExt->bIdrFlag][kiDynamicSliceFlag] (pEncCtx, pCurSlice);
if (ENC_RETURN_SUCCESS != iEncReturn)
return iEncReturn;
WelsWriteSliceEndSyn (pCurSlice, pEncCtx->pSvcParam->iEntropyCodingModeFlag != 0);
return ENC_RETURN_SUCCESS;
}
WelsSliceHeaderExtInit函数
- 过程:
初始化切片头扩展:
pCurSliceExt
:指向切片头扩展SSliceHeaderExt
的指针。pCurSliceHeader
:指向切片头SSliceHeader
的指针。获取参数:
pParamInternal
:获取当前依赖层的内部参数。设置切片类型:
- 将编码上下文的切片类型赋值给当前切片头的切片类型。
设置存储参考基础图像标志:
bStoreRefBasePicFlag
设置为false
,表示不存储参考基础图像。设置帧编号和IDR图像ID:
- 将内部参数中的帧编号和IDR图像ID赋值给切片头。
设置图像顺序计数的最低位:
- 将编码图像的图像顺序计数赋值给切片头。
P切片的参考图像索引设置:
- 如果切片类型为P_SLICE,设置活动参考图像索引数量为1。
- 如果参考计数在有效范围内,覆盖活动参考图像索引数量,并设置标志。
设置量化参数偏移:
- 计算并设置切片QP偏移量,即全局QP减去图像初始化QP。
设置去块滤波器参数:
- 将解码层的去块滤波器IDC、Alpha偏移和Beta偏移赋值给切片头。
设置跨层去块滤波器标志:
- 将解码层的跨层去块滤波器IDC赋值给切片头扩展。
扩展切片头标志处理:
- 如果启用了扩展切片头标志
bSliceHeaderExtFlag
,则调用WelsSliceHeaderScalExtInit
函数进行扩展初始化。- 如果没有启用,将自适应和默认的预测、模式和残差标志设置为
false
。
- 源码:
void WelsSliceHeaderExtInit (sWelsEncCtx* pEncCtx, SDqLayer* pCurLayer, SSlice* pSlice) {
SSliceHeaderExt* pCurSliceExt = &pSlice->sSliceHeaderExt;
SSliceHeader* pCurSliceHeader = &pCurSliceExt->sSliceHeader;
SSpatialLayerInternal* pParamInternal = &pEncCtx->pSvcParam->sDependencyLayers[pEncCtx->uiDependencyId];
pCurSliceHeader->eSliceType = pEncCtx->eSliceType;
pCurSliceExt->bStoreRefBasePicFlag = false;
pCurSliceHeader->iFrameNum = pParamInternal->iFrameNum;
pCurSliceHeader->uiIdrPicId = pParamInternal->uiIdrPicId;
pCurSliceHeader->iPicOrderCntLsb = pEncCtx->pEncPic->iFramePoc; // 0
if (P_SLICE == pEncCtx->eSliceType) {
pCurSliceHeader->uiNumRefIdxL0Active = 1;
if (pCurSliceHeader->uiRefCount > 0 &&
pCurSliceHeader->uiRefCount < pCurLayer->sLayerInfo.pSpsP->iNumRefFrames) {
pCurSliceHeader->bNumRefIdxActiveOverrideFlag = true;
pCurSliceHeader->uiNumRefIdxL0Active = pCurSliceHeader->uiRefCount;
}
//to solve mismatch between debug&release
else {
pCurSliceHeader->bNumRefIdxActiveOverrideFlag = false;
}
}
pCurSliceHeader->iSliceQpDelta = pEncCtx->iGlobalQp - pCurLayer->sLayerInfo.pPpsP->iPicInitQp;
//for deblocking initial
pCurSliceHeader->uiDisableDeblockingFilterIdc = pCurLayer->iLoopFilterDisableIdc;
pCurSliceHeader->iSliceAlphaC0Offset =
pCurLayer->iLoopFilterAlphaC0Offset; // need update iSliceAlphaC0Offset & iSliceBetaOffset for pSlice-header if loop_filter_idc != 1
pCurSliceHeader->iSliceBetaOffset = pCurLayer->iLoopFilterBetaOffset;
pCurSliceExt->uiDisableInterLayerDeblockingFilterIdc = pCurLayer->uiDisableInterLayerDeblockingFilterIdc;
if (pSlice->bSliceHeaderExtFlag) {
WelsSliceHeaderScalExtInit (pCurLayer, pSlice);
} else {
//both adaptive and default flags should equal to 0.
pCurSliceExt->bAdaptiveBaseModeFlag =
pCurSliceExt->bAdaptiveMotionPredFlag =
pCurSliceExt->bAdaptiveResidualPredFlag = false;
pCurSliceExt->bDefaultBaseModeFlag =
pCurSliceExt->bDefaultMotionPredFlag =
pCurSliceExt->bDefaultResidualPredFlag = false;
}
}
GomRCInitForOneSlice函数
- 作用:
主要作用是为当前切片设置基于宏块的速率控制参数,包括起始和结束宏块索引以及目标比特数。这些参数对于后续的编码过程中控制每个切片的比特分配非常重要,有助于实现更高效的编码和传输,同时保持视频质量。 - 源码:
void GomRCInitForOneSlice (SSlice* pSlice, const int32_t kiBitsPerMb) {
SRCSlicing* pSOverRc = &pSlice->sSlicingOverRc;
pSOverRc->iStartMbSlice = pSlice->sSliceHeaderExt.sSliceHeader.iFirstMbInSlice;
pSOverRc->iEndMbSlice = pSOverRc->iStartMbSlice + pSlice->iCountMbNumInSlice - 1;
pSOverRc->iTargetBitsSlice = WELS_DIV_ROUND (static_cast<int64_t> (kiBitsPerMb) * pSlice->iCountMbNumInSlice,
INT_MULTIPLY);
}
g_pWelsWriteSliceHeader[2]数组
- 作用:g_pWelsWriteSliceHeader 数组是一个设计用来根据不同需求选择不同切片头写入函数的工具,它通过索引来决定调用基础还是扩展的切片头写入函数,从而适应不同的编码场景。
- 源码:
static const PWelsSliceHeaderWriteFunc g_pWelsWriteSliceHeader[2] = { // 0: for base; 1: for ext;
WelsSliceHeaderWrite,
WelsSliceHeaderExtWrite
};
g_pWelsSliceCoding[2][2]数组
- 过程:
- 是一个函数指针数组,用于根据切片的类型和动态切片标志来选择相应的编码函数;
- 当第一索引为0时,表示P切片。如果第二索引为0,即非动态P切片,使用的编码函数是
WelsCodePSlice
。如果第二索引为1,即动态P切片,使用的编码函数是WelsCodePOverDynamicSlice
;- 当第一索引为1时,表示I切片。如果第二索引为0,即非动态I切片,使用的编码函数是
WelsISliceMdEnc
。如果第二索引为1,即动态I切片,使用的编码函数是WelsISliceMdEncDynamic
;
- 源码:
// 1st index: 0: for P pSlice; 1: for I pSlice;
// 2nd index: 0: for non-dynamic pSlice; 1: for dynamic I pSlice;
static const PWelsCodingSliceFunc g_pWelsSliceCoding[2][2] = {
{ WelsCodePSlice, WelsCodePOverDynamicSlice }, // P SSlice
{ WelsISliceMdEnc, WelsISliceMdEncDynamic } // I SSlice
};
WelsWriteSliceEndSyn函数
- 作用:在编码单个视频切片的末尾进行必要的编码状态处理和数据输出。它根据编码配置选择不同的编码模式,并确保编码后的数据正确地写入比特流。这对于视频编码过程中正确编码和解码切片至关重要。
- 源码:
void WelsWriteSliceEndSyn (SSlice* pSlice, bool bEntropyCodingModeFlag) {
SBitStringAux* pBs = pSlice->pSliceBsa;
if (bEntropyCodingModeFlag) {
WelsCabacEncodeFlush (&pSlice->sCabacCtx);
pBs->pCurBuf = WelsCabacEncodeGetPtr (&pSlice->sCabacCtx);
} else {
BsRbspTrailingBits (pBs);
BsFlush (pBs);
}
}