前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >H265 Nalu类型判断及 sps 数据解析

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

作者头像
lcyw
发布2022-06-10 19:30:11
3.1K0
发布2022-06-10 19:30:11
举报
文章被收录于专栏:machh的专栏machh的专栏

一,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 需要的相关参数

代码语言:javascript
复制
struct vc_params_t

{

    LONG width,height;

    DWORD profile, level;

    DWORD nal_length_size;

    void clear()

    {

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

    }

};

3、定义网络抽象层Nal类

代码语言:javascript
复制
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、测试代码

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-05-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 音视频开发训练营 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档