• RTP Payload Format for H264 Video


    基础传输结构

    rtp中对于h264数据的存储分为两层,分别是

    • VCL: video coding layer 视频编码层
      这是h264中block, macro block 以及 slice级别的定义,目的是为了独立网络传输,进行高效的编解码

    • NAL: network abstraction layer 网络提取层
      NAL层的不同是为了根据不同的网络把VCL数据进行打包成不同的格式,进行传输

    NAL

    H264中的数据分组有头部(譬如: pkt->data),解码器可以很方便的检测到NAL的分界,依次去除NAL进行解码,但为了节省码流,NAL没有在数据头部设立特定元素,如果编码数据存储在介质上,NAL依次紧密相连(这时就无法通过对象取读取一个nal数据了),就无法区分出不同的nal单元,为了解决该问题,存储的时候会在每一个nal前添加0x000001或0x000000。
    但数据内部也可能出现0x000001或0x000000,为此nal中规定不能出现0x000001或0x000000,源数据需要进行如下变换:

    原数据 替换数据
    0X000000 0X00000300
    0X000001 0X00000301
    0X000002 0X00000302
    0X000003 0X00000303

    在网络传输过程中, NAL中的数据被分成NALU(nal unit)单元,然后由RTP进行封装传输
    image.png

    如上图所示,VCL的数据经过上图所示

    • 在SODB的后面添加了结尾若干比特"0",以用字节对齐成了RBSP
    • 在RBSP的基础上,对数据进行循环检测替换上述的0x000000 ~ 0x000002,避免nal单元边界被破坏,成了EBSP(扩展字节序列载荷,在 RBSP 基础上填加了仿校验字节(0x03),它的原因是:在 NALU 加到 Annexb 上时,需要添加每组 NALU 之前的开始码 StartCodePrefix,如果该 NALU 对应的 slice 为一帧的开始(即为 IDR 帧)则用 4 位字节表示:0x00000001,否则用 3 位字节表示:0x000001。为了使 NALU 主体中不包括与开始码相冲突的,在编码时,每遇到两个字节连续为 0,就插入一个字节的 0x03。解码时将 0x03 去掉。也称为 "脱壳操作"。)
    • 在EBSP上添加一个字节的header后成了 NALU,

    NALU

    上述EBSP添加的一个字节的header的结构如下所示:

    +---------------+
    |0|1|2|3|4|5|6|7|
    +-+-+-+-+-+-+-+-+
    |F|NRI|  Type   |
    +---------------+
    

    F:0表示payload 内容没有错误,1表示payload中的内容可能有错误内容或语法错误。
    NRI:用来表示当前NALU单元的重要性(从00 ~11),00最低,表示这不是一个用于帧间预测的参考帧,可以丢弃。
    Type:表示当前NALU的类型,这些类型如下表所示

    
          Table 1.  Summary of NAL unit types and the corresponding packet
                    types
     
          NAL Unit  Packet    Packet Type Name               Section
          Type      Type
          -------------------------------------------------------------
          0        reserved                                     -
          1-23     NAL unit  Single NAL unit packet             5.6
          24       STAP-A    Single-time aggregation packet     5.7.1
          25       STAP-B    Single-time aggregation packet     5.7.1
          26       MTAP16    Multi-time aggregation packet      5.7.2
          27       MTAP24    Multi-time aggregation packet      5.7.2
          28       FU-A      Fragmentation unit                 5.8
          29       FU-B      Fragmentation unit                 5.8
          30-31    reserved 
    

    再具体一点如下所示:

    nal_unit_type	NAL类型
    1	非IDR图像中不采用数据划分的片段
    2	非IDR图像中A类数据划分片段
    3	非IDR图像中B类数据划分片段
    4	非IDR图像中C类数据划分片段
    5	IDR图像的片
    6	补充增强信息单元(SEI)
    7	序列参数集
    8	图像参数集
    9	分界符
    10	序列结束
    11	码流结束
    12	填充
    13…23	保留
    24…31	不保留(RTP打包时会用到)
    

    比较重要的是
    nal_unit_type = 7 RTP 负载的是序列参数集
    nal_unit_type = 8 RTP 负载的是图像参数集
    nal_unit_type = 5 IDR图像的片 (立即刷新图像,I帧给P帧和B帧作为参考)
    23之前的都是signal-nalu单rtp就可以传输完毕,只是对其细分了说明nalu中的数据类型,而超过23的时候多RTP打包开始用到,此时NALU太大了,决定NALU如何拆分打包进 RTP。(因为NALU 大小有可能远远小于RTP payload,也有可能正好等于RTP payload,或者远大于RTP payload) ,那么NALU 就需要再次拆分包,即一帧拆开发送,拆开发送时,rtp的markbit就可以用上来判断当前这个单元是否为一帧的结束,nalu被拆分的时候,timestamp是不变的

     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             |
    |                             ....                              |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |F|NRI|  type   |                                               |
    +-+-+-+-+-+-+-+-+                                               |
    |                                                               |
    |               Bytes 2..n of a Single NAL unit                 |
    |                                                               |
    |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                               :...OPTIONAL RTP padding        |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    

    聚合包 nal_unit_type = 24~27
    本类型用于聚合多个NAL单元到单个RTP荷载中。本包有四种版本,单时间聚合包类型A (STAP-A),单时间聚合包类型B (STAP-B),多时间聚合包类型(MTAP)16位位移(MTAP16), 多时间聚合包类型(MTAP)24位位移(MTAP24)。赋予STAP-A, STAP-B, MTAP16, MTAP24的NAL单元类型号分别是 24,25, 26, 27;
    下图的单时间聚合包

         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
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                          RTP Header                           |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |STAP-A NAL HDR |         NALU 1 Size           | NALU 1 HDR    |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                         NALU 1 Data                           |
        :                                                               :
        +               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |               | NALU 2 Size                   | NALU 2 HDR    |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                         NALU 2 Data                           |
        :                                                               :
        |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                               :...OPTIONAL RTP padding        |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
        Figure 7.  An example of an RTP packet including an STAP-A
                   containing two single-time aggregation units
    

    分片单元 nal_unit_type = 28 或者 29
    将NALU 单元拆分到多个RTP包中发送 典型的就是FU-A或者FU-B

         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        |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
        Figure 14.  RTP payload format for FU-A
    

    其中FU indicator的格式如下

       +---------------+
       |0|1|2|3|4|5|6|7|
       +-+-+-+-+-+-+-+-+
       |S|E|R|  Type   |
       +---------------+
    
        S:1表示第一包
        E:1表示是最后一个包
        R:1表示中间
        Type:类型
    

    RTP负载PS


    H264通常以NALU的方式进行传输,但是H264还有另一种数据组织和传输方式但是使用较少, 其在NALU的基础上又进行了封装,由于每个IDR NALU 前一般都会包含SPS、PPS 等NALU,因此将SPS、PPS、IDR 的NALU 封装为一个PS 包,包括ps 头,然后加上PS system header,PS system map,PES header+h264 raw data。所以一个IDR NALU PS 包由外到内顺序是:PSheader| PS system header | PS system Map | PES header | h264 raw data。对于其它非关键帧的PS 包,就简单多了,直接加上PS头和PES 头就可以了。顺序为:PS header | PES header | h264raw data。以上是对只有视频video 的情况,如果要把音频Audio也打包进PS 封装,也可以。当有音频数据时,将数据加上PES header 放到视频PES 后就可以了。顺序如下:PS 包=PS头|PES(video)|PES(audio),再用RTP 封装发送就可以了。如上图,其中:

    • ES流(Elementary Stream原始流),是音、视频信号经过编码器之后或数据信号的基本码流。只包含一种内容。每个ES都由若  干个存取单元(AU)组成,每个视频或音频AU都是由头部和编码数据两部分组成,1个AU相当于编码的1幅视频图像或1个  音频帧  也可以说,每个AU实际上是编码数据流的显示单元,即相当于解码的1幅视频图像或1个音频帧的取样。
    • PES(Paketized Elementary Stream)是ES经过打包后的码流,长度可变。视频一般一帧一个包,音频一般不超过64KB.
    • PTS--(presentation time stamp)显示时间戳,表示显示单元出现在系统目标解码器(H.264、MJPEG等)的时间。
    • DTS--(Decoding Time Stamp)解码时间戳,表示将存取单元全部字节从解码缓存器移走的时间。
    • PTS和DTS打在PES包头内,是解决音视频同步,防止解码器输入缓存上溢或下溢的关键。每一个pes header都包含pts和dts,是相对SCR(系统参考)的时间戳,以90000为单位,系统时钟频率(H264采样频率)

      最后由rtp将ps包传输出去,RFC2250中建议96 表示PS 封装,建议97 为MPEG-4,建议98 为H264

    REF

    [h264 基础](https://www.cnblogs.com/jimodetiantang/p/9110647.html)
    H264 NALunit
    RTP包 NALU FU-A等之间的关系
    WEBRTC 接收H264 RTP数据流小结
    RTP PS PES ES H264协议学习
    RTP Payload Format for H.264 Video

  • 相关阅读:
    [Leetcode]480. Sliding Window Median
    C++的一些小的知识点
    extern关键字
    c++的默认构造函数 VS 深拷贝(值拷贝) 与 浅拷贝(位拷贝)
    inline-内联函数的优点以及与宏定义的区别
    char类型输出地址
    c++ 对象的内存布局
    Shell 去掉文本中的空格
    牛客网-网易编程题 双端队列找规律
    计算机网络概观
  • 原文地址:https://www.cnblogs.com/ishen/p/12021043.html
Copyright © 2020-2023  润新知