svc解码学习笔记

前言

本文记录对H.264标准中解码相关的内容和OpenH264解码器相关的内容,用于学习笔记

H.264标准下载地址:

  1. H.264 (08/21),仅英语
  2. H.264 (03/05),版本较老,有中文版

OpenH264项目地址:

OpenH264

标准学习

概念记录

通用概念

帧:一帧即一张图像,包括1个亮度矩阵和2个色度矩阵。

Slice:看官方中文版,翻译为条带;也有些翻译叫片。一个Slice包含了一个图像帧的全部或者部分数据,独立进行编解码。主要是防止误码的扩散用。

宏块:一个宏块包括一个16x16样本点的亮度块和两个对应的色度块。在YUV4:2:0的情况下,是一个16x16的亮度块+8x8的色度块

预测:由于图像信息之间存在相似性,在接收端,解码器可以通过指定方式预测图像信号。预测出的信号一般和实际信号有差异,这种差异被称之为残差。如果解码器知道残差(通过传输残差获取),那么解码器就可以通过残差进行信号的重建。

帧内预测:H.264的帧内预测,即利用图像空域的冗余,使用当前像素的邻近像素值预测像素的方式。

帧间预测:H.264的帧间预测,即利用图像时域的冗余,使用相邻帧的图像信号形成预测。在不同帧中,相似的宏块可能会发生移动,因此通常需要为每一个宏块定义运动矢量mv(x,y)。

分解与变换:图像中的大部分像素相似度很高,只有小部分像素具有丰富的细节,因此,图像中的低频信号多,高频信号少;将图像从空域变换到变换域,就会产生很多统计冗余,方便进行压缩编码。在H.264中一般使用离散余弦变换(DCT),其示例如下:

量化:量化是一种阶梯函数,将连续信号或者大量的离散信号处理为有限组合的电平。在H.264中通过量化步长控制量化的精度。

I帧,P帧和B帧:I帧即只使用帧内预测的帧;P帧可以使用帧内预测,也可以使用前向的帧作为参考进行帧间预测;B帧可以同时参考前向和后向的帧进行预测

VCL:视频编码层

NAL:网络提取层

SPS:参数序列集

PPS:图像序列集

GOP:一组连续的图像,开头为IDR帧。

SVC专有的概念

层:具有相同空域和质量域可伸缩行的一组NAL的集合

基本层:空域和质量域id为0

增强层:空域和质量域任一id大于0

层间预测:利用多层编码在空域上的冗余,使用基本层图像作为增强层图像的参考的图像的编码方式

层间帧内预测:使用基本层重建图像的上采样作为增强层图像的预测值

层间运动预测:使用基本层图像的参考索引,运动矢量,宏块类型,参考帧序号等的推导作为增强层的相应数值

层间残差预测:使用基本层图像的残差信号的上采样作为增强层的预测残差

帧内预测:相较AVC标准,SVC的帧内预测包括了层间帧内预测

EI Slice,EP Slice,EB Slice:增强层的slice,对应nalu type = 20。

解码流程

H.264包含VCL层和NAL层,NAL层包裹着VCL层;解码H.264从解析NAL层开始。H.264标准对于解码过程中的计算有明确规定,并且要求对同一码流不同的解码器具有相同的结果(在编码器方面则完全没有规定,只规定需要输出满足要求的码流)。

整个解码过程的大致系统图如下:

AVC解码系统

总的来说,解码的基本流程由以下几个步骤组成:

  1. 解析NAL –> 熵解码 –> 重排序 –> 反量化 –> 反变换 –> 残差数据
  2. 参考图像/帧内宏块 –> 预测数据
  3. 预测数据 + 残差数据 –> 去块滤波 –> 重建图像

NAL解析

H.264设计NAL层,专门用于网络传输,其基本单元是NALU(NAL Unit)。每一个NAL单元都包括1个字节的NAL头部和剩下的RBSP部分,根据NAL类型的不同,RBSP的内容有所不同;在特定NAL类型下,在NAL头部后一般会紧接数个字节的NAL头部扩展,然后才是RBSP内容,例如SVC扩展下类型14和20会有3字节的SVC扩展头部。NAL层组成示意图如下:

NAL头部标注了RBSP的类型,主要划分为VCL和non-VCL两种,在AVC当中NAL类型1-5就是VCL单元,其他是non-VCL单元,在SVC扩展中添加了NAL类型14和20,其中14是non-VCL单元,20是VCL单元。每种RBSP根据类型有专门的解析语法。在目前实际使用中,包括SVC标准在内,比较常见的NAL类型是1(非IDR帧基本都是1,暂时没见过2、3、4),5(IDR帧),6(SEI),7(SPS),8(PPS),14(Prefix NAL),15(subset SPS),20(scalable extension)。

NAL Header的结构如下:

SVC扩展头部的结构如下:

NAL 解析的流程如下:

non-VCL解析

本节涉及NAL类型7(SPS),8(PPS),14(Prefix NAL unit),15 (Subset SPS);暂时不讨论SEI信息的解析。

non-VCL的NAL单元分为多种,SPS和PPS是解码必须的参数集;Prefix NAL单元携带了下一个NAL单元的可伸缩信息,应当被视为和下一个NAL单元为同一个。

Prefix NAL unit解析

prefix nal unit具有3个bytes的svc扩展头,其后接对应的RBSP。如图:

![](https://nyamori.oss-cn-shanghai.aliyuncs.com/img/SVC的prefix nal的rbsp.png)

参数集解析

SPS序列参数集

sps的内容主要是seq_parameter_set_data,位于标准7.3.2.1.1一节。

PPS图像参数集

ps的内容主要是pic_parameter_set_rbsp,位于标准7.3.2.2一节。

Subset SPS

subset SPS在SVC当中,就是增强层的SPS扩展,除了SPS以外,还携带了对应的扩展。其RBSP如图所示:

VCL解析

本节涉及NAL类型1,5,20。由于没有特殊的排布结构,这三种类型首先都是对Slice进行解析。

Slice也分为Slice的头部和实际的数据,Slice头部保存了全局的信息,用于在实际的宏块数据解析的时候使用。如图所示:

在H.264标准中,第8章规定了AVC解码中需要处理的过程,附录G.8章规定了SVC解码过程中在AVC解码的基础上新增的流程,下面进行一些摘录:

Slice解析过程

POC计算

POC即图像顺序号用于标识图像的播放顺序,同时用于在帧间预测时,标记参考图像的初始顺序。每一帧都有自己的POC,一个编码帧有两个图像顺序号,称为TopFieldOrderCnt和BottomFieldOrderCnt。这两个序号标明了图像的相对图像顺序,这个顺序相对于上一个IDR帧的第一个输出场。

因此可以理解为,对IDR帧,其POC对应的顺序号应该为0。当解析非IDR图像时,AVC使用pic_order_cnt_type(取值为0,1,2)去标识POC的计算方法,流程如下:

MbToSliceGroupMap的计算

在进行Slice解码之前,需要根据PPS和当前的Slice Header计算MbToSliceGroupMap。

这一节主要需要以下参数:

  • num_slice_groups_minus1
  • sclice_groups_map_type
  • slice_groups_change_direction_flag

当num_slice_groups_minus1 = 0时,所有宏块属于同一个组,否则,根据以上参数查表进行计算,即可按照标准8.2.2.1 - 8.2.2.7的扫描方式获取MbToSliceGroupMap。

参考列表的计算

在解码P Slice和B slice等使用帧间预测的slice时,需要进行参考列表的计算。

参考图像分为用frame_num标记的短期参考图像和长期参考帧图像索引标记。下面以P Slice为例,进行说明。

P Slice图像的参考图像列表为RefPicList0,在初始化完成并修改后,参考列表中的总条数为num_ref_idx_l0_active_minus1 + 1。

图像编号的计算

这个处理需要计算以下变量:

  • FrameNum
  • FrameNumWrap
  • PicNum
  • LongTermFrameIdx
  • LongTermPicNum

计算过程如下:

  1. FrameNum = frame_num (来自短期参考图像列表的slice header)
  2. FrameNumWarp = (FrameNum > 当前图像的frame_num)? FrameNum - MaxFrameNum : FrameNum
  3. PicNum = FrameNumWrap
  4. LongTermPicNum = LongTermFrameIdx
RefPicList0的初始化
  1. 排序RefPicList0,使得短期参考帧的序号比长期参考帧的小
  2. 短期参考帧按照PicNum递减
  3. 长期参考帧按照LongTermPicNum递增
参考帧重排序

产生初始化的参考列表后,需要根据slice_header中解析的值进行重排序。流程如图:

已解码图像的标记过程

解码完成的图像,需要存储起来作为后续解码的参考图像。当nal_ref_idc = 0,解码出的图像不会作为参考图像,无须标记;当nal_ref_idc > 0, 图像成为参考图像时,需要进行标记,被标记为短期参考帧或者长期参考帧。其流程图如下:

宏块解析过程

宏块是H.264编码的基本单元,对宏块进行预测,主要分为帧内预测和帧间预测。

宏块的主要句法属性摘录如下:

  • mb_type,宏块切割方式
  • mb_pred,宏块预测类型
  • mb_sub_pred,子宏块的预测类型
  • coded_block_pattern,经常缩写为cbp,指出8x8子块的编码系数
  • mb_qp_delta:量化系数偏移量

帧内预测

  • I_PCM模式,直接恢复像素值
  • 其他AVC预测模式:Intra_16x16, Intra8x8, Intra_4x4
  • 在SVC启用时,帧内预测需要判断base_mode_flag,当base_mode_flag = 1时,进入层间帧内预测。此时多出一种IntraBL类型。

帧间预测

帧间预测模式使用运动补偿技术进行预测,总体流程如下:

其中,主要步骤是 运动矢量分量和参考索引推导 以及 帧间预测样点解码两部分。

运动矢量分量和参考索引推导

运动矢量的推导主要还是要参考宏块类型。

帧间预测样点解码

根据标准预测即可。

DCT反变换和反量化

通过本节的操作,可以获取到实际图像和预测图像之间的残差矩阵。

去块滤波

解码器在完成图像重建之后,通过去块滤波消除边界效应。

SVC上采样

SVC在启用层间预测之后,在三种预测方式中,分别需要进行基本层图像的上采样,运动矢量的上采样和残差的上采样。这些内容被规定在标准的G8.6节。

层间运动预测的推导

层间帧内预测的上采样

这里贴一下用于上采样的16抽头滤波器的矩阵:

层间残差预测的上采样

当前块类型是帧间块,并且residual_prediction_flag = 1,此时存在层间残差预测。通过获取相应的8x8子宏块的残差信号,进行双线性滤波器的上采样,就可以作为增强层宏块的预测残差信号。

OpenH264解码器学习

OpenH264项目提供AVC解码能力并支持不带层间预测的空域SVC码流单层输入进行解码。

主要涉及的文件

解码器的核心内容位于codec/decoder/core/src目录下,主要包括以下文件:

  • au_parser.cpp
  • bit_stream.cpp
  • cabac_decoder.cpp
  • deblocking.cpp //去块滤波
  • decode_mb_aux.cpp
  • decoder_core.cpp //解码核心流程
  • decoder.cpp
  • decoder_data_tables.cpp
  • decode_slice.cpp //slice级别的解码
  • error_concealment.cpp //差错隐藏
  • fmo.cpp
  • get_intra_predictor.cpp
  • manage_dec_ref.cpp
  • memmgr_nal_unit.cpp
  • mv_pred.cpp //运动预测
  • parse_mb_syn_cabac.cpp
  • parse_mb_syn_cavlc.cpp
  • pic_queue.cpp
  • rec_mb.cpp
  • wels_decoder_thread.cpp

整体解码流程

OpenH264项目在编译后,自带一个h264dec可执行程序,本节从该程序入手学习通用的解码循环。

解码器调用循环

h264dec解码的调用如图所示:

OpenH264的解码器API设计的比较简单,主要就是遵循创建 –> 初始化 –> 解码 –> 解初始化 –> 销毁的流程进行调用。在解码上,提供了DecodeFrameNoDelay()和DecodeFrame2()等多种接口,但是最后调用到的是DecodeFrame2()。

DecodeFrame2()内部流程

DecodeFrame2()是对DecodeFrame2WithCtx()的一个简单封装,后者的定义如下:

1
2
3
4
5
6
DECODING_STATE CWelsDecoder::DecodeFrame2WithCtx (
PWelsDecoderContext pDecContext, //解码器句柄
const unsigned char* kpSrc, //输入流
const int kiSrcLen, //输入的长度
unsigned char** ppDst, //解码后的数据指针
SBufferInfo* pDstInfo); //输出的解码数据的信息

这个函数支持单线程解码也支持多线程解码,两者流程略有区别,以单线程解码为例,其处理流程如下:

整个流程也比较简单,真正的解码实现位于WelsDecodeBs(),这个函数是解码核心的入口函数。

WelsDecodeBs()内部流程

本函数的主要作用有以下几点:

  1. 拷贝输入的比特流到内部缓存
  2. 解析内部缓存的比特流,并构建对应的access unit单元,完成解码

其中,拷贝的过程和解析的过程是交织进行的,其流程图如下:

图中解析NALU头部和非VCL单元的解析,按照标准规定的语法解析即可。NALU头部会解析1(不带svc扩展的nalu)或者4(带svc扩展的nalu type 14和type 20),然后根据NALU的类型,对内部的相应结构进行一定的初始化。同时,对于VCL数据,会解析Slice Header并存储,并完成POC计算、宏块映射的生成、参考帧相关的计算、参考图像的标记等解码步骤,这些都是按照标准文档进行计算的。非VCL的解析则包括SPS,PPS和Subset SPS按照标准的解析,并缓存相应数据到内部对应的结构当中。由图可以看到,解码的关键步骤在于被称为AU的单元的建立,其包含了解码过程,加上前后的准备和收尾工作,关键过程为一下三个:

  • 检查之前解码的情况,对应函数CheckAndFinishLastPic()
  • AU的建立,对应函数ConstructAccessUnit()
  • 解码完成之后的更新,对应函数DecodeFinishUpdate()

CheckAndFinishLastPic()的处理

本函数主要完成了两个功能:

  1. 对之前解码状态的检查
  2. 错误隐藏处理

暂时对这块的处理没有深入研究。

ConstructAccessUnit()的处理

这个函数的调用比较简单,这里直接贴代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*
* ConstructAccessUnit
* construct an access unit for given input bitstream, maybe partial NAL Unit, one or more Units are involved to
* joint a collective access unit.
* parameter\
* buf: bitstream data buffer
* bit_len: size in bit length of data
* buf_len: size in byte length of data
* coded_au: mark an Access Unit decoding finished
* return:
* 0 - success; otherwise returned error_no defined in error_no.h
*/
int32_t ConstructAccessUnit (PWelsDecoderContext pCtx, uint8_t** ppDst, SBufferInfo* pDstInfo) {
int32_t iErr = ERR_NONE;
if (GetThreadCount (pCtx) <= 1) {
//初始化
iErr = InitConstructAccessUnit (pCtx, pDstInfo);
if (ERR_NONE != iErr) {
return iErr;
}
}
if (pCtx->pCabacDecEngine == NULL) {
pCtx->pCabacDecEngine = (SWelsCabacDecEngine*)pCtx->pMemAlign->WelsMallocz (sizeof (SWelsCabacDecEngine),
"pCtx->pCabacDecEngine");
WELS_VERIFY_RETURN_IF (ERR_INFO_OUT_OF_MEMORY, (NULL == pCtx->pCabacDecEngine))
}

//解码
iErr = DecodeCurrentAccessUnit (pCtx, ppDst, pDstInfo);

//结束
WelsDecodeAccessUnitEnd (pCtx);

if (ERR_NONE != iErr) {
WelsLog (& (pCtx->sLogCtx), WELS_LOG_DEBUG, "returned error from decoding:[0x%x]", iErr);
return iErr;
}

return ERR_NONE;
}

可以看到,主要调用的接口为以下三个:

  • InitConstructAccessUnit
  • DecodeCurrentAccessUnit
  • WelsDecodeAccessUnitEnd
AU的初始化和解码结束
  • 初始化部分,主要是有以下功能
    • 初始化AU部分参数
    • 检查参数集
    • 检查图像缓存的大小,并在必要时重分配
  • 解码结束,主要还需要做以下处理
    • 保存解码时候的nal,slice等的头部信息,供以后解码使用
    • 清空当前解码句柄中不需要的RBSP的内存
AU的解码

流程图如下:

DecodeFinishUpdate()的处理

本函数主要进行参数集(包括SPS,PPS和Subset SPS)和部分状态的更新。

WelsDecodeSlice()的处理

这个接口,根据Slice的类型分了6种处理,对slice内的宏块进行解码,包括IPB三种帧类型和cavlc/cabac两种熵编码方式。这里不一一描述,其通用流程是类似的。其流程大致如下:

重要数据结构

解码器句柄

这个结构是整个解码流程的核心数据结构,同时很多解码用的数据结构内容均为指向该结构的指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
typedef struct TagWelsDecoderContext {
SLogContext sLogCtx;
// Input
void*
pArgDec; // structured arguments for decoder, reserved here for extension in the future

SDataBuffer sRawData; //内部缓存的比特流
SDataBuffer sSavedData; //for parse only purpose

// Configuration
SDecodingParam* pParam; //解码参数
uint32_t uiCpuFlag; // CPU compatibility detected

VIDEO_BITSTREAM_TYPE eVideoType; //indicate the type of video to decide whether or not to do qp_delta error detection.
bool bHaveGotMemory; // global memory for decoder context related ever requested?

int32_t iImgWidthInPixel; // width of image in pixel reconstruction picture to be output
int32_t iImgHeightInPixel;// height of image in pixel reconstruction picture to be output
int32_t
iLastImgWidthInPixel; // width of image in last successful pixel reconstruction picture to be output
int32_t
iLastImgHeightInPixel;// height of image in last successful pixel reconstruction picture to be output
bool bFreezeOutput; // indicating current frame freezing. Default: true


// Derived common elements
SNalUnitHeader sCurNalHead;
EWelsSliceType eSliceType; // Slice type
bool bUsedAsRef; //flag as ref
int32_t iFrameNum;
int32_t iErrorCode; // error code return while decoding in case packets lost
SFmo sFmoList[MAX_PPS_COUNT]; // list for FMO storage
PFmo pFmo; // current fmo context after parsed slice_header
int32_t iActiveFmoNum; // active count number of fmo context in list

/*needed info by decode slice level and mb level*/
int32_t
iDecBlockOffsetArray[24]; // address talbe for sub 4x4 block in intra4x4_mb, so no need to caculta the address every time.

struct {
uint32_t* pMbType[LAYER_NUM_EXCHANGEABLE]; /* mb type */
int16_t (*pMv[LAYER_NUM_EXCHANGEABLE][LIST_A])[MB_BLOCK4x4_NUM][MV_A]; //[LAYER_NUM_EXCHANGEABLE MB_BLOCK4x4_NUM*]
int8_t (*pRefIndex[LAYER_NUM_EXCHANGEABLE][LIST_A])[MB_BLOCK4x4_NUM];
int8_t (*pDirect[LAYER_NUM_EXCHANGEABLE])[MB_BLOCK4x4_NUM];
bool* pNoSubMbPartSizeLessThan8x8Flag[LAYER_NUM_EXCHANGEABLE];
bool* pTransformSize8x8Flag[LAYER_NUM_EXCHANGEABLE];
int8_t* pLumaQp[LAYER_NUM_EXCHANGEABLE]; /*mb luma_qp*/
int8_t (*pChromaQp[LAYER_NUM_EXCHANGEABLE])[2]; /*mb chroma_qp*/
int16_t (*pMvd[LAYER_NUM_EXCHANGEABLE][LIST_A])[MB_BLOCK4x4_NUM][MV_A]; //[LAYER_NUM_EXCHANGEABLE MB_BLOCK4x4_NUM*]
uint16_t* pCbfDc[LAYER_NUM_EXCHANGEABLE];
int8_t (*pNzc[LAYER_NUM_EXCHANGEABLE])[24];
int8_t (*pNzcRs[LAYER_NUM_EXCHANGEABLE])[24];
int16_t (*pScaledTCoeff[LAYER_NUM_EXCHANGEABLE])[MB_COEFF_LIST_SIZE]; /*need be aligned*/
int8_t (*pIntraPredMode[LAYER_NUM_EXCHANGEABLE])[8]; //0~3 top4x4 ; 4~6 left 4x4; 7 intra16x16
int8_t (*pIntra4x4FinalMode[LAYER_NUM_EXCHANGEABLE])[MB_BLOCK4x4_NUM];
uint8_t* pIntraNxNAvailFlag[LAYER_NUM_EXCHANGEABLE];
int8_t* pChromaPredMode[LAYER_NUM_EXCHANGEABLE];
int8_t* pCbp[LAYER_NUM_EXCHANGEABLE];
uint8_t (*pMotionPredFlag[LAYER_NUM_EXCHANGEABLE][LIST_A])[MB_PARTITION_SIZE]; // 8x8
uint32_t (*pSubMbType[LAYER_NUM_EXCHANGEABLE])[MB_SUB_PARTITION_SIZE];
int32_t* pSliceIdc[LAYER_NUM_EXCHANGEABLE]; // using int32_t for slice_idc
int8_t* pResidualPredFlag[LAYER_NUM_EXCHANGEABLE];
int8_t* pInterPredictionDoneFlag[LAYER_NUM_EXCHANGEABLE];
bool* pMbCorrectlyDecodedFlag[LAYER_NUM_EXCHANGEABLE];
bool* pMbRefConcealedFlag[LAYER_NUM_EXCHANGEABLE];
uint32_t iMbWidth;
uint32_t iMbHeight;
} sMb; //这块的内存在实际解码中由其他结构去引用


// reconstruction picture
PPicture pDec; //pointer to current picture being reconstructed

PPicture
pTempDec; //pointer to temp decoder picture to be used only for Bi Prediction.

// reference pictures
SRefPic sRefPic;
SRefPic sTmpRefPic; //used to temporarily save RefPic for next active thread
SVlcTable* pVlcTable; // vlc table

SBitStringAux sBs;
int32_t iMaxBsBufferSizeInByte; //actual memory size for BS buffer

/* Global memory external */
SWelsDecoderSpsPpsCTX sSpsPpsCtx;
bool bHasNewSps;

SPosOffset sFrameCrop;

PSliceHeader pSliceHeader;

PPicBuff pPicBuff; // Initially allocated memory for pictures which are used in decoding.
int32_t iPicQueueNumber;

PAccessUnit pAccessUnitList; // current access unit list to be performed
//PSps pActiveLayerSps[MAX_LAYER_NUM];
PSps pSps; // used by current AU
PPps pPps; // used by current AU
// Memory for pAccessUnitList is dynamically held till decoder destruction.
PDqLayer
pCurDqLayer; // current DQ layer representation, also carry reference base layer if applicable
PDqLayer pDqLayersList[LAYER_NUM_EXCHANGEABLE]; // DQ layers list with memory allocated
PNalUnit pNalCur; // point to current NAL Nnit
uint8_t uiNalRefIdc; // NalRefIdc for easy access;
int32_t iPicWidthReq; // picture width have requested the memory
int32_t iPicHeightReq; // picture height have requested the memory

uint8_t uiTargetDqId; // maximal DQ ID in current access unit, meaning target layer ID
//bool bAvcBasedFlag; // For decoding bitstream:
bool bEndOfStreamFlag; // Flag on end of stream requested by external application layer
bool bInstantDecFlag; // Flag for no-delay decoding
bool bInitialDqLayersMem; // dq layers related memory is available?

bool bOnlyOneLayerInCurAuFlag; //only one layer in current AU: 1

bool bReferenceLostAtT0Flag;
int32_t iTotalNumMbRec; //record current number of decoded MB
#ifdef LONG_TERM_REF
bool bParamSetsLostFlag; //sps or pps do not exist or not correct

bool
bCurAuContainLtrMarkSeFlag; //current AU has the LTR marking syntax element, mark the previous frame or self
int32_t iFrameNumOfAuMarkedLtr; //if bCurAuContainLtrMarkSeFlag==true, SHOULD set this variable

uint16_t uiCurIdrPicId;
#endif
bool bNewSeqBegin;
bool bNextNewSeqBegin;

//for Parse only
bool bFramePending;
bool bFrameFinish;
int32_t iNalNum;
int32_t iMaxNalNum; //permitted max NAL num stored in parser
SSpsBsInfo sSpsBsInfo [MAX_SPS_COUNT];
SSpsBsInfo sSubsetSpsBsInfo [MAX_PPS_COUNT];
SPpsBsInfo sPpsBsInfo [MAX_PPS_COUNT];
SParserBsInfo* pParserBsInfo;

//PPicture pPreviousDecodedPictureInDpb; //pointer to previously decoded picture in DPB for error concealment
PGetIntraPredFunc pGetI16x16LumaPredFunc[7]; //h264_predict_copy_16x16;
PGetIntraPredFunc pGetI4x4LumaPredFunc[14]; // h264_predict_4x4_t
PGetIntraPredFunc pGetIChromaPredFunc[7]; // h264_predict_8x8_t
PIdctResAddPredFunc pIdctResAddPredFunc;
PIdctFourResAddPredFunc pIdctFourResAddPredFunc;
SMcFunc sMcFunc;
//Transform8x8
PGetIntraPred8x8Func pGetI8x8LumaPredFunc[14];
PIdctResAddPredFunc pIdctResAddPredFunc8x8;

//For error concealment
SCopyFunc sCopyFunc;
/* For Deblocking */
SDeblockingFunc sDeblockingFunc;
SExpandPicFunc sExpandPicFunc;

/* For Block */
SBlockFunc sBlockFunc;

int32_t iCurSeqIntervalTargetDependId;
int32_t iCurSeqIntervalMaxPicWidth;
int32_t iCurSeqIntervalMaxPicHeight;

PWelsFillNeighborMbInfoIntra4x4Func pFillInfoCacheIntraNxNFunc;
PWelsMapNeighToSample pMapNxNNeighToSampleFunc;
PWelsMap16NeighToSample pMap16x16NeighToSampleFunc;

//feedback whether or not have VCL in current AU, and the temporal ID
int32_t iFeedbackVclNalInAu;
int32_t iFeedbackTidInAu;
int32_t iFeedbackNalRefIdc;

bool bAuReadyFlag; // true: one au is ready for decoding; false: default value

bool bPrintFrameErrorTraceFlag; //true: can print info for upper layer
int32_t iIgnoredErrorInfoPacketCount; //store the packet number with error decoding info
//trace handle
void* pTraceHandle;

PWelsLastDecPicInfo pLastDecPicInfo;

SWelsCabacCtx sWelsCabacContexts[4][WELS_QP_MAX + 1][WELS_CONTEXT_COUNT];
bool bCabacInited;
SWelsCabacCtx pCabacCtx[WELS_CONTEXT_COUNT];
PWelsCabacDecEngine pCabacDecEngine;
double dDecTime;
SDecoderStatistics* pDecoderStatistics; // For real time debugging
int32_t iMbEcedNum;
int32_t iMbEcedPropNum;
int32_t iMbNum;
bool bMbRefConcealed;
bool bRPLRError;
int32_t iECMVs[16][2];
PPicture pECRefPic[16];
unsigned long long uiTimeStamp;
uint32_t uiDecodingTimeStamp; //represent relative decoding time stamps
// To support scaling list HP
uint16_t pDequant_coeff_buffer4x4[6][52][16];
uint16_t pDequant_coeff_buffer8x8[6][52][64];
uint16_t (*pDequant_coeff4x4[6])[16];// 4x4 sclaing list value pointer
uint16_t (*pDequant_coeff8x8[6])[64];//64 residual coeff ,with 6 kinds of residual type, 52 qp level
int iDequantCoeffPpsid;//When a new pps actived, reinitialised the scaling list value
bool bDequantCoeff4x4Init;
bool bUseScalingList;
CMemoryAlign* pMemAlign;
void* pThreadCtx;
void* pLastThreadCtx;
WELS_MUTEX* pCsDecoder;
int16_t lastReadyHeightOffset[LIST_A][MAX_REF_PIC_COUNT]; //last ready reference MB offset
PPictInfo pPictInfoList;
PPictReoderingStatus pPictReoderingStatus;
} SWelsDecoderContext, *PWelsDecoderContext;

解码参数

这里只可以指定目标空域,后面可以新增指定目标时域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @brief SVC Decoding Parameters, reserved here and potential applicable in the future
*/
typedef struct TagSVCDecodingParam {
char* pFileNameRestructed; ///< file name of reconstructed frame used for PSNR calculation based debug

unsigned int uiCpuLoad; ///< CPU load
unsigned char uiTargetDqLayer; ///< setting target dq layer id

ERROR_CON_IDC eEcActiveIdc; ///< whether active error concealment feature in decoder
bool bParseOnly; ///< decoder for parse only, no reconstruction. When it is true, SPS/PPS size should not exceed SPS_PPS_BS_SIZE (128). Otherwise, it will return error info

SVideoProperty sVideoProperty; ///< video stream property
} SDecodingParam, *PDecodingParam;

图像结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
struct SPicture {
/************************************payload data*********************************/
uint8_t* pBuffer[4]; // pointer to the first allocated byte, basical offset of buffer, dimension:
uint8_t* pData[4]; // pointer to picture planes respectively
int32_t iLinesize[4];// linesize of picture planes respectively used currently
int32_t iPlanes; // How many planes are introduced due to color space format?
// picture information

/*******************************from EC mv copy****************************/
bool bIdrFlag;

/*******************************from other standard syntax****************************/
/*from sps*/
int32_t iWidthInPixel; // picture width in pixel
int32_t iHeightInPixel;// picture height in pixel
/*from slice header*/
int32_t iFramePoc; // frame POC

/*******************************sef_definition for misc use****************************/
bool bUsedAsRef; //for ref pic management
bool bIsLongRef; // long term reference frame flag //for ref pic management
int8_t iRefCount;

bool bIsComplete; // indicate whether current picture is complete, not from EC
/*******************************for future use****************************/
uint8_t uiTemporalId;
uint8_t uiSpatialId;
uint8_t uiQualityId;

int32_t iFrameNum; // frame number //for ref pic management
int32_t iFrameWrapNum; // frame wrap number //for ref pic management
int32_t iLongTermFrameIdx; //id for long term ref pic
uint32_t uiLongTermPicNum; //long_term_pic_num

int32_t iSpsId; //against mosaic caused by cross-IDR interval reference.
int32_t iPpsId;
unsigned long long uiTimeStamp;
uint32_t uiDecodingTimeStamp; //represent relative decoding time stamps
int32_t iPicBuffIdx;
EWelsSliceType eSliceType;
bool bIsUngroupedMultiSlice; //multi-slice picture with each each slice group contains one slice.
bool bNewSeqBegin;
int32_t iMbEcedNum;
int32_t iMbEcedPropNum;
int32_t iMbNum;

bool* pMbCorrectlyDecodedFlag;
int8_t (*pNzc)[24];
uint32_t* pMbType; // mb type used for direct mode
int16_t (*pMv[LIST_A])[MB_BLOCK4x4_NUM][MV_A]; // used for direct mode
int8_t (*pRefIndex[LIST_A])[MB_BLOCK4x4_NUM]; //used for direct mode
struct SPicture* pRefPic[LIST_A][17]; //ref pictures used for direct mode
SWelsDecEvent* pReadyEvent; //MB line ready event

};// "Picture" declaration is comflict with Mac system

typedef struct SPicture* PPicture;

AU结构

1
2
3
4
5
6
7
8
9
10
typedef struct TagAccessUnits {
PNalUnit* pNalUnitsList; // list of NAL Units pointer in this AU
uint32_t uiAvailUnitsNum; // Number of NAL Units available in each AU list based current bitstream,
uint32_t uiActualUnitsNum; // actual number of NAL units belong to current au
// While available number exceeds count size below, need realloc extra NAL Units for list space.
uint32_t uiCountUnitsNum; // Count size number of malloced NAL Units in each AU list
uint32_t uiStartPos;
uint32_t uiEndPos;
bool bCompletedAuFlag; // Indicate whether it is a completed AU
} SAccessUnit, *PAccessUnit;

层结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
typedef struct TagDqLayer       SDqLayer;
typedef SDqLayer* PDqLayer;
typedef struct TagLayerInfo {
SNalUnitHeaderExt sNalHeaderExt;
SSlice sSliceInLayer; // Here Slice identify to Frame on concept
PSubsetSps pSubsetSps; // current pSubsetSps used, memory alloc in external
PSps pSps; // current sps based avc used, memory alloc in external
PPps pPps; // current pps used
} SLayerInfo, *PLayerInfo;
/* Layer Representation */

struct TagDqLayer {
SLayerInfo sLayerInfo;

PBitStringAux pBitStringAux; // pointer to SBitStringAux
PFmo pFmo; // Current fmo context pointer used
/* 从这里开始下面的指针,真实的内存空间位于解码器句柄的sMb结构 */
uint32_t* pMbType;
int32_t* pSliceIdc; // using int32_t for slice_idc
int16_t (*pMv[LIST_A])[MB_BLOCK4x4_NUM][MV_A];
int16_t (*pMvd[LIST_A])[MB_BLOCK4x4_NUM][MV_A];
int8_t (*pRefIndex[LIST_A])[MB_BLOCK4x4_NUM];
int8_t (*pDirect)[MB_BLOCK4x4_NUM];
bool* pNoSubMbPartSizeLessThan8x8Flag;
bool* pTransformSize8x8Flag;
int8_t* pLumaQp;
int8_t (*pChromaQp)[2];
int8_t* pCbp;
uint16_t *pCbfDc;
int8_t (*pNzc)[24];
int8_t (*pNzcRs)[24];
int8_t* pResidualPredFlag;
int8_t* pInterPredictionDoneFlag;
bool* pMbCorrectlyDecodedFlag;
bool* pMbRefConcealedFlag;
int16_t (*pScaledTCoeff)[MB_COEFF_LIST_SIZE];
int8_t (*pIntraPredMode)[8]; //0~3 top4x4 ; 4~6 left 4x4; 7 intra16x16
int8_t (*pIntra4x4FinalMode)[MB_BLOCK4x4_NUM];
uint8_t *pIntraNxNAvailFlag;
int8_t* pChromaPredMode;
//uint8_t (*motion_pred_flag[LIST_A])[MB_PARTITION_SIZE]; // 8x8
uint32_t (*pSubMbType)[MB_SUB_PARTITION_SIZE];
int32_t iLumaStride;
int32_t iChromaStride;
uint8_t* pPred[3];
int32_t iMbX;
int32_t iMbY;
int32_t iMbXyIndex;
int32_t iMbWidth; // MB width of this picture, equal to sSps.iMbWidth
int32_t iMbHeight; // MB height of this picture, equal to sSps.iMbHeight;
/* 以上内容来自解码器句柄的sMb结构 */

/* Common syntax elements across all slices of a DQLayer */
int32_t iSliceIdcBackup;
uint32_t uiSpsId;
uint32_t uiPpsId;
uint32_t uiDisableInterLayerDeblockingFilterIdc;
int32_t iInterLayerSliceAlphaC0Offset;
int32_t iInterLayerSliceBetaOffset;
//SPosOffset sScaledRefLayer;
int32_t iSliceGroupChangeCycle;

PRefPicListReorderSyn pRefPicListReordering;
PPredWeightTabSyn pPredWeightTable;
PRefPicMarking pRefPicMarking; // Decoded reference picture marking syntaxs
PRefBasePicMarking pRefPicBaseMarking;

PPicture pRef; // reference picture pointer
PPicture pDec; // reconstruction picture pointer for layer

int16_t iColocMv[2][16][2]; //Colocated MV cache
int8_t iColocRefIndex[2][16]; //Colocated RefIndex cache
int8_t iColocIntra[16]; //Colocated Intra cache

bool bUseWeightPredictionFlag;
bool bUseWeightedBiPredIdc;
bool bStoreRefBasePicFlag; // iCurTid == 0 && iCurQid = 0 && bEncodeKeyPic = 1
bool bTCoeffLevelPredFlag;
bool bConstrainedIntraResamplingFlag;
uint8_t uiRefLayerDqId;
uint8_t uiRefLayerChromaPhaseXPlus1Flag;
uint8_t uiRefLayerChromaPhaseYPlus1;
uint8_t uiLayerDqId; // dq_id of current layer
bool bUseRefBasePicFlag; // whether reference pic or reference base pic is referred?
};

svc解码学习笔记
http://nyamori.icu/2022/11/04/svc解码学习笔记/
作者
Nyamori
发布于
2022年11月4日
许可协议