欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Rtp载荷H264解包过程分析,ffmpeg解码qt展示

程序员文章站 2022-07-14 20:31:43
...

一、H264流格式  

1.H264算法在概念上分为两层:    

        VCL层:视频编码层,负责高效的内容表示;    

        NAL层:网络提取层,负责对视频数据进行打包和传送。    

        今天主要记录一下NAL层的知识,以备后期查看。

2.网络抽象层单元 (NALU)

       NALU 头由一个字节组成, 它的语法如下: 
      +---------------+
      |0|1|2|3|4|5|6|7|
      +-+-+-+-+-+-+-+-+
      |F|NRI|  Type  |
      +---------------+ 

     F: 1 个比特.  forbidden_zero_bit. 在 H.264 规范中规定了这一位必须为 0. 
     NRI: 2 个比特. nal_ref_idc. 取 00 ~ 11, 似乎指示这个 NALU 的重要性, 如 00 的 NALU 解码器可以丢弃它而不影响图像,不过一般情况下不太关心这个属性. 
     Type: 5 个比特.nal_unit_type. 这个 NALU 单元的类型. 
     Type       Packet                 Type name                        
      -------------------------------------------------------------------
      0           undefined                                    
      1-23      NAL unit     Single NAL unit packet per H.264  

      1     不分区,非IDR图像的片
      2     片分区A
      3     片分区B
      4     片分区C
      5     IDR图像中的片
      6     补充增强信息单元(SEI)
      7     SPS
      8     PPS
      9     序列结束
     10    序列结束
     11    码流借宿
     12    填充
     13-23 保留 
      24         STAP-A      Single-time aggregation packet     
      25         STAP-B      Single-time aggregation packet     
      26         MTAP16     Multi-time aggregation packet      
      27         MTAP24     Multi-time aggregation packet      
      28         FU-A          Fragmentation unit                 
      29         FU-B          Fragmentation unit                 
      30-31    undefined   
      --------------------------------表1------------------------------------

     H264 over RTP基本上分三种类型:
     (1)Single NAL unit packet 也就是实际的NAL类型,可以理解为一个包就是一帧H264数据,这个在实际中是比较多的。
     (2)Aggregation packet 一包数据中含有多个H264帧。
     STAP-A 包内的帧含有相同的NALU-Time,没有DON
     STAP-B 包内的帧含有相同的NALU-Time,有DON
     MTAP16 包内的帧含有不同的NALU-Time,timestamp offset = 16
     MTAP24 包内的帧含有不同的NALU-Time,timestamp offset = 24
     封装在Aggregation packet中的 NAL单元大小为65535字节
     (3) Fragmentation unit 一帧数据被分为多个RTP包,这也是很常见的,特别是对于关键帧。现存两个版本FU-A,FU-B。实际应用就是要加上个H264 STREAM 的头h264_stream_head = 0x00,0x00,0x00,0x01 4字节,送去解码即可。

3.分包规则

单个NAL单元包(1-23)

     对于 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 包即可。

     一个封装单个NAL单元包到RTP的NAL单元流的RTP序号必须符合NAL单元的解码顺序。单个NAL单元包的结构显示如图。(NAL单元的第一字节和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
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |F|NRI|      type     |                                                                            |
      +-+-+-+-+-+-+-+-+                                                                              |
      |                                                                                                          |
      |               Bytes 2..n of a Single NAL unit                                          |
      |                                                                                                          |
      |                                                    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                    :...OPTIONAL RTP padding          |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    一个包就是一帧数据。h264_stream_head + NAL_unit_type... 就可以直接送去解码了。 

组合包(24-27)

  单时间组合包(24-25)

       STAP应该用于当组合在一起的NAL单元共享相同的NALU时刻。STAP-A(24)荷载不包括DON,至少包含一个单时刻组合单元. STAP-B(25)荷载包含一个16位的无符号解码顺序号(DON) (网络字节序)紧跟至少一个单时刻组合单元.

       DON域指定STAP-B传输顺序中第一个NAL单元的DON值. 对每个后续出现在STAP-B中的NAL单元,它的DON值等于(STAP-B中前一个NAL的DON值+1)%65535, %是取模运算。

       单时刻组合单元有一个16位无符号大小信息(网络字节序),它指示后续NAL单元的大小(以字节为单位)(不包括这两个字节,但包括NAL单元类型字节),后面紧跟NAL单元本身, 包括它的NAL单元类型字节. 单时刻聚合单元在RTP荷载中是字节对齐的,但是可以不是32位字边界对齐。

       STAP-A:一个RTP包包含一个STAP-A. STAP包含两个单时刻组合单元:

      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         |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

     STAP-B:一个RTP包包含一个STAP-B. STAP包含两个单时刻组合单元:

       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-B NAL HDR |              DON                           | NALU 1 Size   |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      | NALU 1 Size   | NALU 1 HDR    | NALU 1 Data                              |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                                                   +
      :                                                                                                         :
      +                         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                           | NALU 2 Size                   | NALU 2 HDR              |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                    NALU 2 Data                                 |
      :                                                                                                          :
      |                                                     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                      :...OPTIONAL RTP padding        |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        看这个结构应该很清楚了,先是16位的长度,就可以得到一帧,h264_stream_head + NALU 1 HDR...送去解码。再算下一帧。需要注意的这个NALU Size 是不包括他本身这2个字节。STAP-B还要考虑DON。

   多时间组合包(26-27)

        多时刻时间包的NAL单元荷载有16位的无符号解码顺序号基址(DONB) (网络字节序)以及一个或多个多时刻聚合单元,DONB 必须包含MTAP中NAL单元的第一个NAL的DON的值。

        NAL解码顺序中的第一个NAL单元不必要是封装在MTAP中的第一个NAL单元

        两个多时刻组合单元都有16位的无符号大小信息用于后续NAL单元(网络字节序),一个8位无符号解码序号差值(DOND), 和n位 (网络字节序) 时戳位移(TS 位移)用于本NAL单元,n可以是16/24. 不同MTAP类型的选择是应用相关的时戳位移越大, MTAP的灵活性越大, 但是负担也越大。

         MTAP16/MTAP24多时刻组合单元的结构如图示。

MTAP16:
       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
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      :        NAL unit size                  |      DOND                 |  TS offset      |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |  TS offset          |                                                                              |
      +-+-+-+-+-+-+-+-+              NAL unit                                                  |
      |                                                                                                         |
      |                                                    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                    :
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

MTAP24:
       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
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      :               NALU unit size             |          DOND        |   TS offset      |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |         TS offset                             |                                                    |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                                                   |
      |                                                NAL unit                                           |
      |                                                   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                    :
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        一个包中的组合单元的开始/结束不要求位于32位的边界。跟随NAL单元的DON 等于(DONB + DOND) % 65536,  %代表取摸操作. 本文没有指定MTAP内的NAL单元如何排序,但大多数情况,应该使用NAL单元解码顺序。

        时戳位移域必须设置成等于以下公式的值:如果NALU-time大于等于包的RTP时戳,则时戳位移等于(NALU-time - 包的RTP时戳).如果NALU-time小于包的RTP时戳,则时戳位移等于 NALU-time + (2^32 - 包的RTP时戳)。

(1)一个RTP包包含一个多时刻MTAP16类型的组合包,包括两个多时刻组合单元 

       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                                         |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |MTAP16 NAL HDR | decoding order number base  | NALU 1 Size |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |  NALU 1 Size     |  NALU 1 DOND       |       NALU 1 TS offset        |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |  NALU 1 HDR   |  NALU 1 DATA                                                      |
      +-+-+-+-+-+-+-+-+                                                                             +
      :                                                                                                         :
      +                         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                           | NALU 2 SIZE                   |         NALU 2 DOND  |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       NALU 2 TS offset       |  NALU 2 HDR   |       NALU 2 DATA      |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                         |
      :                                                                                                         :
      |                                                    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                     :...OPTIONAL RTP padding        |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

(2)一个RTP包包含一个多时刻MTAP24类型的组合包,包括两个多时刻组合单元

       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                                        |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |MTAP24 NAL HDR |  decoding order number base | NALU 1 Size |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |  NALU 1 Size   | NALU 1 DOND |       NALU 1 TS offs                    |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |NALU 1 TS offs |  NALU 1 HDR   |      NALU 1 DATA                       |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                                                   +
      :                                                                                                         :
      +                         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                           | NALU 2 SIZE                   |         NALU 2 DOND  |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       NALU 2 TS offset                                  |          NALU 2 HDR    |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |  NALU 2 DATA                                                                                 |
      :                                                                                                         :
      |                                                    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                     :...OPTIONAL RTP padding        |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

       看这个结构应该很清楚了,先是16位的DONB,然后是16位的长度,8位的DOND,根据DONB计算出DON,去掉时间戳(16-24bits),就可以得到一帧,h264_stream_head + NALU 1 HDR...。得到该RTP包中所有的NAL单元后,根据DON确定解码顺序。需要注意的这个NALU Size 是不包括他本身这2个字节。

    分片单元 (FUs)(28-29)

       当 NALU 的长度超过 MTU 时, 就必须对 NALU 单元进行分片封包,NAL单元的一个分片由整数个连续NAL单元字节组成. 每个NAL单元字节必须正好是该NAL单元一个分片的一部分。相同NAL单元的分片必须使用递增的RTP序号连续顺序发送(第一和最后分片之间没有其他的RTP包)。相似, NAL单元必须按照RTP顺序号的顺序装配。

       当一个NAL单元被分片运送在分片单元(FUs)中时,被引用为分片NAL单元。STAPs,MTAP不可以被分片。 FUs不可以嵌套,即,一个FU 不可以包含另一个FU. 运送FU的RTP时戳被设置成分片NAL单元的NALU时刻.

       FU-A的RTP荷载格式。FU-A由1字节的分片单元指示1字节的分片单元头,和分片单元荷载组成。

       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荷载格式. FU-B由1字节的分片单元指示,1字节的分片单元头,和解码顺序号(DON)以及分片单元荷载组成。

       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        |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

       对于分片NAL单元的第一个分片如果用于交错打包方式,则必须使用NAL单元类型FU-B。NAL单元类型FU-B MUST不可以用于其他情况。换句话, 在交错打包方式,每个被分片的NALU,FU-B作为第一个分片,后面跟随的是一个或多个FU-A分片。

       FU指示字节有以下格式:

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

       FU指示字节的类型域的28,29表示FU-A和FU-B。NRI域的值必须根据分片NAL单元的NRI域的值设置。

       FU头的格式如下:

      +--------------------+
      |0 |1|2 |3|4 |5|6|7|
      +-+-+-+-+-+-+-+-+
      |S|E|R|    Type    |
      +--------------------+

      S:1 表示是一帧的开始包

      E:1 表示是一帧的结束包,和RTP marker位一致

      R:0 必须

      这里要注意一下,组包时,NAL unit type 必须自己拼装FU Indicator前四字节+ FU Header后四字节。也就是type字段是 FU header里的nal_unit_type = (fu_indicator & 0xe0) | (fu_header & 0x1f)等帧收齐了,加上H264_streaming_head + nal_unit_type....送去解码。(上述部分内容来源于网络)

二、数据分析

首先看一下序列参数集sps、图像参数集pps的处理方式:

Rtp载荷H264解包过程分析,ffmpeg解码qt展示

{0x80, 0x60, 0x00, 0x01, 0x05, 0xac, 0x32, 0xf8, 0x11, 0xe1, 0xa6, 0x6a, 0x67, 0x42, 0x00, 0x29, 
0x8d, 0x8d, 0x40, 0x3c, 0x01, 0x13, 0xf2, 0xcd, 0x41, 0x40, 0x80, 0x81, 0xe1, 0x10, 0x8d, 0xc0}

红色部分为RTP头,这部分在解析H264时可以暂时不用处理,先忽略掉。后面第一个字节为0x67,PayLoadType=0x67 & 0x1F,计算后通过表1可以看到为sps,在处理这部分时只需要去掉RTP头部,在前面加上00 00 00 01四个字节后直接写入缓存。

Rtp载荷H264解包过程分析,ffmpeg解码qt展示

{0x80, 0x60, 0x00, 0x02, 0x05, 0xac, 0x32, 0xf8, 0x11, 0xe1, 0xa6, 0x6a, 0x68, 0xca, 0x43, 0xc8}

同样,根据sps的分析过程,该帧为pps,处理方式与sps一样即可。

Rtp载荷H264解包过程分析,ffmpeg解码qt展示

{0x80, 0x60, 0x00, 0x03, 0x05, 0xac, 0x32, 0xf8, 0x11, 0xe1, 0xa6, 0x6a, 0x7c, 0x85, 0x88, 0x80, 
0x00, 0x40, 0x00, 0x00, 0xa7, 0xff, 0xff, 0xc5, 0xc5, 0x00, 0x02, 0xbf, 0xc0, 0x03, 0xdb, 0x8a,  截取了一部分数据。

红色部分为RTP头,后面一个字节0x7C为分片单元指示,0x85为分片单元头。

0x7C & 0x1F = 28(FU-A类型) ,0x85的二进制为:1000 0101‬

       1                       0                    0              0 0 1 0 1

开始标志(1)      结束标志(0)          0            NALType(5)

通过分析可以看到数据包为FU-A类型,该分片是一帧的开始,而且是关键帧。在处理FU-A时,上面已经介绍了方法,再来计算一下NAL unit type= 0x7C & 0xE0 | NALType,为0x65,在组包时需要把RTP头、分片单元指示字节和分片单元头字节去掉,在前面加上0x00 0x00 0x00 0x01 0x65字节然后存入缓存即可。

Rtp载荷H264解包过程分析,ffmpeg解码qt展示

{0x80, 0x60, 0x00, 0x04, 0x05, 0xac, 0x32, 0xf8, 0x11, 0xe1, 0xa6, 0x6a, 0x7c, 0x05, 0xea, 0x3f, 
0x09, 0xf3, 0xda, 0x7f, 0x57, 0x7f, 0xa7, 0xf5, 0xff, 0xfb, 0xe2, 0xba, 0xdd, 0x77, 0xd2, 0xff, 截取了一部分数据。

红色部分为RTP头,后面一个字节0x7C为分片单元指示,0x05为分片单元头。

0x7C & 0x1F = 28(FU-A类型) ,0x05的二进制为:0000 0101‬

       0                       0                    0              0 0 1 0 1

开始标志(0)      结束标志(0)          0            NALType(5)

通过分析可以看出该包为中间包,处理方式比较简单,去掉RTP头、分片单元指示分片单元头,继续存入缓存中即可。

Rtp载荷H264解包过程分析,ffmpeg解码qt展示

{0x80, 0xe0, 0x00, 0x2c, 0x05, 0xac, 0x32, 0xf8, 0x11, 0xe1, 0xa6, 0x6a, 0x7c, 0x45, 0x1c, 0x67, 
0xba, 0x5a, 0x9a, 0x2e, 0x6a, 0x73, 0x98, 0xfa, 0x99, 0x8b, 0x86, 0x2f, 0xcf, 0xf1, 0x4c, 0x5b,截取了一部分数据。

红色部分为RTP头,后面一个字节0x7C为分片单元指示,0x45为分片单元头。

0x7C & 0x1F = 28(FU-A类型) ,0x45的二进制为:0100 0101‬

       0                       1                    0              0 0 1 0 1

开始标志(0)      结束标志(1)          0            NALType(5)

通过分析可以看出该包为结束包,处理方式比较简单,去掉RTP头、分片单元指示分片单元头,继续存入缓存中即可。

到此一帧数据提取完毕,就可以送给ffmpeg解析了。

三、使用ffmpeg解码H264

在使用ffmpeg解码时,需要循环接收你提取的H264数据,然后再进行解析即可。

int pos = 0;
do
{
    uint8_t *poutBuf;
    int pout_len;
    int len = av_parser_parse2(m_pCodecParserContext, m_pCodecContext, 
		&poutBuf, &pout_len, (uint8_t*)pData + pos, nLength - pos,
		AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE);
    pos += len;
    if (pout_len > 0)
    {
        AVPacket packet;
	av_init_packet(&packet);
	packet.data = poutBuf;
	packet.size = len ;
	packet.pts = 90000/25 * Count++;
	int ret = -1;
	ret = avcodec_send_packet(m_pCodecContext, &packet);
	if (ret < 0)
	{
	    continue;
	}
	while (!ret)
	{
		ret = avcodec_receive_frame(m_pCodecContext, m_pSrcFrame);
		if (!ret)
		{
			msleep(20);
			int w = m_pCodecContext->width;
			int h = m_pCodecContext->height;
			if (m_pRGBSwsContext == NULL)
			{
				m_pRGBSwsContext = sws_getContext(w, h, m_pCodecContext->pix_fmt, w, h, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
				av_image_alloc(m_pFrameRGB->data, m_pFrameRGB->linesize, w, h, AV_PIX_FMT_RGB32, 1);
			}
			sws_scale(m_pRGBSwsContext, (uint8_t const * const *)m_pSrcFrame->data, m_pSrcFrame->linesize, 0, h, m_pFrameRGB->data, m_pFrameRGB->linesize);
			//把这个RGB数据 用QImage加载
			QImage tmpImg((uchar *)m_pFrameRGB->data[0], m_pCodecContext->width, m_pCodecContext->height, QImage::Format_RGB32);
			QImage image = tmpImg.copy();
			//把图像复制一份 传递给界面显示
			emit signal_sendQImage(image);
			av_frame_unref(m_pSrcFrame);
		}

	}
    }
}

这是使用Qt + VS开发的,视频能够正常播放。

相关标签: C++ Qt 算法