海康7816使用ps流来封装h.264数据,这里使用的解码器无法识别ps流,因此需要将h264数据从ps流里提取出来

对于ps流的规定可以参考13818-1文档

这里从7816里获取到一些数据取样

00 00 01 BA 44 73 26 B8 34 01 00 00 03 FE FF FF 00 00 00 0100 00 01 BC00 5A E0 FF 00 24 40 0E 48 4B 00 01 0D AF C5 D3 E0 07 FF FF FF FF 41 12 48 4B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2C 1B E0 00 10 42 0E 00 00 A0 21 02 C0 02 40 12 1F FF 00 1C 21 91 C0 00 0C 43 0A 00 00 FE 00 7D 03 03 E8 03 FF BD BD 00 00 BF BF 00 00 00 00 00 0000 00 01 E0 00 1A 8C 80 0A 21 1C C9 AE 0D FF FF FF FF FC 00 00 00 01 67 42 00 1E 95 A8 2C 04 99 00 00 01 E0 00 0E 8C 0003 FF FF FC 00 00 00 01 68 CE 3C 80 00 00 01 E0 13 FA 8C 00 02 FF FD 。。。

如上是一个i帧的数据的开始部分,如下是一个非i帧的数据的开始部分

00 00 01 BA 44 73 27 99 34 01 00 00 03 FE FF FF 00 00 00 03 00 00 01 E0 07 12 8C 80 0A 21 1C C9 E6 4D FF FF FF FF F8。。。

可见都是以00 00 01 BA开头,这是ps的包头(Program Stream pack header),其中00 00 01是pack_start_code,是一个数据包的开始标识,接下来的1byte(BA)是流标识(stream_id),在文档13818-1的Table 2-33和2.5.3.4节有Program Stream pack header的描述。

这里把上面i帧的的(Program Stream pack header列出来

00 00 01 BA 44 73 26 B8 34 01 00 00 03 FE FF FF 00 00 00 01

根据文档描述包头最少有14个字节,第14个字节的最后3bit说明了包头14字节后填充数据的长度,这里是pack_stuffing_length=FE&0x07=6,有6byte的填充数据,既是FF FF 00 00 00 01,海康7816使用这部分填充数据来说明每帧的序号,01说明是第1帧数据。

要注意的是包头可能还有系统标题头,id为bb,他也是包头的一部分,并且,他的长度并未算在pack_stufing_length里,比如:

00 00 01 BB 00 0C 80 CC F5 04 E1 7F E0 E0 E8 C0 C0 20

这里起始码后的 00 0C 说明了其后数据的长度,这里是12个字节

接在Program Stream pack header后的是以00 00 01 BC开始的一个包,00 00 01是pack_start_code,BC是stream_id流标识,说明跟在Program Stream pack header后的是Program Stream map。文档13818-1的Table 2-35和2.5.4.2节有Program Stream pack header的描述。

跟在00 00 01 BC后的两位是说明了Program Stream map,他也是pes包的一种,包的长度program_stream_map_length,这里是00 5A,说明跟在其后的数据长度为90,跳过这其后的90byte数据是以00 00 01 E0开始的包,E表示是GB/TXXXX.2或GB/TAAAA.2视频流编号xxxx规格的pes包了,0表示流id为0,h264数据就在这个包里。

从Program Stream map里我们还能得知pes里的流是何种流(stream_type和elementary_stream_id表明),以及帧率()等

1110XXXX(0xex)表示视频数据,111XXXXX表示audio数据,其后的帧有关信息共5字节,2字节PES包长度是00 1A,表示此PES数据包的长度是0x001a 即26字节;2字节标准位信息是8C 80,5字节中的最后一字节表示附加数据长度是0A,跟在附加数据长度后的就是视频数据负载了。

pes包可以有多个,这里的i帧就把数据放到了多个pes包里,这里的非i帧就只有一个pes包

有了以上信息就已经可以从7816里剥离出h246数据了,更详细的说明请参考文档。

截取一段pes包头进行分析

00 00 01 E0 00 1A 8C 80 0A 21 1C C9 AE 0D FF FF FF FF FC 
00 1A: 2字节表示长度
8C(10 00 1 1 00): 首先是固定值10,。
接下来的两位为(PES加扰控制字段)PES_scrambling_control,这里是00,表示没有加扰(加密)。剩下的01,10,11由用户自定义。
接下来第4位为PES优先级字段(PES_priority),当为1时为高优先级,0为低优先级。这里为1。
接下来第3位为(数据对齐指示符字段)PESdata_alignment_indicator,
接下来第2位为版权位,
接下来第1位为版权位,
80(10 000000):
首先是PTS,DTS标志字段,这里是10,表示有PTS,没有DTS。
接下来第6位是ESCR标志字段,这里为0,表示没有该段
接下来第5位是ES速率标志字段,,这里为0,表示没有该段
接下来第4位是DSM特技方式标志字段,,这里为0,表示没有该段
接下来第3位是附加版权信息标志字段,,这里为0,表示没有该段
接下来第2位是PES CRC标志字段,,这里为0,表示没有该段
接下来第1位是PES扩展标志字段,,这里为0,表示没有该段
0A(10):8个字节,指出包含在PES分组标题中的可选字段和任何填充字节所占用的总字节数。该字段之前的字节指出了有无可选字段(这里只有PTS)。
因为这里PTS,DTS标志字段是10,那就有5个字节的PTS段,就是这里的21 1C C9 AE 0D
最后的五个字节的FF FF FF FF FC是海康自己的一个自减计数值

[cpp] view plaincopy
  1. #pragma pack(1)
  2. union littel_endian_size
  3. {
  4. unsigned short int  length;
  5. unsigned char       byte[2];
  6. };
  7. struct pack_start_code
  8. {
  9. unsigned char start_code[3];
  10. unsigned char stream_id[1];
  11. };
  12. struct program_stream_pack_header
  13. {
  14. pack_start_code PackStart;// 4
  15. unsigned char Buf[9];
  16. unsigned char stuffinglen;
  17. };
  18. struct program_stream_map
  19. {
  20. pack_start_code PackStart;
  21. littel_endian_size PackLength;//we mast do exchange
  22. //program_stream_info_length
  23. //info
  24. //elementary_stream_map_length
  25. //elem
  26. };
  27. struct program_stream_e
  28. {
  29. pack_start_code     PackStart;
  30. littel_endian_size  PackLength;//we mast do exchange
  31. char                PackInfo1[2];
  32. unsigned char       stuffing_length;
  33. };
  34. #pragma pack()
  35. int inline ProgramStreamPackHeader(char* Pack, int length, char **NextPack, int *leftlength)
  36. {
  37. //printf("[%s]%x %x %x %x\n", __FUNCTION__, Pack[0], Pack[1], Pack[2], Pack[3]);
  38. //通过 00 00 01 ba头的第14个字节的最后3位来确定头部填充了多少字节
  39. program_stream_pack_header *PsHead = (program_stream_pack_header *)Pack;
  40. unsigned char pack_stuffing_length = PsHead->stuffinglen & '\x07';
  41. *leftlength = length - sizeof(program_stream_pack_header) - pack_stuffing_length;//减去头和填充的字节
  42. *NextPack = Pack+sizeof(program_stream_pack_header) + pack_stuffing_length;
  43. if(*leftlength<4) return 0;
  44. //printf("[%s]2 %x %x %x %x\n", __FUNCTION__, (*NextPack)[0], (*NextPack)[1], (*NextPack)[2], (*NextPack)[3]);
  45. return *leftlength;
  46. }
  47. inline int ProgramStreamMap(char* Pack, int length, char **NextPack, int *leftlength, char **PayloadData, int *PayloadDataLen)
  48. {
  49. //printf("[%s]%x %x %x %x\n", __FUNCTION__, Pack[0], Pack[1], Pack[2], Pack[3]);
  50. program_stream_map* PSMPack = (program_stream_map*)Pack;
  51. //no payload
  52. *PayloadData = 0;
  53. *PayloadDataLen = 0;
  54. if(length < sizeof(program_stream_map)) return 0;
  55. littel_endian_size psm_length;
  56. psm_length.byte[0] = PSMPack->PackLength.byte[1];
  57. psm_length.byte[1] = PSMPack->PackLength.byte[0];
  58. *leftlength = length - psm_length.length - sizeof(program_stream_map);
  59. //printf("[%s]leftlength %d\n", __FUNCTION__, *leftlength);
  60. if(*leftlength<=0) return 0;
  61. *NextPack = Pack + psm_length.length + sizeof(program_stream_map);
  62. return *leftlength;
  63. }
  64. inline int Pes(char* Pack, int length, char **NextPack, int *leftlength, char **PayloadData, int *PayloadDataLen)
  65. {
  66. //printf("[%s]%x %x %x %x\n", __FUNCTION__, Pack[0], Pack[1], Pack[2], Pack[3]);
  67. program_stream_e* PSEPack = (program_stream_e*)Pack;
  68. *PayloadData = 0;
  69. *PayloadDataLen = 0;
  70. if(length < sizeof(program_stream_e)) return 0;
  71. littel_endian_size pse_length;
  72. pse_length.byte[0] = PSEPack->PackLength.byte[1];
  73. pse_length.byte[1] = PSEPack->PackLength.byte[0];
  74. *PayloadDataLen = pse_length.length - 2 - 1 - PSEPack->stuffing_length;
  75. if(*PayloadDataLen>0)
  76. *PayloadData = Pack + sizeof(program_stream_e) + PSEPack->stuffing_length;
  77. *leftlength = length - pse_length.length - sizeof(pack_start_code) - sizeof(littel_endian_size);
  78. //printf("[%s]leftlength %d\n", __FUNCTION__, *leftlength);
  79. if(*leftlength<=0) return 0;
  80. *NextPack = Pack + sizeof(pack_start_code) + sizeof(littel_endian_size) + pse_length.length;
  81. return *leftlength;
  82. }
  83. int inline GetH246FromPs(char* buffer,int length,CallbackHead& head, char **h264Buffer, int *h264length)
  84. {
  85. int leftlength = 0;
  86. char *NextPack = 0;
  87. *h264Buffer = buffer;
  88. *h264length = 0;
  89. if(ProgramStreamPackHeader(buffer, length, &NextPack, &leftlength)==0)
  90. return 0;
  91. char *PayloadData=NULL;
  92. int PayloadDataLen=0;
  93. while(leftlength >= sizeof(pack_start_code))
  94. {
  95. PayloadData=NULL;
  96. PayloadDataLen=0;
  97. if(NextPack
  98. && NextPack[0]=='\x00'
  99. && NextPack[1]=='\x00'
  100. && NextPack[2]=='\x01'
  101. && NextPack[3]=='\xE0')
  102. {
  103. //接着就是流包,说明是非i帧
  104. if(Pes(NextPack, leftlength, &NextPack, &leftlength, &PayloadData, &PayloadDataLen))
  105. {
  106. if(PayloadDataLen)
  107. {
  108. memcpy(buffer, PayloadData, PayloadDataLen);
  109. buffer += PayloadDataLen;
  110. *h264length += PayloadDataLen;
  111. }
  112. }
  113. else
  114. {
  115. if(PayloadDataLen)
  116. {
  117. memcpy(buffer, PayloadData, PayloadDataLen);
  118. buffer += PayloadDataLen;
  119. *h264length += PayloadDataLen;
  120. }
  121. break;
  122. }
  123. }
  124. else if(NextPack
  125. && NextPack[0]=='\x00'
  126. && NextPack[1]=='\x00'
  127. && NextPack[2]=='\x01'
  128. && NextPack[3]=='\xBC')
  129. {
  130. if(ProgramStreamMap(NextPack, leftlength, &NextPack, &leftlength, &PayloadData, &PayloadDataLen)==0)
  131. break;
  132. }
  133. else
  134. {
  135. //printf("[%s]no konw %x %x %x %x\n", __FUNCTION__, NextPack[0], NextPack[1], NextPack[2], NextPack[3]);
  136. break;
  137. }
  138. }
  139. return *h264length;
  140. }

从海康7816的ps流里获取数据h264数据相关推荐

  1. java ps h264_从海康28181的PS流解析H264的补充

    前文 <简单解析海康PS流获取H264> 针对海康摄像头的PS流解析做了简单处理,基本逻辑是正确的,但最近几个摄像头出现了一些奇怪的问题,需要针对相关现象进行查询. RTP传输层 项目中为 ...

  2. 对海康28181摄像头PS流解码的支持(一)

    背景 我们的项目是基于sip的IMS系统,需要添加对海康28181摄像头的支持,所以分为以下几步: 向海康摄像头发起点播请求,基于sip. PS流过来后,剥出h264流. 对h264流进行解码. 一. ...

  3. 海康sdk捕获码流数据通过JavaCV推成rtmp流的实现思路(PS流转封装RTMP)

    海康sdk捕获码流数据通过JavaCV推成rtmp流的实现思路(PS流转封装RTMP) 问题分析 转码推rtmp PS流转封装 码云(Gitee)主页:https://gitee.com/banmaj ...

  4. 获取海康摄像机的rtsp流,并用VLC测试

    1.下载VLC软件(视频播放软件),个人觉得这个播放软件蛮强大的,对于程序员来说,还是可以放电脑里的. https://vlc-media-player.en.softonic.com/ 这是VLC下 ...

  5. 大华海康摄像头视频拉流

    流程 技术 海康&大华&DSS获取RTSP 实时流 海康: rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[su ...

  6. 海康相机rtsp取流

    海康相机使用流程 https://www.hikvision.com/Cn/download_60.html下载ivms-4200 通过ip修改本地电脑IP置相同网段 可以通过rtsp取流 用户名:a ...

  7. 海康录像机RTSP取流路径

    最近在帮别人搞直播,需要录像机的RTSP地址,奈何海康录像机只能设置端口,地址没写出来,找了好久最后找到的资料,还要尝试组合...就没有直接显示出来的吗?最后试出来的地址rtsp://aaa:jt12 ...

  8. python通过ctypes调用海康网络sdk取流

    项目中经常需要使用海康的网络摄像头,做视频图像算法经常会用到rtsp流,但是rtsp一般很难保证实时性且解码效率不能保证.通过海康给的python的demo,对模块进行封装以方便python像调用op ...

  9. python 海康工业相机二次开发 参数获取

    对于调用海康相机的整个流程,下面的这个博客说的很详细了,我一开始也是看的这个 python调用海康工业相机并用opencv显示(整体实现)_J&A~ing的博客-CSDN博客_python调用 ...

  10. java dvr_java实现海康NVR/DVR设备工作状态获取

    该功能需要到JNA的知识. 通过查阅海康设备网络SDK编程指南(DVR&NVR)可以知道海康SDK的调用方法都要同过如下过程: 虚线框的流程是可选部分,不会影响其他流程和模块的功能使用.具体的 ...

最新文章

  1. Xcode10:Implicit declaration of function '' is invalid in C99
  2. 计算机书籍- 网络爬虫开发实战
  3. 一文看懂BATH新基建:5G是基础,AI是内核
  4. tensorflow学习(5.实际图片的读取以及lenet-5的搭建)
  5. ibatis实现1对多
  6. 浅析SQL Server 2005中的主动式通知机制
  7. Hibernate C3P0连接池配置
  8. Android Hook技术防范漫谈
  9. 小b和灯泡(51Nod-2489)
  10. linux 简单命令
  11. 解决Admob Banner首次展示不显示的问题
  12. Leetcode 363.矩形区域不超过k的最大数值和
  13. 问题:虚拟环境名字相同遇到bug,please specify a different sdk name?
  14. node-sass安装失败完美解决方法
  15. (转)iOS及Mac开源项目和学习资料【超级全面】
  16. 高低压配电柜温度在线监测系统解决方案
  17. gg修改器修改数值没有用怎么办_GG修改器修改完成然而被修改数值还是不变。?...
  18. 【Numpy入门实例:图像的手绘效果】
  19. 互联网公司的裁员,能玩出多少种花样?
  20. monthcalendar控件

热门文章

  1. easyui学习笔记3—在展开行内的增删改操作
  2. SharePoint 2010 大局观(1~3)
  3. 建筑电气工程设计常用图形和文字符号_建筑电气施工图设计正误案例对比
  4. java循环语句_Java十四天零基础入门-Java for循环语句
  5. JMJS系统总结系列----XSLT的语句规则(一)
  6. js判断当前的访问是手机还是电脑
  7. word2vec原理CBOW与Skip-Gram模型基础
  8. RHCE 学习笔记(32) - DNS
  9. Python结合selenium自动领取无忧币的脚本
  10. 以Graphicslayer为管理组来管理Element.