H264 RTP 封包原理

1.NAL概述

    NAL全称Network Abstract Layer,即网络抽象层。在H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:视频编码层面(VCL)和网络抽象层面(NAL)。其 中,前者负责有效表示视频数据的内容,而后者则负责格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的传输。

    现实中的传输系统是多样化的,其可靠性,服务质量,封装方式等特征各不相同,NAL这一概念的提出提供了一个视频编码器和传输系统的友好接口,使得编码后的视频数据能够有效地在各种不同的网络环境中传输。

2.  RTP 协议关键参数的设置
         RTP 协议是 IETF 在 1996 年提出的适合实时数据传输的新型协议。RTP 协议实际上是由实时传输协议RTP(Real-time Transport Protocol)和实时传输控制协议RTCP(Real-time Transport Control Protocol)两部分组成。RTP 协议基于多播或单播网络为用户提供连续媒体数据的实时传输服务;RTCP 协议是 RTP 协议的控制部分,用于实时监控数据传输质量,为系统提供拥塞控制和流控制。RTP 协议在RFC3550 中有详细介绍。每一个 RTP 数据包都由固定包头(Header )和载荷(Payload)两个部分组成,其中包头前12个字节的含义是固定的,而载荷则可以是音频或视频数据。RTP 固定包头的格式如图1所示: 
       其中比较关键的参数设置解释如下: 
      (1)标示位(M ):1 位,该标示位的含义一般由具体的媒体应用框架(profile )定义, 目的在于标记处RTP 流中的重要事件。

      (2)载荷类型(PT):7 位,用来指出RTP负载的具体格式。在RFC3551中,对常用的音视频格式的RTP 传输载荷类型做了默认的取值规定,例如,类型2 表明该RTP数据包中承载的是用ITU G.721 算法编码的语音数据,采用频率为 8000HZ,并且采用单声道。 

      (3)序号:16 位,每发送一个 RTP 数据包,序号加 1。接受者可以用它来检测分组丢失和恢复分组顺序。

      (4)时间戳:32 位,时间戳表示了 RTP 数据分组中第一个字节的采样时间,反映出各RTP 包相对于时间戳初始值的偏差。对于RTP 发送端而言,采样时间必须来源于一个线性单调递增的时钟。 

    从 RTP 数据包的格式不难看出,它包含了传输媒体的类型、格式、序列号、时间戳以及是否有附加数据等信息。这些都为实时的流媒体传输提供了相应的基础。而传输控制协议RTCP为 RTP传输提供了拥塞控制和流控制,它的具体包结构和各字段的含义可参考RFC3550,此处不再赘述。
 
3.  H.264 基本流结构及其传输机制

3.1  H.264 基本流的结构

H.264 的基本流(elementary stream,ES)的结构分为两层,包括视频编码层(VCL)和网络适配层(NAL)。视频编码层负责高效的视频内容表示,而网络适配层负责以网络所要求的恰当的方式对数据进行打包和传送。引入NAL并使之与VCL分离带来的好处包括两方面:其一、使信号处理和网络传输分离,VCL 和NAL 可以在不同的处理平台上实现;其二、VCL 和NAL 分离设计,使得在不同的网络环境内,网关不需要因为网络环境不同而对VCL比特流进行重构和重编码。

   H.264 的基本流由一系列NALU (Network Abstraction Layer Unit )组成,不同的NALU数据量各不相同。H.264 草案指出[2],当数据流是储存在介质上时,在每个NALU 前添加起始码:0x000001,用来指示一个 NALU的起始和终止位置。在这样的机制下,解码器在码流中检测起始码,作为一个NALU得起始标识,当检测到下一个起始码时,当前NALU结束。每个NALU单元由一个字节的 NALU头(NALU Header)和若干个字节的载荷数据(RBSP)组成。其中NALU 头的格式如图2 所示:

 
        F:forbidden_zero_bit.1 位,如果有语法冲突,则为 1。当网络识别此单元存在比特错误时,可将其设为 1,以便接收方丢掉该单元。 

        NRI:nal_ref_idc.2 位,用来指示该NALU 的重要性等级。值越大,表示当前NALU越重要。具体大于0 时取何值,没有具体规定。

     Type:5 位,指出NALU 的类型。具体如表1 所示:

 
       需要特别指出的是,NRI 值为 7 和 8 的NALU 分别为序列参数集(sps)和图像参数集(pps)。参数集是一组很少改变的,为大量VCL NALU 提供解码信息的数据。其中序列参数集作用于一系列连续的编码图像,而图像参数集作用于编码视频序列中一个或多个独立的图像。如果解码器没能正确接收到这两个参数集,那么其他NALU 也是无法解码的。因此它们一般在发送其它 NALU 之前发送,并且使用不同的信道或者更加可靠的传输协议(如TCP)进行传输,也可以重复传输。
 
3.2  适用于 H.264 视频的传输机制 
       前面分别讨论了RTP 协议及H.264基本流的结构,那么如何使用RTP协议来传输H.264视频呢?一个有效的办法就是从H.264视频中剥离出每个NALU,在每个NALU前添加相应的RTP包头,然后将包含RTP 包头和NALU 的数据包发送出去。下面就从RTP包头和NALU两方面分别阐述。
 
      完整的 RTP 固定包头的格式在前面图 1 中已经指出,根据RFC3984[3],这里详细给出各个位的具体设置。 
      V:版本号,2 位。根据RFC3984,目前使用的RTP 版本号应设为0x10。 
      P:填充位,1 位。当前不使用特殊的加密算法,因此该位设为 0。 
      X:扩展位,1 位。当前固定头后面不跟随头扩展,因此该位也为 0。 
      CC:CSRC 计数,4 位。表示跟在 RTP 固定包头后面CSRC 的数目,对于本文所要实现的基本的流媒体服务器来说,没有用到混合器,该位也设为 0x0。 
      M:标示位,1 位。如果当前 NALU为一个接入单元最后的那个NALU,那么将M位置 1;或者当前RTP 数据包为一个NALU 的最后的那个分片时(NALU 的分片在后面讲述),M位置 1。其余情况下M 位保持为 0。 
      PT:载荷类型,7 位。对于H.264 视频格式,当前并没有规定一个默认的PT 值。因此选用大于 95 的值可以。此处设为0x60(十进制96)。 
      SQ:序号,16 位。序号的起始值为随机值,此处设为 0,每发送一个RTP 数据包,序号值加 1。 
      TS:时间戳,32 位。同序号一样,时间戳的起始值也为随机值,此处设为0。根据RFC3984, 与时间戳相应的时钟频率必须为90000HZ。 
      SSRC:同步源标示,32 位。SSRC应该被随机生成,以使在同一个RTP会话期中没有任何两个同步源具有相同的SSRC 识别符。此处仅有一个同步源,因此将其设为0x12345678。

      对于每一个NALU,根据其包含的数据量的不同,其大小也有差异。在IP网络中,当要传输的IP 报文大小超过最大传输单元MTU(Maximum Transmission Unit )时就会产生IP分片情况。在以太网环境中可传输的最大 IP 报文(MTU)的大小为 1500 字节。除去IP头20字节,UDP头8字节,RTP头12字节,RTP的最大有效负载为1460字节。如果发送的IP数据包大于MTU,数据包就会被拆开来传送,这样就会产生很多数据包碎片,增加丢包率,降低网络速度。对于视频传输而言,若RTP 包大于MTU 而由底层协议任意拆包,可能会导致接收端播放器的延时播放甚至无法正常播放。因此对于大于MTU 的NALU 单元,必须进行拆包处理。

RFC3984 给出了3 中不同的RTP 打包方案:
(1)Single NALU Packet(单一 NAL 单元模式):
 即一个 RTP 包仅由一个完整的 NALU 组成在一个RTP 包中只封装一个NALU,在本文中对于小于 1400字节的NALU 便采用这种打包方案。 这种情况下 RTP NAL 头类型字段和原始的 H.264的 NALU 头类型字段是一样的.

   (2)Aggregation Packet(
组合封包模式):
在一个RTP 包中封装多个NALU,对于较小的NALU 可以采用这种打包方案,从而提高传输效率。 即可能是由多个 NAL 单元组成一个 RTP 包。 分别有4种组合方式: STAP-A, STAP-B, MTAP16, MTAP24.  那么这里的类型值分别是 24, 25, 26 以及 27.

   (3)Fragmentation Unit(
分片封包模式FU):
一个NALU 封装在多个RTP包中,在本文中,对于大于1400字节的NALU 便采用这种方案进行拆包处理。 用于把一个 NALU 单元封装成多个 RTP 包。 存在两种类型 FU-A 和 FU-B. 类型值分别是 28 和 29.

 FU-A的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
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      | FU indicator  |   FU header   |                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
      |                                                               |
      |                         FU payload                            |
      |                                                               |
      |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                               :...OPTIONAL RTP padding        |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

 FU-B的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
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      | FU indicator  |   FU header   |               DON             |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
      |                                                               |
      |                         FU payload                            |
      |                                                               |
      |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                               :...OPTIONAL RTP padding        |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

  这里解释下DON(解码顺序号):
  在交错打包方式, NAL单元的传输顺序允许和NAL单元的解码顺序不同。解码顺序号(DON)是荷载结构中的一个域 或一个获得变量指示NAL单元的解码顺序。

FU指示字节(FU indicator)有以下格式:
      +---------------+
      |0|1|2|3|4|5|6|7|
      +-+-+-+-+-+-+-+-+
      |F|NRI|  Type   |
      +---------------+

FU头的格式(FU header)如下:
      +---------------+
      |0|1|2|3|4|5|6|7|
      +-+-+-+-+-+-+-+-+
      |S|E|R|  Type   |
      +---------------+

FU头的格式说明:

S: 1 bit
      当设置成1,开始位指示分片NAL单元的开始。当跟随的FU荷载不是分片NAL单元荷载的开始,开始位设为0。
   E: 1 bit
      当设置成1, 结束位指示分片NAL单元的结束,即, 荷载的最后字节也是分片NAL单元的最后一个字节。当跟随的
      FU荷载不是分片NAL单元的最后分片,结束位设置为0。
   R: 1 bit
      保留位必须设置为0,接收者必须忽略该位。
      
   Type: 5 bits
      NAL单元荷载类型定义1~12.

RTP封包处理:
 对于一个原始的 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 包即可.

4.  H.264 流媒体传输系统的实现

       一个完整的流媒体传输系统包含服务器端和客户端两个部分[5][6]。对于服务器端,其主要任务是读取H.264 视频,从码流中分离出每个NALU 单元,分析NALU 的类型,设置相应的 RTP 包头,封装 RTP 数据包并发送。而对于客户端来说,其主要任务则是接收 RTP数据包,从RTP 包中解析出NALU 单元,然后送至解码器进行解码播放。该流媒体传输系统的框架如图3 所示。

参考资料:http://blog.sina.com.cn/s/blog_6fe1bc2e0100s5x2.html

没有更多推荐了,返回首页