zoukankan      html  css  js  c++  java
  • 国标28181视频流分析

    转载:《GB28181协议支持的H264的PS封装实现》

    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头:

    #define PS_HDR_LEN  14  
    #define SYS_HDR_LEN 18  
    #define PSM_HDR_LEN 24  
    #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播放时会一闪而过,修改后的函数如下:

    static void Packet_PS_header(char* pDestBuf, int length, int currPts)  
    {  
        unsigned long long lScrExt = 0;//(currPts) % 100;  
        unsigned long s64Scr = currPts;//currPts / 100;  
      
        bits_buffer_t bits;  
        if ( NULL == pDestBuf)  
        {  
            return PS_Error_Param;  
        }  
        bits_initwrite( &bits, length, pDestBuf);  
        bits_write(&bits, 32, 0x000001BA);            /*start codes*/  
        bits_write(&bits, 2,  1);                     /*marker bits '01b'*/  
        bits_write(&bits, 3,  (s64Scr>>30)&0x07);     /*System clock [32..30]*/  
        bits_write(&bits, 1,  1);                     /*marker bit*/  
        bits_write(&bits, 15, (s64Scr>>15)&0x7FFF);   /*System clock [29..15]*/  
        bits_write(&bits, 1,  1);                     /*marker bit*/  
        bits_write(&bits, 15, s64Scr&0x7fff);         /*System clock [29..15]*/  
        bits_write(&bits, 1,  1);                     /*marker bit*/  
        bits_write(&bits, 9,  lScrExt&0x01ff);        /*System clock [14..0]*/  
        bits_write(&bits, 1,  1);                     /*marker bit*/  
        bits_write(&bits, 22, (160001)&0x3fffff);        /*bit rate(n units of 50 bytes per second.)*/  
        bits_write(&bits, 2,  3);                     /*marker bits '11'*/  
        bits_write(&bits, 5,  0x1f);                  /*reserved(reserved for future use)*/  
        bits_write(&bits, 3,  0);                     /*stuffing length*/  
    }  

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

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

     
    static void gb28181_make_pes_header (unsigned char *dst , int32_t dstlen, int32_t data_length, int pts)  
    {  
        short datalen = data_length + 8;  
        bits_buffer_t bits;  
        bits_initwrite( &bits, dstlen, dst);  
        bits_write( &bits, 24, 0x000001 ); // header  
        bits_write( &bits, 8, 0xe0 );  
      
        bits_write( &bits, 16, datalen); //pes_packet_length : es len and the following pes len  
        bits_write( &bits, 8, 0x8c ); //  
        bits_write( &bits, 2, 0x02 ); //第七字节的高两位是PTS和DTS指示位,00表示无PTS无DTS,01禁止使用,10表示PES头部字段会附加PTS结构,11表示PTS和DTS都包括  
        bits_write( &bits, 6, 0x00 ); //  
        bits_write( &bits, 8, 0x05 ); //8  
        //UINT64 i_scr = I_SCR(_iFrameIndextemp);  
      
        bits_write( &bits, 4, 2 );                    /*'0010'*/  
        bits_write( &bits, 3, ((pts)>>30)&0x07 );     /*PTS[32..30]*/  
        bits_write( &bits, 1, 1 );   
        bits_write( &bits, 15,((pts)>>15)&0x7FFF);    /*PTS[29..15]*/  
        bits_write( &bits, 1, 1 );  
        bits_write( &bits, 15,(pts)&0x7FFF);          /*PTS[14..0]*/  
        bits_write( &bits, 1, 1 );  
    }  

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

  • 相关阅读:
    mysql中删除表
    js上传文件获取客户端地址
    form表单普通提交预览显示,读取显示tmp文件
    PHP中获取中英文混合字符串长度[主要是指个数,而不是字符串长度](转)
    离开页面提醒功能 (实现博客园离开编辑页面时的提醒功能)(转)
    Google maps API开发(一)(转)
    Python的getattr(),setattr(),delattr(),hasattr()
    Python读写文件
    Python命令行解析argparse常用语法使用简介
    面向对象设计与分析实例
  • 原文地址:https://www.cnblogs.com/lynsen/p/8231525.html
Copyright © 2011-2022 走看看