项目背景是java,前端angular按照java的尿性做设备驱动如果没有底层支持的话直播会是个入坑点。
直播的方案如果不去说主流直播行业,摄像头只是单独的功能点的话,基本上都是绕不开ffmpeg的,有window系统直接用ffmpeg.exe拼接命令推流的,有使用javacv的。鉴于服务是多平台部署,windows,linux多端运行的,还是考虑一下兼容性采用javacv方案。
sdk方式和onvif方式不同点在于云台控制和预置点操作一个调用SDK,一个是采用onvif通信协议,两者实现方式不尽相同,但是效果大同小异。
话不多说,开干!
去海康平台下载最新的sdk的实例,文档写的比较杂,但是还是能找到对应版本的实现的。
先解决sdk调用动态库的问题,我们先需要一个sdk的实例。
写一个SdkInstanceUtil的工具类来创建sdk的实例
加载实例需要注意windows加载实例和linux加载实例有区别,我们的动态库放在配置文件中,
动态库配置完成后,增加一个LoadRsource的类将动态库加载,sdk文档中有对应的加载方式,这里就不多写了,这是第一个避坑点,多读文档没坏处。(我是踩了几次的,因为我们是docker部署的,那个酸爽。。。。)
还是介绍一下docker里面dockerfile的傻瓜配置方式,就是容器加载动态库,不用增加loadResouce这个操作。
接下来就是调用sdk提供的接口进行业务组装的过程。
首先我们是两套sdk,windows和linux版本的,也就是说我们会有两套sdk的接口代码,但是我们的业务可以只写一套撒,面向接口编程撒,
我们首先抽象出一套接口,建一个接口叫SdkProxyService,当当当当,粗线吧:
接下来需要实现一下具体平台对应的service
然后根据平台注入对应的service,
接下来就是我们的业务接口了,再写一个接口,这个就是我们的业务组装,需要sdk中哪些业务组合起来构成我们自身的业务逻辑。
具体的实现应该是播放,云台和预置点操作等等:
sdk的实时播放有提供回调的方式和不提供回调的方式,两者区别不大,基本上都有返回播放句柄的参数,但是问题在于sdk播放时给的流是ps码流,前端是无法直接播放的,关于ps码流抽流的操作比较复杂,我翻了一篇文档大概十几万字,看自闭了。
实时播放采用rtsp->javacv->rtmp容器,前端播放rtmp或者http地址。这里需要增加一个rtmp的容器。
推流引包:
真正的坑来了哈。
开始进坑,这个坑怎么说呢,rtsp协议是一种流媒体的协议,可以远程查看摄像头画面,但是问题是前端浏览器么有一款播放器可以播放rtsp的流,有童鞋就说了 ,我可以用vlc插件,vlc可以,但是chrome41以上就不支持了,ie是可以的,火狐也不行,有童鞋又说了我们可以用websocket中转帧,这就是另外的解决方案了,我们随后再讲,先讲javacv的拉流推流的操作。
采用h264转flv的方式直接转码推,talk is cheap,show me the code!
先把录制器和解码器构建完毕后放入线程开始推画面
推流线程中有一个录制器的结束标识,这里的作用主要是用来结束线程,之前用的callable这里后来改掉了可以改成runnable,这里的直播采用的是多个用户看一个,也就是说一个摄像头只推一路,再来一个人就把redis里面的计数器+1,当最后一个人关掉视频时,将录制器里的结束输入流置为true,这时候线程就可以结束了。
dto.getGrabber().setCloseInputStream(true);
如果是多实例部署的话,需要注意访问实例是否是推流线程所在实例,我们这边采用的是kafka广播做的,当然这种方法比较懒省事,可以记录一下实例id去调对应实例的接口,多实例必然要用redis在记录当前设备推流的信息,避免多个实例同时推一个设备的现象,比较浪费资源。
sdk可能会出现临时文件限制的问题,这个百度一下即可解决,转码需要关注内存消耗情况。
再讲一下websocket代理转帧的方式,以上方案采用的方式大体流程如下:
rtsp->ffpmeg->rtmp->前端
其中rtmp是一个容器,websocket转帧的话采用的方式与这个方式大同小异前面两部分不变,不再使用rtmp容器而是将流推入websocket服务器,前端使用jsmpeg.js将websocket中的帧画面绘制在canvas中。
推流方式不变将转码的方式由flv变为mpeg1video(敲黑板),推流地址变为一个websocket服务器,前端不再使用vedio.js这种播放器啦。具体实现方式看各自项目需求。下篇讲onvif。