openh264 帧间预测编码
- 帧间预测编码是视频压缩技术中的关键方法之一,它主要用于减少视频序列中时间维度上的冗余。这种编码方式依赖于视频帧之间的空间相关性,通过预测和补偿来减少数据量,从而实现高效的视频压缩。帧间预测编码广泛应用于各种视频编码标准,包括H.264/AVC、H.265/HEVC、VP9和AV1等。通过减少时间冗余,帧间预测编码显著提高了视频数据的压缩率,同时保持了视频质量。
- 关于 openh264 帧间预测编码整体过程可以参考:openh264 帧间预测编码过程源码分析。
WelsMdP16x16函数介绍
- 功能:实现了对16x16宏块的运动估计(Motion Estimation, ME)和运动补偿(Motion Compensation, MC),并得到 P16x16 块的代价 cost;
- 参数:
- pFunc: SWelsFuncPtrList* 类型的指针,指向一个函数指针列表,包含编码过程中使用的各种函数。
- pCurLayer: SDqLayer* 类型的指针,指向当前处理的解码队列层(Decoding Queue Layer),包含当前宏块的编码和解码信息。
- pWelsMd: SWelsMD* 类型的指针,指向运动检测(Motion Detection)相关的上下文信息,包括运动估计和补偿的数据结构。
- pSlice: SSlice* 类型的指针,指向当前处理的切片(Slice)信息,切片是视频帧中的一个逻辑分段,包含一系列的宏块。
- pCurMb: SMB* 类型的指针,指向当前正在处理的宏块。
- 返回值:SWelsME* pMe16x16中代价uiSatdCost;
- 函数关系图:
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/6af19b1d9de041429110e1de17224dec.jpeg#pic_center)
WelsMdP16x16函数原理
- 通过 pMbCache = &pSlice->sMbCacheInfo; 获取当前切片的宏块缓存信息;
- pMe16x16 指向用于16x16块运动估计的结构体,它被初始化为 pWelsMd->sMe.sMe16x16;
- uiNeighborAvail 存储了当前宏块邻居(左侧、顶部)的存在性信息;
- kiMbWidth 和 kiMbHeight 分别获取编码图像的宏块宽度和高度;
InitMe
函数用于初始化运动估计过程,包括编码宏块、参考宏块、屏幕块特征存储等信息;- pMe16x16->uSadPredISatd.uiSadPred 设置为 pWelsMd->iSadPredMb,这是预测的SAD值;
- MV候选列表初始化:
- pSlice->uiMvcNum 重置为0,表示MV候选列表的开始;
- pMe16x16->sMvBase 作为基础MV添加到候选列表;
- 空间MV预测:
- 如果当前宏块的左侧或上方有邻居宏块,它们各自的16x16 MV被添加到候选列表。
- 时间MV预测:
- 如果参考帧是P切片,并且当前宏块在图像的右侧或下方有宏块,这些宏块的MV也被添加到候选列表,并根据 pSlice->sScaleShift 进行缩放。
- MV预测:
PredMv
函数用于根据MV候选列表预测当前宏块的MV。
- 执行运动搜索:
pFunc->pfMotionSearch[0]
调用指定的运动搜索算法来找到最佳MV,指向WelsMotionEstimateSearch
函数完成运动估计搜索过程;
- 更新宏块MV:
- pCurMb->sP16x16Mv 和解码图像的MV列表 pCurLayer->pDecPic->sMvList 被更新为找到的最佳MV。
- 返回SATD成本:
- 函数返回 pMe16x16->uiSatdCost,这是与最佳MV相关的SATD(Sum of Absolute Transformed Differences)成本。
WelsMdP16x16函数源码
int32_t WelsMdP16x16 (SWelsFuncPtrList* pFunc, SDqLayer* pCurLayer, SWelsMD* pWelsMd, SSlice* pSlice, SMB* pCurMb) {
SMbCache* pMbCache = &pSlice->sMbCacheInfo;
SWelsME* pMe16x16 = &pWelsMd->sMe.sMe16x16;
uint32_t uiNeighborAvail = pCurMb->uiNeighborAvail;
const int32_t kiMbWidth = pCurLayer->iMbWidth;
const int32_t kiMbHeight = pCurLayer->iMbHeight;
InitMe (*pWelsMd, BLOCK_16x16, pMbCache->SPicData.pEncMb[0], pMbCache->SPicData.pRefMb[0],
pCurLayer->pRefPic->pScreenBlockFeatureStorage,
*pMe16x16);
pMe16x16->uSadPredISatd.uiSadPred = pWelsMd->iSadPredMb;
pSlice->uiMvcNum = 0;
pSlice->sMvc[pSlice->uiMvcNum++] = pMe16x16->sMvBase;
if (uiNeighborAvail & LEFT_MB_POS) {
pSlice->sMvc[pSlice->uiMvcNum++] = (pCurMb - 1)->sP16x16Mv;
}
if (uiNeighborAvail & TOP_MB_POS) {
pSlice->sMvc[pSlice->uiMvcNum++] = (pCurMb - kiMbWidth)->sP16x16Mv;
}
if (pCurLayer->pRefPic->iPictureType == P_SLICE) {
if (pCurMb->iMbX < kiMbWidth - 1) {
SMVUnitXY sTempMv = pCurLayer->pRefPic->sMvList[pCurMb->iMbXY + 1];
pSlice->sMvc[pSlice->uiMvcNum].iMvX = sTempMv.iMvX >> pSlice->sScaleShift;
pSlice->sMvc[pSlice->uiMvcNum].iMvY = sTempMv.iMvY >> pSlice->sScaleShift;
++ pSlice->uiMvcNum;
}
if (pCurMb->iMbY < kiMbHeight - 1) {
SMVUnitXY sTempMv = pCurLayer->pRefPic->sMvList[pCurMb->iMbXY + kiMbWidth];
pSlice->sMvc[pSlice->uiMvcNum].iMvX = sTempMv.iMvX >> pSlice->sScaleShift;
pSlice->sMvc[pSlice->uiMvcNum].iMvY = sTempMv.iMvY >> pSlice->sScaleShift;
++ pSlice->uiMvcNum;
}
}
PredMv (&pMbCache->sMvComponents, 0, 4, 0, & (pMe16x16->sMvp));
pFunc->pfMotionSearch[0] (pFunc, pCurLayer, pMe16x16, pSlice);
pCurMb->sP16x16Mv = pMe16x16->sMv;
pCurLayer->pDecPic->sMvList[pCurMb->iMbXY] = pMe16x16->sMv;
return pMe16x16->uiSatdCost;
}