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

简介: <h1 style="text-align:center"><strong>x265<span style="font-family:宋体">探索与研究(十):</span><span style="font-family:Calibri">encodeSlice()</span><span style="font-family:宋体">函数、</span><span style="fon

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

 

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

 

 

 

1encodeSlice()函数

 

        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();
}

 

2encodeCTU()函数

 

        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
}

 

3encodeCU()函数

 

        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的最终写入
}

 

4finishCU()函数

 

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

 

目录
相关文章
|
1月前
|
存储 编译器 C++
13函数
13函数
11 0
|
19天前
|
程序员 C语言 Python
函数—C(上)
函数—C(上)
25 0
|
25天前
|
Java 测试技术 Python
为什么要用函数
在编程中,函数是一种重要的抽象工具,它使我们能够组织和复用代码,提高代码的可读性、可维护性和效率。函数允许我们将一段代码块封装起来,给它一个名字,并通过参数和返回值来与外部世界交互。下面,我们将深入探讨为什么要使用函数,并附上相应的代码示例。
15 1
|
6月前
|
程序员
函数
一、函数 函数是一段封装了特定功能的可重复使用的代码块。它接受输入参数,执行特定的操作,并返回一个结果。函数可以在程序中被多次调用,避免了重复编写相同的代码,提高了代码的复用性和可维护性。 函数通常具有以下几个特点: 1. 输入参数:函数可以接受零个或多个输入参数,用于传递数据给函数。输入参数可以是任意类型的数据,如整数、浮点数、字符串、数组等。函数可以使用输入参数来执行特定的操作。 2. 函数体:函数体是函数的核心部分,包含了函数要执行的操作。函数体是由一系列的语句组成的代码块,可以包含各种控制语句、变量声明、表达式等。函数体定义了函数的具体功能。 3. 返回值:函数可以返回一个结果给调用者
33 0
|
9月前
|
程序员 C语言 C++
函函函函函函函函函函函数——one
函函函函函函函函函函函数——one
56 0
|
Java vr&ar
函数那些题 — P1
函数那些题 — P1
函数那些题 — P1
|
SQL 分布式计算 关系型数据库
not_in函数致错
Not in 函数-致错 我们开发当中有些业务逻辑会用到not in()这个函数, 岗位角度:不管是后端开发还是大数据开发还是数据分析师… 技术角度:不管是Mysql、Hive、Maxcompute…
120 0
|
SQL
last函数
last函数
107 0
|
C语言
可变长参数函数
可变长参数函数
97 0