RTP头介绍完后,可以介绍下负载,本文以H264为例,进行介绍。
前言
RTP头介绍完后,可以介绍下负载,本文以H264为例,进行介绍。
编码和H.264编码概述
一、H264是什么?
作为一款视频压缩编码,h264强调更高的压缩率和可靠的传播性(相同码率下压缩率高且图像质量高、同时具有网络亲和性)。压缩 算法的细节暂时不学习,主要还是了解包的封装格式,slice格式也暂时不涉及。。
经过一些列的压缩好,形成了I帧,P帧,B帧。
- I帧:关键帧,采用帧内压缩技术。
- P帧:向前参考帧,在压缩时,只参考前面已经处理的帧。采用帧音压缩技术。
- B帧:双向参考帧,在压缩时,它即参考前而的帧,又参考它后面的帧。采用帧间压缩技术。
此外还有一个GOP概念,两个I帧之间是一个图像序列,在一个图像序列中只有一个I帧。
二、H264的编码格式
H264功能划分为2层:
VCL(Video Coding Layer),视频编码层,H264编码/压缩的核心,主要负责将视频数据编码/压缩,再切分。
NAL(Network Abstraction Layer),网络抽象层,这里的网络指的是计算机网络而不是神经网络,负责将VCL的数据组织打包。
下面这张图很传神:
1.SODB
String Of Data Bits 数据比特串-->最原始的编码数据
2.RBSP
Raw Byte Sequence Payload: 原始字节序列载荷,是在原始编码数据后面添加了结尾比特,一个bit“1”和若干个比特“0”,用于字节对齐。
3EBSP
Encapsulated Byte Sequence Payload扩展字节序列载荷(这几个名字翻译的真别扭),RBSP插入防竞争字节0x03。
原因是这样的,最终一个编码报文,前面会有0x000001或者0x00000001,用于标记是一个报文开始。但是,编码数据是多种多样的,有可能数据里面会和这个起始一样,因此,就说明在连续两个0x00后面插入一个0x03。解码时将0x03去掉。
总结:编码数据字节对齐后,然后将连续的两个0x00中间插上0x03,加上一字节的nal头,在最前面加上起始码0x00000001.
载荷说完后,然后看头:
4.NAL header
NALU头用来标识后面的RBSP是什么类型的数据。长度只有一字节,短小精悍起作用。
| forbidden_zero_bit | nal_ref_idc | nal_unit_type |
`--------------------+-------------+---------------`
| 1 bit | 2 bit | 5 bit |
F: 1 个比特.
forbidden_zero_bit. 在 H.264 规范中规定了这一位必须为 0.
NRI: 2 个比特.
nal_ref_idc. 取 00 ~ 11, 似乎指示这个 NALU 的重要性, 如00的NALU解码器可以丢弃它而不影响图像的回放,0~3,取值越大,表示当前NAL越重要,需要优先受到保护。如果当前NAL是属于参考帧的片,或是序列参数集,或是图像参数集这些重要的单位时,本句法元素必需大于0。
Type: 5 个比特.
nal_unit_type. 这个 NALU 单元的类型. 简述如下:
0 没有定义
1 一个非IDR图像的编码条带 (bp帧)
2 编码条带数据分割块A
3 编码条带数据分割块B
4 编码条带数据分割块C
5 IDR图像的编码条带 (i帧)
6 辅助增强信息 (SEI)
7 序列参数集 (sps帧)
8 图像参数集
9 访问单元分隔符
10 序列结尾
11 流结尾
12 填充数据
13 序列参数集扩展
14...18 保留
19 未分割的辅助编码图像的编码条带
20...23 保留
24 STAP-A 单一时间的组合包
25 STAP-B 单一时间的组合包
26 MTAP16 多个时间的组合包
27 MTAP24 多个时间的组合包
28 FU-A 分片的单元
29 FU-B 分片的单元
30-31 没有定义
一帧压缩数据有几个NALU
一个视频帧输出的结果可以打包成一个NALU,也可能有多个NALU,含有多个NALU叫多个slice(multiple slices)。Slice(分片),h264编码可以将一幅图片分割成若干个Slice,Slice承载固定数目的宏块。将一幅图片分割成若干Slice的目的是,为限制误码的扩散和传输。如果H264的一个帧被分片编码,则每个slice最后将产生一个NALU。
如果NALU对应的Slice为一帧的开始,则用4字节表示,即0x00000001,代表一个帧头的起始码前缀;否则用3字节表示,0x000001,就是一个完整的帧被编为多个slice的时候,第二个slice及以后的Slice的nalu使用3字节起始码。
总结:4字节头只出现在SPS、PPS和7.4.1.2.3规定的Access Unit的首个nalu。其余情况都是3字节头。
h264仅用1-23,24以后的用在RTP H264负载类型头中,这就意味着仅仅在h264这一层类型1-23,如果将其打包进RTP中是,在RTP头后的第一字节虽然和H264的nal header 长得一样,但是还是要根据具体情况进行类型的分配。因为RTP牵扯到了网络传输,在网络传输对数据长度有限制,因此有时需要对一个nalu进行拆包,拆包后的rtp负载类型可能就是28,FU-A
[start code]–[NALU]–[start code]–[NALU]…
1.区分帧类型。
在实际的H264数据帧中,往往帧前面带有00 00 00 01 或 00 00 01分隔符,一般来说编码器编出的首帧数据为PPS与SPS,接着为I帧。
例如上面00000001后有67,68以及65,41
其中0x67的二进制码为:
0110 0111
4-8为00111,转为十进制7,对应序列参数集SPS
其中0x68的二进制码为:
0110 1000
4-8为01000,转为十进制8,对应图像参数集PPS
其中0x65的二进制码为:
0110 0101
4-8为00101,转为十进制5,对应IDR图像中的片(I帧)
要判断B帧和P帧,则需要到slice层去判断用到“熵编码”。
一个frame是可以分割成多个Slice来编码的,而一个Slice编码之后被打包进一个NAL单元,不过NAL单元除了容纳Slice编码的码流外,还可以容纳其他数据,比如序列参数集SPS。
(NALU类型 & 0001 1111) = 5
if(pData[i] == 0 && pData[i+1] == 0 &&
pData[i+2] == 0 && pData[i+3] == 1 &&
(pData[i+4] & 0x1F) == 5)
{
cout<<"IS I_FRAME"<<endl;
}
else if(..........)
{
}
h264编码格式就看完了,下一个就是NALU 打包到RTP中。