如何实现H.264的实时传输?

2021-09-03 14:55:25 浏览数 (1)

实时视频系统中的媒体传输,绝大多数都会采用RTP(实时传输协议)标准。H.264视频作为当前应用最广泛的视频编码标准,其传输协议也会首选RTP标准。在设计实现H.264的实时传输时,H.264协议基于RTP的打包和解包定义于IETF标准-RFC6184,RTC系统需要遵循这个标准来设计打包和解包处理模块。在通信理论中,这个过程可以被认为是基于传输的信道编码。本篇技术文章带你了解H.264在RTP中的基本格式和技术实践。

#01

基本格式

使用RTP对H.264打包和解包需要遵循IETF标准RFC6184, 我们先来了解一下H.264在RTP中的封包协议。

01

H.264的RTP报头

图1 RTP报头

对于H.264的RTP负载格式而言,RTP报头的格式和RFC 3550里面的定义是一致的,不过有一些字段需要特别说明一下。

标记位 (M):1位

对RTP时间戳所对应访问单元的最后一个数据包来设置标记位,符合视频中M位的正常使用格式,以允许有效的播放缓冲处理。解码器可以使用这个位作为访问单元最后一个数据包的早期指示,但是不能完全依赖这个属性。

负载类型 (PT):7位

没有特别指定的负载类型,需要通过协商来确定。

序列号(SN):16位

根据RFC 3550设置和使用。对于单NAL单元和非交错打包模式,序列号用于确定NAL单元的解码顺序。

时间戳:32位

RTP时间戳设置为视频内容的采样时间戳。必须使用90 kHz时钟频率。

02

H.264的RTP负载类型

H.264的RTP负载可分为三大类,类型如下:

单个NAL单元数据包:

此类RTP负载中仅包含单个NAL单元。负载报头类型编号等于原始NAL单元类型,即从 1 到 23 的范围值,详见H.264规范。

聚合数据包:

此类型用于聚合多个NAL单元成为单个 RTP 负载。这类数据包有四个细分版本:单时间聚合包A (STAP-A)、单时间聚合包B (STAP-B)、16位偏移多时间聚合包 (MTAP16) 和24位偏移多时间聚合包 (MTAP24)。负载类型编号分配给 STAP-A、STAP-B、MTAP16 和 MTAP24 的值分别为 24、25、26 和27。

分片单元:

用于将单个NAL单元分片到多个RTP 数据包。存在两个版本:FU-A 和 FU-B,负载类型编号分别为 28 和 29。

负载类型

数据包类型

数据包类型名

1-23

NAL单元

单个NAL单元包

24

STAP-A

单个时间聚合包

25

STAP-B

单个时间聚合包

26

MTAP16

多个时间聚合包

27

MTAP24

多个时间聚合包

28

FU-A

分片单元

29

FU-B

分片单元

表1 H.264负载类型

03

H.264的RTP打包模式

H.264的RTP打包模式有三种:

单NAL单元模式

所有的接收端都必须支持这种模式,主要应用于兼容低时延应用中的硬件设备。只有单NAL单元数据包可以在这种模式下使用。

非交错模式

建议接收端去支持这种模式,主要应用于低时延应用。只有单NAL单元、STAP-A和FU-A数据包可以在这种模式下使用。

交错模式

有需求的接收端可以去支持这种模式,主要应用于非低延时应用。STAP-B、两种MTAP、FU-A和FU-B数据包可以在这种模式下使用。

负载类型

数据包类型

单NAL单元模式

非交错模式

交错模式

1-23

NAL单元

允许

允许

不允许

24

STAP-A

不允许

允许

不允许

25

STAP-B

不允许

不允许

允许

26

MTAP16

不允许

不允许

允许

27

MTAP24

不允许

不允许

允许

28

FU-A

不允许

允许

允许

29

FU-B

不允许

不允许

允许

表2 H.264打包模式允许的负载类型

单NAL单元和非交错模式中,NAL单元必须以NAL单元解码顺序传输,这两种模式更适合低延时需求的交互系统。

交错模式中NAL单元的传输顺序和解码顺序可以是不一致的,导致接收端的解包过程中需要按照解码顺序重新排序,引入更多的时延,因此并不适合需要低时延的交互系统。

04

H.264的RTP负载报头

图2 H.264的RTP负载报头

H.264的RTP负载报头位于负载的第1个字节,分成三个字段:

F:1位

forbidden_zero_bit。值为 0 表示NAL单元类型字节和负载不应包含位错误或其他语法违规。值为 1 表示NAL单元类型字节和负载可能包含位错误或其他语法违规。

NRI:2位

nal_ref_idc。00值和非零值的语义与H.264规范保持不变。值00表示NAL单元的内容不是用于重建图片间预测的参考图片,这样的NAL单元可以被丢弃并不会导致参考图片的不完整。值大于00表示需要对NAL单元进行解码以保持参考图片的完整性。

类型:5位

负载类型,包括表1里面列举的所有类型。

05

H.264的RTP负载格式

因为只有单NAL单元模式和非交错模式打包模式更适合应用于低时延交互系统中,而这两种打包模式所涉及的只有单NAL数据包、单时间聚合包A(STAP-A)和分片单元A(FU-A)三种RTP负载,所以在这里只对这三种负载格式做个简单的介绍。

单NAL数据包

图3 单NAL数据包负载格式

单NAL数据包就是将原始的NAL单元直接放置到RTP的负载中,NAL单元头就是作为单NAL数据包的负载类型。

单时间聚合包A(STAP-A)

图4 聚合数据包负载格式

聚合数据包的负载中包含一个或者多个聚合单元。一个聚合包可以携带尽可能多的聚合单元;不过聚合数据包中的总数据量应该选择合适大小,以便生成的IP数据包小于MTU大小。聚合数据包负载报头中的NRI字段的值必须是所有聚合NAL单元中最大值。

图5 单时间聚合单元格式

STAP-A数据包中,每个聚合单元的NAL都应该是共享相同的NALU时间。负载的首字节是STAP-A负载报头,每个聚合单元是由两字节的NAL单元尺寸字段和原始NAL单元组成。如果STAP-A数据包中包含两个聚合单元,负载格式如下图:

图6 包含两个聚合单元的STAP-A数据包示例

分片单元A(FU-A)

图7 FU-A数据包负载格式

FU-A数据包的负载包含1字节的分片单元标识(负载报头)、1字节的分片单元报头和分片单元负载。分片单元负载报头中的NRI字段的值等同于被分片NAL单元的值。

分片单元报头的格式如下:

图8 分片单元报头

S: 1 位

起始位。当设置为 1 时,指示一个分片NAL 单元的开始。当 FU 负载不是分片NAL单元的开始片段,设置起始位为 0。

E: 1 位

结束位。当设置为 1 时,指示一个分片NAL单元的结束。当 FU负载不是分片NAL单元的最后一个片段,设置结束位为 0 。

R: 1 位

保留位。必须等于 0,并且必须被接收者忽略。

类型:5位

被分片的原始NAL单元类型(1 - 23)。

#02

实践分享

RTC系统中的视频处理的结构大致如下图,RTP打包解包是视频编解码和传输之间的桥梁。

图9 视频流工作流程

01

H.264打包

H.264的打包的基本流程大致如下:

  • 输入H.264 NAL,判决当前的H.264 NAL的打包格式,可以选择单NAL单元包格式、STAP-A包格式,或者是FU-A格式。MTAP格式一般不在实时系统中使用,考量的重点在于兼顾打包效率和传输效率。
  • Single-NAL-Unit 打包比较简单,一个NAL封装为一个RTP包。
  • STAP-A在NAL包比较小的时候采用,多个相同时间戳的NAL包被打到一个RTP包。
  • FU在NAL包比较大的时候采用,限制RTP包的大小小于MTU。一个NAL包被拆成多个碎片(Fragment), 碎片被打成RTP包。

02

H.264解包

在此只对三种打包模式下的解包过程做一个大致的介绍。

单NAL单元和非交错模式

接收端包括一个接收缓冲器来补偿传输延迟和抖动。接收端将传入的数据包按照接收顺序存储到接收缓冲器中。数据包按RTP序列号的顺序被解包。如果解包的数据包是单个NAL单元包,包中包含的NAL单元直接传递给解码器。如果解包的数据包是 STAP-A,则包含在数据包中的NAL单元按照它们封装在数据包中的顺序被传递给解码器。对于所有 FU-A包含单个NAL单元片段的数据包,解包的片段按其发送顺序恢复出NAL单元,然后传递给解码器。

交错模式

交错模式的解包规则一般是从传输顺序到解码顺序来重新排序NAL单元。在实时系统中应用比较少见,具体过程在此就不展开了。

参考文献

1、RFC 3550 – RTP: A Transport Protocol for Real-time Application

2、RFC 6184 – RTP Payload Format for H.264 Video

0 人点赞