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 } }