【HEVC学习与研究】28、第一帧第一个宏块的SAO部分完整解析结果

来源:互联网 发布:注册免费域名 编辑:程序博客网 时间:2024/05/29 08:49

经过了前面一段时间的研究,现在大致将这第一个宏块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在CODE上查看代码片派生到我的代码片
  1. struct SAOParam  
  2. {  
  3.   Bool       bSaoFlag[2];  
  4.   SAOQTPart* psSaoPart[3];  
  5.   Int        iMaxSplitLevel;  
  6.   Bool         oneUnitFlag[3];  
  7.   SaoLcuParam* saoLcuParam[3];  
  8.   Int          numCuInHeight;  
  9.   Int          numCuInWidth;  
  10.   ~SAOParam();  
  11. };  

在该结构中,TDecSbac::parseSaoOneLcuInterleaving()解析的内容赋予SAOParam类型saoLcuParam[3]数组,定义如下:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. typedef struct _SaoLcuParam  
  2. {  
  3.   Bool       mergeUpFlag;  
  4.   Bool       mergeLeftFlag;  
  5.   Int        typeIdx;  
  6.   Int        subTypeIdx;                  ///< indicates EO class or BO band position  
  7.   Int        offset[4];  
  8.   Int        partIdx;  
  9.   Int        partIdxTmp;  
  10.   Int        length;  
  11. } SaoLcuParam;  

TDecSbac::parseSaoOneLcuInterleaving()函数首先对pSaoParam参数进行初始化:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. for (Int iCompIdx=0; iCompIdx<3; iCompIdx++)  
  2. {  
  3.     pSaoParam->saoLcuParam[iCompIdx][iAddr].mergeUpFlag    = 0;  
  4.     pSaoParam->saoLcuParam[iCompIdx][iAddr].mergeLeftFlag  = 0;  
  5.     pSaoParam->saoLcuParam[iCompIdx][iAddr].subTypeIdx     = 0;  
  6.     pSaoParam->saoLcuParam[iCompIdx][iAddr].typeIdx        = -1;  
  7.     pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[0]     = 0;  
  8.     pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[1]     = 0;  
  9.     pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[2]     = 0;  
  10.     pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[3]     = 0;  
  11. }  

由于当前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部分解析完成。

0 0
原创粉丝点击