解析H265的SPS

分类: ffmpeg 发布时间: 2018-03-31 17:59

struct vc_params_t
{
LONG width, height;
DWORD profile, level;
DWORD nal_length_size;
void clear()
{
memset(this, 0, sizeof(*this));
}

};

 

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++];

// to avoid start-code emulation, a byte 0x03 is inserted
// after any 00 00 pair. Discard that here.
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()
{

// Exp-Golomb entropy coding: leading zeros, then a one, then
// the data bits. The number of leading zeros is the number of
// data bits, counting up from that number of 1s as the base.
// That is, if you see
//      0001010
// You have three leading zeros, so there are three data bits (010)
// counting up from a base of 111: thus 111 + 010 = 1001 = 9
int zeros = 0;
while (m_idx < m_len && GetBit() == 0) zeros++;
return GetWord(zeros) + ((1 << zeros) – 1);
};

INT32 GetSE()
{

// same as UE but signed.
// basically the unsigned numbers are used as codes to indicate signed numbers in pairs
// in increasing value. Thus the encoded values
//      0, 1, 2, 3, 4
// mean
//      0, 1, -1, 2, -2 etc
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;
};

enum NALUnitType {
NAL_TRAIL_N = 0,
NAL_TRAIL_R = 1,
NAL_TSA_N = 2,
NAL_TSA_R = 3,
NAL_STSA_N = 4,
NAL_STSA_R = 5,
NAL_RADL_N = 6,
NAL_RADL_R = 7,
NAL_RASL_N = 8,
NAL_RASL_R = 9,
NAL_BLA_W_LP = 16,
NAL_BLA_W_RADL = 17,
NAL_BLA_N_LP = 18,
NAL_IDR_W_RADL = 19,
NAL_IDR_N_LP = 20,
NAL_CRA_NUT = 21,
NAL_VPS = 32,
NAL_SPS = 33,
NAL_PPS = 34,
NAL_AUD = 35,
NAL_EOS_NUT = 36,
NAL_EOB_NUT = 37,
NAL_FD_NUT = 38,
NAL_SEI_PREFIX = 39,
NAL_SEI_SUFFIX = 40,
};

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

unsigned int code = -1;
int vps = 0, sps = 0, pps = 0, irap = 0;
int i;
for (i = 0; i < size – 1; i++)
{
code = (code << 8) + data[i];
if ((code & 0xffffff00) == 0x100)
{
char nal2 = data[i + 1];
int type = (code & 0x7E) >> 1;
if (code & 0x81)
// forbidden and reserved zero bits
return 0;
if (nal2 & 0xf8)
// reserved zero
return 0;
switch (type)
{
case NAL_VPS:
vps++;
break;
case NAL_SPS:
{
if (size < 20)
{
return false;
}

NALBitstream bs( data+i, size-i);

// seq_parameter_set_rbsp()
bs.GetWord(4);// sps_video_parameter_set_id
int sps_max_sub_layers_minus1 = bs.GetWord(3); // “The value of sps_max_sub_layers_minus1 shall be in the range of 0 to 6, inclusive.”
if (sps_max_sub_layers_minus1 > 6)
{
return false;
}
bs.GetWord(1);// sps_temporal_id_nesting_flag
// profile_tier_level( sps_max_sub_layers_minus1 )
{
bs.GetWord(2);// general_profile_space
bs.GetWord(1);// general_tier_flag
params.profile = bs.GetWord(5);// general_profile_idc
bs.GetWord(32);// general_profile_compatibility_flag[32]
bs.GetWord(1);// general_progressive_source_flag
bs.GetWord(1);// general_interlaced_source_flag
bs.GetWord(1);// general_non_packed_constraint_flag
bs.GetWord(1);// general_frame_only_constraint_flag
bs.GetWord(44);// general_reserved_zero_44bits
params.level = bs.GetWord(8);// general_level_idc
unsigned char sub_layer_profile_present_flag[6] = { 0 };
unsigned char  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++) {
unsigned char 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);// sub_layer_profile_space[i]
bs.GetWord(1);// sub_layer_tier_flag[i]
bs.GetWord(5);// sub_layer_profile_idc[i]
bs.GetWord(32);// sub_layer_profile_compatibility_flag[i][32]
bs.GetWord(1);// sub_layer_progressive_source_flag[i]
bs.GetWord(1);// sub_layer_interlaced_source_flag[i]
bs.GetWord(1);// sub_layer_non_packed_constraint_flag[i]
bs.GetWord(1);// sub_layer_frame_only_constraint_flag[i]
bs.GetWord(44);// sub_layer_reserved_zero_44bits[i]
}
if (sub_layer_level_present_flag[i]) {
bs.GetWord(8);// sub_layer_level_idc[i]
}
}
}
unsigned long  sps_seq_parameter_set_id = bs.GetUE(); // “The  value  of sps_seq_parameter_set_id shall be in the range of 0 to 15, inclusive.”
if (sps_seq_parameter_set_id > 15) {
return false;
}
unsigned long chroma_format_idc = bs.GetUE(); // “The value of chroma_format_idc shall be in the range of 0 to 3, inclusive.”
if (sps_seq_parameter_set_id > 3) {
return false;
}
if (chroma_format_idc == 3) {
bs.GetWord(1);// separate_colour_plane_flag
}
params.width = bs.GetUE(); // pic_width_in_luma_samples
params.height = bs.GetUE(); // pic_height_in_luma_samples
if (bs.GetWord(1)) {// conformance_window_flag
bs.GetUE(); // conf_win_left_offset
bs.GetUE(); // conf_win_right_offset
bs.GetUE(); // conf_win_top_offset
bs.GetUE(); // conf_win_bottom_offset
}
unsigned long bit_depth_luma_minus8 = bs.GetUE();
unsigned long  bit_depth_chroma_minus8 = bs.GetUE();
if (bit_depth_luma_minus8 != bit_depth_chroma_minus8) {
return false;
}
return true;
}
break;
case NAL_PPS:
pps++;
break;
case NAL_BLA_N_LP:
case NAL_BLA_W_LP:
case NAL_BLA_W_RADL:
case NAL_CRA_NUT:
case NAL_IDR_N_LP:
case NAL_IDR_W_RADL:
irap++;
break;
}
}
}

}

调用的地方
    vc_params_t params = { 0 };
bRet = ParseSequenceParameterSet((unsigned char *)encbuf, encdatalen, params );
if ( bRet )
{
width = params.width;
height = params.height;
}

扫描下方二维码,关注业余草微信公众号,回复“FFmpeg”关键词,获取 FFmpeg 视频教程!

关注公众号获取视频教程

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!