RTP封装音视频
该文记录RTP封装音视频。
RTP封装音视频
1. RTP介绍
详情请看 2023-08-05-音视频协议篇
。
2. RTP结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* 0 1 2 3
* 7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |V=2|P|X| CC |M| PT | sequence number |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | timestamp |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | synchronization source (SSRC) identifier |
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
* | contributing source (CSRC) identifiers |
* : .... :
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
struct RtpHeader
{
/* byte 0 */
uint8_t csrcLen : 4; // CSRC计数器,占4位,指示CSRC 标识符的个数。
uint8_t extension : 1; // 占1位,如果X=1,则在RTP报头后跟有一个扩展报头。
uint8_t padding : 1; // 填充标志,占1位,如果P=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分。
uint8_t version : 2; // RTP协议的版本号,占2位,当前协议版本号为2。
/* byte 1 */
uint8_t payloadType : 7; // 有效载荷类型,占7位,用于说明RTP报文中有效载荷的类型,如GSM音频、JPEM图像等。
uint8_t marker : 1; // 标记,占1位,不同的有效载荷有不同的含义,对于视频,标记一帧的结束;对于音频,标记会话的开始。
/* bytes 2,3 */
uint16_t seq; // 占16位,用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1。接收者通过序列号来检测报文丢失情况,重新排序报文,恢复数据。
/* bytes 4-7 */
uint32_t timestamp; // 占32位,时戳反映了该RTP报文的第一个八位组的采样时刻。接收者使用时戳来计算延迟和延迟抖动,并进行同步控制。
/* bytes 8-11 */
uint32_t ssrc; // 占32位,用于标识同步信源。该标识符是随机选择的,参加同一视频会议的两个同步信源不能有相同的SSRC。
/**
* 标准的RTP Header 还可能存在 0-15个特约信源(CSRC)标识符
* 每个CSRC标识符占32位,可以有0~15个。每个CSRC标识了包含在该RTP报文有效载荷中的所有特约信源
*/
};
struct RtpPacket
{
struct RtpHeader rtpHeader;
uint8_t payload[0];
};
3. RTP封装H2641
3.1. NALU(Network Abstract Layer Unit)
H.264
由一个个的 NALU
组成,每个 NALU
之间使用 00 00 00 01
或 00 00 01
分隔开,每个 NALU
的第一字节都有特殊的含义。
NALU = NALU header + NALU payload
,下图显示的是 NALU header
F
(forbidden_zero_bit):1 位,初始为 0。当网络识别此单元存在比特错误时,可将其设为 1,以便接收方丢掉该单元NRI
(nal_ref_idc):2 位,用来指示该NALU
的重要性等级。值越大,表示当前NALU
越重要。具体大于 0 时取何值,没有明确规定Type
(nal_unit_type):5 位,指出NALU
的类型,如下所示:
nal_unit_type | NAL 单元和 RBSP 语法结构的内容 |
---|---|
0 | 未指定 |
1 | 非 IDR 图像的编码条带 slice_layer_without_partitioning_rbsp ( ) |
2 | 编码条带数据分割块 A slice_data_partition_a_layer_rbsp ( ) |
3 | 编码条带数据分割块 B slice_data_partition_b_layer_rbsp ( ) |
4 | 编码条带数据分割块 C slice_data_partition_c_layer_rbsp ( ) |
5 | IDR 图像的编码条带 slice_layer_without_partitioning_rbsp ( ) |
6 | 辅助增强信息 (SEI) sei_rbsp ( ) |
7 | 序列参数集 seq_parameter_set_rbsp ( ) |
8 | 图像参数集 pic_parameter_set_rbsp ( ) |
9 | 访问单元分隔符 access_unit_delimiter_rbsp ( ) |
10 | 序列结尾 end_of_seq_rbsp ( ) |
11 | 流结尾 end_of_stream_rbsp ( ) |
12 | 填充数据 filler_data_rbsp ( ) |
13 | 序列参数集扩展 seq_parameter_set_extension_rbsp ( ) |
14…18 | 保留 |
19 | 未分割的辅助编码图像的编码条带 slice_layer_without_partitioning_rbsp ( ) |
20…23 | 保留 |
24…31 | 未指定 |
常见 NALU Type
1
2
3
4
5
6
7
8
9
0x06(0 00 00110) SEI type = 6
0x67(0 11 00111) SPS type = 7
0x68(0 11 01000) PPS type = 8
0x65(0 11 00101) IDR type = 5
0x61(0 11 00001) I帧 type = 1
0x41(0 10 00001) P帧 type = 1
0x01(0 00 00001) B帧 type = 1
3.2. H.264三种RTP打包方式2
每一个RTP包都包含一个RTP头部和RTP荷载,这是固定的。而H.264发送数据可支持三种RTP打包方式。
3.2.1. 单NALU打包
一个 RTP
包包含一个完整的 NALU
。所谓单 NALU
打包就是将一整个 NALU 的数据放入 RTP
包的载荷中,这是最简单的一种方式。
3.2.2. 聚合打包
对于较小的 NALU
,一个 RTP
包可包含多个完整的 NALU
。
3.2.3. 分片打包
对于较大的 NALU
,一个 NALU
可以分为多个 RTP
包发送。每个 RTP
包都有大小限制的,因为 RTP
一般都是使用 UDP
发送,UDP
没有流量控制,所以要限制每一次发送的大小,所以如果一个 NALU
的太大,就需要分成多个 RTP
包发送。
RTP
包的格式是绝不会变的,永远多是 RTP头+RTP载荷
。RTP
头部是固定的,那么只能在 RTP
载荷中去添加额外信息来说明这个 RTP
包是表示同一个 NALU
如果是分片打包的话,那么在 RTP
载荷开始有两个字节的信息,然后再是 NALU
的内容
3.2.3.1. FU Indicator
- 高三位:与NALU第一个字节的高三位相同;
- Type:28,表示该RTP包一个分片。
3.2.3.2. FU Header
- S:1表示NAL分片开始,当它为1时,E不能为1
- E:1表示NAL分片结束,当它为1时,S不能为1
- R: 保留位
- Type:NALU的Type
4. RTP封装AAC
4.1. ADTS
4.1.1. ADTS结构
adts的结构是每一帧都带 adts
头部,头部后面跟着是 aac
的原始流(aac es),结构如下:
4.1.2. ADTS头结构
adts的头部一共有15个字段,共7bytes,如果有校验位则会在尾部增加2bytesCRC校验。具体如下:
字段 | 长度(位) | 描述 |
---|---|---|
synword | 12 | 同步头,总是0xFFF,代表着⼀个ADTS帧的开始。 |
id | 1 | MPEG版本,设置为0表示MPEG-4,设置为1表示MPEG-2 |
layer | 2 | Layer,始终设置为0 |
protection_absent | 1 | CRC校验位,如果没有CRC,则设置为1,如果有CRC,则设置为0 |
profile | 2 | AAC级别,比如AAC LC=1。profile的值等于Audio Object Type的值减1。 |
sampling_frequency_index | 4 | 采样率下标,下标对应的采样率如下 。 0: 96000 Hz 1: 88200 Hz 2 : 64000 Hz 3 : 48000 Hz 4 : 44100 Hz 5 : 32000 Hz 6 : 24000 Hz 7 : 22050 Hz 8 : 16000 Hz 9 : 12000 Hz 10 : 11025 Hz 11 : 8000 Hz 12 : 7350 Hz 13 : Reserved 14 : Reserved 15 : frequency is written explictly |
private_bit | 1 | 私有位,MPEG保证不使用,编码时设置为0,解码时忽略 |
channel_configuration | 3 | 声道数。 0: Defined in AOT Specifc Config 1: 1 channel : front - center 2 : 2 channels : front - left, front - right 3 : 3 channels : front - center, front - left, front - right 4 : 4 channels : front - center, front - left, front - right, back - center 5 : 5 channels : front - center, front - left, front - right, back - left, back - right 6 : 6 channels : front - center, front - left, front - right, back - left, back - right, LFE - channel 7 : 8 channels : front - center, front - left, front - right, side - left, side - right, back - left, back - right, LFE - channel 8 - 15 : Reserved |
orininal_copy | 1 | 原始性,设置为1表示音频为原始内容,设置为0表示非原始内容 |
home | 1 | 家庭使用,设置为1表示音频为家庭使用,设置为0表示非家庭使用 |
copyrigth_identification_bit | 1 | 版权标识位,用于传输中心注册的版权标识符的下一位,以LSB优先顺序滑动位字符串,将当前位值放在该字段中,如果到达末尾,则回到开头(循环缓冲区) |
copyrigth_identification_stat | 1 | 版权标识起始位,设置为1表示该帧的版权标识位为第一个,否则设置为0 |
aac_frame_length | 13 | 帧长度,包括ADTS头部和CRC校验的长度 |
adts_bufferfullness | 11 | 缓冲区满度,表示每帧的位保留池状态 |
number_of_raw_data_blocks_in_frame | 2 | AAC帧数减1(RDBs(原始数据块)在ADTS帧中),为确保最大兼容性,请始终每个ADTS帧使用一个AAC帧 |
crc | 16 | CRC校验(根据ISO/IEC 11172-3,条款2.4.3.1),如果Protection absent为0,则存在CRC校验位 |
4.2. AAC的RTP打包方式3
AAC
的 RTP
打包方式就是将 ADTS
帧取出 ADTS
头部,取出 AAC
数据,每帧数据封装成一个 RTP
包。
其中 RTP
载荷的一个字节为 0x00
,第二个字节为 0x10
。第三个字节和第四个字节保存 AAC Data
的大小,最多只能保存 13bit
,第三个字节保存数据大小的高八位,第四个字节的高5位保存数据大小的低5位。
1
2
3
4
rtpPacket->payload[0] = 0x00;
rtpPacket->payload[1] = 0x10;
rtpPacket->payload[2] = (frameSize & 0x1FE0) >> 5; // 高8位
rtpPacket->payload[3] = (frameSize & 0x1F) << 3; // 低5位
4.3. AAC时间戳计算
假设音频的采样率位44100,即每秒钟采样44100次。 AAC一般将1024次采样编码成一帧,所以一秒就有44100/1024=43帧。 RTP包发送的每一帧数据的时间增量为44100/43=1025。 每一帧数据的时间间隔为1000/43=23ms。