多媒体开发之h264中的sps---sps信息提取之分辨率宽高提取2
-------------------author:pkf
-----------------------------time:2015-8-20
---------------------------------------------qq:1327706646
(1) sps 数据结构
(2) 指数哥伦布码(Exponential-Golomb code, 即Exp-Golomb code)
(3) 宽高计算
(4) 代码实现
-----------------------------------------------------------------------
(1) sps 数据结构
描述子描述从Bit流中取出句法元素的方法。
编号 |
语法 |
说明 |
1 |
ae(e) |
CABAC |
2 |
b(8) |
读进连续的8个Bit |
3 |
ce(v) |
CAVLC |
4 |
f(n) |
读进连续的n个Bit |
5 |
i(n)/i(v) |
读进连续的若干Bit,并把它们解释为有符号整数 |
6 |
me(v) |
映射指数Golomb熵编码 |
7 |
se(v) |
有符号指数Golomb熵编码 |
8 |
te(v) |
截断指数Golomb熵编码 |
9 |
u(n)/u(v) |
读进连续的若干Bit,并把它们解释为无符号整数 |
10 |
ue(v) |
无符号指数Golomb熵编码 |
表1
句法元素的名称由小写字母和一系列下划线组成,变量名称是大小写字母组成,中间没有下划线。
定义了H.264的句法,指明在码流中依次出现的句法元素及它们出现的条件、提取描述子等。句法表是分层嵌套的。
句法表中的C字段表示该句法元素的分类,这是为片区服务,分类的具体含义如下表描述。
nal_unit_type |
NAL类型 |
C |
0 |
未使用 |
|
1 |
不分区、非IDR的片 |
2,3,4 |
2 |
片分区A |
2 |
3 |
片分区B |
3 |
4 |
版分区C |
4 |
5 |
IDR图像中的片 |
2,3 |
6 |
补充增强信息单元(SEI) |
5 |
7 |
序列参数集 |
0 |
8 |
图像参数集 |
1 |
9 |
分界符 |
6 |
10 |
序列结束 |
7 |
11 |
码流结束 |
8 |
12 |
填充 |
9 |
13..23 |
保留 |
|
24..31 |
不保留 |
|
表2
2.1 NAL语法
句法 |
C |
Desc |
nal_nuit(NumBytesInNALunit){/* NumBytesInNALunit为统计出来的数据长度 */ |
|
|
forbidden_zero_bit /* 等于0 */ |
All |
f(1) |
nal_ref_idc/* 当前NAL的优先级,取值范围0-3 */ |
All |
u(2) |
nal_unit_type /* NAL类型,见表2描述 */ |
All |
u(5) |
NumBytesInRBSP=0 |
|
|
for(i=1;i<NumBytesInNALunit;i++){ |
|
|
if(i+2<NumBytesInNALunit && next_bits(24)==0x000003{ |
|
|
/* 0x000003伪起始码,需要删除0x03这个字节 */ |
|
|
rbsp_byte[NumBytesInRBSP++] |
All |
b(8) |
rbsp_byte[NumBytesInRBSP++] |
All |
b(8) |
i+=2/* 取出前两个0x00后,跳过0x03 */ |
|
|
emulation_prevention_three_byte/* equal to 0x03 */ |
All |
f(8) |
}else{ |
|
|
rbsp_byte[NumBytesInRBSP++] /* 继续读取后面的字节 */ |
All |
b(8) |
} |
|
|
} |
|
|
表3
2.2序列参数集(SPS)
句法 |
C |
Desc |
seq_parameter_set_rbsp(){ |
|
|
profile_idc/* 指明所用的Profile */ |
0 |
u(8) |
constraint_set0_flag |
0 |
u(1) |
constraint_set1_flag |
0 |
u(1) |
constraint_set1_flag |
0 |
u(1) |
reserved_zero_5bits /* equal to 0 */ |
0 |
u(5) |
level_idc /* 指明所用的Level */ |
0 |
u(8) |
seq_parameter_set_id /* 指明本序列参数集的id号,0-31,被图像集引用,编码需要产生新的序列集时,使用新的id,而不是改变原来参数集的内容 */ |
0 |
ue(v) |
log2_max_frame_num_minus4/* 为读取元素frame_num服务,frame_num标识图像的解码顺序,frame_num的解码函数是ue(v),其中v=log2_max_frame_num_minus4+4,该元素同时指明frame_num的最大值MaxFrameNum=2( log2_max_frame_num_minus4+4)*/ |
0 |
ue(v) |
pic_order_cnt_type /* 指明poc的编码方法,poc标识图像的播放顺序,poc可以由frame_num计算,也可以显示传送。poc共三种计算方式 */ |
0 |
ue(v) |
if(pic_order_cnt_type==0) |
|
|
log2_max_pic_order_cnt_lsb_minus4 /* 指明变量MaxPicOrderCntLsb的值,MaxPicOrderCntLsb=2(log2_max_pic_order_cnt_lsb_minus4+4) */ |
0 |
ue(v) |
else if(pic_order_cnt_type==1){ |
|
|
delta_pic_order_always_zero_flag /* 等于1时,元素delta_pic_order_cnt[0]和delta_pic_order_cnt[1]不在片头中出现,并且它们的默认值是0,等于0时,上述两元素出现的片头中 */ |
0 |
u(1) |
offset_for_non_ref_pic /* 用来计算非参考帧或场的poc,[-231,231-1] */ |
0 |
se(v) |
offset_for_top_to_bottom_field/* 计算帧的底场的poc */ |
0 |
se(v) |
num_ref_frames_inpic_order_cnt_cycle /* 用来解码poc,[0.255] */ |
0 |
ue(v) |
for(i=0;i<num_ref_frames_inpic_order_cnt_cycle;i++) |
|
|
offset_for_ref_frame[i]/* 用来解码poc,对于循环中的每个元素指定一个偏移 */ |
0 |
se(v) |
} |
|
|
num_ref_frames /* 参考帧队列可达到的最大长度,[0,16] */ |
0 |
ue(v) |
gaps_in_frame_num_value_allowed_flag /* 为1,允许slice header中的frame_num不连续 */ |
0 |
u(1) |
pic_width_inmbs_minus1 /* 本元素加1,指明以宏块为单位的图像宽度 PicWidthInMbs=pic_width_in_mbs_minus1+1 */ |
0 |
ue(v) |
pic_height_in_map_units_minus1 /* 本元素加1,指明以宏块为单位的图像高宽度PicHeightInMapUnitsMbs=pic_height_in_map_units_minus1+1 */ |
0 |
ue(v) |
frame_mbs_only_flag /* 等于0表示本序列中所有图像均为帧编码;等于1,表示可能是帧,也可能场或帧场自适应,具体编码方式由其它元素决定。结合前一元素:FrameHeightInMbs=(2-frame_mbs_only_flag)*PicHeightInMapUnits */ |
0 |
ue(v) |
if(frame_mbs_only_flag) |
|
|
mb_adaptiv_frame_field_flag /* 指明本序列是否是帧场自适应模式: frame_mbs_only_flag=1,全部是帧 frame_mbs_only_flag=0, mb_adaptiv_frame_field_flag=0,帧场共存 frame_mbs_only_flag=0, mb_adaptiv_frame_field_flag=1,帧场自适应和场共存*/ |
0 |
u(1) |
direct_8x8_inference_flag /* 用于指明B片的直接和skip模式下的运动矢量的计算方式 */ |
0 |
u(1) |
frame_cropping_flag /* 解码器是否要将图像裁剪后输出,如果是,后面为裁剪的左右上下的宽度 */ |
0 |
u(1) |
if(frame_cropping_flag){ |
|
|
frame_crop_left_offset |
0 |
ue(1) |
frame_crop_right_offset |
0 |
ue(1) |
frame_crop_top_offset |
0 |
ue(1) |
frame_crop_bottom_offset |
0 |
ue(1) |
} |
|
|
vui_parameters_present_flag /* 指明vui子结构是否出现在码流中,vui子结构在附录中指明,用于表征视频格式的信息 */ |
0 |
u(1) |
if(vui_parameters_present_flag) |
|
|
vui_parameters() |
0 |
|
rbsp_trailing_bits() |
0 |
|
} |
|
|
Start dumping SPS:
profile_idc = 66
constrained_set0_flag = 1
constrained_set1_flag = 1
constrained_set2_flag = 1
constrained_set3_flag = 0
level_idc = 20
seq_parameter_set_id = 0
chroma_format_idc = 1
bit_depth_luma_minus8 = 0
bit_depth_chroma_minus8 = 0
seq_scaling_matrix_present_flag = 0
log2_max_frame_num_minus4 = 0
pic_order_cnt_type = 2
log2_max_pic_order_cnt_lsb_minus4 = 0
delta_pic_order_always_zero_flag = 0
offset_for_non_ref_pic = 0
offset_for_top_to_bottom_field = 0
num_ref_frames_in_pic_order_cnt_cycle = 0
num_ref_frames = 1
gaps_in_frame_num_value_allowed_flag = 0
pic_width_in_mbs_minus1 = 21
pic_height_in_mbs_minus1 = 17
frame_mbs_only_flag = 1
mb_adaptive_frame_field_flag = 0
direct_8x8_interence_flag = 0
frame_cropping_flag = 0
frame_cropping_rect_left_offset = 0
frame_cropping_rect_right_offset = 0
frame_cropping_rect_top_offset = 0
frame_cropping_rect_bottom_offset = 0
vui_parameters_present_flag = 0
http://blog.csdn.net/xfding/article/details/5468347 sps 数据结构
注意:
if(frame_mbs_only_flag==0)的时候会出现两个slice_header:00 slice_heaer 01 slice_header i+p(67 65) 帧在一起的去交错图像,旧版本ffmpeg 不支持要更新去交错
(2) 指数哥伦布码(Exponential-Golomb code, 即Exp-Golomb code)
作用:
采用指数哥伦布码的优势在于:一方面,它的硬件复杂度比较低,可以根据闭合公式解析码字,无需查表;另一方面,它可以根据编码元素的概率分布灵活地确定以k阶指数哥伦布码编码,如果k选得恰当,则编码效率可以逼近信息熵。
指数哥伦布编码分为:无符号和有符号:
步骤:
计算前面有多少个bit位为0,记为n,后面的有效数据就是n+1位(如果n等于0后面的有效数据就是1位),这样2n+1位就是一个编码,其中前面n位是0前缀,后面的n+1位是有效数据。再看定义的是有符号还是无符号型的,无符号型的就是直接后面n+1位有效数据的排列的二进制数据大小,有符号型的要将无符号型的数据稍微转换一下,变成正数或负数(前n位表示数据,最后一位表示符号)。具体详细介绍看H264标准文档说明。
无符号哥伦布编码:
有前缀和后缀的概念:(zheli have erro)
1 先将要编码的数值写成二进制:0x6 ==> 0b110
2 以最高位为界(从右到左第一个值为1的位),后面的就是后缀(就是二进制10了),总共两位,所以前缀也是两位,而且必须是0,
3 得出数值6编码后的二进制表示为:00111
解码就是反过程:
从高位开始统计0的个数直至非零位, 假设0的个数是k, 那后面k+1位就是要找的数值了
http://www.360doc.com/content/13/0125/16/9008018_262356572.shtml 无符号c实现及原理
有符号哥伦布编码:
指数哥伦布码(Exponential-Golomb code, 即Exp-Golomb code)压缩编码方法过程:
用来表示非负整数的k阶指数哥伦布码可用如下步骤生成:
1. 将数字以二进制形式写出,去掉最低的k个比特位,之后加1
2. 计算留下的比特数,将此数减一,即是需要增加的前导零个数
3.将第一步中去掉的最低k个比特位补回比特串尾部
比特串格式为“前缀1后缀”。1)1后缀=codeNum+1,如codeNum = 3,则1后缀=4,即为100,后缀为00;2)前缀与后缀的比特数相同,且前缀的各位比特为0,
下例为对不同codeNum进行的编码结果:
0 => 1 => 1
1 => 10 => 010
2 => 11 => 011
3 => 100 => 00100
4 => 101 => 00101
5 => 110 => 00110
6 => 111 => 00111
7 => 1000 => 0001000
8 => 1001 => 0001001
与此相反,这些语法元素的解析过程是由比特流当前位置比特开始读取,包括非0 比特,直至leading_bits 的数量为0。具体过程如下所示:
leadingZeroBits = ?1;
for( b = 0; !b; leadingZeroBits++ )
b = read_bits( 1 )
变量codeNum 按照如下方式赋值:
codeNum = 2leadingZeroBits ? 1 + read_bits( leadingZeroBits )
这里read_bits( leadingZeroBits )的返回值使用高位在先的二进制无符号整数表示。
如下示例:
二进制比特数 长度 解析值
1001 1 0
001 1001 5 5
01 1010 3 2
010 3 1
000 1011 7 10
0001 001 7 8
指数哥伦布编码
规定语法元素的编解码模式的描述符如下:
比特串:
b(8):任意形式的8比特字节(就是为了说明语法元素是为8个比特,没有语法上的含义)
f(n):n位固定模式比特串(其值固定,如forbidden_zero_bit的值恒为0)
i(n):使用n比特的有符号整数(语法中没有采用此格式)
u(n):n位无符号整数
指数哥伦布编码:
ue(v):无符号整数指数哥伦布码编码的语法元素
se(v):有符号整数指数哥伦布编码的语法元素,左位在先
te(v):舍位指数哥伦布码编码语法元素,左位在先
以及ce(v):CAVLC和ae(v):CABAC。
指数哥伦布编码过程:
在表9-1中,比特串格式为“前缀1后缀”。1)1后缀=codeNum+1,如codeNum = 3,则1后缀=4,即为100,后缀为00;2)前缀与后缀的比特数相同,且前缀的各位比特为0,如codeNum=3,则最终编码所得的比特串为:00100.
对于ue(v),按上述规则进行编码;
对于se(v),则按照表9-3转换成codeNum,然后按上述规则进行编码;
在表9-3中,1)语法元素值为负数,则乘2取反,转换成codeNum,2)语法元素为正数,则乘2减1,转换成codeNum;
对于te(v),只有7.3.5.1节“宏块预测语法”和7.3.5.2节“子宏块预测语法”中的ref_idx_l0[mbPartIdx]和ref_idx_l1[mbPartIdx]用此模式编码,
如果语法元素值为0,则编码为1,如果语法元素值为1,则编码为0,如果为其他大于1的值,则按ue(v)进行编码。
(3) 宽高计算
宽高计算720p 计算一般没啥问题,只是到了1080p就会解析错误包括elecard stream eye 也是提示1088多了个8
更正的代码如下
// 宽高计算公式 width = (sps->pic_width_in_mbs_minus1+1) * 16;
height = (2 - sps->frame_mbs_only_flag)* (sps->pic_height_in_map_units_minus1 +1) * 16);
if(sps->frame_cropping_flag)
{ unsigned int crop_unit_x; unsigned int crop_unit_y; if (0 == sps->chroma_format_idc) // monochrome { crop_unit_x = 1; crop_unit_y = 2 - sps->frame_mbs_only_flag; }
else if (1 == sps->chroma_format_idc) // 4:2:0 { crop_unit_x = 2; crop_unit_y = 2 * (2 - sps->frame_mbs_only_flag); }
else if (2 == sps->chroma_format_idc) // 4:2:2 { crop_unit_x = 2; crop_unit_y = 2 - sps->frame_mbs_only_flag; }
else // 3 == sps.chroma_format_idc // 4:4:4 { crop_unit_x = 1; crop_unit_y = 2 - sps->frame_mbs_only_flag; }
width -= crop_unit_x * (sps->frame_crop_left_offset + sps->frame_crop_right_offset);
height -= crop_unit_y * (sps->frame_crop_top_offset + sps->frame_crop_bottom_offset); }
http://ju.outofmemory.cn/entry/208773 更正代码
http://blog.csdn.net/sunnylgz/article/details/7680262
(4) 代码实现
// Ue find the num of zeros and get (num+1) bits from the first 1, and
// change it to decimal
// e.g. 00110 -> return 6(110)
unsigned int Ue(unsigned char *pBuff, unsigned int nLen, unsigned int &nStartBit)
{
//计算0bit的个数
unsigned int nZeroNum = 0;
while (nStartBit < nLen * 8)
{
//&:按位与,%取余
if (pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8)))
{
break;
}
nZeroNum++;
nStartBit++;
}
nStartBit++;
//计算结果
unsigned int dwRet = 0;
for (unsigned int i = 0; i < nZeroNum; i++)
{
dwRet <<= 1;
if (pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8)))
{
dwRet += 1;
}
nStartBit++;
}
return (1 << nZeroNum) - 1 + dwRet;
}
int Se(unsigned char *pBuff, unsigned int nLen, unsigned int &nStartBit)
{
int nUeVal = Ue(pBuff, nLen, nStartBit);
double k = nUeVal;
//ceil函数:ceil函数的作用是求不小于给定实数的最小整数。ceil(2)=ceil(1.2)=cei(1.5)=2.00
int nValue=ceil(k/2);
if (nUeVal % 2 == 0)
nValue = -nValue;
return nValue;
}
// u Just returns the BitCount bits of buf and change it to decimal.
// e.g. BitCount = 4, buf = 01011100, then return 5(0101)
unsigned int u(unsigned int nBitCount, unsigned char* buf, unsigned int &nStartBit)
{
unsigned int dwRet = 0;
for (unsigned int i = 0; i < nBitCount; i++)
{
dwRet <<= 1;
if (buf[nStartBit / 8] & (0x80 >> (nStartBit % 8)))
{
dwRet += 1;
}
nStartBit++;
}
return dwRet;
}
bool GetResulotion(unsigned char *pData, int nDataLen, int &nWidth, int &nHeight, int &nDeinterlace)
{
bool bSpsComplete = false;
// Find SPS
unsigned char ucLastNalType = 0;
if (pData[0] == 0x00 && pData[1] == 0x00 && pData[2] == 0x00 && pData[3] == 0x01)
{
ucLastNalType = pData[4];
//Modify by HJ 20140717
unsigned char ucSPS = pData[4] & 0x7;
if (ucSPS == 0x7)//SPS后5bit 和为7
{
bSpsComplete = true;
}
}
//先找到SPS类型的帧序列
if (bSpsComplete == false) return false;
//LOG4C_DEBUG(g_Logger, "GetResulotion:: Find SPS frame, nDataLen(%d)", nDataLen);
//Analyze SPS to find width and height
unsigned int nStartBit = 0;
unsigned char *pBuf = pData + 4;
int nDataLeft = nDataLen - 4;
char temp[200];
int forbidden_zero_bit = u(1, pBuf, nStartBit);
int nal_ref_idc = u(2, pBuf, nStartBit);
int nal_unit_type = u(5, pBuf, nStartBit);
//sprintf_s(temp,"GetResulotion forbidden_zero_bit=%d, nal_ref_idc=%d, nal_unit_type=%d ",forbidden_zero_bit, nal_ref_idc, nal_unit_type);
//OutputDebugStringA(temp);
printf("GetResulotion forbidden_zero_bit=%d, nal_ref_idc=%d, nal_unit_type=%d \n",forbidden_zero_bit, nal_ref_idc, nal_unit_type);
if (nal_unit_type == 7)
{
int profile_idc = u(8, pBuf, nStartBit);
int constraint_set0_flag = u(1, pBuf, nStartBit);//(buf[1] & 0x80)>>7;
int constraint_set1_flag = u(1, pBuf, nStartBit);//(buf[1] & 0x40)>>6;
int constraint_set2_flag = u(1, pBuf, nStartBit);//(buf[1] & 0x20)>>5;
int constraint_set3_flag = u(1, pBuf, nStartBit);//(buf[1] & 0x10)>>4;
int reserved_zero_4bits = u(4, pBuf, nStartBit);
int level_idc = u(8, pBuf, nStartBit);
int seq_parameter_set_id = Ue(pBuf, nDataLeft, nStartBit);
//if (profile_idc == 100 || profile_idc == 110 ||
// profile_idc == 122 || profile_idc == 144)
if (profile_idc == 100 || // High profile
profile_idc == 110 || // High10 profile
profile_idc == 122 || // High422 profile
profile_idc == 244 || // High444 Predictive profile
profile_idc == 44 || // Cavlc444 profile
profile_idc == 83 || // Scalable Constrained High profile (SVC)
profile_idc == 86 || // Scalable High Intra profile (SVC)
profile_idc == 118 || // Stereo High profile (MVC)
profile_idc == 128 || // Multiview High profile (MVC)
profile_idc == 138 || // Multiview Depth High profile (MVCD)
profile_idc == 144) // old High444 profile
{
int chroma_format_idc = Ue(pBuf, nDataLeft, nStartBit);
if(chroma_format_idc == 3)
int residual_colour_transform_flag = u(1, pBuf, nStartBit);
int bit_depth_luma_minus8 = Ue(pBuf, nDataLeft, nStartBit);
int bit_depth_chroma_minus8 = Ue(pBuf, nDataLeft, nStartBit);
int qpprime_y_zero_transform_bypass_flag = u(1, pBuf, nStartBit);
int seq_scaling_matrix_present_flag = u(1, pBuf, nStartBit);
int seq_scaling_list_present_flag[8];
if (seq_scaling_matrix_present_flag)
{
for (int i = 0; i < 8; i++)
{
seq_scaling_list_present_flag[i] = u(1, pBuf, nStartBit);
}
}
}
int log2_max_frame_num_minus4 = Ue(pBuf, nDataLeft, nStartBit);
int pic_order_cnt_type = Ue(pBuf, nDataLeft, nStartBit);
//LOG4C_DEBUG(g_Logger, "GetResulotion:: Find SPS frame, nDataLen(%d), nStarBit(%d), pic_order_cnt_type(%d)",
// nDataLen, nStartBit, pic_order_cnt_type);
if (pic_order_cnt_type == 0)
{
int log2_max_pic_order_cnt_lsb_minus4 = Ue(pBuf, nDataLeft, nStartBit);
}
else if (pic_order_cnt_type == 1)
{
int delta_pic_order_always_zero_flag = u(1, pBuf, nStartBit);
int offset_for_non_ref_pic = Se(pBuf, nDataLeft, nStartBit);
int offset_for_top_to_bottom_field = Se(pBuf, nDataLeft, nStartBit);
int num_ref_frames_in_pic_order_cnt_cycle = Ue(pBuf, nDataLeft, nStartBit);
int *offset_for_ref_frame = new int[num_ref_frames_in_pic_order_cnt_cycle];
for( int i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ )
offset_for_ref_frame[i] = Se(pBuf, nDataLeft, nStartBit);
delete []offset_for_ref_frame;
}
int num_ref_frames = Ue(pBuf, nDataLeft, nStartBit);
int gaps_in_frame_num_value_allowed_flag = u(1, pBuf, nStartBit);
int pic_width_in_mbs_minus1 = Ue(pBuf, nDataLeft, nStartBit);
int pic_height_in_map_units_minus1 = Ue(pBuf, nDataLeft, nStartBit);
int frame_mbs_only_flag = u(1, pBuf, nStartBit);
//LOG4C_DEBUG(g_Logger, "GetResulotion:: Find SPS frame, nDataLen(%d), nStarBit(%d), pic_order_cnt_type(%d), pic_width_in_mbs_minus1(%d), pic_height_in_map_units_minus1(%d)",
// nDataLen, nStartBit, pic_order_cnt_type, pic_width_in_mbs_minus1, pic_height_in_map_units_minus1);
nWidth = (pic_width_in_mbs_minus1 + 1) * 16;
nHeight = (pic_height_in_map_units_minus1 + 1) * 16;
nDeinterlace = 0;
if (0 == frame_mbs_only_flag)
{
nHeight *= 2;
nDeinterlace = 1;
}
//if(nHeight == 1088) nHeight = 1080;
//LOG4C_DEBUG(g_Logger, "GetResulotion:: Find SPS frame, nDataLen(%d), width(%d), height(%d) frame_mbs_only_flag(%d)", nDataLen, nWidth, nHeight,frame_mbs_only_flag);
printf("GetResulotion:: Find SPS frame, nDataLen(%d), width(%d), height(%d) frame_mbs_only_flag(%d)", nDataLen, nWidth, nHeight,frame_mbs_only_flag);
return true;
}
else
{
//LOG4C_DEBUG(g_Logger, "GetResulotion:: Find SPS frame, Invalid nal unit type, nDataLen(%d), nal_unit_type(%d)", nDataLen, nal_unit_type);
printf("GetResulotion:: Find SPS frame, Invalid nal unit type, nDataLen(%d), nal_unit_type(%d)", nDataLen, nal_unit_type);
return false;
}
}
http://www.bubuko.com/infodetail-261836.html 实现一
http://blog.csdn.net/dxpqxb/article/details/17140239 实现二
完整rtp rtsp 工程代码在百度云里
附录:
http://www.cnblogs.com/xkfz007/articles/2615088.html
http://blog.csdn.net/ccskyer/article/details/25993685
http://bbs.csdn.net/topics/360043617 指数哥伦布
http://blog.csdn.net/hemmingway/article/details/44193971
http://blog.csdn.net/zblue78/article/details/5948538 一个nalu parser class
http://blog.csdn.net/a2657222/article/details/7931241 宽高计算
http://www.cnblogs.com/likwo/p/3531241.html
http://blog.csdn.net/stpeace/article/details/8226239 jm 提取sps pps
RTCP包中的NTP Time rtcp