RTP载荷H264视频流

  1. H264 RTP包解析
    1. 预备

      视频

             由一副副连续的图像构成,由于数据量比较大,因此为了节省带宽以及存储,就需要进行必要的压缩与解压缩,也就是编解码

      h264裸码流

             对一个图像或者一个视频序列进行压缩,即产生码流,采用H264编码后形成的码流就是h264裸码流

      码流传输

             发送端将H264裸码流打包后进行网络传输,接收端接收后进行组包还原裸码流,然后可以再进行存储,转发,或者播放等等相关的处理

             存储转发可以直接使用裸码流,播放则需要进行解码和显示处

      解码显示:       

             一般会解成YUV数据,然后交给显示相关模块做处理,如使用openGL或者D3D等进行渲染(采用3d的方式来显示2d的图像称为渲染

              解码又分软件解码和硬件解码

              软解一般ffmpeg

              硬解则各不同,由各硬件厂商开放sdk来处理,如:hisiintel media sdknvida gpuapple ios videotoolbox

  

    1. h264码流传输

      类似发送网页文本数据有http一样,视频数据在网络上传输也有专门的网络协议来支持,如:rtsprtmp

      由于码流是一帧一帧的图片数据,所以传输的时候也是一帧帧来传输的,因此这里就会涉及到各种类型的帧处理

                            I 帧: 参考帧或者关键帧,可以理解为是一帧完整画面,解码时只需要本帧数据就解码成一幅完整的图片,数据量比较大

                            P帧: 差别帧,只有与前面的帧或I帧的差别数据,需要依赖前面已编码图象作为参考图象,才能解码成一幅完整的图片,数据量小

                            B帧: 双向差别帧,也就是B帧记录的是本帧与前后帧的差别,需要依赖前后帧和I帧才能解码出一幅完整的图片,数据量小

      由于i帧比较大,已经超出mtu最大1500,所以需要拆包分片传输,这里说的拆包发送不是指发送超过1500的数据包时tcp的分段传输或者updip分片传输,而是指rtp协议本身对264的拆包

      rtp打包后将数据交给tcp或者upd进行传输的时候就已经控制在1500以内,这样可以提高传输效率,避免一个I帧万一丢失就会造成花屏或者重传造成延时卡顿等等问题

      (顺便提一句,rtmp打包就比较简单,由于是基于tcp的协议,大包直接交给tcp去做分段传输,rtmp通过设置合适的trunk size去发送一帧帧数据

      既然要进行拆包发送与接收,就少不了需要相关的包结构以及打包组包了,继续。。。。。

                            

    1. H264在网络传输的单元:NALU

        NALU结构:NALU header(1byte) + NALU payload

        header 部分可以判断类型等信息,从右往左5bit

SPS:  0x67    header & 0x1F = 7 

I Frame: 0x65  header & 0x1F = 5 

PPS:  0x68    header & 0x1F = 8  

P Frame: 0x41   header & 0x1F = 1

SEI:  0x66    header & 0x1F = 6

 

        payload 部分可以简单理解为 编码后的264帧数据      

        详细可以去查阅 h264 NALU 语法结

      

    1. h264的RTP打包

下面是 RFC 3550 中规定的 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
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |V=2|P|X|  CC   |M|     PT      |       sequence number         |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                           timestamp                           |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |           synchronization source (SSRC) identifier            |
      +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
      |            contributing source (CSRC) identifiers             |
      |                             ....                              |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

  负载类型 Payload type (PT): 7 bits
  *** Sequence number (SN): 16 bits
  时间戳 Timestamp: 32 bits
  rtp打包h264,包含了三种类型的包:

 

 一个rtp包携带了一帧数据(single)

 多个rtp包携带了一帧数据(FU-A)

 一个rtp包携带了多帧数据(STAP-A)

 

 

在实际应用中绝大部分采用的是前两种方式,对方式1常见的是对nalusps,pps进行打包,因为spspps数据量很小,一个rtp包足以携带,一般采用 sps,pps分别由一个rtp包携带的方式。对IDR数据及其他类型数据通常是采用方式2,因为视频帧数据通常比较大,一个rtp包不足以携带,分成多个rtp包携带,分包携带后对最后一个rtp包的mark字段是要设置为true的。包格式定义详细见    rfc3984


  H.264 Payload 格式定义了三种不同的基本的负载(Payload)结构. 接收端可通过 RTP Payload 的第一个字节来识别它们. 这一个字节类似 NALU 头的格式, 而这个头结构的 NAL 单元类型字段则指出了代表的是哪一种结构。这个字节的结构如下, 可以看出它和 H.264 NALU 头结构是一样的:

      +---------------+
      |0|1|2|3|4|5|6|7|
      +-+-+-+-+-+-+-+-+
      |F|NRI|  Type   |
      +---------------+
 
字段 Type: 这个 RTP payload NAL 单元的类型. 

  这个字段和 H.264 中类型字段的区别是, type 的值为 24 ~ 31 表示这是一个特别格式的 NAL 单元, H.264 , 只取 1~23 是有效的值.

一句话: h264仅用1-23,24以后的用在RTP H264负载类型头中


   


不同类型的NALU的重要性指示如下表所示:

RTP载荷H264视频流

 

  24    STAP-A   单一时间的组合包
  25    STAP-B   单一时间的组合包
  26    MTAP16   多个时间的组合包
  27    MTAP24   多个时间的组合包
  28    FU-A     分片的单元
  29    FU-B     分片的单元
  30-31 没有定义

 

  可能的结构类型分别有:

  1. 单一 NAL 单元模式
     即一个 RTP 包仅由一个完整的 NALU 组成. 这种情况下 RTP NAL 头类型字段和原始的 H.264 NALU 头类型字段是一样的.

  2. 组合封包模式
    即可能是由多个 NAL 单元组成一个 RTP . 分别有4种组合方式: STAP-A, STAP-B, MTAP16, MTAP24.
  那么这里的类型值分别是 24, 25, 26 以及 27.

  3. 分片封包模式
    用于把一个 NALU 单元封装成多个 RTP . 存在两种类型 FU-A FU-B. 类型值分别是 28 29.

 

               1.  NALU   P帧或者B帧比较小的包,直接将NALU打包成RTP包进行传输    RTP header(12bytes) + NALU header (1byte) + NALU payload

               2.  NALU   特别小的包几个NALU放在一个RTP包中  

               3.  FUs(Fragment Units):   I帧长度超过MTU的,就必须要拆包组成RTP包了,FU-AFU-B

                    RTP header (12bytes)+ FU Indicator (1byte)  +  FU header(1 byte) + NALU payload

                         看到这里会不禁思考

                     1. NALU头不见了,如何判断类型?实际上NALU头被分散填充到FU indicatorFU header里面

                         bit位按照从左到右编号0-7来算,nalu头中0-2前三个bit放在FU indicator0-2前三个bit中,后3-7五个bit放入FU header的后3-7五个中      

//NALU header = (FU indicator & 0xe0) | (FU header & 0x1F)   取FU indicator前三和FU header后五  
headerStart[1] = (headerStart[0]&0xE0)|(headerStart[1]&0x1F);  

 因此查看Ip帧类型,遇到FU分片的,直接看第二个字节,Fu header后五位,这个跟直接看NALU头并无差异,一般有:0x850x050x45

                   2.  多个RTP包如何还原组合成回一个完整的I帧? FU header中有标记为判

                         照旧从左到右,Fu header前两个bit表示startend标记,start1表示一个i帧分片开始,end1表示一个i帧分片结

                  3.   如何查看是一个I帧分片开始?   

                        看第一个字节FU Indicator照旧从左到右,Fu Indicator前三个bitNALU头的前三个bit,后五位为类型FU-A2811100),FU-B2911101

                         RTP抓包看下来整个字节是0x7c

                         如果不是分片,第一字节就是NALU头,如:0x67,0x68,0x41

 

    1. 抓包分析如下图

        --->RTP包中接收的264包是不含有0x00,0x00,0x00,0x01头的,这部分是rtp接收以后,另外再加上去的,解码的时候再做判断的

      1. SPS

       RTP载荷H264视频流

     2.  I帧分片开始,第一个字节FU Indicator0x7c, 后五位1110028FU-A

                                第二个字节FU Header0x85前两个bit start1end0 表示 分片开始,后五个bit5I

        RTP载荷H264视频流

        I帧分片0x7c开头,第二个字节0x05, FU Header startend 0,后五个bit5I

         RTP载荷H264视频流

        I帧分片结束7c开头,第二个字节0x45,FU Headrstart 0,end1,后五个bit5I帧, Mark标记一帧结

          RTP载荷H264视频流

 

    3.  p帧,第一个字节:0x41

          RTP载荷H264视频流

 

       4. P帧分片开始:第一个字节FU Indicator0x7c, 后五位1110028FU-A

                                   第二个字节FU Header0x81, 前两个bit start1end0 表示 分片开始,后五个bit 值是1p

           RTP载荷H264视频流

           P帧分片结束:0x5c开头,第二个字节0x41,FU Headrstart 0,end1,后五个bit1P帧, Mark标记一帧结

         RTP载荷H264视频流

    1.  SDP

 

  下面描述了如何在 SDP 中表示一个 H.264 :

  . "m=" 行中的媒体名必须是 "video"
  . "a=rtpmap"
行中的编码名称必须是 "H264".
  . "a=rtpmap"
行中的时钟频率必须是 90000.
  .
其他参数都包括在 "a=fmtp" 行中.

  :

  m=video 49170 RTP/AVP 98
  a=rtpmap:98 H264/90000
  a=fmtp:98 profile-level-id=42A01E; sprop-parameter-sets=Z0IACpZTBYmI,aMljiA==

  下面介绍一些常用的参数.

 

      1. packetization-mode:

  表示支持的封包模式
 
packetization-mode 的值为 0 时或不存在时, 必须使用单一 NALU 单元模式.
 
packetization-mode 的值为 1 时必须使用非交错(non-interleaved)封包模式.
 
packetization-mode 的值为 2 时必须使用交错(interleaved)封包模式.
 
这个参数不可以取其他的值.

 

 

      1. sprop-parameter-sets:

  这个参数可以用于传输 H.264 的序列参数集和图像参数 NAL 单元. 这个参数的值采用 Base64 进行编码. 不同的参数集间用","号隔开.
  

      1. profile-level-id:

  这个参数用于指示 H.264 流的 profile 类型和级别. Base16(十六进制) 表示的 3 个字节. 第一个字节表示 H.264 Profile 类型,

 

三个字节表示 H.264 Profile 级别:
  

      1. max-mbps:

  这个参数的值是一个整型, 指出了每一秒最大的宏块处理速度.

 

参考:

https://www.cnblogs.com/leehm/p/11009504.html

https://blog.****.net/mo4776/article/details/78391969

https://www.cnblogs.com/stnlcd/p/7266797.html

https://blog.****.net/bytxl/article/details/50393198