coding_unit函数,对一个cu中的所有预测信息、变换系数等信息进行编码。
其实对一个cu的编码,主要编码3个信息:预测模式、预测数据、变换系数。
由于VTM3中加入了很多的编码工具,所以相较于VTM1,coding_unit函数会对更多的模式的信息进行编码。
对于帧内pu的几种模式的码字编写过程没有细看,以后补上。
void CABACWriter::coding_unit( const CodingUnit& cu, Partitioner& partitioner, CUCtx& cuCtx )
{
CodingStructure& cs = *cu.cs;
#if JVET_L0293_CPR
cs.chType = partitioner.chType;
#endif
// transquant bypass flag
if( cs.pps->getTransquantBypassEnabledFlag() )
{
cu_transquant_bypass_flag( cu ); //变换系数旁路编码标志
}
// skip flag
#if JVET_L0293_CPR
if (!cs.slice->isIntra() && cu.Y().valid())
#else
if( !cs.slice->isIntra() )
#endif
{
cu_skip_flag( cu ); //skip模式时编码skip_flag
}
// skip data
if( cu.skip )
{
CHECK( !cu.firstPU->mergeFlag, "Merge flag has to be on!" );
PredictionUnit& pu = *cu.firstPU;
prediction_unit ( pu ); //skip模式时编码预测数据:merge_idx
end_of_ctu ( cu, cuCtx ); //如果ctu结束,编码一个0(一帧最后一个ctu不编码这个0)
return;
}
// prediction mode and partitioning data
pred_mode ( cu ); //编码帧内还是帧间
#if JVET_L0283_MULTI_REF_LINE
extend_ref_line(cu); //MRL
#endif
// pcm samples
if( CU::isIntra(cu) && cu.partSize == SIZE_2Nx2N )
{
pcm_data( cu ); //pcm模式
if( cu.ipcm )
{
end_of_ctu( cu, cuCtx );
return;
}
}
// prediction data ( intra prediction modes / reference indexes + motion vectors )
cu_pred_data( cu ); //预测信息的编码,帧内亮度色度的具体模式,或帧间的merge_Idx、refIdx、mv、mvd等这些
// residual data ( coded block flags + transform coefficient levels )
cu_residual( cu, partitioner, cuCtx ); //残差数据,变换系数的编码
// end of cu
end_of_ctu( cu, cuCtx ); //如果ctu结束,编码一个0(一帧最后一个ctu不编码这个0)
}
//编码帧内还是帧间预测模式
void CABACWriter::pred_mode( const CodingUnit& cu )
{
if( cu.cs->slice->isIntra() ) //I帧时默认帧内模式,不用编码
{
return;
} //不是I帧时,intra编码1,inter编码0
m_BinEncoder.encodeBin( ( CU::isIntra( cu ) ), Ctx::PredMode() );
}
//帧内MRL模式时的编码
void CABACWriter::extend_ref_line(const CodingUnit& cu)
{
if (!cu.Y().valid() || cu.predMode != MODE_INTRA || !isLuma(cu.chType))
{
return;
}
const int numBlocks = CU::getNumPUs(cu);
const PredictionUnit* pu = cu.firstPU;
for (int k = 0; k < numBlocks; k++) //对cu的每个pu编码
{
bool isFirstLineOfCtu = (((cu.block(COMPONENT_Y).y)&((cu.cs->sps)->getMaxCUWidth() - 1)) == 0);
if (isFirstLineOfCtu) //Ctu第一行的cu不允许MRL模式
{
return;
}
int multiRefIdx = pu->multiRefIdx; //MRL模式的参考像素行idx
if (MRL_NUM_REF_LINES > 1) //编码MRL模式的参考像素行idx
{
m_BinEncoder.encodeBin(multiRefIdx != MULTI_REF_LINE_IDX[0], Ctx::MultiRefLineIdx(0));
if (MRL_NUM_REF_LINES > 2 && multiRefIdx != MULTI_REF_LINE_IDX[0])
{
m_BinEncoder.encodeBin(multiRefIdx != MULTI_REF_LINE_IDX[1], Ctx::MultiRefLineIdx(1));
if (MRL_NUM_REF_LINES > 3 && multiRefIdx != MULTI_REF_LINE_IDX[1])
{
m_BinEncoder.encodeBin(multiRefIdx != MULTI_REF_LINE_IDX[2], Ctx::MultiRefLineIdx(2));
}
}
}
pu = pu->next;
}
}
//编码具体的在帧内和帧间预测模式下的预测信息
void CABACWriter::cu_pred_data( const CodingUnit& cu )
{
if( CU::isIntra( cu ) )
{
intra_luma_pred_modes ( cu ); //对亮度帧内模式和色度帧内模式的编码,帧内模式号的编码没细看
intra_chroma_pred_modes( cu );
return;
}
#if JVET_L0293_CPR
if (!cu.Y().valid()) // dual tree chroma CU
{
return;
}
#endif
for( auto &pu : CU::traversePUs( cu ) )
{
prediction_unit( pu ); //帧间模式的编码
}
imv_mode ( cu ); //整数iMV模式
#if JVET_L0646_GBI
cu_gbi_flag( cu ); //广义Bi时的flag编码
#endif
}
//帧间模式的编码,即编码skip模式、merge模式、inter_ME模式
void CABACWriter::prediction_unit( const PredictionUnit& pu )
{
#if ENABLE_SPLIT_PARALLELISM || ENABLE_WPP_PARALLELISM
CHECK( pu.cacheUsed, "Processing a PU that should be in cache!" );
CHECK( pu.cu->cacheUsed, "Processing a CU that should be in cache!" );
#endif
if( pu.cu->skip )
{
//skip模式编码skip_flag之后,默认merge_flag为true,不用编码merge_flag
CHECK( !pu.mergeFlag, "merge_flag must be true for skipped CUs" );
}
else
{
merge_flag( pu ); //merge_flag编码,表示是否为merge模式
}
if( pu.mergeFlag ) //merge一类模式
{
#if JVET_L0369_SUBBLOCK_MERGE
subblock_merge_flag( *pu.cu ); //ATMVP
#else
affine_flag ( *pu.cu ); //Affine_Merge
#endif
#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
MHIntra_flag( pu ); //MHIntra
if ( pu.mhIntraFlag )
{
MHIntra_luma_pred_modes( *pu.cu ); //MHIntra模式的信息编码
}
#endif
#if JVET_L0124_L0208_TRIANGLE
triangle_mode( *pu.cu ); //三角预测模式信息编码
#endif
#if JVET_L0054_MMVD
if (pu.mmvdMergeFlag)
{
mmvd_merge_idx(pu); //MMVD模式信息编码
}
else
#endif
merge_idx ( pu ); //编码merge_idx
}
else //非merge一类模式
{
inter_pred_idc( pu ); //编码前后双向预测flag
affine_flag ( *pu.cu ); //Affine_ME的flag
if( pu.interDir != 2 /* PRED_L1 */ ) //前向
{
ref_idx ( pu, REF_PIC_LIST_0 ); //refIdx
if ( pu.cu->affine )
{
//Affine_ME编码2、3个mvd
mvd_coding(pu.mvdAffi[REF_PIC_LIST_0][0], 0);
mvd_coding(pu.mvdAffi[REF_PIC_LIST_0][1], 0);
if ( pu.cu->affineType == AFFINEMODEL_6PARAM )
{
mvd_coding(pu.mvdAffi[REF_PIC_LIST_0][2], 0);
}
}
else
{
//普通inter编码一个mvd
mvd_coding( pu.mvd[REF_PIC_LIST_0], pu.cu->imv );
}
mvp_flag ( pu, REF_PIC_LIST_0 ); //mvpIdx编码
}
if( pu.interDir != 1 /* PRED_L0 */ ) //后向
{
ref_idx ( pu, REF_PIC_LIST_1 );
if( !pu.cs->slice->getMvdL1ZeroFlag() || pu.interDir != 3 /* PRED_BI */ )
{
if ( pu.cu->affine )
{
mvd_coding(pu.mvdAffi[REF_PIC_LIST_1][0], 0);
mvd_coding(pu.mvdAffi[REF_PIC_LIST_1][1], 0);
if ( pu.cu->affineType == AFFINEMODEL_6PARAM )
{
mvd_coding(pu.mvdAffi[REF_PIC_LIST_1][2], 0);
}
}
else
{
mvd_coding( pu.mvd[REF_PIC_LIST_1], pu.cu->imv );
}
}
mvp_flag ( pu, REF_PIC_LIST_1 ); //mvpIdx编码
}
}
}
//如果编码的cu为ctu的最后一个cu,则编码一个0表示ctu结束(ctu若为一帧中的最后一个ctu时不编码这个0)
void CABACWriter::end_of_ctu( const CodingUnit& cu, CUCtx& cuCtx )
{
const Slice* slice = cu.cs->slice;
#if HEVC_TILES_WPP
const TileMap& tileMap = *cu.cs->picture->tileMap;
const int currentCTUTsAddr = tileMap.getCtuRsToTsAddrMap( CU::getCtuAddr( cu ) );
#else
const int currentCTUTsAddr = CU::getCtuAddr( cu );
#endif
const bool isLastSubCUOfCtu = CU::isLastSubCUOfCtu( cu );
if ( isLastSubCUOfCtu //DualITree时,编码完ctu色度分量的最后一个cu,才编码0
&& ( !CS::isDualITree( *cu.cs ) || cu.chromaFormat == CHROMA_400 || isChroma( cu.chType ) )
)
{
cuCtx.isDQPCoded = ( cu.cs->pps->getUseDQP() && !cuCtx.isDQPCoded );
// The 1-terminating bit is added to all streams, so don't add it here when it's 1.
// i.e. when the slice segment CurEnd CTU address is the current CTU address+1.
#if HEVC_DEPENDENT_SLICES
if( slice->getSliceSegmentCurEndCtuTsAddr() != currentCTUTsAddr + 1 )
#else
if(slice->getSliceCurEndCtuTsAddr() != currentCTUTsAddr + 1)
#endif
{
m_BinEncoder.encodeBinTrm( 0 ); //当前cu如果是ctu中的最后一个cu,而且ctu不是一帧中的最后一个ctu,则编码一个0,表示ctu编码结束
}
}
}