GB28181协议支持的H264的PS封装实现
2016-08-16 11:24
567 查看
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播放时会一闪而过,修改后的函数如下:
3、PES头中,如果只包括PTS时间戳,则需要修改为下面代码:
修改的时候把DTS去掉了,然后配套修改了第8个字节,但没有检查原来参考文章中设置的是同时包括PTS和DTS,所以需要关注修改:第七字节的高两位是PTS和DTS指示位,00表示无PTS无DTS,01禁止使用,10表示PES头部字段会附加PTS结构,11表示PTS和DTS都包括
4、RTP的组包发送上面,根据H264的RTP打包方式,有单NALU、FU-A、FU-B多种形式,根据适配需要调整。
最开始接触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多种形式,根据适配需要调整。
相关文章推荐
- Xen前后端分离设备驱动模型通信过程分析(包括事件通道、授权表、环缓冲区)
- Apache UIMA(二):配置Eclipse来使用UIMA
- java编程思想--继承
- Android 6.0 新功能及主要 API 变更
- Mybatis缓存
- 命令别名(alias)
- Linux中的伪终端编程
- 【caffe-Windows】mnist实例编译之model的使用-classification
- 入门lr
- 数据结构实验之查找四:二分查找
- 在linux下永久保存路由表的写法(Suse和Redhat)
- 【caffe-Windows】mnist实例编译之model的使用-classification
- SetMsgHandled()
- mysql 联合更新
- EF for MySql 开发配置手册
- 创建mysql触发器
- 属性配置文件工具类
- tomcat内存溢出
- 1-1-3 Makefile工程管理初探
- hdu3555(数位dp)