网上很多大佬对VVC的代码进行过分析,基本都是从编码端入手。
考虑到从解码端分析代码,一是更加简单(解码流程无需编码工具和编码参数的择优),二是可以配合Draft文本更好地理解视频编解码的流程(解码端也都包含预测、量化、环路滤波、熵解码等流程),所以我想从解码端入手分析一下VVC大致的流程。等到解码端代码分析完后,可以再从编码端深入分析一下。
本文是本系列的第一篇博客,内容是分析解码端将收到的二进制码流bin文件提取成一个个NALU的过程。
该系列相关博客为:
VVC/H.266代码阅读(VTM8.0)(一. NALU提取)
VVC/H.266代码阅读(VTM8.0)(二. non-VCLU解码)
VVC/H.266代码阅读(VTM8.0)(三. Slice到CTU的处理 )
VVC/H.266代码阅读(VTM8.0)(四. CU划分 )
VVC/H.266常见资源为:
VVC/H.266常见资源整理(提案地址、代码、资料等)
注:
- 本文分析的bin文件是利用VTM8.0的编码器,以All Intra配置(IBC 打开)编码100帧得到的二进制码流(TemporalSubsampleRatio: 8,实际编码 ⌈100 / 8⌉ = 13帧)。
- 解码用最简单的:-b str.bin -o dec.yuv
1. 入口函数是decmain.cpp 中的main()函数
(1) main函数先会打印若干信息,比如设备信息。
(2) 调用 DecAppCfg::parseCfg() 解析输入参数,如本文所用的bin文件的名字、重建YUV文件的名字等信息。
(3) 调用 DecApp::decode() 开启解码流程。
2. DecApp::decode()函数
(1) 首先构造了名为nalu的 InputNALUnit 类。
InputNALUnit nalu;
① InputNALUnit是NALUnit的子类,NALUnit类中包含NalUnitType、temporalId等相关信息、还含有判断当前NALU是不是VCLU和SliceNALU的成员函数isVcl() 、isSlice()。
② 此外,InputNALUnit多包含了名为m_Bitstream数据成员(InputBitstream类)。
③ InputBitstream类中的m_fifo数据成员以FIFO顺序存储当前NALU的Bytes数据。
class InputNALUnit : public NALUnit
{
private:
InputBitstream m_Bitstream;
……
};
struct NALUnit
{
NalUnitType m_nalUnitType; ///< nal_unit_type
uint32_t m_temporalId; ///< temporal_id
uint32_t m_nuhLayerId; ///< nuh_layer_id
uint32_t m_forbiddenZeroBit;
uint32_t m_nuhReservedZeroBit;
……
}
(2) 然后判断下一个NALU是不是新图像/AU的第一个NALU。
check if next NAL unit will be the first NAL unit from a new picture or access unit
bool bNewPicture = isNewPicture(&bitstreamFile, &bytestream);
bool bNewAccessUnit = bNewPicture && isNewAccessUnit( bNewPicture, &bitstreamFile, &bytestream );
bool DecApp::isNewPicture(ifstream *bitstreamFile, class InputByteStream *bytestream)
{
// cannot be a new picture if there haven't been any slices yet、
//该函数检查Declib::m_bFirstSliceInPicture变量,见下段代码。
if(m_cDecLib.getFirstSliceInPicture())
{
return false;
}
……
bool getFirstSliceInPicture () const
{
return m_bFirstSliceInPicture; }
Declib::m_bFirstSliceInPicture是一个bool值,该bool值默认为true。在DecSlice::decompressSlice()之后,该值会被设置为false,表示一个slice编码完毕。然后isNewPicture()函数会预先读取下一个NALU的NalUnitType,例如,如果下一个NALU是SPS、PPS等参数集,那就说明是新的图像的新NALU,返回True。
3. bNewPicture == false,进入NALU的提取环节,为本文的分析重点。否则进入第4步,完成该帧的后续处理和输出环节。
(1) 调用byteStreamNALUnit() 解析当前NALU。该函数核心调用_byteStreamNALUnit()。
// find next NAL

最低0.47元/天 解锁文章
2628





