文章

RTP封装音视频

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 0100 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_typeNAL 单元和 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 ( )
5IDR 图像的编码条带 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校验。具体如下:

字段长度(位)描述
synword12同步头,总是0xFFF,代表着⼀个ADTS帧的开始。
id1MPEG版本,设置为0表示MPEG-4,设置为1表示MPEG-2
layer2Layer,始终设置为0
protection_absent1CRC校验位,如果没有CRC,则设置为1,如果有CRC,则设置为0
profile2AAC级别,比如AAC LC=1。profile的值等于Audio Object Type的值减1。
sampling_frequency_index4采样率下标,下标对应的采样率如下 。
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_bit1私有位,MPEG保证不使用,编码时设置为0,解码时忽略
channel_configuration3声道数。
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_copy1原始性,设置为1表示音频为原始内容,设置为0表示非原始内容
home1家庭使用,设置为1表示音频为家庭使用,设置为0表示非家庭使用
copyrigth_identification_bit1版权标识位,用于传输中心注册的版权标识符的下一位,以LSB优先顺序滑动位字符串,将当前位值放在该字段中,如果到达末尾,则回到开头(循环缓冲区)
copyrigth_identification_stat1版权标识起始位,设置为1表示该帧的版权标识位为第一个,否则设置为0
aac_frame_length13帧长度,包括ADTS头部和CRC校验的长度
adts_bufferfullness11缓冲区满度,表示每帧的位保留池状态
number_of_raw_data_blocks_in_frame2AAC帧数减1(RDBs(原始数据块)在ADTS帧中),为确保最大兼容性,请始终每个ADTS帧使用一个AAC帧
crc16CRC校验(根据ISO/IEC 11172-3,条款2.4.3.1),如果Protection absent为0,则存在CRC校验位

4.2. AAC的RTP打包方式3

AACRTP 打包方式就是将 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。

参考

本文由作者按照 CC BY 4.0 进行授权