PS封装格式:GB28181协议RTP传输
PS封装格式:GB28181协议RTP传输
- 1. GB28181要求的RTP流格式
- 2. 头部信息
在安防行业,有个协议是无论如何都要适配的,因为公安监控网络用的就是它,它就是: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 12
static 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 14
static 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 18
static 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);//24Bytesbits_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*//*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 *//*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*//*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 19
static 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;
}
关于时间戳的比特位结构,下图表示的比较清晰:
PS封装格式:GB28181协议RTP传输相关推荐
- 海康摄像头PS流格式解析(RTP/PS/H264)
海康威视视频录像以PS格式打包,解析的过程按照PS包-->system header--->program stream map--->音视频PES包一路下来,海康在包中自定义了一些 ...
- 网络流媒体--PS封装格式
(1)PS流基本概念: MPEG2-PS是一种多路复用数字音频,视频等的封装容器.PS是Program Stream(程序流或节目流)的简称.程序流将一个或多个分组但有共同的时间基准的基本数据流(PE ...
- GB28181协议实现系列之----PS流封装(6)
前面的文章收到了不少同仁希望我开放源码,由于之前时间太忙了,一直在做另外产品线的架构设计及架构演化,核心模块的编码,关键技术问题解决等工作确实有较长的时间未继续写文章了.以至于博客的排名越来越低,从2 ...
- GB28181协议实现系列之----IPC音视频PS封装(5)
RTP封装PS RTP报文头格式(见RFC3550 Page12): 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 12 3 4 5 6 7 8 9 0 1 + ...
- RTP PS PES ES H264协议学习
参考:https://www.cnblogs.com/wainiwann/p/7477794.html https://blog.csdn.net/chenhande1990chenhan/artic ...
- ps流 转发_(转)RTP协议全解(H264码流和PS流)
写在前面:RTP的解析,网上找了很多资料,但是都不全,所以我力图整理出一个比较全面的解析, 其中借鉴了很多文章,我都列在了文章最后,在此表示感谢. 互联网的发展离不开大家的无私奉献,我决定从我做起,希 ...
- php视频流传输,视频流传输协议RTP/RTCP/RTSP/HTTP的区别
用一句简单的话总结:RTSP发起/终结流媒体.RTP传输流媒体数据.RTCP对RTP进行控制,同步.之所以以前对这几个有点分不清,是因为CTC标准里没有对RTCP进行要求,因此在标准RTSP的代码中没 ...
- 视频流传输协议RTP/RTCP/RTSP/HTTP的区别
在转载之前:我研究主要是基于HTTP的视频流,正在研读的论文名:"Modeling and Analyzing the Influence of Chunk Size Variation o ...
- MPEG PS流格式
MPEG PS流格式 1. PS包头 图1 1) Pack start code:包起始码字段,值为0x000001BA的位串,用来标志一个包的开始 2) SCR base,SCR extenstio ...
最新文章
- 趣谈HTTP协议中的那些MIME header
- Django之Form组件
- 20145202、20145225、20145234 《信息安全系统设计基础》实验五 简单嵌入式WEB 服务器实验...
- 牛客题霸 NC14 按之字形顺序打印二叉树
- gridview实现分页
- React-Native填坑之TextInput value属性
- C语言经典算法100例-031-判断星期几
- (数据科学学习手札05)Python与R数据读入存出方式的总结与比较
- PHP无法使用file_get_contents或者curl_init()函数解决办法
- MySQL数据库安装和介绍
- 如何开始学习使用TensorFlow?
- bat代码如何处理中文目录
- JAVA的Properties集合
- java计算机毕业设计旅游管理系统MyBatis+系统+LW文档+源码+调试部署
- 字号,行距,磅,像素……(word排版)
- laravel-admin多图上传小技巧
- 今日头条的排名算法_今日头条三大智能算法揭秘 让你快速明头条的推荐原理...
- Windows制作启动U盘太麻烦,用CMD快速解决
- php动漫随机图源码,随机图片API源码
- 基于Labview的重量检测系统