coding_tree递归调用,按照ctu在xCompressCU中得到的划分树,寻址到ctu中的每个cu,对于每个cu,调用coding_unit函数进行编码。
本函数基本上和VTM1中的流程一致,但要对>64的DualITree块进行特别处理:
对与>64的DualITree块,在QT时要对亮度和色度统一进行,保持QT对亮度和色度划分一致。
对于>64的DualITree块,本函数入口参数既有亮度partitioner,又有色度chromaPartitioner。
void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitioner, CUCtx& cuCtx, Partitioner* pPartitionerChroma, CUCtx* pCuCtxChroma)
{
const PPS &pps = *cs.pps;
const UnitArea &currArea = partitioner.currArea(); //当前递归到的处理区域
const CodingUnit &cu = *cs.getCU(currArea.blocks[partitioner.chType], partitioner.chType); //get目前区域的左上角位置的cu
// Reset delta QP coding flag and ChromaQPAdjustemt coding flag //按照该cu中的depth信息,判断当前区域是否继续划分,如果不需要划分,就表示目前区域就是cu,直接调用coding_unit函数进行编码
if (pps.getUseDQP() && partitioner.currDepth <= pps.getMaxCuDQPDepth()) //如果按照cu中的depth信息依然需要划分,那么递归调用coding_tree划分当前区域,继续寻址cu
{
cuCtx.isDQPCoded = false;
}
if (cs.slice->getUseChromaQpAdj() && partitioner.currDepth <= pps.getPpsRangeExtension().getDiffCuChromaQpOffsetDepth())
{
cuCtx.isChromaQpAdjCoded = false;
}
// Reset delta QP coding flag and ChromaQPAdjustemt coding flag
if (CS::isDualITree(cs) && pPartitionerChroma != nullptr) //即:I帧DualITree且CTU为128x128时的情况
{
if (pps.getUseDQP() && pPartitionerChroma->currDepth <= pps.getMaxCuDQPDepth())
{
pCuCtxChroma->isDQPCoded = false;
}
if (cs.slice->getUseChromaQpAdj() && pPartitionerChroma->currDepth <= pps.getPpsRangeExtension().getDiffCuChromaQpOffsetDepth())
{
pCuCtxChroma->isChromaQpAdjCoded = false;
}
}
const PartSplit implicitSplit = partitioner.getImplicitSplit(cs);
// QT
bool canQtSplit = partitioner.canSplit(CU_QUAD_SPLIT, cs); //当前区域是否可以进行QT划分
//长宽不等或者已经经过BT/TT划分,那么不能QT
if (canQtSplit)
{
// split_cu_flag
bool qtSplit = implicitSplit == CU_QUAD_SPLIT;
if (!qtSplit && implicitSplit != CU_QUAD_SPLIT)
{
qtSplit = (cu.qtDepth > partitioner.currQtDepth); //根据cu中的depth信息,判断当前区域是否QT划分
split_cu_flag(qtSplit, cs, partitioner); //编码当前区域是否QT划分的flag
}
// quad-tree split
if (qtSplit)
{
//MTT在CTU的划分时,首先进行QT划分,QT叶子节点进行BT/TT划分,VVC中CTU一般为128x128
//这一段代码,对I帧DualITree且CTU为128x128时的块进行限制,让亮度和色度在QT划分阶段保持一致(虽然下面包含等于64,其实等于64时不必一致)
if (CS::isDualITree(cs) && pPartitionerChroma != nullptr && (partitioner.currArea().lwidth() >= 64 || partitioner.currArea().lheight() >= 64))
{
partitioner.splitCurrArea(CU_QUAD_SPLIT, cs); //亮度和色度同时QT划分
pPartitionerChroma->splitCurrArea(CU_QUAD_SPLIT, cs);
bool beContinue = true;
bool lumaContinue = true;
bool chromaContinue = true;
while (beContinue)
{
if (partitioner.currArea().lwidth() > 64 || partitioner.currArea().lheight() > 64)
{
//对大于64的DualITree块进行coding_tree时,亮度色度同时
if (cs.picture->blocks[partitioner.chType].contains(partitioner.currArea().blocks[partitioner.chType].pos()))
{
coding_tree(cs, partitioner, cuCtx, pPartitionerChroma, pCuCtxChroma); //既有亮度partitioner,又有色度chromaPartitioner
}
lumaContinue = partitioner.nextPart(cs);
chromaContinue = pPartitionerChroma->nextPart(cs);
CHECK(lumaContinue != chromaContinue, "luma chroma partition should be matched");
beContinue = lumaContinue;
}
else
{
//dual tree coding under 64x64 block
if (cs.picture->blocks[partitioner.chType].contains(partitioner.currArea().blocks[partitioner.chType].pos()))
{
//对小于64的DualITree块进行coding_tree时,亮度和色度分开进行编码
coding_tree(cs, partitioner, cuCtx); //亮度
}
lumaContinue = partitioner.nextPart(cs);
if (cs.picture->blocks[pPartitionerChroma->chType].contains(pPartitionerChroma->currArea().blocks[pPartitionerChroma->chType].pos()))
{
coding_tree(cs, *pPartitionerChroma, *pCuCtxChroma); //色度
}
chromaContinue = pPartitionerChroma->nextPart(cs);
CHECK(lumaContinue != chromaContinue, "luma chroma partition should be matched");
beContinue = lumaContinue;
}
}
partitioner.exitCurrSplit();
pPartitionerChroma->exitCurrSplit();
}
else //P/B帧、I帧非DualITree、I帧块小于等于64
{
partitioner.splitCurrArea(CU_QUAD_SPLIT, cs); //当前区域QT划分,4个子区域在partitioner中压栈
do
{
if (cs.picture->blocks[partitioner.chType].contains(partitioner.currArea().blocks[partitioner.chType].pos()))
{
coding_tree(cs, partitioner, cuCtx); //对每个qt子区域,递归调用coding_tree,寻址其中的cu
}
} while (partitioner.nextPart(cs));
partitioner.exitCurrSplit(); //4个子区域在partitioner中出栈
}
return; //当前区域处理完毕,直接return
}
}
{
bool mtSplit = partitioner.canSplit(CU_MT_SPLIT, cs); //当前区域是否可以进行BT/TT划分
if (mtSplit)
{
const PartSplit splitMode = CU::getSplitAtDepth(cu, partitioner.currDepth); //getSplitAtDepth函数获取currDepth时,cu会进行的MT划分方式
split_cu_mode_mt(splitMode, cs, partitioner); //编码MT划分方式的信息
if (splitMode != CU_DONT_SPLIT)
{
partitioner.splitCurrArea(splitMode, cs); //按照得到的mt划分方式,划分得到子区域
do
{
if (cs.picture->blocks[partitioner.chType].contains(partitioner.currArea().blocks[partitioner.chType].pos()))
{
coding_tree(cs, partitioner, cuCtx); //对每个mt子区域,递归调用coding_tree,寻址其中的cu
}
} while (partitioner.nextPart(cs));
partitioner.exitCurrSplit();
return; //当前区域处理完毕,直接return
}
}
}
// Predict QP on start of quantization group
if (pps.getUseDQP() && !cuCtx.isDQPCoded && CU::isQGStart(cu))
{
cuCtx.qp = CU::predictQP(cu, cuCtx.qp);
}
// coding unit
coding_unit(cu, partitioner, cuCtx); //当前区域既不QT也不MT划分,表示已经寻址到CTU划分树的叶子节点,此时即为cu区域,直接编码cu
DTRACE_COND( ( isEncoding() ), g_trace_ctx, D_QP, "x=%d, y=%d, w=%d, h=%d, qp=%d\n", cu.Y().x, cu.Y().y, cu.Y().width, cu.Y().height, cu.qp );
DTRACE_BLOCK_REC_COND( ( !isEncoding() ), cs.picture->getRecoBuf( cu ), cu, cu.predMode );
}