H265 Nalu类型判断及 sps 数据解析

H265 Nalu类型判断及 sps 数据解析

  一,Nalu解析

首先来介绍下h265(HEVC)nal单元头,与h264的nal层相比,h265的nal unit header有两个字节构成,如下图所示 :

0                   1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| F | Type | LayerId | TID |
+———— – +—————- – +

其语法如下表中的定义:

 

nal_unit_header( ) {

Descriptor

     forbidden_zero_bit

f(1)

     nal_unit_type

u(6)

     nuh_reserved_zero_6bits

u(6)

     nuh_temporal_id_plus1

u(3)

}

 
Nalu Type的定义:

enum NalUnitType

{

NAL_UNIT_CODED_SLICE_TRAIL_N = 0,   // 0

NAL_UNIT_CODED_SLICE_TRAIL_R,   // 1

NAL_UNIT_CODED_SLICE_TSA_N,  // 2

NAL_UNIT_CODED_SLICE_TLA,       // 3

NAL_UNIT_CODED_SLICE_STSA_N,    // 4

NAL_UNIT_CODED_SLICE_STSA_R,    // 5

NAL_UNIT_CODED_SLICE_RADL_N,  // 6

NAL_UNIT_CODED_SLICE_DLP,       // 7

NAL_UNIT_CODED_SLICE_RASL_N,    // 8

NAL_UNIT_CODED_SLICE_TFD,       // 9

NAL_UNIT_RESERVED_10,

NAL_UNIT_RESERVED_11,

NAL_UNIT_RESERVED_12,

NAL_UNIT_RESERVED_13,

NAL_UNIT_RESERVED_14,

NAL_UNIT_RESERVED_15, NAL_UNIT_CODED_SLICE_BLA,    // 16

NAL_UNIT_CODED_SLICE_BLA,       // 16

NAL_UNIT_CODED_SLICE_BLANT,     // 17

NAL_UNIT_CODED_SLICE_BLA_N_LP,  // 18

NAL_UNIT_CODED_SLICE_IDR,       // 19  // Current name in the spec: IDR_W_DLP

NAL_UNIT_CODED_SLICE_IDR_N_LP,  // 20

NAL_UNIT_CODED_SLICE_CRA,       // 21

NAL_UNIT_RESERVED_22,

NAL_UNIT_RESERVED_23,

NAL_UNIT_RESERVED_24,

NAL_UNIT_RESERVED_25,

NAL_UNIT_RESERVED_26,

NAL_UNIT_RESERVED_27,

NAL_UNIT_RESERVED_28,

NAL_UNIT_RESERVED_29,

NAL_UNIT_RESERVED_30,

NAL_UNIT_RESERVED_31,

NAL_UNIT_VPS,                   // 32  

NAL_UNIT_SPS,                   // 33  

NAL_UNIT_PPS,                   // 34  

NAL_UNIT_ACCESS_UNIT_DELIMITER, // 35

NAL_UNIT_EOS,                   // 36

NAL_UNIT_EOB,                   // 37

NAL_UNIT_FILLER_DATA,           // 38

NAL_UNIT_SEI,                   // 39 Prefix SEI

NAL_UNIT_SEI_SUFFIX,            // 40 Suffix SEI

NAL_UNIT_RESERVED_41,

NAL_UNIT_RESERVED_42,

NAL_UNIT_RESERVED_43,

NAL_UNIT_RESERVED_44,

NAL_UNIT_RESERVED_45,

NAL_UNIT_RESERVED_46,

NAL_UNIT_RESERVED_47,

NAL_UNIT_UNSPECIFIED_48,

NAL_UNIT_UNSPECIFIED_49,

NAL_UNIT_UNSPECIFIED_50,

NAL_UNIT_UNSPECIFIED_51,

NAL_UNIT_UNSPECIFIED_52,

NAL_UNIT_UNSPECIFIED_53,

NAL_UNIT_UNSPECIFIED_54,

NAL_UNIT_UNSPECIFIED_55,

NAL_UNIT_UNSPECIFIED_56,

NAL_UNIT_UNSPECIFIED_57,

NAL_UNIT_UNSPECIFIED_58,

NAL_UNIT_UNSPECIFIED_59,

NAL_UNIT_UNSPECIFIED_60,

NAL_UNIT_UNSPECIFIED_61,

NAL_UNIT_UNSPECIFIED_62,

NAL_UNIT_UNSPECIFIED_63,

NAL_UNIT_INVALID,

};

通过录制H265的ES流,进行分析NALU头,发现在有6种开头分别为:

 1) 00 00 00 01 40 01

 2) 00 00 00 01 42 01

 3) 00 00 00 01 44 01

 4) 00 00 00 01 4E 01

 5) 00 00 00 01 26 01

 6) 00 00 00 01 02 01

再根据H265的NALU类型定义分析,

       00 00 00 01 40 01  的nuh_unit_type的值为32, 语义为视频参数集

       00 00 00 01 42 01  的nuh_unit_type的值为33, 语义为序列参数集

       00 00 00 01 44 01  的nuh_unit_type的值为34, 语义为图像参数集

       00 00 00 01 4E 01  的nuh_unit_type的值为39, 语义为补充增强信息

       00 00 00 01 26 01  的nuh_unit_type的值为19, 语义为可能有RADL图像的IDR图像的SS编码数据

       00 00 00 01 02 01  的nuh_unit_type的值为1, 语义为被参考的后置图像,且非TSA、非STSA的SS编码数据

      在编码过程中,从编码器获取码流的时候,1、2、3、4、5是在一帧数据当中。相当于H264的I帧。

二,SPS解析

 

一段HEVC码流可能包含一个或者多个编码视频序列CVS,每个CVS由一个随机接入点开始,即 IDR/BLA/CRA。在H.264和HEVC中,序列参数集SPS包含该CVS中所有slice需要的信息。SPS的内容大致可以分为几个部分:

1、自引ID;

2、解码相关信息,如档次级别、分辨率、子层数等;

3、某档次中的功能开关标识及该功能的参数;

4、对结构和变换系数编码灵活性的限制信息;

5、时域可分级信息;

6、VUI。

以下是对每一个语法元素的讨论解释:

1、sps_video_parameter_set_id :指定了当前活动的VPS的ID号,当前例子的取值为0,这也与前文的VPS解析的结果一直;

2、sps_max_sub_layers_minus1 : 该值+1表示引用该SPS的CVS所包含的最大时域子层数,取值范围0-6;本例取值为0,即只有1个时域子层;

3、sps_temporal_id_nesting_flag :标识时域可分级中的帧间预测参考帧的限制信息;此处该值取0【好像与标准文档里写的有点矛盾啊……】;

4、sps_seq_parameter_set_id :【其实在这个参数之前码流中还有很多数据没有解释清楚,留到以后看吧】本SPS的ID值,此处取0;

5、chroma_format_idc:色度采样格式,此处取值为1,代表采用4:2:0格式;

6、separate_colour_plane_flag :这个参数是4:4:4格式专用的,在本例中不存在;

7、pic_width_in_luma_samples和pic_height_in_luma_samples :图像的分辨率信息,用指数哥伦布编码,本例取176*144;

8、conformance_window_flag :指示一致窗口裁剪偏移参数conformance cropping window offset parameters的信息;此处取值为1,表示后续几位为conf_win_left_offset、conf_win_right_offset、conf_win_top_offset、conf_win_bottom_offset等参数;

9、bit_depth_luma_minus8 :指定了亮度矩阵的比特深度以及亮度量化参数范围偏移量;此处取0,表示每个亮度像素用8为表示,QpBdOffset参数为0;

10、bit_depth_chroma_minus8 :与bit_depth_luma_minus8类似,只不过是针对色度的;

11、log2_max_pic_order_cnt_lsb_minus4 :负责计算变量MaxPicOrderCntLsb的值【这个值是干嘛的……】;

12、sps_sub_layer_ordering_info_present_flag :时域子层顺序标识开关,该值取1,表示后续几位分别是sps_max_dec_pic_buffering、sps_num_reorder_pics、sps_max_latency_increase等参数;

13、log2_min_coding_block_size_minus3 :用于计算最小亮度CB的尺寸,此处取0;

14、log2_diff_max_min_coding_block_size :用于计算最大最小亮度CB尺寸的差值,此处取3;

15、log2_min_transform_block_size_minus2:用于计算最小TB尺寸,此处取0;

16、log2_diff_max_min_transform_block_size :用于计算最大最小TB尺寸的差值,此处取3;

17、max_transform_hierarchy_depth_inter :帧间模式CB中TB的最大层级深度,此处为2;

18、max_transform_hierarchy_depth_intra ::帧内模式CB中TB的最大层级深度,此处为2;

19、scaling_list_enabled_flag:标识是否在变换系数量化中使用量化列表,此处取0;

20、amp_enabled_flag :标识是否使用不对称运动划分,此处为1;

21、sample_adaptive_offset_enabled_flag :标识是否使用SAO,此处为1;

22、pcm_enabled_flag :标识是否使用PCM,此处为0,即不实用PCM数据;

23、num_short_term_ref_pic_sets :指示SPS中short_term_ref_pic_set( )这个结构的数目,此处为1;

24、long_term_ref_pics_present_flag :指示帧间预测中是否使用长期参考帧,此处为1,即使用长期参考帧;

25、sps_temporal_mvp_enable_flag :标识CVS中非IDR帧的条带头中是否包含slice_temporal_mvp_enabled_flag标识,此处为1,即含有;

26、sps_strong_intra_smoothing_enable_flag :标识平滑滤波过程中是否使用双线性差值方法,此处为1,即使用;

27、vui_parameters_present_flag :标识是否有


以下是解析代码

1、重新定义类型

 

typedef unsigned char   uint8;

typedef unsigned short uint16;

typedef unsigned longuint32;
typedef unsigned __int64uint64;
typedef signed charint8;
typedef signed shortint16;
typedef signed longint32;
typedef signed __int64int64;

2、定义Sps 需要的相关参数

struct vc_params_t

{

   LONG width,height;

   DWORD profile, level;

   DWORD nal_length_size;

   void clear()

   {

memset(this, 0, sizeof(*this));

   }

};

3、定义网络抽象层Nal类 

 

 class NALBitstream

{

public:

NALBitstream() : m_data(NULL), m_len(0), m_idx(0), m_bits(0), m_byte(0), m_zeros(0)

{

};

NALBitstream(void * data, int len)

{

   Init(data, len);

};

void Init(void * data, int len)

{

   m_data = (LPBYTE)data;

   m_len = len;

m_idx = 0;

   m_bits = 0;

   m_byte = 0;

   m_zeros = 0;

};


BYTE
GetBYTE()

{

   if ( m_idx >= m_len )

return 0;

   BYTE b = m_data[m_idx++];

   if ( b == 0 )

   {

    m_zeros++;

    if ( (m_idx < m_len) && (m_zeros == 2) && (m_data[m_idx] == 0x03) )

{

m_idx++;

m_zeros=0;

}

   }

   else  m_zeros = 0;


return
b;

};

UINT32 GetBit()

{

   if (m_bits == 0)

   {

m_byte = GetBYTE();

m_bits = 8;

   }


m_bits
;

   return (m_byte >> m_bits) & 0x1;

};

UINT32 GetWord(int bits)

{

   UINT32 u = 0;

   while ( bits > 0 )

   {

    u <<= 1;

    u |= GetBit();

    bits;

   }

   return u;

};

UINT32 GetUE()

{

   int zeros = 0;

   while (m_idx < m_len && GetBit() == 0 )
       zeros++;

   return GetWord(zeros) + ((1 << zeros) 1);

};

INT32 GetSE()

{

   UINT32 UE = GetUE();

   bool positive = UE & 1;

   INT32 SE = (UE + 1) >> 1;

   if ( !positive )

SE = SE;

   return SE;

};

private:

   LPBYTE m_data;

   int m_len;
   int m_idx;

   int m_bits;

   BYTE m_byte;

   int m_zeros;

};

bool  ParseSequenceParameterSet(BYTE* data,int size, vc_params_t& params)

{

   if (size < 20)

    return false;

 

   NALBitstream bs(data, size);

   // seq_parameter_set_rbsp()

   bs.GetWord(4);// sps_video_parameter_set_id

   int sps_max_sub_layers_minus1 = bs.GetWord(3);

   if (sps_max_sub_layers_minus1 > 6)

    return false;


bs
.GetWord(1);

   bs.GetWord(2);

   bs.GetWord(1);

   params.profile = bs.GetWord(5);

   bs.GetWord(32);//

   bs.GetWord(1);//

   bs.GetWord(1);//

   bs.GetWord(1);//

   bs.GetWord(1);//  

   bs.GetWord(44);//

   params.level   = bs.GetWord(8);// general_level_idc

   uint8 sub_layer_profile_present_flag[6] = {0};

   uint8 sub_layer_level_present_flag[6]   = {0};

   for (int i = 0; i < sps_max_sub_layers_minus1; i++)
{

       sub_layer_profile_present_flag[i]= bs.GetWord(1);

sub_layer_level_present_flag[i]= bs.GetWord(1);

   }

   if (sps_max_sub_layers_minus1 > 0)

   {

       for (int i = sps_max_sub_layers_minus1; i < 8; i++)

uint8 reserved_zero_2bits = bs.GetWord(2);

   }

   for (int i = 0; i < sps_max_sub_layers_minus1; i++)

   {

       if (sub_layer_profile_present_flag[i])
       {

bs.GetWord(2);

bs.GetWord(1);

bs.GetWord(5);/

bs.GetWord(32);

bs.GetWord(1);

bs.GetWord(1);

bs.GetWord(1);

bs.GetWord(1);

bs.GetWord(44);

}

if (sub_layer_level_present_flag[i])

bs.GetWord(8);// sub_layer_level_idc[i]    

   }

   uint32 sps_seq_parameter_set_id= bs.GetUE();  

   if (sps_seq_parameter_set_id > 15)

    return false;

   

   uint32 chroma_format_idc = bs.GetUE();

   if (sps_seq_parameter_set_id > 3)

    return false;

   if (chroma_format_idc == 3)

    bs.GetWord(1);//  

   params.width  = bs.GetUE(); // pic_width_in_luma_samples

   params.height  = bs.GetUE(); // pic_height_in_luma_samples

   if (bs.GetWord(1))
   {

bs.GetUE();

bs.GetUE();

bs.GetUE();

bs.GetUE();

   }

   uint32 bit_depth_luma_minus8= bs.GetUE();

   uint32 bit_depth_chroma_minus8= bs.GetUE();

   if (bit_depth_luma_minus8 != bit_depth_chroma_minus8)

    return false;

//…

}

4、测试代码

 

H265 Nalu类型判断及 sps 数据解析

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/2439.html

(1)

相关推荐

发表回复

登录后才能评论