H264协议字段与RTP协议字段之东拼西凑却很有用
简介
在音视频编解码领域中,不仅仅要掌握编码技能和编码原理,还需要掌握其相关的协议字段,这样在我们自己开发一些流媒体传输时,可以选择适合自己的协议进行数据组包,如H264+RTP+UDP,推流到我们的媒体后台,实现我们的流媒体系统
H264协议字段分析
H264是视频编码中相对优秀的一种,比同类型编解码算法更好,在相同带宽下能够提供更好的图像质量!其编解码流程主要包括5个部分:帧间和帧内预测(Estimation)、变换(Transform)和反变换、量化(Quantization)和反量化、环路滤波(Loop Filter)、熵编码(Entropy Coding),更多相关细节,可点击参考
那经过H264编码后的数据是什么样子?
大致就会如上图一样!
图像数据编码后是一个一个NALU单元构成
一
个
N
A
L
U
单
元
=
N
A
L
_
h
e
a
d
e
r
+
R
B
S
P
一个NALU单元 = NAL\_header + RBSP
一个NALU单元=NAL_header+RBSP
NAL header:这个NAL Unit发送的是什么类型的数据,里面包含了解释RBSP数据的相关信息
RBSP(raw byte Sequence Payload):原始字节序列负载,就是存储了视频编码的数据
NALU单元
NALU由这种格式组成:
Start Code 用于标示这是一个NALU 单元的开始,必须是 “00 00 00 01”或“00 00 01”,而且NALU内部其他位置不可能出现以上的字节结构,用于区分一个NALU的开始。
关于两种前缀startCode的区别:
NALU header
forbidden_bit: 此位必须为0,H264协议规定
nal_ref_idc: NAL unit的权重位,值越大,说明此单元的数据越重要,在解码器处理不及时时,可以丢掉为0的NAL unit
- 不等于0时,NALU可能是SPS/PPS/参考帧的片
SPS(Sequence Paramater Set):参数序列集,其保存了一组编码序列GOP所需要的全局参数,如视频尺寸、分辨率等;所以丢失掉SPS会导致GOP这个图像序列无法解码;PPS是图像序列集, - 等于0时,可能是非参考帧的片
nal_unit_type: 如下表所示:
其中,只有1~5才包含图像数据,其中 Slice表示片的意思,一个Slice包含一幅图像的一个或者所有的宏块数据,一个NALU载荷存放的就是Slice片结构,并且每个Slice之前是相互独立的,意思就是此片中的编码数据解码不需要其他片作为依赖,这样的好处就是可以防止错误扩大,如果出现错误就只在当前片内才有
最后,RBSP也就是数据负载了,真实的数据了!在献上整体结构体的数据组成图
感谢岁月斑驳7博主的文章H264 编解码协议详解
RTP协议字段分析
上述H264封装只是在编解码时会用到,如果要发送到网络,则要配置响应的网络协议标准,RTP是一款媒体流实时传输协议,可以用在单播/多播媒体系统中。此外,RTP可以和TCP组成RTCP协议,UDP配合成RUDP协议等,也即是RTP协议在开发中可以自己选择使用UDP还是TCP协议,而RTMP协议属于应用层协议,底层是TCP不可修改,RTP属于传输层的子层
所以,这里我们分析RTP协议字段构成,最后在使用UDP/TCP发送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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
V: RTP协议的版本号,占2bits,当前协议版本号为2
P: 填充标志,占1bit,如果P=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分。
X: 扩展标志,占1bit,如果X=1,则在RTP报头后跟有一个扩展报头
CC: CSRC计数器,占4位,指示CSRC 标识符的个数
M: 1bit,标记解释由设置定义,目的在于允许重要事件在包流中标记出来。如不同的有效载荷有不同的含义,对于视频,标记一帧的结束;对于音频,标记会话的开始。
负载类型 Payload type(PT): 7bits
注:rfc里面对一些早期的格式定义了这个payload type。但是后来的,如h264并没有分配,那就用96来代替。因此现在96以上都不表示特定的格式,具体表示什么要用sdp或者其他协议来协商。
序列号 Sequence number(SN): 16bits,用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1,序列号的初始值是随机产生的。可以用于检查丢包以及进行数据包排序。
时间戳 Timestamp: 32bits,必须使用90kHz时钟频率。
同步信源(SSRC)标识符: 32bits,用于标识同步信源。该标识符是随机随机产生的,参加同一视频会议的两个同步信源不能有相同的SSRC。
特约信源(CSRC)标识符: 每个CSRC标识符占32bits,可以有0~15个。每个CSRC标识了包含在该RTP报文有效载荷中的所有特约信源。(可省略该字段)
在配置协议字段时,还要注意其网络字节序!
关于SSRC和CSRC字段的理解
SSRC同步信源,产生这个媒体流的源头,来自麦克风、camera摄像机还是混合器,参加同一视频会议的两个SSRC不能相同,因为RTP接收方根据SSRC来分组RTP包
CSRC特约信源,当多路不同信源SSRC的RTP包经过混合器时,混合器将把这多路信源RTP流转换成一个流发送出去,首先混合器会把多路流的SSRC记录下来,形成一个列表,叫CSRC表;然后经过混合器发送的第一个RTP包会将CSRC填充CSRC表,此包的复杂部分可能是无意义或无负载数据,接收方接收到后就知道有来自哪些地方的SSRC源了;此后,每经过混合器发送的流,SSRC都填写混合器自己的SSRC,而CSRC则填写流本身自己的SSRC,这样接收方接收到后就知道这个流来自哪里了
H264+RTP组合
RTP与H264组合包在实际发送中,还会再添加RTP的Header头,类似于NAL Header,占用1个字节,添加在RTP协议头后面,NALU前面,如下:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
对应不同的组包方式,如一个RTP包含几个NALU单元,这里展示最简单的,一个RTP包一个NALU单元,直接删除掉NALU的startCode即可,上述:
F= NALU fobidden
NRI = NALU nal ref idc
type = NALU type
就是一一对应关系,同时删除NALU的start code部分,如下:
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|NRI| type | |
+-+-+-+-+-+-+-+-+ |
| |
| Bytes 2..n of a Single NAL unit |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
注意:删除NALU startcode,同时因为NALU header也已经补充到了RTP的相关bit位了,那NALU的header也要省略掉了,最后RTP的协议头在以上数据的最前面(上图没有写出来),F、NRI和Type前面
关于其他组包方式,可以参考H264协议数据如何封装成RTP协议数据,再次感谢jwybobo2007作者
扩展阅读
RTP包封装好后,选择哪个传输层协议就由你定义了,这里提供一个mbedtls传输加密库,由ARM公司研发开源的,特别适合以ARM结构基础的嵌入式系统