解码端coding_unit函数在coding_tree函数中被调用,用来解码一个cu的预测模式、残差等信息。
其实,编码端的coding_unit是如何编码的,解码端的coding_unit按照相同的顺序如何解码就行。
函数return一个bool值,表示当前cu是否是这一帧的最后一个cu。
主要解码三个信息:预测模式、预测数据、残差数据。
bool CABACReader::coding_unit( CodingUnit &cu, Partitioner &partitioner, CUCtx& cuCtx )
{
CodingStructure& cs = *cu.cs;
// transquant bypass flag
if( cs.pps->getTransquantBypassEnabledFlag() )
{
cu_transquant_bypass_flag( cu ); //变换系数旁路编码标志
}
// skip flag
if( !cs.slice->isIntra() )
{
cu_skip_flag( cu ); //skip模式时的skip_flag
}
// skip data
if( cu.skip )
{
cs.addTU ( cu, partitioner.chType );
PredictionUnit& pu = cs.addPU( cu, partitioner.chType );
MergeCtx mrgCtx; //merge候选列表
prediction_unit ( pu, mrgCtx ); //解码skip模式数据,构建merge列表,解码得到merge_idx,从而得到skip模式信息
return end_of_ctu( cu, cuCtx );
}
// prediction mode and partitioning data
pred_mode ( cu ); //解码帧内还是帧间
cu.partSize = SIZE_2Nx2N;
// --> create PUs
CU::addPUs( cu );
// pcm samples
if( CU::isIntra(cu) && cu.partSize == SIZE_2Nx2N )
{
pcm_flag( cu ); //pcm模式
if( cu.ipcm )
{
TransformUnit& tu = cs.addTU( cu, partitioner.chType );
pcm_samples( tu );
return end_of_ctu( cu, cuCtx );
}
}
// 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 ); //残差数据,变换系数的解码
// check end of cu
return end_of_ctu( cu, cuCtx ); //return当前cu是否是这一帧的最后一个cu
}
//解码帧内帧间
void CABACReader::pred_mode( CodingUnit& cu )
{
RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET( STATS__CABAC_BITS__PRED_MODE );
if( cu.cs->slice->isIntra() || m_BinDecoder.decodeBin( Ctx::PredMode() ) )
{ //I帧,或解码为1,则为帧内
cu.predMode = MODE_INTRA;
}
else
{
cu.predMode = MODE_INTER;
}
}
//解码cu的预测数据
void CABACReader::cu_pred_data( CodingUnit &cu )
{
if( CU::isIntra( cu ) )
{
intra_luma_pred_modes( cu );
intra_chroma_pred_modes( cu ); //解码帧内预测的模式信息,亮度模式和色度模式
return;
}
MergeCtx mrgCtx; //merge列表,好像没啥用,prediction_unit中用不到
for( auto &pu : CU::traversePUs( cu ) ) //对于cu下的所有pu
{
prediction_unit( pu, mrgCtx ); //解码帧间预测的数据,包括skip、merge、inter_ME模式
}
}
//解码帧间预测的数据,包括skip、merge、inter_ME模式
void CABACReader::prediction_unit( PredictionUnit& pu, MergeCtx& mrgCtx )
{
if( pu.cu->skip )
{
pu.mergeFlag = true; //skip_flag为真时,skip模式默认merge_flag为true
}
else
{
merge_flag( pu ); //merge和inter_ME模式需要解码merge_falg
}
if( pu.mergeFlag ) //merge模式
{
merge_data ( pu ); //解码merge_idx
}
else //inter_ME模式
{
inter_pred_idc( pu ); //解码帧间预测方向,00表示前向、01表示后向、1表示双向
if( pu.interDir != 2 /* PRED_L1 */ ) //解码前向预测信息,refIdx、mvd、mvpIdx
{
ref_idx ( pu, REF_PIC_LIST_0 );
{
mvd_coding( pu.mvd[REF_PIC_LIST_0] );
}
mvp_flag ( pu, REF_PIC_LIST_0 );
}
if( pu.interDir != 1 /* PRED_L0 */ ) //解码后向预测信息,refIdx、mvd、mvpIdx
{
ref_idx ( pu, REF_PIC_LIST_1 );
if( pu.cu->cs->slice->getMvdL1ZeroFlag() && pu.interDir == 3 /* PRED_BI */ )
{
pu.mvd[ REF_PIC_LIST_1 ] = Mv();
}
else
{
mvd_coding( pu.mvd[REF_PIC_LIST_1] );
}
mvp_flag ( pu, REF_PIC_LIST_1 );
}
}
if( pu.interDir == 3 /* PRED_BI */ && PU::isBipredRestriction(pu) )
{
pu.mv [REF_PIC_LIST_1] = Mv(0, 0);
pu.refIdx[REF_PIC_LIST_1] = -1;
pu.interDir = 1;
}
PU::spanMotionInfo( pu, mrgCtx ); //将pu解码得到的信息,扩展到pu所在区域的所有小块
}
//解码ctu结束时的标志bit 0,return当前cu是否是一帧的最后一个cu
bool CABACReader::end_of_ctu( CodingUnit& cu, CUCtx& cuCtx )
{
const SPS &sps = *cu.cs->sps; //rbPox为cu的右下角顶点的坐标
const Position rbPos = recalcPosition( cu.chromaFormat, cu.chType, CHANNEL_TYPE_LUMA, cu.blocks[cu.chType].bottomRight().offset( 1, 1 ) );
if ( ( ( rbPos.x & cu.cs->pcv->maxCUWidthMask ) == 0 || rbPos.x == sps.getPicWidthInLumaSamples () )
&& ( ( rbPos.y & cu.cs->pcv->maxCUHeightMask ) == 0 || rbPos.y == sps.getPicHeightInLumaSamples() )
&& ( !CS::isDualITree( *cu.cs ) || cu.chromaFormat == CHROMA_400 || isChroma( cu.chType ) )
) //DualITree的luma时,并不会解码这个ctu结束标志。DualITree的色度cu解码完成时才会解码这个ctu结束码
{
cuCtx.isDQPCoded = ( cu.cs->pps->getUseDQP() && !cuCtx.isDQPCoded );
return terminating_bit(); //return当前帧是否已经解码完成
}
return false; //cu如果不是ctu的最后一个,直接false
}
bool CABACReader::terminating_bit() //解码ctu的结束标志,以此判断并return当前帧是否已经解码完成
{
if( m_BinDecoder.decodeBinTrm() ) //解码编码端在ctu结束后编码的那个0
{ //如果解码为0,return false;表示当前ctu并不是一帧的最后一个ctu
m_BinDecoder.finish(); //如果解码为1,if条件满足,即当前cu为一帧的最后一个cu,再解码尾部剩余bit,返回true,即一帧解码完成
m_Bitstream->readOutTrailingBits();
return true;
}
return false;
}