【HEVC学习与研究】28、第一帧第一个宏块的SAO部分完整解析结果
2014-12-16 22:29
435 查看
经过了前面一段时间的研究,现在大致将这第一个宏块SAO由码流到语法元素值的解析过程完整整理一下。这里没有太多原理部分,更多的像是一篇流水账一样,聊作记录。
在代码中,我们首先查看一下解析完条带头数据后,当前NAL中带解析的码流。还是看我们一直使用的这个demo序列的编码结果,码流中正式用语解析条带数据的值如:
(后面是第一个CTU的SAO参数部分二进制流中的数据)CE 67 A2 6B A7 57 (后面是CTU部分) F3 70 1D 23 2C 03 F5 45 C5 C7...
以上是当前NAL中带解析的数据,包含了slice中每一个CTU的SAO信息和Coding Quardtree信息。具体实现步骤如:
1、slice数据的解析由TDecTop::xDecodeSlice()执行,该函数调用TDecGop::decompressSlice()实现具体的工作。在该函数中调用TDecSbac::resetEntropy()函数,在末尾的TDecBinCABAC::start()函数中,码流中的前两个字符206和103赋予m_uiValue作为一个UInt值的低两位,m_uiValue的值为52839作为初值;
2、TDecGop::decompressSlice()调用TDecSlice::decompressSlice(),解析SAO的部分在TDecSbac::parseSaoOneLcuInterleaving()中实现。该函数中的一个参数为SAOParam结构体指针pSaoParam,该结构体的定义为:
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/9e12f1d3e499fc949c886e7c9e0484f9)
struct SAOParam
{
Bool bSaoFlag[2];
SAOQTPart* psSaoPart[3];
Int iMaxSplitLevel;
Bool oneUnitFlag[3];
SaoLcuParam* saoLcuParam[3];
Int numCuInHeight;
Int numCuInWidth;
~SAOParam();
};
在该结构中,TDecSbac::parseSaoOneLcuInterleaving()解析的内容赋予SAOParam类型saoLcuParam[3]数组,定义如下:
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/9e12f1d3e499fc949c886e7c9e0484f9)
typedef struct _SaoLcuParam
{
Bool mergeUpFlag;
Bool mergeLeftFlag;
Int typeIdx;
Int subTypeIdx; ///< indicates EO class or BO band position
Int offset[4];
Int partIdx;
Int partIdxTmp;
Int length;
} SaoLcuParam;
TDecSbac::parseSaoOneLcuInterleaving()函数首先对pSaoParam参数进行初始化:
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/9e12f1d3e499fc949c886e7c9e0484f9)
for (Int iCompIdx=0; iCompIdx<3; iCompIdx++)
{
pSaoParam->saoLcuParam[iCompIdx][iAddr].mergeUpFlag = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].mergeLeftFlag = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].subTypeIdx = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].typeIdx = -1;
pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[0] = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[1] = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[2] = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[3] = 0;
}
由于当前CTU是slice中的第一个CTU,不存在左侧和上方的元素,因此parseSaoMerge()函数将不被执行。随后在3次for循环中,调用方法为parseSaoOffset(&(pSaoParam->saoLcuParam[iCompIdx][iAddr]), iCompIdx)。
第一次循环:
调用parseSaoType(),解析结果pSaoParam->saoLcuParam[0][0].typeIdx = -1;pSaoParam->saoLcuParam[0][0].length = 0。
第二次循环:
调用parseSaoType(),解析结果pSaoParam->saoLcuParam[1][0].typeIdx = 4;pSaoParam->saoLcuParam[0][0].length = 4;
循环调用4次parseSaoMaxUvlc(),解析结果为:pSaoParam->saoLcuParam[1][0].offset[0] = 0; pSaoParam->saoLcuParam[1][0].offset[1] = 3,解析过程中读取该段码流中第三个字符‘A2’; pSaoParam->saoLcuParam[1][0].offset[2] = 1; pSaoParam->saoLcuParam[1][0].offset[3]
= 1;
针对四个offset值中的非零者,解码下一个bit,若为非0值,则该offset取反。修正后, pSaoParam->saoLcuParam[1][0].offset[3] = -1;
调用parseSaoUflc(5, uiSymbol )函数,该函数读取码流中第四个字符‘6B’,解析结果pSaoParam->saoLcuParam[1][0].subTypeIdx = 13;
第三次循环:
由于compIdx为2,因此不再调用parseSaoType(),pSaoParam->saoLcuParam[2][0].typeIdx = pSaoParam->saoLcuParam[1][0].typeIdx = 4;
循环调用4次parseSaoMaxUvlc(),解析结果为:pSaoParam->saoLcuParam[2][0].offset[0] = 0; pSaoParam->saoLcuParam[2][0].offset[1] = 1;
pSaoParam->saoLcuParam[2][0].offset[2] = 3,解析过程中读取码流中下一个字符‘A7’; pSaoParam->saoLcuParam[2][0].offset[3] = 2;
针对四个offset值中的非零者,解码下一个bit,若为非0值,则该offset取反。修正后, pSaoParam->saoLcuParam[1][0].offset[1] = -1; pSaoParam->saoLcuParam[2][0].offset[2] = 3,解析该bit时读取下一个字符‘57’;pSaoParam->saoLcuParam[2][0].offset[3] = -2;
调用parseSaoUflc(5, uiSymbol )函数,解析结果pSaoParam->saoLcuParam[2][0].subTypeIdx = 10;
至此,TDecSbac::parseSaoOneLcuInterleaving()对第一个CTU的SAO部分解析完成。
在代码中,我们首先查看一下解析完条带头数据后,当前NAL中带解析的码流。还是看我们一直使用的这个demo序列的编码结果,码流中正式用语解析条带数据的值如:
(后面是第一个CTU的SAO参数部分二进制流中的数据)CE 67 A2 6B A7 57 (后面是CTU部分) F3 70 1D 23 2C 03 F5 45 C5 C7...
以上是当前NAL中带解析的数据,包含了slice中每一个CTU的SAO信息和Coding Quardtree信息。具体实现步骤如:
1、slice数据的解析由TDecTop::xDecodeSlice()执行,该函数调用TDecGop::decompressSlice()实现具体的工作。在该函数中调用TDecSbac::resetEntropy()函数,在末尾的TDecBinCABAC::start()函数中,码流中的前两个字符206和103赋予m_uiValue作为一个UInt值的低两位,m_uiValue的值为52839作为初值;
2、TDecGop::decompressSlice()调用TDecSlice::decompressSlice(),解析SAO的部分在TDecSbac::parseSaoOneLcuInterleaving()中实现。该函数中的一个参数为SAOParam结构体指针pSaoParam,该结构体的定义为:
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
struct SAOParam
{
Bool bSaoFlag[2];
SAOQTPart* psSaoPart[3];
Int iMaxSplitLevel;
Bool oneUnitFlag[3];
SaoLcuParam* saoLcuParam[3];
Int numCuInHeight;
Int numCuInWidth;
~SAOParam();
};
在该结构中,TDecSbac::parseSaoOneLcuInterleaving()解析的内容赋予SAOParam类型saoLcuParam[3]数组,定义如下:
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
typedef struct _SaoLcuParam
{
Bool mergeUpFlag;
Bool mergeLeftFlag;
Int typeIdx;
Int subTypeIdx; ///< indicates EO class or BO band position
Int offset[4];
Int partIdx;
Int partIdxTmp;
Int length;
} SaoLcuParam;
TDecSbac::parseSaoOneLcuInterleaving()函数首先对pSaoParam参数进行初始化:
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
for (Int iCompIdx=0; iCompIdx<3; iCompIdx++)
{
pSaoParam->saoLcuParam[iCompIdx][iAddr].mergeUpFlag = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].mergeLeftFlag = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].subTypeIdx = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].typeIdx = -1;
pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[0] = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[1] = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[2] = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[3] = 0;
}
由于当前CTU是slice中的第一个CTU,不存在左侧和上方的元素,因此parseSaoMerge()函数将不被执行。随后在3次for循环中,调用方法为parseSaoOffset(&(pSaoParam->saoLcuParam[iCompIdx][iAddr]), iCompIdx)。
第一次循环:
调用parseSaoType(),解析结果pSaoParam->saoLcuParam[0][0].typeIdx = -1;pSaoParam->saoLcuParam[0][0].length = 0。
第二次循环:
调用parseSaoType(),解析结果pSaoParam->saoLcuParam[1][0].typeIdx = 4;pSaoParam->saoLcuParam[0][0].length = 4;
循环调用4次parseSaoMaxUvlc(),解析结果为:pSaoParam->saoLcuParam[1][0].offset[0] = 0; pSaoParam->saoLcuParam[1][0].offset[1] = 3,解析过程中读取该段码流中第三个字符‘A2’; pSaoParam->saoLcuParam[1][0].offset[2] = 1; pSaoParam->saoLcuParam[1][0].offset[3]
= 1;
针对四个offset值中的非零者,解码下一个bit,若为非0值,则该offset取反。修正后, pSaoParam->saoLcuParam[1][0].offset[3] = -1;
调用parseSaoUflc(5, uiSymbol )函数,该函数读取码流中第四个字符‘6B’,解析结果pSaoParam->saoLcuParam[1][0].subTypeIdx = 13;
第三次循环:
由于compIdx为2,因此不再调用parseSaoType(),pSaoParam->saoLcuParam[2][0].typeIdx = pSaoParam->saoLcuParam[1][0].typeIdx = 4;
循环调用4次parseSaoMaxUvlc(),解析结果为:pSaoParam->saoLcuParam[2][0].offset[0] = 0; pSaoParam->saoLcuParam[2][0].offset[1] = 1;
pSaoParam->saoLcuParam[2][0].offset[2] = 3,解析过程中读取码流中下一个字符‘A7’; pSaoParam->saoLcuParam[2][0].offset[3] = 2;
针对四个offset值中的非零者,解码下一个bit,若为非0值,则该offset取反。修正后, pSaoParam->saoLcuParam[1][0].offset[1] = -1; pSaoParam->saoLcuParam[2][0].offset[2] = 3,解析该bit时读取下一个字符‘57’;pSaoParam->saoLcuParam[2][0].offset[3] = -2;
调用parseSaoUflc(5, uiSymbol )函数,解析结果pSaoParam->saoLcuParam[2][0].subTypeIdx = 10;
至此,TDecSbac::parseSaoOneLcuInterleaving()对第一个CTU的SAO部分解析完成。
相关文章推荐
- 【HEVC学习与研究】28、第一帧第一个宏块的SAO部分完整解析结果
- 【HEVC学习与研究】27、CABAC解析语法元素SAO
- 【HEVC学习与研究】27、CABAC解析语法元素SAO
- 【HEVC学习与研究】14.HEVC解码中VPS参数集解析
- 【HEVC学习与研究】15、HEVC解码中的SPS解析
- HEVC学习与研究】11.HEVC参考解码器的设置及参数解析过程
- 【HEVC学习与研究】18.HEVC的条带头解析
- 【HEVC学习与研究】44、HEVC量化系数的解析——反量化过程
- 【HEVC学习与研究】6.HEVC综述(第二部分)
- 【HEVC学习与研究】46、HEVC参考代码中SAO的实现
- HEVC学习(十四) —— SAO函数解析之二
- HEVC学习(十六) —— SAO函数解析之四
- 【HEVC学习与研究】45、HEVC的自适应采样点补偿SAO
- 【HEVC学习与研究】37、HM编码器的基本结构2:帧内编码部分的代码骨架
- HEVC学习(十五) —— SAO函数解析之三
- 【HEVC学习与研究】16、HEVC中的PPS解析
- 【HEVC学习与研究】11.HEVC参考解码器的设置及参数解析过程
- 【HEVC学习与研究】30、解码Coding Unit数据