转自:https://blog.csdn.net/chen495810242/article/details/39207305
针对H264 做如下PS 封装:每个IDR NALU 前一般都会包含SPS、PPS 等NALU,因此将SPS、PPS、IDR 的NALU 封装为一个PS 包,包括ps 头,然后加上PS system header,PS system map,PES header+h264 raw data。所以一个IDR NALU PS 包由外到内顺序是:PSheader| PS system header | PS system Map | PES header | h264 raw data。对于其它非关键帧的PS 包,就简单多了,直接加上PS头和PES 头就可以了。顺序为:PS header | PES header | h264raw data。以上是对只有视频video 的情况,如果要把音频Audio也打包进PS 封装,也可以。当有音频数据时,将数据加上PES header 放到视频PES 后就可以了。顺序如下:PS 包=PS头|PES(video)|PES(audio),再用RTP 封装发送就可以了。
GB28181 对RTP 传输的数据负载类型有规定(参考GB28181 附录B),负载类型中96-127
RFC2250 建议96 表示PS 封装,建议97 为MPEG-4,建议98 为H264
即我们接收到的RTP 包首先需要判断负载类型,若负载类型为96,则采用PS 解复用,将音视频分开解码。若负载类型为98,直接按照H264 的解码类型解码。
注:此方法不一定准确,取决于打包格式是否标准
PS 包中的流类型(stream type)的取值如下:
1) MPEG-4 视频流: 0x10;
2) H.264 视频流: 0x1B;
3) SVAC 视频流: 0x80;
4) G.711 音频流: 0x90;
5) G.722.1 音频流: 0x92;
6) G.723.1 音频流: 0x93;
7) G.729 音频流: 0x99;
8) SVAC音频流: 0x9B。
3.1、PS包头
图7
1) Pack start code:包起始码字段,值为0x000001BA的位串,用来标志一个包的开始。
2) System clock reference base,system clock reference extenstion:系统时钟参考字段。
3) Pack stuffing length :包填充长度字段,3 位整数,规定该字段后填充字节的个数
80 60 53 1f 00 94 89 00 00 0000 00 00 00 01 ba €`S..??........?
7e ff 3e fb 44 01 00 5f 6b f8 00 00 01 e0 14 53 ~.>?D.._k?...?.S
80 80 05 2f bf cf bed1 1c 42 56 7b 13 58 0a 1e €€./????.BV{.X..
08 b1 4f 33 69 35 0453 6d 33 a8 04 15 58 d9 21 .?O3i5.Sm3?..X?!
9741 b9 f1 75 3d 94 2b 1f bc 0b b2 b4 97 bf 93 ?A??u=?+.?.?????
前12位是RTP Header,这里不再赘述;
000001ba是包头起始码;
接下来的9位包括了SCR,SCRE,MUXRate,具体看图7
最后一位是保留位(0xf8),定义了是否有扩展,二进制如下
1111 1000
前5位跳过,后3位指示了扩展长度,这里是0.
3.2、系统标题
图8
Systemheader当且仅当pack是第一个数据包时才存在,即PS包头之后就是系统标题。取值0x000001BB的位串,指出系统标题的开始,暂时不需要处理,读取Header Length直接跳过即可。
3.3、节目映射流
Systemheader当且仅当pack是第一个数据包时才存在,即系统标题之后就是节目流映射。取值0x000001BC的位串,指出节目流映射的开始,暂时不需要处理,读取Header Length直接跳过即可。前5字节的结构同系统标题,见图8。
取一段码流分析系统标题和节目映射流
00 00 01 ba 45 a9 d4 5c 34 0100 5f 6b f8 00 00 ...?E??4.._k?..
01 bb 00 0c 80 cc f5 04 e1 7f e0 e0 e8 c0 c0 20 .?..€??.?.?????
00 00 01 bc 00 1e e1 ff00 00 00 18 1b e0 00 0c ...?..?......?..
2a 0a 7f ff 00 00 0708 1f fe a0 5a 90 c0 00 00 *........??Z??..
00 00 00 00 00 00 01 e0 7f e0 80 80 0521 6a 75 .......?.?€€.!ju
前14个字节是PS包头(注意,没有扩展);
接下来的00 00 01 bb是系统标题起始码;
接下来的00 0c说明了系统标题的长度(不包括起始码和长度字节本身);
接下来的12个字节是系统标题的具体内容,这里不做解析;
继续看到00 00 01 bc,这是节目映射流起始码;
紧接着的00 1e同样代表长度;
跳过e1 ff,基本没用;
接下来是00 18,代表基本流长度,说明了后面还有24个字节;
接下来的1b,意思是H264编码格式;
下一个字节e0,意思是视频流;
接下里00 0c,同样代表接下的长度12个字节;
跳过这12个字节,看到90,这是G.711音频格式;
下一个字节是c0,代表音频流;
接下来的00 00同样代表长度,这里是0;
接下来4个字节是CRC,循环冗余校验。
到这里节目映射流解析完毕。(好累)。
好戏还在后头呢。
3.4、PES分组头部
图9
别被这么长的图吓到,其实原理相同,但是,你必须处理其中的每一位。
1) Packet start code prefix:值为0x000001的位串,它和后面的stream id 构成了标识分组开始的分组起始码,用来标志一个包的开始。
2) Stream id:在节目流中,它规定了基本流的号码和类型。0x(C0~DF)指音频,0x(E0~EF)为视频
3) PES packet length:16 位字段,指出了PES 分组中跟在该字段后的字节数目。值为0 表示PES 分组长度要么没有规定要么没有限制。这种情况只允许出现在有效负载包含来源于传输流分组中某个视频基本流的字节的PES 分组中。
4) PTS_DTS:2 位字段。当值为'10'时,PTS 字段应出现在PES 分组标题中;当值为'11'时,PTS 字段和DTS 字段都应出现在PES 分组标题中;当值为'00'时,PTS 字段和DTS 字段都不出现在PES分组标题中。值'01'是不允许的。
5) ESCR:1位。置'1'时表示ESCR 基础和扩展字段出现在PES 分组标题中;值为'0'表示没有ESCR 字段。
6) ESrate:1 位。置'1'时表示ES rate 字段出现在PES 分组标题中;值为'0'表示没有ES rate 字段。
7) DSMtrick mode:1 位。置'1'时表示有8 位特技方式字段;值为'0'表示没有该字段。
8) Additionalinfo:1 位。附加版权信息标志字段。置'1'时表示有附加拷贝信息字段;值为'0'表示没有该字段。
9) CRC:1 位。置'1'时表示CRC 字段出现在PES 分组标题中;值为'0'表示没有该字段。
10) Extensionflag:1 位标志。置'1'时表示PES 分组标题中有扩展字段;值为'0'表示没有该字段。
PES header data length: 8 位。PES 标题数据长度字段。指出包含在PES 分组标题中的可选字段和任何填充字节所占用的总字节数。该字段之前的字节指出了有无可选字段。
老规矩,上码流:
00 00 01 e0 21 33 80 80 05 2b 5f df 5c 95 71 84 ...?!3€€.+_??q?
aa e4 e9 e9 ec 40 cc17 e0 68 7b 23 f6 89 df 90 ?????@?.?h{#????
a9d4 be 74 b9 67 ad 34 6d f0 92 0d 5a 48 dd 13 ???t?g?4m??.ZH?.
00 00 01是起始码;
e0是视频流;
21 33 是帧长度;
接下来的两个80 80见下面的二进制解析;
下一个字节05指出了可选字段的长度,前一字节指出了有无可选字段;
接下来的5字节是PTS;
第7、8字节的二进制如下:
1000 0000 1000 0000
按顺序解析:
第7个字节:
10 是标志位,必须是10;
00 是加扰控制字段,‘00’表示没有加密,剩下的01,10,11由用户自定义;
0 是优先级,1为高,0为低;
0 是数据对齐指示字段;
0 是版权字段;
0 是原始或拷贝字段。置'1'时表示相关PES分组有效负载的内容是原始的;'0'表示内容是一份拷贝;
第8个字节:
10 是PTS_DTS字段,这里是10,表示有PTS,没有DTS;
0 是ESCR标志字段,这里为0,表示没有该段;
0 是ES速率标志字段,,这里为0,表示没有该段;
0 是DSM特技方式标志字段,,这里为0,表示没有该段;
0 是附加版权信息标志字段,,这里为0,表示没有该段;
0 是PESCRC标志字段,,这里为0,表示没有该段;
0 是PES扩展标志字段,,这里为0,表示没有该段;
本段码流只有PTS,贴一下解析函数
unsigned long parse_time_stamp (const unsigned char *p)
{
unsigned long b;
//共33位,溢出后从0开始
unsigned long val;
//第1个字节的第5、6、7位
b = *p++;
val = (b & 0x0e) << 29;
//第2个字节的8位和第3个字节的前7位
b = (*(p++)) << 8;
b += *(p++);
val += ((b & 0xfffe) << 14);
//第4个字节的8位和第5个字节的前7位
b = (*(p++)) << 8;
b += *(p++);
val += ((b & 0xfffe) >> 1);
return val;
}
其他字段可参考协议解析
ps:
遇到00 00 01 bd的,这个是私有流的标识
ps:
另外,有的hk摄像头回调然后解读出来的原始h.264码流,有的一包里只有分界符数据(nal_unit_type=9)或补充增强信息单元(nal_unit_type=6),如果直接送入解码器,有可能会出现问题,这里的处理方式要么丢弃这两个部分,要么和之后的数据合起来,再送入解码器里,如有遇到的朋友可以交流一下:)