Analysis::checkInter_rd0_4()和Analysis::checkInter_rd5_6()函数高度相似,唯一的不同在其确定了CU中每个PU的最优预测后,计算当前CU的rdcost的方式
- Analysis::checkInter_rd0_4()的计算方式是
- 计算fencYUV和predYUV计算distortion
- 计算interMode的bits = block_mode_bits + mvp_idx_bits + refIdx_bits + mvd_bits
- 通过distortion和bits计算rdcost
- Analysis::checkInter_rd5_6()的计算方式是通过调用encodeResAndCalcRdInterCU()函数计算得到的
- 得到resiYUV = fencYUV - predYUV ,初始化上下文,并对残差进行变换、量化、编码,以及反量化、反变换来重建编码帧
- 计算bits = SkipFlag_bits + MergeIndex_bits
或bits = SkipFlag_bits + PredMode_bits + PartSize_bits + PredInfo_bits + Coeff_bits - 得到reconYUV,计算reconYUV和fencYUV的distortion
- 通过distortion和bits计算rdcost
也就是说,rdlevel5-6相比0-4,在计算distortion和bits上更加严谨,rdlevel0-4是的distortion和bits只是一个估算,而rdlevel5-6将所有的编码流程全部走完了,包括上下文初始化、对残差的编码、重建编码帧,严谨的计算distortion和bits
Analysis::checkInter_rd0_4()
/*
计算rdlevel0~4下,当前CU在partSize划分下的最优预测(即每个PU的帧间预测信息),及其rdcost
过程:
1.初始化该预测模式下的cost
2.设置partSize、设置predMode
3.得到预测的方向个数
4.若analysisLoad,则从m_reuseRef中载入refIdx
5.若analysisMultiPassRefine,则从m_reuseRef、m_reuseMv及m_reuseMvpIdx中载入refIdx、MV及mvpIdx
6.针对CU下的每个PU,得到其最佳帧间预测,即mergeFlag,mvpIdx,refIdx,mvd以及bits信息
7.取原始YUV即预测的YUV,计算其distortion
8.根据6中得到的bits和7中得到的distortion,计算其rdcost
9.若analysisSave,则将6中得到的ref存储回m_reuseRef
*/
void Analysis::checkInter_rd0_4(Mode& interMode, const CUGeom& cuGeom, PartSize partSize, uint32_t refMask[2])
{
//初始化interMode的cost
interMode.initCosts();
//设置partSize
interMode.cu.setPartSizeSubParts(partSize);
//设置pred mode
interMode.cu.setPredModeSubParts(MODE_INTER);
//单向预测 双向预测?
int numPredDir = m_slice->isInterP() ? 1 : 2;
//若analysisLoad && m_reuseInterDataCTU && analysisReuseLevel > 1 && analysisReuseLevel != 10,则从m_reuseRef载入ref
if (m_param->analysisLoad && m_reuseInterDataCTU && m_param->analysisReuseLevel > 1 && m_param->analysisReuseLevel != 10)
{
//计算当前CU的ref在m_reuseRef存储的偏移量
int refOffset = cuGeom.geomRecurId * 16 * numPredDir + partSize * numPredDir * 2;
int index = 0;
//得到当前CU的PU个数
uint32_t numPU = interMode.cu.getNumPartInter(0);
//针对每一个PU的每一个预测方向,载入其ref
for (uint32_t part = 0; part < numPU; part++)
{
MotionData* bestME = interMode.bestME[part];
for (int32_t i = 0; i < numPredDir; i++)
bestME[i].ref = m_reuseRef[refOffset + index++];
}
}
//若analysisMultiPassRefine && bStatRead && m_reuseInterDataCTU
if (m_param->analysisMultiPassRefine && m_param->rc.bStatRead && m_reuseInterDataCTU)
{
//得到PU个数
uint32_t numPU = interMode.cu.getNumPartInter(0);
//针对每一个PU
for (uint32_t part = 0; part < numPU; part++)
{
MotionData* bestME = interMode.bestME[part];
//针对每个预测方向,载入ref,mv,mvpIdx到bestME中
for (int32_t i = 0; i < numPredDir; i++)
{
int* ref = &m_reuseRef[i * m_frame->m_analysisData.numPartitions * m_frame->m_analysisData.numCUsInFrame];
bestME[i].ref = ref[cuGeom.absPartIdx];
bestME[i].mv = m_reuseMv[i][cuGeom.absPartIdx].word;
bestME[i].mvpIdx = m_reuseMvpIdx[i][cuGeom.absPartIdx];
}
}
}
//对CU的每一个PU都进行最优帧间预测决定,得到每个PU的mergeFlag,mvpIdx,refIdx,mvd等,并累加上其bits
predInterSearch(interMode, cuGeom, m_bChromaSa8d && (m_csp != X265_CSP_I400 && m_frame->m_fencPic->m_picCsp != X265_CSP_I400), refMask);
/* predInterSearch sets interMode.sa8dBits */
//取原始的YUV和预测的YUV
const Yuv& fencYuv = *interMode.fencYuv;
Yuv& predYuv = interMode.predYuv;
int part = partitionFromLog2Size(cuGeom.log2CUSize);
//计算luma的distortion
interMode.distortion = primitives.cu[part].sa8d(fencYuv.m_buf[0], fencYuv.m_size, predYuv.m_buf[0], predYuv.m_size);
//若有色度且m_bChromaSa8d,则累加chroma的distortion
if (m_bChromaSa8d && (m_csp != X265_CSP_I400 && m_frame->m_fencPic->m_picCsp != X265_CSP_I400))
{
interMode.distortion += primitives.chroma[m_csp].cu[part].sa8d(fencYuv.m_buf[1], fencYuv.m_csize, predYuv.m_buf[1], predYuv.m_csize);
interMode.distortion += primitives.chroma[m_csp].cu[part].sa8d(fencYuv.m_buf[2], fencYuv.m_csize, predYuv.m_buf[2], predYuv.m_csize);
}
//根据distortion和bits计算rdcost
interMode.sa8dCost = m_rdCost.calcRdSADCost((uint32_t)interMode.distortion, interMode.sa8dBits);
//若analysisSave && m_reuseInterDataCTU && analysisReuseLevel>1,则将计算得到的ref存储到m_reuseRef中
if (m_param->analysisSave && m_reuseInterDataCTU && m_param->analysisReuseLevel > 1)
{
//找到当前CU在m_reuseRef中的偏移量
int refOffset = cuGeom.geomRecurId * 16 * numPredDir + partSize * numPredDir * 2;
int index = 0;
//取CU的PU个数
uint32_t numPU = interMode.cu.getNumPartInter(0);
//针对每一个PU的每一个预测方向,将其ref存储回m_reuseRef中
for (uint32_t puIdx = 0; puIdx < numPU; puIdx++)
{
MotionData* bestME = interMode.bestME[puIdx];
for (int32_t i = 0; i < numPredDir; i++)
m_reuseRef[refOffset + index++] = bestME[i].ref;
}
}
}
Analysis::checkInter_rd5_6()
/*
计算rdlevel5~6下,当前CU在partSize划分下的最优预测(即每个PU的帧间预测信息),及其rdcost
过程:
1.初始化该预测模式下的cost
2.设置partSize、设置predMode
3.得到预测的方向个数
4.若analysisLoad,则从m_reuseRef中载入refIdx
5.若analysisMultiPassRefine,则从m_reuseRef、m_reuseMv及m_reuseMvpIdx中载入refIdx、MV及mvpIdx
6.针对CU下的每个PU,得到其最佳帧间预测,即mergeFlag,mvpIdx,refIdx,mvd以及bits信息
7.为当前CU进行残差编码,并计算rdcost
8.若analysisSave,则将6中得到的ref存储回m_reuseRef
*/
void Analysis::checkInter_rd5_6(Mode& interMode, const CUGeom& cuGeom, PartSize partSize, uint32_t refMask[2])
{
//初始化cost
interMode.initCosts();
//设置partSize
interMode.cu.setPartSizeSubParts(partSize);
//设置predMode
interMode.cu.setPredModeSubParts(MODE_INTER);
//得到预测方向的个数
int numPredDir = m_slice->isInterP() ? 1 : 2;
//若analysisLoad && m_reuseInterDataCTU && analysisReuseLevel > 1 && m_param->analysisReuseLevel != 10,则从m_reuseRef载入ref
if (m_param->analysisLoad && m_reuseInterDataCTU && m_param->analysisReuseLevel > 1 && m_param->analysisReuseLevel != 10)
{
//计算当前CU的ref在m_reuseRef存储的偏移量
int refOffset = cuGeom.geomRecurId * 16 * numPredDir + partSize * numPredDir * 2;
int index = 0;
//得到当前CU的PU个数
uint32_t numPU = interMode.cu.getNumPartInter(0);
//针对每一个PU的每一个预测方向,载入其ref
for (uint32_t puIdx = 0; puIdx < numPU; puIdx++)
{
MotionData* bestME = interMode.bestME[puIdx];
for (int32_t i = 0; i < numPredDir; i++)
bestME[i].ref = m_reuseRef[refOffset + index++];
}
}
//若analysisMultiPassRefine && bStatRead && m_reuseInterDataCTU
if (m_param->analysisMultiPassRefine && m_param->rc.bStatRead && m_reuseInterDataCTU)
{
//得到PU个数
uint32_t numPU = interMode.cu.getNumPartInter(0);
//针对每一个PU
for (uint32_t part = 0; part < numPU; part++)
{
MotionData* bestME = interMode.bestME[part];
//针对每个预测方向,载入ref,mv,mvpIdx到bestME中
for (int32_t i = 0; i < numPredDir; i++)
{
int* ref = &m_reuseRef[i * m_frame->m_analysisData.numPartitions * m_frame->m_analysisData.numCUsInFrame];
bestME[i].ref = ref[cuGeom.absPartIdx];
bestME[i].mv = m_reuseMv[i][cuGeom.absPartIdx].word;
bestME[i].mvpIdx = m_reuseMvpIdx[i][cuGeom.absPartIdx];
}
}
}
对CU的每一个PU都进行最优帧间预测决定,得到每个PU的mergeFlag,mvpIdx,refIdx,mvd等,并累加上其bits
predInterSearch(interMode, cuGeom, m_csp != X265_CSP_I400 && m_frame->m_fencPic->m_picCsp != X265_CSP_I400, refMask);
/* predInterSearch sets interMode.sa8dBits, but this is ignored */
//为当前CU进行残差编码,并计算rdcost
encodeResAndCalcRdInterCU(interMode, cuGeom);
//若analysisSave && m_reuseInterDataCTU && analysisReuseLevel>1,则将计算得到的ref存储到m_reuseRef中
if (m_param->analysisSave && m_reuseInterDataCTU && m_param->analysisReuseLevel > 1)
{
//找到当前CU在m_reuseRef中的偏移量
int refOffset = cuGeom.geomRecurId * 16 * numPredDir + partSize * numPredDir * 2;
int index = 0;
//取CU的PU个数
uint32_t numPU = interMode.cu.getNumPartInter(0);
//针对每一个PU的每一个预测方向,将其ref存储回m_reuseRef中
for (uint32_t puIdx = 0; puIdx < numPU; puIdx++)
{
MotionData* bestME = interMode.bestME[puIdx];
for (int32_t i = 0; i < numPredDir; i++)
m_reuseRef[refOffset + index++] = bestME[i].ref;
}
}
}