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

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

首先来介绍下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 |
+------------ - +---------------- - +

其语法如下表中的定义:

image.png

从图中可以看出hHEVC的nal包结构与h264有明显的不同,hevc加入了nal所在的时间层的ID,取去除了nal_ref_idc,此信息合并到了naltype中

通常情况下F为0,layerid为0, TID为1。
H265 帧类型判断:
和264的&0x1f不同。265是 :

int type = (code & 0x7E)>>1;

在文件中查找00 00 00 01NALU头,发现在有6种开头分别为:

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

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

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

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

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

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

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

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   // Current name in the spec: TSA_R  
    
  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 // Current name in the spec: RADL_R  
    
  NAL_UNIT_CODED_SLICE_RASL_N,    // 8  
  NAL_UNIT_CODED_SLICE_TFD,       // 9 // Current name in the spec: RASL_R  
  
  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   // Current name in the spec: BLA_W_LP  
NAL_UNIT_CODED_SLICE_BLA,       // 16   // Current name in the spec: BLA_W_LP  
  NAL_UNIT_CODED_SLICE_BLANT,     // 17   // Current name in the spec: BLA_W_DLP  
  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,  
};  

二. SPS解析

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;
     }
     //...
     return true;
 }

4、测试代码

int _tmain(int argc, _TCHAR* argv[])
 
{
    vc_params_t params = { 0 };
 
    BYTE Sps[41] = { 0X42,0X01,0X01,0X01,0X60,0X00,0X00,0X03,0X00,0X80,0X00,0X00,0X03,0X00,0X00,
        0X03,0X00,0X5D,0XA0,0X02,0X80, 0X80,0X2D,0X16,0X59,0X5E,0X49,0X32,0XB8,0X04,0X00,0X00,0X03,
        0X00,0X04,0X00,0X00,0X03,0X00,0X64,0X20 };
 
    ParseSequenceParameterSet(Sps, 41, params);
 
    printf("%d-%d-%d\n", params.width, params.height, params.level);
    system("pause");
    return 0;
}

————————————————
版权声明:本文为CSDN博主「lcyw」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/machh/article/details/72190321

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容