《ffmpeg处理hikvision平台PS流》由会员分享,可在线阅读,更多相关《ffmpeg处理hikvision平台PS流(3页珍藏版)》请在金锄头文库上搜索。
1、ffmpeg 处理 hikvision平台 PS流在多媒体指挥调度系统中,将监控平台的IPC 音视频转发到视频会议、视频话机以及直播平台,是一个常见的需求。常见的监控平台在提供的SDK中通常以回调的方式将音视频媒体用复合流的方式送给应用程序。所谓的复合流,安防行业当然以PS流见多,hikvision平台也是如此。既然是 PS流,当然要仔细研究一下iso13818-1规范,本来想自己写PS流的解析代码,但考虑到已经有众多的 PS流解析开源代码, 咱就不必自己再造个汽车轮子了,趁着自己熟悉 ffmpeg ,还是用 ffmpeg 去解析吧,但没承想掉进一个大坑。用 ffmpeg 的好处是,可以通过
2、avio 的方式,将内存中的 ps 流送入 ffmpeg 进行自动化的处理,通过探测流格式,寻找流,自动确定对应的流格式和音视频编码,自动拼帧,最终demux得到音频帧和视频帧。这样,不管是复合流(PS流或 TS 流),还是裸流( H.264 或 H.265 等),都能用同一种机制进行处理,代码简洁。但坏处就是,必须对 ffmpeg 非常熟悉才行,如果稍有不慎掉坑了,必须要自己翻出来。 。不幸的是,我也掉坑了。程序开发完毕,信心满满的进行测试, 发现取到的 PS流分离出 H.265 视频帧并转发到视频会议,视频会议中的IPC 分屏小几率偶尔会闪一下屏,客户能不能忍咱不知道,但作为处女座人士,不
3、能忍,遂一步步的排查原因。首先怀疑是不是视频会议解码H.265 解错了?毕竟视频会议的 H.265 codec 部分也是我刚加上的,虽然也通过了测试,但毕竟心里没有底气。于是就抓包分析,不幸的是又卡在H.265 上,tcpdump 抓包如果是H.264 码流,大把的分析工具可以分离出码流,如果是 H.265 的抓包,一时没有找到合适的,毕竟H.265 跟 H.264的 RTP打包规则有些差别,不通用。一怒之下,自己写了一个简单的分离程序,分离出了 H.265 码流,然后使用播放工具播放,发现果然也是有闪屏现象,于是首先排除了视频会议的问题。其次,是不是RTP打包H.265时搞错了?于是在RTP
4、打包发送之前将H.265码流录制下来,使用播放工具播放,发现竟然也有闪屏现象。晕倒,竟然是 ffmpeg 分离出的 H.265 视频帧就有问题了!是 hikvision的PS流有问题,还是ffmpeg 分离 265 视频帧有问题?面对业界内的两个大牌,我无语了。只好将hikvisionSDK接口回调中的PS流录制下来,使用 ffplay播放,也有闪屏现象, 再改用 potplayer 播放,神奇了, potplayer如丝滑般流畅。 于是,焦点集中在了 ffmpeg 上。从输入 PS流到取到H265 视频帧, ffmpeg 其实经历了2 步:分析 PS包和使用 parser 分析 H265,哪
5、一步有问题呢?决定先从 PS包分析查起。用 winhex 打开录制的 PS文件,定位到闪屏时程序日志记录的 position:00 00 01 BA 4D 0C AD BF 84 01 00 0003 FE FF FF00 00 01 BF 00 00 01 E0 13 FA 8C 80 07 2343 2B 00 00 01 BA是 PS头,之后有 10 个固定的字节,其中第十个字节的后 3 位是 stuffing填充长度, 在本例中是 FE,FE&7等于 6,则其后的 6个字节是 stuffing填充字节, 咨询 hikvision ,说这 stuffing他们一般用来填充帧序号,
6、 好吧,不管是什么都可以略过。但是等等,这stuffing字节怎么恰好有00 00 01 BF ?凭着对 H264 H265 以及 PS的了解,对00 00 01还是敏感的,这个熟悉的起始码出现的实在太不是时候了,难不成 ffmpeg 把这个 stuffing里面的字节认成了PS里面的 PRIVATE_STREAM,2从而造成错误?于是赶紧查看 ffmpeg里面的 PS解析代码。 ffmpeg中的 PS解析在 mpeg.c 文件中的mpegps_read_packet 函数中,此函数先解析PS头,然后根据不同的pes 取出不同的媒体流。既然是在BA头中的 stuffing中发现的00 00 0
7、1 起始头,肯定要查看解析PS头的函数,于是继续定位到mpeg.c 的 mpegps_read_pes_header 函数,果然看出了端倪!if (startcode =PACK_START_CODE)goto redo; mpegps_read_pes_header 查找到 00 00 01 BA 后,没跳过 PS头,而是直接 goto redo 去找下一个 00 00 01 起始码,而上述 PS头的stuffing填充中恰好有00 00 01 BF ,于是 ffmpeg 便搜索到了一个PRIVATE_STREAM!2想不到大名鼎鼎的ffmpeg 竟然也出了一个如此低级的错误!定位出原因就很容易修改ffmpeg代码了,跳过9字节的固定头,取出10 字节中的stuffing长度,再跳过stuffing长度的填充数,然后goto redo就可以了。重新编译ffmpeg,测试程序,果然OK了!if (startcode =PACK_START_CODE)avio_skip(s->pb,9);avio_skip(s->pb,avio_r8(s->pb)&0x7);goto redo;