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) |
} |
從圖中可以看出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種開頭分別為:
再根據H265的NALU型別定義分析,
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;
}