博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PS封装格式:GB28181协议RTP传输
阅读量:4135 次
发布时间:2019-05-25

本文共 9448 字,大约阅读时间需要 31 分钟。

转载 PS封装格式:GB28181协议RTP传输

    在安防行业,有个协议是无论如何都要适配的,因为公安监控网络用的就是它,它就是:GB28181。而这份协议主要由海康制定,所以除了海康其他厂商想要适配都会少许有点儿麻烦。题主前东家便是海康,这里稍微分析下该协议几个容易搞混的细节,记录在此,方便以后自己查阅,也方便诸位。

 

1. GB28181要求的RTP流格式

    首先,我们来看看I帧的PS流格式,这里需要注意的是SPS、PPS之前要加上PES头部。如下图所示,其中绿色部分就是我们拿到的H.264裸流数据,须将它拆分成三段并在前面加上PES头部。这一点在GB28181标准中没有细说,需要通过分析海康IPC流才能看出。

流格式
    一般情况下IDR帧很大,超过了RTP的负载长度限制(1400字节),所以上面这一个I帧要拆分成若干包RTP分多次发送。第一包的结构如上图所示,第二包以后RTP的结构就简单多了,它是这样的:
在这里插入图片描述
    上面提到的是I帧的情况,相比它,P/B帧的帧格式真是太简单了,因为它既没有SYS、PSM,也没有SPS、PPS:
在这里插入图片描述
P/B帧大小一般不超过1400字节,如果超过1400字节,也需分成多包RTP数据进行传输,超出1400部分的第二包RTP结构:
在这里插入图片描述

2. 头部信息

    首先是RTP包头信息,它一般长度为12个字节

#define RTP_HDR_LEN 12static int gb28181_make_rtp_header(char *pData, int marker_flag, unsigned short cseq, long long curpts, unsigned int ssrc){    bits_buffer_s      bitsBuffer;    if (pData == NULL)        return -1;    bitsBuffer.i_size = RTP_HDR_LEN;    bitsBuffer.i_data = 0;    bitsBuffer.i_mask = 0x80;    bitsBuffer.p_data =    (unsigned char *)(pData);        memset(bitsBuffer.p_data, 0, RTP_HDR_SIZE);    bits_write(&bitsBuffer, 2, RTP_VERSION);    /* rtp version   版本号,固定为2  */    bits_write(&bitsBuffer, 1, 0);                           /* rtp padding     */    bits_write(&bitsBuffer, 1, 0);                           /* rtp extension     */    bits_write(&bitsBuffer, 4, 0);                            /* rtp CSRC count */    bits_write(&bitsBuffer, 1, (marker_flag));         /* rtp marker  结束标志位,一帧图像的最后一包RTP置1*/    bits_write(&bitsBuffer, 7, 96);                  /* rtp payload type,96代表PS*/    bits_write(&bitsBuffer, 16, (cseq));            /* rtp sequence      */    bits_write(&bitsBuffer, 32, (curpts));         /* rtp timestamp      */    bits_write(&bitsBuffer, 32, (ssrc));         /* rtp SSRC          */    return 0;}

然后是PSH头部,它占了14个字节:

#define PS_HDR_LEN  14static int gb28181_make_ps_header(char *pData, unsigned long long s64Scr){    unsigned long long lScrExt = (s64Scr) % 100;        s64Scr = s64Scr / 100;    bits_buffer_s      bitsBuffer;    bitsBuffer.i_size = PS_HDR_LEN;        bitsBuffer.i_data = 0;    bitsBuffer.i_mask = 0x80;    bitsBuffer.p_data =    (unsigned char *)(pData);    memset(bitsBuffer.p_data, 0, PS_HDR_LEN);    bits_write(&bitsBuffer, 32, 0x000001BA);            /*start codes 起始码*/    bits_write(&bitsBuffer, 2,     1);                        /*marker bits '01b'*/    bits_write(&bitsBuffer, 3,     (s64Scr>>30)&0x07);     /*System clock [32..30]*/    bits_write(&bitsBuffer, 1,     1);                        			/*marker bit*/    bits_write(&bitsBuffer, 15, (s64Scr>>15)&0x7FFF);   /*System clock [29..15]*/    bits_write(&bitsBuffer, 1,     1);                        		/*marker bit*/    bits_write(&bitsBuffer, 15, s64Scr & 0x7fff);        /*System clock [14..0]*/    bits_write(&bitsBuffer, 1,     1);                        /*marker bit*/    bits_write(&bitsBuffer, 9,     0);                     /*SCR extension*/    bits_write(&bitsBuffer, 1,     1);                        /*marker bit*/    bits_write(&bitsBuffer, 22, (255)&0x3fffff);        /*bit rate(n units of 50 bytes per second.)*/    bits_write(&bitsBuffer, 2,     3);                        /*marker bits '11'*/    bits_write(&bitsBuffer, 5,     0x1f);                    /*reserved(reserved for future use)*/    bits_write(&bitsBuffer, 3,     0);                        /*stuffing length*/    return 0;}

后面是SYS,它包含了流类型信息,比如音频还是视频、视频编码格式

#define SYS_HDR_LEN 18static int gb28181_make_sys_header(char *pData){        bits_buffer_s      bitsBuffer;    bitsBuffer.i_size = SYS_HDR_LEN;    bitsBuffer.i_data = 0;    bitsBuffer.i_mask = 0x80;    bitsBuffer.p_data =    (unsigned char *)(pData);    memset(bitsBuffer.p_data, 0, SYS_HDR_LEN);    /*system header*/    bits_write( &bitsBuffer, 32, 0x000001BB);    /*start code*/    bits_write( &bitsBuffer, 16, SYS_HDR_LEN-6);  /* 减6,是因为start code加上length这两位,占了6个字节*/    bits_write( &bitsBuffer, 1,     1);            /*marker_bit*/    bits_write( &bitsBuffer, 22, 50000);        /*rate_bound*/    bits_write( &bitsBuffer, 1,  1);            /*marker_bit*/    bits_write( &bitsBuffer, 6,  1);            /*audio_bound*/    bits_write( &bitsBuffer, 1,  0);            /*fixed_flag */    bits_write( &bitsBuffer, 1,  1);            /*CSPS_flag */    bits_write( &bitsBuffer, 1,  1);            /*system_audio_lock_flag*/    bits_write( &bitsBuffer, 1,  1);            /*system_video_lock_flag*/    bits_write( &bitsBuffer, 1,  1);            /*marker_bit*/    bits_write( &bitsBuffer, 5,  1);            /*video_bound*/    bits_write( &bitsBuffer, 1,  0);            /*dif from mpeg1*/    bits_write( &bitsBuffer, 7,  0x7F);         /*reserver*/    /*audio stream bound*/    bits_write( &bitsBuffer, 8,  0xC0);         /*stream_id 音频的流id*/    bits_write( &bitsBuffer, 2,  3);            /*marker_bit */    bits_write( &bitsBuffer, 1,  0);            /*PSTD_buffer_bound_scale*/    bits_write( &bitsBuffer, 13, 512);          /*PSTD_buffer_size_bound*/    /*video stream bound*/    bits_write( &bitsBuffer, 8,  0xE0);         /*stream_id 视频的流id*/    bits_write( &bitsBuffer, 2,  3);            /*marker_bit */    bits_write( &bitsBuffer, 1,  1);            /*PSTD_buffer_bound_scale*/    bits_write( &bitsBuffer, 13, 2048);         /*PSTD_buffer_size_bound*/    return 0;}

接下来是PSM,这里面记录了媒体信息,比如音视频的编码格式:

static int gb28181_make_psm_header(char *pData){        bits_buffer_s      bitsBuffer;    bitsBuffer.i_size = PSM_HDR_LEN;     bitsBuffer.i_data = 0;    bitsBuffer.i_mask = 0x80;    bitsBuffer.p_data =    (unsigned char *)(pData);    memset(bitsBuffer.p_data, 0, PSM_HDR_LEN);//24Bytes    bits_write(&bitsBuffer, 24,0x000001);    /*start code*/    bits_write(&bitsBuffer, 8, 0xBC);        /*map stream id*/    bits_write(&bitsBuffer, 16,18);            /*program stream map length*/     bits_write(&bitsBuffer, 1, 1);            /*current next indicator */    bits_write(&bitsBuffer, 2, 3);            /*reserved*/    bits_write(&bitsBuffer, 5, 0);             /*program stream map version*/    bits_write(&bitsBuffer, 7, 0x7F);        /*reserved */    bits_write(&bitsBuffer, 1, 1);            /*marker bit */    bits_write(&bitsBuffer, 16,0);             /*programe stream info length*/    bits_write(&bitsBuffer, 16, 8);         /*elementary stream map length    is*/    /*audio*/    bits_write(&bitsBuffer, 8, 0x90);       /*stream_type 音频编码格式G711*/    bits_write(&bitsBuffer, 8, 0xC0);        /*elementary_stream_id*/    bits_write(&bitsBuffer, 16, 0);         /*elementary_stream_info_length is*/    /*video*/    bits_write(&bitsBuffer, 8, 0x1B);       /*stream_type 视频编码格式H.264*/    bits_write(&bitsBuffer, 8, 0xE0);        /*elementary_stream_id*/    bits_write(&bitsBuffer, 16, 0);         /*elementary_stream_info_length */    /*crc (2e b9 0f 3d)*/    bits_write(&bitsBuffer, 8, 0x45);        /*crc (24~31) bits*/    bits_write(&bitsBuffer, 8, 0xBD);        /*crc (16~23) bits*/    bits_write(&bitsBuffer, 8, 0xDC);        /*crc (8~15) bits*/    bits_write(&bitsBuffer, 8, 0xF4);        /*crc (0~7) bits*/    return 0;}

最后是PES头部,它记录了帧的时间戳,DTS可以不填,如果填写要和PTS保持一致,且同一帧数据的PTS要也要一样(即SPS、PPS、IDR的PES要一致):

#define PES_HDR_LEN 19static int gb28181_make_pes_header(char *pData, int stream_id, int payload_len, unsigned long long pts, unsigned long long dts){        bits_buffer_s      bitsBuffer;    bitsBuffer.i_size = PES_HDR_LEN;    bitsBuffer.i_data = 0;    bitsBuffer.i_mask = 0x80;    bitsBuffer.p_data =    (unsigned char *)(pData);    memset(bitsBuffer.p_data, 0, PES_HDR_LEN);    /*system header*/    bits_write( &bitsBuffer, 24,0x000001);    /*start code*/    bits_write( &bitsBuffer, 8, (stream_id));    /*streamID*/    bits_write( &bitsBuffer, 16,(payload_len)+13);    /*packet_len pes剩余头部以及后面的es长度之和,比如SPS长度+13*/    bits_write( &bitsBuffer, 2, 2 );        /*'10'*/    bits_write( &bitsBuffer, 2, 0 );        /*scrambling_control*/    bits_write( &bitsBuffer, 1, 1 );        /*priority*/    bits_write( &bitsBuffer, 1, 1 );        /*data_alignment_indicator*/    bits_write( &bitsBuffer, 1, 0 );        /*copyright*/    bits_write( &bitsBuffer, 1, 0 );        /*original_or_copy*/    bits_write( &bitsBuffer, 1, 1 );        /*PTS_flag 是否有PTS*/    bits_write( &bitsBuffer, 1, 1 );        /*DTS_flag 是否有DTS信息*/    bits_write( &bitsBuffer, 1, 0 );        /*ESCR_flag*/    bits_write( &bitsBuffer, 1, 0 );        /*ES_rate_flag*/    bits_write( &bitsBuffer, 1, 0 );        /*DSM_trick_mode_flag*/    bits_write( &bitsBuffer, 1, 0 );        /*additional_copy_info_flag*/    bits_write( &bitsBuffer, 1, 0 );        /*PES_CRC_flag*/    bits_write( &bitsBuffer, 1, 0 );        /*PES_extension_flag*/    bits_write( &bitsBuffer, 8, 10);        /*header_data_length*/         /*PTS,DTS*/        bits_write( &bitsBuffer, 4, 3 );                    /*'0011'*/    bits_write( &bitsBuffer, 3, ((pts)>>30)&0x07 );     /*PTS[32..30]*/    bits_write( &bitsBuffer, 1, 1 );    bits_write( &bitsBuffer, 15,((pts)>>15)&0x7FFF);    /*PTS[29..15]*/    bits_write( &bitsBuffer, 1, 1 );    bits_write( &bitsBuffer, 15,(pts)&0x7FFF);          /*PTS[14..0]*/    bits_write( &bitsBuffer, 1, 1 );    bits_write( &bitsBuffer, 4, 1 );                    /*'0001'*/    bits_write( &bitsBuffer, 3, ((dts)>>30)&0x07 );     /*DTS[32..30]*/    bits_write( &bitsBuffer, 1, 1 );    bits_write( &bitsBuffer, 15,((dts)>>15)&0x7FFF);    /*DTS[29..15]*/    bits_write( &bitsBuffer, 1, 1 );    bits_write( &bitsBuffer, 15,(dts)&0x7FFF);          /*DTS[14..0]*/    bits_write( &bitsBuffer, 1, 1 );    return 0;}

关于时间戳的比特位结构,下图表示的比较清晰:

在这里插入图片描述

你可能感兴趣的文章
中国最完整的sysctl.conf优化方案
查看>>
高性能服务器设计
查看>>
性能扩展问题要趁早
查看>>
PlentyOfFish 网站架构学习
查看>>
Tomcat集群与负载均衡
查看>>
多服务器的用户身份认证方案
查看>>
Tomcat 的集群和负载均衡
查看>>
Lucene构建index性能调整
查看>>
Map Reduce - the Free Lunch is not over?
查看>>
众说纷“云”:看云计算在存储领域异军突起
查看>>
Using Nutch 0.8.1 for Intranet Crawling and Searching
查看>>
类似Google构架的开源项目Hadoop近获社区关注
查看>>
hadoop 分布式文件系统:体系和设计
查看>>
用Hadoop搭建分布式存储和分布式运算集群
查看>>
IBM推动MapReduce发布Eclipse插件
查看>>
Linux 内核剖析
查看>>
使用 Linux 系统调用的内核命令
查看>>
Yahoo!社区架构
查看>>
Tailrank 网站架构
查看>>
Web缓存加速指南
查看>>