x265探索与研究(十):encodeSlice()函数、encodeCTU()函数、encodeCU()函数与finishCU()函数分析-阿里云开发者社区

开发者社区> 方瑞东> 正文

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

 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
[实变函数]2.1 度量空间 (metric space), $n$ 维 Euclidean 空间
1 回忆:    $$\bex    \lim_{n\to\infty}a_n=a\lra \forall\ \ve>0,\ \exists\ N,\ \forall\ n\geq N,\mbox{ 有 }|a_n-a|
626 0
xcode反汇编调试iOS模拟器程序(六)函数出入口处的处理与局部变量
引用第二节的例子: 函数的入口处,通常都是把esp的值传给ebp保存,然后下面的操作以ebp为基准做偏移量引用。因为esp作为栈指针,push和pop都会自动修改其值,所以用ebp可以不受影响。
708 0
xcode反汇编调试iOS模拟器程序(五)调试objc_msgSend函数
反汇编调试objective-c,遇到最多的就是objc_msgSend这函数了,本节主要讲讲它的实现以及调试过程的一些技巧。 以UIWebView为例子,看看它在loadRequest时做了什么。
1054 0
[实变函数]4.4 依测度收敛
1 以前学过 $$\bex \mbox{ 点态收敛},\quad \mbox{ 一致收敛}, \eex$$      本节将用测度引进另外一种收敛概念---``依测度收敛'': $$\beex \bea &\quad (f_k\ra f)\ (\mbox{菲赫金哥尔茨的记号})\\ &\lra \...
821 0
7.4 匿名函数和高阶函数
1、匿名函数:没有名字的函数 def sum(s, y):     return x * y m = lambda x, y: x * y print(m) print(m(4, 5)) 2、sorted() 高阶函数 对字典进行排序 mm=dict(a=1,c=10,b=4,d=9) for i in mm:     print(i) for j in mm.
667 0
xcode反汇编调试iOS模拟器程序(三)查看Objective-C函数与参数
在Objective-C函数的入口处(第一行)加断点,可用esp指针来探查参数。 以esp为基址,往后的偏移分别是: 0:函数执行完毕后的返回地址(不是返回值的地址哦) 4:对象实例的指针,即self指针 8:selector,实际是一个...
999 0
strcmp函数实现及分析
最近看C,看到strcmp函数,对它的实现原型不很清楚,于是到网上搜。网上算法一大堆,看了很多代码后自己做了一下总结  strcmp函数是C/C++中基本的函数,它对两个字符串进行比较,然后返回比较结果,函数形式如下:int strcmp(const char* str1, const char* str2); 其中str1和str2可以是字符串常量或者字符串变量,返回值为整形。
1418 0
+关注
方瑞东
关注视频处理,如视频编码、视频云服务等。
158
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载