H264解码之RTP流解析

RTP概述

RTP全名是Real-time Transport Protocol(实时传输协议)。它是IETF提出的一个标准,对应的RFC文档为RFC3550(RFC1889为其过期版本)。RFC3550不仅定义了RTP,而且定义了配套的相关协议RTCP(Real-time Transport Control Protocol,即实时传输控制协议)。RTP用来为IP网上的语音、图像、传真等多种需要实时传输的多媒体数据提供端到端的实时传输服务。RTP为Internet上端到端的实时传输提供时间信息和流同步,但并不保证服务质量,服务质量由RTCP来提供。

RTP的应用环境

RTP用于在单播或多播网络中传送实时数据。它们典型的应用场合有如下几个。

  • 简单的多播音频会议 语音通信通过一个多播地址和一对端口来实现。一个用于音频数据(RTP),另一个用于控制包(RTCP)。
  • 音频和视频会议 如果在一次会议中同时使用了音频和视频会议,这两种媒体将分别在不同的RTP会话中传送,每一个会话使用不同的传输地址(IP地址+端口)。如果一个用户同时使用了两个会话,则每个会话对应的RTCP包都使用规范化名字CNAME(Canonical Name)。与会者可以根据RTCP包中的CNAME来获取相关联的音频和视频,然后根据RTCP包中的计时信息(Network time protocol)来实现音频和视频的同步。
  • 翻译器和混合 翻译器和混合器都是RTP级的中继系统。翻译器用在通过IP多播不能直接到达的用户区,例如发送者和接收者之间存在防火墙。当与会者能接收的音频编码格式不一样,比如有一个与会者通过一条低速链路接入到高速会议,这时就要使用混合器。在进入音频数据格式需要变化的网络前,混合器将来自一个源或多个源的音频包进行重构,并把重构后的多个音频合并,采用另一种音频编码进行编码后,再转发这个新的RTP包。从一个混合器出来的所有数据包要用混合器作为它们的同步源(SSRC,见RTP的封装)来识别,可以通过贡献源列表(CSRC表,见RTP的封装)可以确认谈话者

RTP包头格式

RTP包头占位12个字节,其字段组成如下图所示:
图一

字段 含义
V RTP协议版本号,占位2bit,当前协议为V=2
P 填充字段,占位1bit,如果P=1,则表示此包的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分。
X 扩展字段,占位1bit,如果x=1,则表示RTP包头后面跟有一个扩展报头;
CC CSRC计数器,占位4bit,表示CSRC标识符的个数;
M 标记,占位1bit,不同的载荷有不同的含义,M=1对于视频,标记一帧的结束;对于音频,标记会话的开始。
PT 有效载荷类型,占位7bit,用于说明RTP报文中有效载荷数据的类型,在流媒体中大部分用来区分音频和视频,便于客户端解析;
Sequence number 序列号,占位16bit,用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1。这个字段当下层的承载协议用UDP的时候,网络状况不好的时候可以用来检查丢包。同时出现网络抖动的情况可以用来对数据进行重新排序,序列号的初始值是随机的,同时音频包和视频包的sequence是分别记数的。
Timestamp 时间戳字段,占位32bit,必须使用90kHz 时钟频率。时戳反映了该RTP报文的第一个八位组的采样时刻。接收者使用时戳来计算延迟和延迟抖动,并进行同步控制。
SSRC 同步信源标识符,占位32bit,用于标志同步信源,该标识符是随机选择的,参加同一视频会议的两个同步信源不能有相同的SSRC。
CSRC 特约信源标识符,每个CSRC标识符占位32bit,可以有0~15个。每个CSRC标识了包含在该RTP报文有效载荷中的所有特约信源。

注:基本的RTP说明并不定义任何头扩展本身,如果遇到X=1,需要特殊处理
如果扩展标志被置位则说明紧跟在报头后面是一个头扩展,其格式如下:
图二
取一段码流如下:
图三
其中:

码流 含义
80 V_P_X_CC
e0 M_PT
00 1e SequenceNum
00 00 d2 f0 Timestamp
00 00 00 00 SSRC

把前两字节换成二进制如下

1000 0000 1110 0000

按顺序解释如下:

10 V
0 P
0 X
0000 CC
1 M
110 0000 PT

NAL单元

每个NAL单元是一个一定语法元素的可变长字节字符串,包括包含一个字节的头信息(用来表示数据类型),以及若干整数字节的负荷数据。一个NAL单元可以携带一个编码片、A/B/C型数据分割或一个序列或图像参数集。

NALU

NALU封包成RTP有三种方式:

  • 单一NAL单元模式
  • 组合封包模式
  • 分片封包模式

一般我们常用的就是单一NAL单元和分片封包模式两种。即我们所使用的RTP+H264一般都是一个RTP包中最多只有一个NALU包;在nal_unit_type的定义中(见表一),0到23是给H264用的,24~31未使用,在rtp打包时,如果一个NALU放在一个RTP包里,可以使用NALU的nal_unit_type,但是当需要把多个NALU打包成一个RTP包,或者需要把一个NALU打包成多个RTP包时,就定义新的type来标识。
表一:

nal_unit_type NAL类型 C
0 未指定
1 不分区、非IDR图像的片
slice_layer_without_partitioning_rbsp( )
2,3,4
2 片分区A
slice_data_partition_a_layer_rbsp( )
2
3 片分区B
slice_data_partition_b_layer_rbsp( )
3
4 片分区C
slice_data_partition_c_layer_rbsp( )
4
5 IDR图像的片
slice_layer_without_partitioning_rbsp( )
2,3
6 辅助增强信息 (SEI)
sei_rbsp( )
5
7 序列参数集
seq_parameter_set_rbsp( )
0
8 图像参数集
pic_parameter_set_rbsp( )
1
9 访问单元分隔符
access_unit_delimiter_rbsp( )
6
10 序列结尾
end_of_seq_rbsp( )
7
11 码流结尾
end_of_stream_rbsp( )
8
12 填充数据
filler_data_rbsp( )
9
13 序列参数集扩展
seq_parameter_set_extension_rbsp( )
14…18 保留
19 未分区的辅助编码图像的片
slice_layer_without_partitioning_rbsp( )
20…23 保留
24…31 未指定

NALU头

NALU 头由一个字节组成, 它的语法如下:
nalu头结构
NAL单元按RTP序列号按序传送。其中,F为禁止位,占1bit,R为重要性指示位,占2个bit;T为负荷数据类型,占5bit。具体如下:

  • 禁止位
    编码中默认值为0,当网络识别此单元中存在比特错误时,可将其设为1,以便接收方丢掉该单元,主要用于适应不同种类的网络环境(比如有线无线相结合的环境)。
  • 重要性指示位
    用于在重构过程中标记一个NAL单元的重要性,值越大,越重要。值为0表示这个NAL单元没有用于预测,因此可被解码器抛弃而不会有错误扩散;值高于0表示此NAL单元要用于无漂移重构,且值越高,对此NAL单元丢失的影响越大。
  • NALU类型位
    可以表示NALU的32种不同类型特征,类型1~12是H.264定义的,类型24~31是用于H.264以外的,RTP负载规范使用这其中的一些值来定义封包和解包,其他值为H.264保留(见表一)。

图像序列

  1. H264一个图像序列的组成:SPS+PPS+SEI+一个I帧+若干个P帧。SPS、PPS、SEI、一个I帧、一个P帧都可以称为一个NALU。

  2. H264的NALU结构:开始码+NALU头+NALU数据
    264常见的帧头数据为:
    00 00 00 01 67 (SPS)
    00 00 00 01 68 (PPS)
    00 00 00 01 65 ( IDR 帧)
    00 00 00 01 61 (P帧)
    00 00 00 01 41 (P帧)

    • 开始码大小为四个字节,是一个固定值00 00 00 01(十六进制),标识一个NALU的开始。
    • NALU头大小为一个字节,如图:
      NALU头
      • F:禁止位,0表示正常,1表示错误,一般都是0
      • NRI:重要级别,11表示非常重要。
      • TYPE:表示该NALU的类型是什么,见上表,由此可知7为序列参数集(SPS),8为图像参数集(PPS),5代表I帧。1代表非I帧。由此可知,61和41其实都是P帧(type值为1),只是重要级别不一样(它们的NRI一个是11BIN,一个是10BIN)。
        那么我们怎么计算呢,我们对数据进行层层分析,以00 00 00 01分割之后的下一个字节就是NALU类型,将其转为二进制数据后,
        解读顺序为从左往右算,如下(对应上图):
        (1)第1位禁止位,值为1表示语法出错
        (2)第2~3位为参考级别
        (3)第4~8为是nal单元类型
        例如上面00000001后有67,68以及65
        其中0x67的二进制码为:
        0110 0111
        4-8位为00111,转为十进制7,参考表一:7对应序列参数集SPS
        其中0x68的二进制码为:
        0110 1000
        4-8位为01000,转为十进制8,参考表一:8对应图像参数集PPS
        其中0x65的二进制码为:
        011 00101
        4-8位为00101,转为十进制5,参考表一:5对应IDR图像中的片(I帧)
        所以判断是否为I帧的算法为:
        (NALU类型 & 0001 1111) = 5 即 (NALU类型 & 31) = 5
        比如0x65 & 31 = 5
    • NALU数据为编码器编出来的图像信息或图像数据。
  3. 五种类型的NALU
    (1)、SPS(序列参数集):NALU头值为0x67(十六进制),NALU头type位值为7(十进制)。
    (2)、PPS(图像参数集):NALU头值为0x68(十六进制),NALU头type位值为8(十进制)。
    (3)、SEI(补充增强信息):NALU头值为0x06(十六进制),NALU头type位值为6(十进制)。
    (4)、I帧:NALU头值为0x65(十六进制),NALU头type位值为5(十进制)。
    (5)、P帧:NALU头值为0x61(十六进制),NALU头type位值为1(十进制)。

  4. H264的NALU打包成RTP包的模式(下面是用到的两种模式)

    • 一个NALU打包成一个RTP包,只需要在一个12字节的RTP包头后添加去掉开始码的NALU即可
      (这种模式在一个NALU的大小小于MTU时使用)。
    • 一个NALU打包成几个RTP包(FU_A模式),在12个字节的RTP头后面加上一个字节的FU indicator和一个字节的FU header。FU indicator前3位是NALU头的前3位,后5位是28(十进制), FU header第1位标记RTP包是否为NALU的第一片,第2位标记RTP包是否为NALU的最后一片。第3位是保 留位,后5位是NALU头的type位

单个NAL包单元

12字节的RTP头后面的就是音视频数据,比较简单。一个封装单个NAL单元包到RTP的NAL单元流的RTP序号必须符合NAL单元的解码顺序。
对于 NALU 的长度小于 MTU 大小的包, 一般采用单一 NAL 单元模式.
对于一个原始的 H.264 NALU 单元常由[Start Code] [NALU Header] [NALU Payload]三部分组成, 其中 Start Code 用于标示这是一个
NALU 单元的开始, 必须是 “00 00 00 01” 或 “00 00 01”, NALU 头仅一个字节, 其后都是 NALU 单元内容.
打包时去除 “00 00 01” 或 “00 00 00 01” 的开始码, 把其他数据封包的 RTP 包即可.
NALU
如有一个 H.264 的 NALU 是这样的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F … ]
这是一个序列参数集 NAL 单元. [00 00 00 01] 是四个字节的开始码, 67 是 NALU 头, 42 开始的数据是 NALU 内容.
封装成 RTP 包将如下:
[ RTP Header ] [ 67 42 A0 1E 23 56 0E 2F ]
即只要去掉 4 个字节的开始码就可以了.

组合封包模式

当 NALU 的长度特别小时, 可以把几个 NALU 单元封在一个 RTP 包中.

FU-A的分片格式

数据比较大的H264视频包,被RTP分片发送。12字节的RTP头后面跟随的就是FU-A分片:
而当 NALU 的长度超过 MTU 时, 就必须对 NALU 单元进行分片封包. 也称为 Fragmentation Units (FUs).
FU-A
1) FU indicator有以下格式:
FU indicator
FU指示字节的类型域的28,29表示FU-A和FU-B。NRI域的值必须根据分片NAL单元的NRI域的值设置。(此处Type就是rtp分片类型) 见下表

Type Packet Type name
0 没有定义
1-23 NAL单元 单个 NAL 单元包.
24 STAP-A 单一时间的组合包
24 STAP-B 单一时间的组合包
26 MTAP16 多个时间的组合包
27 MTAP24 多个时间的组合包
28 FU-A 分片的单元
29 FU-B 分片的单元
30-31 没有定义

2 ) FU header的格式如下:
FU header
S: 1 bit 当设置成1,开始位指示分片NAL单元的开始。当跟随的FU荷载不是分片NAL单元荷载的开始,开始位设为0。
E: 1 bit 当设置成1, 结束位指示分片NAL单元的结束,即, 荷载的最后字节也是分片NAL单元的最后一个字节。
当跟随的FU荷载不是分片NAL单元的最后分片,结束位设置为0。
R: 1 bit
保留位必须设置为0,接收者必须忽略该位。
Type: 5 bits
此处的Type就是NALU头中的Type,取1-23的那个值,表示 NAL单元荷载类型定义,见表一

发布了135 篇原创文章 · 获赞 67 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/y601500359/article/details/97785295