对于H264的I帧、P帧等主要是FU(分片)发送,那么FU到底是怎样一个过程呢。
相同NAL单元的分片必须使用递增的RTP序号连续顺序发送(第一和最后分片之间没有其他的RTP包)。相似, NAL单元必须按照RTP顺序号的顺序装配。FUs不可以嵌套。即 一个FU 不可以包含另一个FU。运送FU的RTP时戳被设置成分片NALU的NALU的时刻。
FU-A的RTP荷载格式:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| FU indicator | FU header | |
+--+--+--+--+--+--+--+--+--+--+--+-+--+--+--+--+ |
| |
| FU payload |
| |
| +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| :...OPTIONAL RTP padding |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
FU indicator : 1字节的分片单元指示
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
NRI: 2 bits, 00值指示NAL单元的内容不用于重建影响图像的帧间图像预测.这样的NAL单元可以被丢弃而不用冒影响图像完整性的风险。大于00的值指示NAL单元的解码要求维护引用图像的完整性。
注意:任何非零的NRI在H.264 解码器的处理是相同的。因此,接收者在传送NAL单元给解码器时不必操作NRI的值。NRI值必须根据分片NAL单元的NRI值设置。H.264编码器必须根据H.264规范设置NRI值。
当nal_unit_type 范围的是1到12。特别是,H.264规范要求对于nal_unit_type为6,9,10,11,12的NAL单元的NRI的值应该为0。
对于nal_unit_type等于7,8 (指示顺序参数集或图像参数集)的NAL单元,H.264编码器应该设置NRI为11 (二进制格式)。
对于nal_unit_type等于5的主编码图像的编码片NAL单元(指示编码片属于一个IDR图像), H.264编码器应设置NRI为11。
FU header: 1字节的分片单元头
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+
|S|E|R| Type |
+---------------+
S: (1 bit)
当设置成1,开始位指示分片NAL单元的开始。当跟随的FU荷载不是分片NAL单元荷载的开始,开始位设为0。
E: (1 bit)
当设置成1,结束位指示分片NAL单元的结束,即荷载的最后字节也是分片NAL单元的最后一个字节。
当跟随的FU荷载不是分片NAL单元的最后分片,结束位设置为0。
R: (1 bit)
保留位必须设置为0,接收者必须忽略该位。
Type: (5 bit)
NAL单元荷载类型定义。
FU payload : 分片单元荷载。
关于时间戳,需要注意的是h264的采样率为90000HZ,因此时间戳的单位为1(秒)/90000,因此如果当前视频帧率为25fps,那时间戳间隔或者说增量应该为3600,怎么算出来的呢,每帧是1/25秒,那么这1/25秒有多少个时间戳单元呢,除以1/90000即可。而如果帧率为30fps,则增量为3000,以此类推。
1)第一个FU-A包的FU indicator:
F应该为当前NALU头的F,而NRI应该为当前NALU头的NRI,Type则等于28,表明它是FU-A包。
FU header生成方法:S = 1,E = 0,R = 0,Type则等于NALU头中的Type。
2)后续的N个FU-A包的FU indicator和第一个是完全一样的,如果不是最后一个包,则FU header应该为:S = 0,E = 0,R = 0,
Type等于NALU头中的Type。
3)最后一个FU-A包FU header应该为:S = 0,E = 1,R = 0,Type等于NALU头中的Type。
因此总结就是:
同一个NALU分包的FU indicator头是完全一致的,FU header只有S以及E位有区别,分别标记开始和结束,它们的RTP分包的序列号应该是依次递增的,并且它们的时间戳必须一致,而负载数据为NALU包去掉1个字节的NALU头后对剩余数据的拆分,这点很关键,你可以认为NALU头被拆分成了FU indicator和FU header,所以不再需要1字节的NALU头了。
关于SPS以及PPS,配置帧的传输我采用了先发SPS,再发送PPS,并使用同样的时间戳,或者按照正常时间戳增量再或者组包发送的形式处理貌似都可以,看播放器怎么解码了,另外提一下,如果我们使用vlc进行播放的话,可以在sdp文件中设置SPS以及PPS,这样就可以不用发送它们了。
使用VLC播放时,sdp文件中的分包模式选项:packetization-mode=1,否则有问题。另外sdp里面设置的编码type必须和rtp包中的一致。
ok关于H264的打包就说到这了,其实这些都有现成的了,不用自己去怎么琢磨,但是感兴趣的可以研究下,毕竟知道原理在看代码或者编代码的时候都会有一定的帮助,至少会提高点自信心吧。