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

 

目录
相关文章
|
Linux Perl
sed删除匹配正则表达式的行
sed删除匹配正则表达式的行
641 1
|
缓存 API 计算机视觉
OpenCV-实现天空变换(图像分割)
OpenCV-实现天空变换(图像分割)
434 0
|
Web App开发 监控 算法
详解 WebRTC 高音质低延时的背后 — AGC(自动增益控制)
本文将结合实例全面解析 WebRTC AGC 的基本框架,一起探索其基本原理、模式的差异、存在的问题以及优化方向。
详解 WebRTC 高音质低延时的背后 — AGC(自动增益控制)
|
存储 缓存 编解码
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码(一)
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码
436 0
|
编解码 弹性计算 Cloud Native
倚天ECS视频编解码之x264性能
在平头哥发布了首颗为云而生的 CPU 芯片倚天710之后,搭载倚天 710 的 ECS 实例表现出强劲的性能实力,在x264编解码场景下有着极高的性价比。
|
存储 编解码 缓存
x264代码剖析(十七):核心算法之熵编码(Entropy Encoding)
<h1 style="text-align:center"><strong>x264<span style="font-family:宋体">代码剖析(十七):核心算法之熵编码(Entropy Encoding)</span></strong></h1> <p align="center"><strong></strong></p> <p> </p> <p><span style="
4692 0
|
编解码 IDE 测试技术
HEVC编码之HM学习
HM是HEVC的官方标准测试模型,想要对HEVC进行了解,需要对HM源码进行一定解读才能够更加深入。
903 0
|
存储 网络协议 NoSQL
Dapr在Java中的实践 之 状态管理
状态管理(State Management)使用键值对作为存储机制,可以轻松的使长时运行、高可用的有状态服务和无状态服务共同运行在我们的服务中。
1353 0
Dapr在Java中的实践 之 状态管理
VirtualBox虚拟机如何选中“启用嵌套 VT-x/AMD-V”
VirtualBox虚拟机如何选中“启用嵌套 VT-x/AMD-V”
1604 0
VirtualBox虚拟机如何选中“启用嵌套 VT-x/AMD-V”
ValueError: not enough values to unpack (expected 3, got 2)
这个错误通常是因为在解包(unpacking)元组(tuple)时,元组中的元素数量与期望不符,导致无法将所有元素正确解包。 例如,在以下代码中,元组中只有两个元素,但我们尝试将其解包为三个变量:
967 0