JSVM解码器数据流操作代码阅读笔记一 联系客服

发布时间 : 星期六 文章JSVM解码器数据流操作代码阅读笔记一更新完毕开始阅读234888c408a1284ac85043a7

JSVM解码器数据流操作代码阅读笔记一

从数据读入到解码的整体流程大致如下:

1 由DecodeFrame作为入口,处理每一个Frame的数据

2 对于每一个Frame的数据,处理对应的AccessUnit中的每一个NALUnit数据

3 对于每一个NAL包,处理每个DependenceLayer的SliceData(程序定义的最大

DependenceLayer数为8,在当前码流无Spatial Scalability特性的情况下,每个NAL包只有一个DependanceLayer)

4 对于每一个DependenceLayer的SliceData,共需要经历如下过程:

4.1 InitSlice,绝大多数数据结构的空间申请以及部分数据结构内容的重置

4.2 ParseSlice,最为核心的是解码部分数据结构的初始化及反cabac获得残差系数 4.3 DecodeSlice,每一个Slice数据的解码工作,包含反量化、按照预测结果和宏块

类型重建出图像帧

4.4 FinishSlice,核心工作是去块效应环路滤波,另外包含为后续Slice解码的预处理

很显然,从整体逻辑上来讲,解码时按照AccessUnit->NALUnit->SliceData->MB的顺序进行,每个AccessUnit包含了一帧的信息,在当前码流中表现为某一帧的基本层和两个增强层的信息;NALUnit中的SliceData在不考虑空域可伸缩特性的情况下表现为基本层或任何一个增强层的信息;MB则是对应于当前slice中的每一个宏块(在当前CIF格式视频中,每一个slice共有396个宏块)。解码过程就是如此的循环过程,直到所有帧解码完毕。

前1-3步骤从码流中读取了诸多关键语法信息,利用VLC或者哥伦布码方法反解出诸如SPS、PPS等先期核心信息,为4的具体解码做了初步准备,从整体解码器的逻辑框架上来讲十分重要,但因为前1-3步信息量有限,涉及的数据结构有限,占用的CPU资源也非常有限(不到总时间的2%),目前解码器优化的主要工作主要是针对于4这一步骤进行。以下内容会详细阐述第4步骤中的有关重要细节。

第4步骤中涉及的函数和数据结构错综复杂,主要功能模块可以从GOPDecode.cpp中找到。在LayerDecoder::processSliceData函数中,4个最主要函数是initSlice、parseSlice、decodeSlice以及finishSlice。代码阅读工作目前主要瞄准的是initSlice和parseSlice以及decodeSlice的正式对宏块解码前的数据流跟踪,至于decodeSlice的核心以及finishSlice,前者包含了解码模式和运动补偿等核心细节,后者包括了环路滤波的细节,这两部分数据流是下一步跟踪的主要内容。

initSlice函数:

首先来看initSlice函数,该函数代表了每个slice会对解码涉及到的核心数据结构的空间申请工作,需要注意的是LayerDecoder::xInitSPS与LayerDecoder::xInitDPBUnit两个函数,前者利用已解码获得的SliceHeader信息申请空间和初始化相关结构,后者主要负责DPB部分的数据结构的空间申请和初始化工作。在LayerDecoder::xInitSPS中,最重要的是LayerDecoder::xDeleteData()函数与LayerDecoder:: xCreateData函数,前者清空一切存储frame的临时数组,后者对于解码遇到的需要使用的数据结构申请空间。在LayerDecoder:: xCreateData中,这一部分的初始化工作有以下子步骤组成:

1) frame::create函数初始化frame中的YuvbufferCtrl类型的结构m_cFullPelYuvBuffer,

frame::init则为frame中的YuvbufferCtrl类型的结构m_cFullPelYuvBuffer中的Xpel类型数组申请空间,并设置Y、Cb、Cr数据区的初始指针,create函数和init函数成对出现,每个frame具备的YuvbufferCtrl类型结构负责针对整个frame中YUV数据的访问和操作控制。

2) LayerDecoder::m_pcBaselayerCtrl在此处首先创建MbDataCtrl类型的自身对象,然后

调用MbDataCtrl::init函数利用SPS进行初始化。m_pcBaselayerCtrl中管理了用于motionCompensation的数据结构m_apcMbMvdData[]数组以及m_apcMbMotionData[]数组:前者为MbMvData类型,用于记录前后向MV的差值;后者为MbMotionData类型,用于记录前后向MV的数值。对于每个Slice,由于buffer会重置(删除+重新申请),MbDataCtrl类型在此处表现为MbDataCtrl::xDeleteData()函数(重置MbDataCtrl)+ MbDataCtrl::xCreateData()函数(重新申请空间),申请的空间包括MbTransformCoeffs类型数组m_pcMbTCoeffs(含有每一个当前slice中的Mb的残差系数数据)、MbData类型的数组m_pcMbData(数组的每一项都包含指向pcMbTCoeffs中对应项的指针)、以及m_apcMbMotionData、m_apcMbMvdData(包含每个Mb对应的MV与MVd)。注意到申请时,会让uiSize++(当前Slice中的Mb数+1),因为在最后完成了Mb对应数据结构的空间申请后,m_pcMbData[uiSize-1].m_pcMbTCoeffs会清理最后的系数count字段。init函数最后将当前DataCtrl管理的所有Mb数目初始化成当前Slice中的Mb数目m_uiSize,并初始化m_cDPBFPBuffer与m_cILDBFPBuffer,最后置位m_bInitDone为true。 3) 之后还对updateWeight Array和writebuffer m_cDownConvert初始化

4) 创建了StatusMap m_pacMbStatus,用来记录每个Mb状态,是否初始化完成、是否

解码完成等。

LayerDecoder::xInitDPBUnit函数完成对于DPB结构的空间申请工作,DPB结构存放多个图像帧数据,用于支持参考帧选择、B帧解码以及帧缓冲等等。在CurrenDPBUnit::xCreateData函数用来申请空间。CurrDPBUnit类型对象含有变量m_pcControlData用来管理Pic中所有宏块的数据结构,m_pcControlData包含一个MbDataCtrl类型对象m_pcMbDataCtrl作为成员,m_pcMbDataCtrl用来控制当前slice中每个Mb解码的关键数据,如之前所叙述的它包含一个MbTransformCoeffs类型数组,用来存储当前slice中所有宏块的预测、变换系数和level值,用指针m_pcMbTCoeff存放,另外还有mbStride、Qp、m_pcMbDataAccess等重要内容,其中m_pcMbDataAccess作为访问当前宏块和周边宏块关系、数据的最重要通路。和之前一样,调用MbDataCtrl::init函数利用SPS完成初始化。CurrDPBUnit类型对象含有MbDataCtrl类型变量m_pcMbDataBaseLayer,该变量功能等同于m_pcMbDataCtrl的功能,作用是为控制和管理baseLayer的关键数据。在这一切完毕之后,xInitDPBUnit的主要工作就基本结束了。需要提及的是,对于LayerDecoder类型变量来说,拥有的DPB功能成员不仅仅是CurrDPB,还有专门为interLayer prediction准备的DPB功能成员CurrILDPB。对于baseLayer的意义、分析和数据操作过程以及两种不同的DPB的意义、分析和数据操作过程在parseSlice以及后续部分会进一步深入阐释。

结束了以上两个主要模块之后,initSlice的主要操作就全部完成了。