经过了前面一段时间的研究,现在大致将这第一个宏块SAO由码流到语法元素值的解析过程完整整理一下。这里没有太多原理部分,更多的像是一篇流水账一样,聊作记录。
在代码中,我们首先查看一下解析完条带头数据后,当前NAL中带解析的码流。还是看我们一直使用的这个demo序列的编码结果,码流中正式用语解析条带数据的值如:
206 103 162 107 167 87 243 112 29 35 44 3 245 69 197 199 130 168 75 91 169 13 159 38 44 174 148 159 37 172 43 217 73 169 33 74 88 146 195 80 196 231 214 59 238 104 42 137 56 252 115 11 102 195 85 4 47 66 181 232 246 222 92 26 84 152 189 7 62 243 15 16 231 158 235 4 90 252 47 41 204 119 249 104 7 178 137 231 213 62 100 80 122 148 87 35 46 139 55 177 40 227 175 3 129 248 70 5 219 190 208 79 169 200 205 61 36 197 240 51 42 243 229 201 62 49 207 2 104 181 68 53 25 98 248 54 26 176 237 220 224 72 115 168 55 148 140 140 197 203 62 189 25 189 183 69 172 218 189 37 249 242 225 138 233 55 202 66 0 136 113 212 253 71 92 157 212 121 84 53 48 69 117 229 187 241 18 60 42 35 101 161 142 2 247 106 43 25 92 141 181 219 34 71 6 98 42 245 84 140 62 252 245 190 24 ......
以上是当前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,该结构体的定义为:
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]数组,定义如下:
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参数进行初始化:
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,解析过程中读取该段码流中第三个字符‘162’; 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 )函数,该函数读取码流中第四个字符‘107’,解析结果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,解析过程中读取码流中下一个字符‘167’; 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时读取下一个字符‘87’;pSaoParam->saoLcuParam[2][0].offset[3]
= -2;
调用parseSaoUflc(5,
uiSymbol )函数,解析结果pSaoParam->saoLcuParam[2][0].subTypeIdx = 10;
至此,TDecSbac::parseSaoOneLcuInterleaving()对第一个CTU的SAO部分解析完成。