GB28181协议支持的H264的PS封装实现
  Wpb04DveGzXI 2023年11月02日 37 0


1、写在前面:

最开始接触H264的PS封装的时候,参考的是:​​关于对H264码流的PS的封装的相关代码实现​​ , 确实是很有帮助,但完全参照这个实现,发现问题也很多,主要还是对MPEG213818的封装协议理解不深产生,所以我们在参考代码实现时,还是需要对原理做深入细致的分析,特别是封装涉及到bit级别的配置,一个bit配错了,可能就播放不了,所以记录下,做个备份。

2、封装需要基本了解的概念:

RTP:是流媒体实时传输协议,RTP头有12个字节

H264视频帧:由NALU单元组成,其中I帧起始是00 00 00 01 65

                                                非I帧 00 00 00 01 41

                                                 SPS 00 00 00 01 67

                                                 PPS 00 00 00 01 68

根据上面参考文章的说法,I帧前面需要增加PS头+System 头+ System Map 头+ PES 头

非I帧前面增加PS 头 + PES 头


3、对比参考文章做的一些修改:

1、PS封装头的长度是可以变化的,不是固定长度

      参考文章中PS头:

      

  1. #define PS_HDR_LEN  14  
  2. #define SYS_HDR_LEN 18  
  3. #define PSM_HDR_LEN 24  
  4. #define PES_HDR_LEN 19

我们定义的长度如下:

#define PS_HDR_LEN  14
#define PSM_HDR_LEN 24
#define SYS_HDR_LEN 18
#define PES_HDR_LEN 14


2、关键的PTS和DTS是播放的关键因素,PTS:显示时间戳,DTS:解码时间戳

      PTS可以是一个相对值,以90KHZ采样,25fps的视频为例,每帧视频的步长应该为3600

      另外:参考文章中PTS的计算和DTS的计算有问题,封装后的视频通过VLC播放时会一闪而过,修改后的函数如下:




[cpp] ​​ view plain​​ ​​ copy​​



  1. static void Packet_PS_header(char* pDestBuf, int length, int
  2. {  
  3. long long lScrExt = 0;//(currPts) % 100;
  4. long s64Scr = currPts;//currPts / 100;
  5.   
  6.     bits_buffer_t bits;  
  7. if
  8.     {  
  9. return
  10.     }  
  11.     bits_initwrite( &bits, length, pDestBuf);  
  12. /*start codes*/
  13. /*marker bits '01b'*/
  14. /*System clock [32..30]*/
  15. /*marker bit*/
  16. /*System clock [29..15]*/
  17. /*marker bit*/
  18. /*System clock [29..15]*/
  19. /*marker bit*/
  20. /*System clock [14..0]*/
  21. /*marker bit*/
  22. /*bit rate(n units of 50 bytes per second.)*/
  23. /*marker bits '11'*/
  24. /*reserved(reserved for future use)*/
  25. /*stuffing length*/
  26. }  



3、PES头中,如果只包括PTS时间戳,则需要修改为下面代码:

修改的时候把DTS去掉了,然后配套修改了第8个字节,但没有检查原来参考文章中设置的是同时包括PTS和DTS,所以需要关注修改:第七字节的高两位是PTS和DTS指示位,00表示无PTS无DTS,01禁止使用,10表示PES头部字段会附加PTS结构,11表示PTS和DTS都包括



[cpp] ​​ view plain​​ ​​ copy​​



  1. static void gb28181_make_pes_header (unsigned char *dst , int32_t dstlen, int32_t data_length, int
  2. {  
  3. short
  4.     bits_buffer_t bits;  
  5.     bits_initwrite( &bits, dstlen, dst);  
  6. // header
  7.     bits_write( &bits, 8, 0xe0 );  
  8.   
  9. //pes_packet_length : es len and the following pes len
  10. //
  11. //第七字节的高两位是PTS和DTS指示位,00表示无PTS无DTS,01禁止使用,10表示PES头部字段会附加PTS结构,11表示PTS和DTS都包括
  12. //
  13. //8
  14. //UINT64 i_scr = I_SCR(_iFrameIndextemp);
  15.   
  16. /*'0010'*/
  17. /*PTS[32..30]*/
  18.     bits_write( &bits, 1, 1 );   
  19. /*PTS[29..15]*/
  20.     bits_write( &bits, 1, 1 );  
  21. /*PTS[14..0]*/
  22.     bits_write( &bits, 1, 1 );  
  23. }  



4、RTP的组包发送上面,根据H264的RTP打包方式,有单NALU、FU-A、FU-B多种形式,根据适配需要调整。


1、写在前面:

最开始接触H264的PS封装的时候,参考的是:​​关于对H264码流的PS的封装的相关代码实现​​ , 确实是很有帮助,但完全参照这个实现,发现问题也很多,主要还是对MPEG213818的封装协议理解不深产生,所以我们在参考代码实现时,还是需要对原理做深入细致的分析,特别是封装涉及到bit级别的配置,一个bit配错了,可能就播放不了,所以记录下,做个备份。

2、封装需要基本了解的概念:

RTP:是流媒体实时传输协议,RTP头有12个字节

H264视频帧:由NALU单元组成,其中I帧起始是00 00 00 01 65

                                                非I帧 00 00 00 01 41

                                                 SPS 00 00 00 01 67

                                                 PPS 00 00 00 01 68

根据上面参考文章的说法,I帧前面需要增加PS头+System 头+ System Map 头+ PES 头

非I帧前面增加PS 头 + PES 头


3、对比参考文章做的一些修改:

1、PS封装头的长度是可以变化的,不是固定长度

      参考文章中PS头:

      

  1. #define PS_HDR_LEN  14  
  2. #define SYS_HDR_LEN 18  
  3. #define PSM_HDR_LEN 24  
  4. #define PES_HDR_LEN 19

我们定义的长度如下:

#define PS_HDR_LEN  14
#define PSM_HDR_LEN 24
#define SYS_HDR_LEN 18
#define PES_HDR_LEN 14


2、关键的PTS和DTS是播放的关键因素,PTS:显示时间戳,DTS:解码时间戳

      PTS可以是一个相对值,以90KHZ采样,25fps的视频为例,每帧视频的步长应该为3600

      另外:参考文章中PTS的计算和DTS的计算有问题,封装后的视频通过VLC播放时会一闪而过,修改后的函数如下:




[cpp] ​​ view plain​​ ​​ copy​​



  1. static void Packet_PS_header(char* pDestBuf, int length, int
  2. {  
  3. long long lScrExt = 0;//(currPts) % 100;
  4. long s64Scr = currPts;//currPts / 100;
  5.   
  6.     bits_buffer_t bits;  
  7. if
  8.     {  
  9. return
  10.     }  
  11.     bits_initwrite( &bits, length, pDestBuf);  
  12. /*start codes*/
  13. /*marker bits '01b'*/
  14. /*System clock [32..30]*/
  15. /*marker bit*/
  16. /*System clock [29..15]*/
  17. /*marker bit*/
  18. /*System clock [29..15]*/
  19. /*marker bit*/
  20. /*System clock [14..0]*/
  21. /*marker bit*/
  22. /*bit rate(n units of 50 bytes per second.)*/
  23. /*marker bits '11'*/
  24. /*reserved(reserved for future use)*/
  25. /*stuffing length*/
  26. }  



3、PES头中,如果只包括PTS时间戳,则需要修改为下面代码:

修改的时候把DTS去掉了,然后配套修改了第8个字节,但没有检查原来参考文章中设置的是同时包括PTS和DTS,所以需要关注修改:第七字节的高两位是PTS和DTS指示位,00表示无PTS无DTS,01禁止使用,10表示PES头部字段会附加PTS结构,11表示PTS和DTS都包括



[cpp] ​​ view plain​​ ​​ copy​​



  1. static void gb28181_make_pes_header (unsigned char *dst , int32_t dstlen, int32_t data_length, int
  2. {  
  3. short
  4.     bits_buffer_t bits;  
  5.     bits_initwrite( &bits, dstlen, dst);  
  6. // header
  7.     bits_write( &bits, 8, 0xe0 );  
  8.   
  9. //pes_packet_length : es len and the following pes len
  10. //
  11. //第七字节的高两位是PTS和DTS指示位,00表示无PTS无DTS,01禁止使用,10表示PES头部字段会附加PTS结构,11表示PTS和DTS都包括
  12. //
  13. //8
  14. //UINT64 i_scr = I_SCR(_iFrameIndextemp);
  15.   
  16. /*'0010'*/
  17. /*PTS[32..30]*/
  18.     bits_write( &bits, 1, 1 );   
  19. /*PTS[29..15]*/
  20.     bits_write( &bits, 1, 1 );  
  21. /*PTS[14..0]*/
  22.     bits_write( &bits, 1, 1 );  
  23. }  



4、RTP的组包发送上面,根据H264的RTP打包方式,有单NALU、FU-A、FU-B多种形式,根据适配需要调整。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
Wpb04DveGzXI