您的位置:首页 > 其它

x265探索与研究(十):encodeSlice()函数、encodeCTU()函数、encodeCU()函数与finishCU()函数分析

2016-04-19 22:43 411 查看

x265探索与研究(十):encodeSlice()函数、encodeCTU()函数、encodeCU()函数与finishCU()函数分析

 

        encodeSlice()函数、encodeCTU()函数、encodeCU()函数与finishCU()函数都是编码的重要函数,根据函数命名均可得知其各自的功能。下面首先给出这几个函数之间的调用关系图。

 

 


 

1、encodeSlice()函数

 

        encodeSlice()函数位于frameencoder.cpp中,其主要功能就是遍历当前Slice中的CTU,并调用encodeCTU()函数对每个CTU进行进一步地处理,对应的代码分析如下:

 /*=============================================================*/
/*
====== Analysed by: RuiDong Fang
====== Csdn Blog: http://blog.csdn.net/frd2009041510 ====== Date: 2016.04.19
====== Funtion: encodeSlice()函数
*/
/*=============================================================*/
void FrameEncoder::encodeSlice()
{
Slice* slice = m_frame->m_encData->m_slice; //获取当前slice,通常一帧中包括很多Slice,但默认情况一帧即一个Slice
const uint32_t widthInLCUs = slice->m_sps->numCuInWidth;
// printf("widthInLCUs=%d",widthInLCUs);
const uint32_t lastCUAddr = (slice->m_endCUAddr + NUM_4x4_PARTITIONS - 1) / NUM_4x4_PARTITIONS;
const uint32_t numSubstreams = m_param->bEnableWavefront ? slice->m_sps->numCuInHeight : 1;

SAOParam* saoParam = slice->m_sps->bUseSAO ? m_frame->m_encData->m_saoParam : NULL; //saoParam

//遍历当前Slice中的CU
for (uint32_t cuAddr = 0; cuAddr < lastCUAddr; cuAddr++)
{
uint32_t col = cuAddr % widthInLCUs;
uint32_t lin = cuAddr / widthInLCUs;
uint32_t subStrm = lin % numSubstreams;
CUData* ctu = m_frame->m_encData->getPicCTU(cuAddr); //获取CTU

m_entropyCoder.setBitstream(&m_outStreams[subStrm]);

// Synchronize cabac probabilities with upper-right CTU if it's available and we're at the start of a line.
if (m_param->bEnableWavefront && !col && lin)
{
m_entropyCoder.copyState(m_initSliceContext);
m_entropyCoder.loadContexts(m_rows[lin - 1].bufferedEntropy);
}

//若saoParam
if (saoParam)
{
if (saoParam->bSaoFlag[0] || saoParam->bSaoFlag[1])
{
int mergeLeft = col && saoParam->ctuParam[0][cuAddr].mergeMode == SAO_MERGE_LEFT;
int mergeUp = lin && saoParam->ctuParam[0][cuAddr].mergeMode == SAO_MERGE_UP;
if (col)
m_entropyCoder.codeSaoMerge(mergeLeft);
if (lin && !mergeLeft)
m_entropyCoder.codeSaoMerge(mergeUp);
if (!mergeLeft && !mergeUp)
{
if (saoParam->bSaoFlag[0])
m_entropyCoder.codeSaoOffset(saoParam->ctuParam[0][cuAddr], 0);
if (saoParam->bSaoFlag[1])
{
m_entropyCoder.codeSaoOffset(saoParam->ctuParam[1][cuAddr], 1);
m_entropyCoder.codeSaoOffset(saoParam->ctuParam[2][cuAddr], 2);
}
}
}
else
{
for (int i = 0; i < 3; i++)
saoParam->ctuParam[i][cuAddr].reset();
}
}

// final coding (bitstream generation) for this CU滤波过后的熵编码处理
m_entropyCoder.encodeCTU(*ctu, m_cuGeoms[m_ctuGeomMap[cuAddr]]); //=====================调用encodeCTU()

if (m_param->bEnableWavefront) //若使能波前
{
if (col == 1)
// Store probabilities of second CTU in line into buffer
m_rows[lin].bufferedEntropy.loadContexts(m_entropyCoder);

if (col == widthInLCUs - 1)
m_entropyCoder.finishSlice();
}
}
if (!m_param->bEnableWavefront) //若不使能波前
m_entropyCoder.finishSlice();
}

 

2、encodeCTU()函数

 

        encodeCTU()函数位于entropy.cpp中,其主要功能就是对CTU中包含的每个CU调用encodeCU()函数进行进一步地处理,对应的代码分析如下:

 void Entropy::encodeCTU(const CUData& ctu, const CUGeom& cuGeom)
{
bool bEncodeDQP = ctu.m_slice->m_pps->bUseDQP;
encodeCU(ctu, cuGeom, 0, 0, bEncodeDQP); //===============================调用encodeCU(),即开始编码一个CTU,深度为0
}

 

3、encodeCU()函数

 

        encodeCU()函数位于entropy.cpp中,其主要功能包括CU的继续分割及其递归调用、对预测信息、编码系数与预测模式等的编码,并最后调用finishCU()函数完成比特流的写入,对应的代码分析如下:

 /* encode a CU block recursively */
void Entropy::encodeCU(const CUData& ctu, const CUGeom& cuGeom, uint32_t absPartIdx, uint32_t depth, bool& bEncodeDQP)
{
const Slice* slice = ctu.m_slice;

int cuSplitFlag = !(cuGeom.flags & CUGeom::LEAF); //CU分割标志
int cuUnsplitFlag = !(cuGeom.flags & CUGeom::SPLIT_MANDATORY); //CU不分割标志

//CU不继续分割时
if (!cuUnsplitFlag)
{
uint32_t qNumParts = cuGeom.numPartitions >> 2;
if (depth == slice->m_pps->maxCuDQPDepth && slice->m_pps->bUseDQP) //获取深度
bEncodeDQP = true;
for (uint32_t qIdx = 0; qIdx < 4; ++qIdx, absPartIdx += qNumParts) //当前深度的4个CU
{
const CUGeom& childGeom = *(&cuGeom + cuGeom.childOffset + qIdx);
if (childGeom.flags & CUGeom::PRESENT)
encodeCU(ctu, childGeom, absPartIdx, depth + 1, bEncodeDQP); //递归调用
}
return;
}

//CU继续分割时
if (cuSplitFlag)
codeSplitFlag(ctu, absPartIdx, depth); //===============================编码分割标志(此处包含CU的继续分割过程)

if (depth < ctu.m_cuDepth[absPartIdx] && depth < g_maxCUDepth) //分割后的深度若符合要求,则递归调用
{
uint32_t qNumParts = cuGeom.numPartitions >> 2;
if (depth == slice->m_pps->maxCuDQPDepth && slice->m_pps->bUseDQP) //获取深度,由于继续分割,此处的深度应该比之前的深度+1
bEncodeDQP = true;
for (uint32_t qIdx = 0; qIdx < 4; ++qIdx, absPartIdx += qNumParts) //分割成的4个更小CU,即分割后的4个CU
{
const CUGeom& childGeom = *(&cuGeom + cuGeom.childOffset + qIdx);
encodeCU(ctu, childGeom, absPartIdx, depth + 1, bEncodeDQP); //递归调用
}
return;
}

if (depth <= slice->m_pps->maxCuDQPDepth && slice->m_pps->bUseDQP)
bEncodeDQP = true;

if (slice->m_pps->bTransquantBypassEnabled)
codeCUTransquantBypassFlag(ctu.m_tqBypass[absPartIdx]); //===============================编码忽略的CU变换、量化的标志

if (!slice->isIntra()) //若不为帧内,即不是I帧
{
codeSkipFlag(ctu, absPartIdx); //===============================编码Skip标志
if (ctu.isSkipped(absPartIdx)) //若为Skip模式
{
codeMergeIndex(ctu, absPartIdx); //===============================编码合并索引
finishCU(ctu, absPartIdx, depth, bEncodeDQP); //===============================调用finishCU(),完成Bit的最终写入
return;
}
codePredMode(ctu.m_predMode[absPartIdx]); //===============================编码预测模式
}

codePartSize(ctu, absPartIdx, depth); //===============================编码分割大小

// prediction Info ( Intra : direction mode, Inter : Mv, reference idx )
codePredInfo(ctu, absPartIdx); //===============================编码预测信息

uint32_t tuDepthRange[2];
if (ctu.isIntra(absPartIdx))
ctu.getIntraTUQtDepthRange(tuDepthRange, absPartIdx);
else
ctu.getInterTUQtDepthRange(tuDepthRange, absPartIdx);

// Encode Coefficients, allow codeCoeff() to modify bEncodeDQP
codeCoeff(ctu, absPartIdx, bEncodeDQP, tuDepthRange); //===============================编码系数

// --- write terminating bit ---
finishCU(ctu, absPartIdx, depth, bEncodeDQP); //===============================调用finishCU(),完成Bit的最终写入
}

 

4、finishCU()函数

 

        finishCU()函数位于entropy.cpp中,其主要功能是完成比特流的写入及其临界条件的判断等,对应的代码分析如下:

 /* finish encoding a cu and handle end-of-slice conditions */
void Entropy::finishCU(const CUData& ctu, uint32_t absPartIdx, uint32_t depth, bool bCodeDQP)
{
const Slice* slice = ctu.m_slice;
uint32_t realEndAddress = slice->m_endCUAddr; //真正的结束地址
uint32_t cuAddr = ctu.getSCUAddr() + absPartIdx; //CU的地址
X265_CHECK(realEndAddress == slice->realEndAddress(slice->m_endCUAddr), "real end address expected\n");

uint32_t granularityMask = g_maxCUSize - 1;
uint32_t cuSize = 1 << ctu.m_log2CUSize[absPartIdx];
uint32_t rpelx = ctu.m_cuPelX + g_zscanToPelX[absPartIdx] + cuSize;
uint32_t bpely = ctu.m_cuPelY + g_zscanToPelY[absPartIdx] + cuSize;
bool granularityBoundary = (((rpelx & granularityMask) == 0 || (rpelx == slice->m_sps->picWidthInLumaSamples )) &&
((bpely & granularityMask) == 0 || (bpely == slice->m_sps->picHeightInLumaSamples)));

if (slice->m_pps->bUseDQP)
const_cast<CUData&>(ctu).setQPSubParts(bCodeDQP ? ctu.getRefQP(absPartIdx) : ctu.m_qp[absPartIdx], absPartIdx, depth);

if (granularityBoundary)
{
// Encode slice finish
bool bTerminateSlice = false; //初始化bTerminateSlice为false
if (cuAddr + (NUM_4x4_PARTITIONS >> (depth << 1)) == realEndAddress) //若条件成立,则Slice结束
bTerminateSlice = true;

// The 1-terminating bit is added to all streams, so don't add it here when it's 1.
if (!bTerminateSlice)
encodeBinTrm(0); //======================Encode terminating bin

if (!m_bitIf)
resetBits(); // TODO: most likely unnecessary
}
}

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息