之前做的SDP对于h264编码的视频没有做"a=fmtp"字段的解析,今天加上,主要是为了解析出视频的宽高信息。
RFC3984的8.2节对field做了介绍,这里只介绍sprop-parameter-sets字段的解码方法,因为我的主要目的是解析出视频宽高信息
该字段用BASE-64编码,因此,首先解码BASE-64,这里给出解码方法:
long CBase64::Decode(const char *pSrc, long srcLen, unsigned char *pDest, long destLen)
{
int i = 0;
int iCnt = 0;
unsigned char * p = pDest;
for (i=0; i<srcLen; i++)
{
if (pSrc[i] > 127)
{
continue;
}
if (pSrc[i] == '=')
{
return p-pDest+1;
}
unsigned char a = Base64IdxTab[pSrc[i]];
if (a == 255)
{
continue;
}
switch (iCnt)
{
case 0:
{
*p = a << 2;
iCnt++;
}
break;
case 1:
{
*p++ |= a >> 4;
*p = a << 4;
iCnt++;
}
break;
case 2:
{
*p++ |= a >> 2;
*p = a << 6;
iCnt++;
}
break;
case 3:
{
*p++ |= a;
iCnt = 0;
}
break;
}
}
*p = 0x00;
return p-pDest;
}
其中返回值为解出的目标串的长度
然后,针对接触的字符串,它的第一个字节是个NUL头,我们希望它的NAL_TYPE为7,也就是序列参数集(sps)
针对该参数集,对照h.264文档的7.3.2.1对语法,我们希望解出pic_width_in_mbs_minus1字段和pic_height_in_map_units_minus1字段
这里遇到了一个问题,因为它是指数哥伦布编码,因此又需要解码:
int GolombDecode(bool *pBinaryArray, int &Des)
{
if (pBinaryArray == NULL)
{
return -1;
}
int iter = 0;
int NumOfZero = 0;
int M_bit = 0;
int INFO = 0;
double result;
//找到第一个1
while(pBinaryArray[iter++] == 0)
{
NumOfZero++;
}
for (int i=NumOfZero-1; i>=0; i--)
{
INFO += pBinaryArray[iter++]<<i;
}
result = pow((double)2, (double)NumOfZero) + INFO - 1;
Des = result;
return iter;
}
其中,返回值是二进制字符串的偏移地址
因为指数哥伦布编码不定长的特性,我们需要解出整个SPS(依据ITU-T H264 7.3.2.1.1):
int ParseSPS(unsigned char *pSrc, unsigned long SrcLen, unsigned long &VideoWidth, unsigned long &VideoHeight)
{
int ret = 0;
unsigned long offset = 0;
bool *pBinaryArray = new bool[SrcLen*8];
unsigned long iter = 0;
unsigned char mask = 0x80;
for (int i=0; i<SrcLen; i++)
{
mask = 0x80;
for (int j=0; j<8; j++)
{
if (pSrc[i] & (mask >> j))
{
pBinaryArray[iter] = 1;
}
else
{
pBinaryArray[iter] = 0;
}
iter++;
}
}
iter = 0;
unsigned char profile_idc = *pSrc;
int seq_parameter_set_id;
offset = 24;
ret = GolombDecode(pBinaryArray+offset, seq_parameter_set_id);
offset += ret;
if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144)
{
int chroma_format_idc;
ret = GolombDecode(pBinaryArray+offset, chroma_format_idc);
offset += ret;
if(chroma_format_idc == 3)
{
offset += 1;
}
int bit_depth_luma_minus8, bit_depth_chroma_minus8;
ret = GolombDecode(pBinaryArray+offset, bit_depth_luma_minus8);
offset += ret;
ret = GolombDecode(pBinaryArray+offset, bit_depth_chroma_minus8);
offset += ret;
offset += 2;
bool seq_scaling_matrix_present_flag = pBinaryArray[offset];
if (seq_scaling_matrix_present_flag)
{
offset += 1;
}
}
int log2_max_frame_num_minus4, pic_order_cnt_type;
ret = GolombDecode(pBinaryArray+offset, log2_max_frame_num_minus4);
offset += ret;
ret = GolombDecode(pBinaryArray+offset, pic_order_cnt_type);
offset += ret;
if (pic_order_cnt_type == 0)
{
int log2_max_pic_order_cnt_lsb_minus4;
ret = GolombDecode(pBinaryArray+offset, pic_order_cnt_type);
offset += ret;
}
else if (pic_order_cnt_type == 1)
{
offset += 1;
int offset_for_non_ref_pic, offset_for_top_to_bottom_field, num_ref_frames_in_pic_order_cnt_cycle, offset_for_ref_frame;
ret = GolombDecode(pBinaryArray+offset, offset_for_non_ref_pic);
offset += ret;
ret = GolombDecode(pBinaryArray+offset, offset_for_top_to_bottom_field);
offset += ret;
ret = GolombDecode(pBinaryArray+offset, num_ref_frames_in_pic_order_cnt_cycle);
offset += ret;
for (int i=0; i<num_ref_frames_in_pic_order_cnt_cycle ;i++)
{
ret = GolombDecode(pBinaryArray+offset, offset_for_ref_frame);
offset += ret;
}
}
int num_ref_frames;
ret = GolombDecode(pBinaryArray+offset, num_ref_frames);
offset += ret;
offset += 1;
int pic_width_in_mbs_minus1, pic_height_in_map_units_minus1;
ret = GolombDecode(pBinaryArray+offset, pic_width_in_mbs_minus1);
offset += ret;
ret = GolombDecode(pBinaryArray+offset, pic_height_in_map_units_minus1);
offset += ret;
VideoWidth = (pic_width_in_mbs_minus1 + 1) * 16;
VideoHeight = (pic_height_in_map_units_minus1 + 1) * 16;
delete []pBinaryArray;
pBinaryArray = NULL;
return 0;
}
这样,就完成了目标,得到了视频信息的长和宽