最近在开发嵌入式设备视频监控的前端程序,前端使用vue框架实现基本逻辑,后端是C++,用webpack编译器编译成asp过程,服务器是goahead,用goahead解析asp过程,通过websocket调用Ajax通信,进而调用底层C++接口实现整体功能。
goahead是一种嵌入式服务器,经常用于搭载小型Linux的终端设备中,这种服务器小巧简洁,但功能单一,受限于硬件设备,作用是将C++获取到的底层数据转化成http数据报发给前端,且只支持asp过程。
视频编码主要分为存储型编码和压制型编码:
压制型编码是将画面中的音频,视频、字幕等多个轨道合并到一起,然后再进行传输的编码,这种编码的好处是可以有效解决传输过程中产生的音画不同步问题,典型的编码方案有mp4、mkv、rmvb等方式
存储型编码主要是为视频生成一系列关键帧,其他帧只保留与关键帧有差异的地方,没有差异的部分全部删掉以节省存储空间,在播放时通过实时计算恢复出整个视频的全貌,所以这种编码也被称作残片编码。主流残片存储编码方案:H265(较新,未来不可限量,但目前编码时间过长),VP8(成熟,但码率较高,对带宽有一定要求),H264(久经考验,成熟,画质高,码率低,当下最优解), 我们项目中使用的是H264编码存储方案
由于要在浏览器中获取视频流,而浏览器不支持H264解码,所以必须进行转码,我调研了目前主流的方案:
第一种是海康、大华以及众多二次集成商,他们使用的是基于微软ActiveX插件实现实时显示相机画面的方案,好处是人员需求少,方案成熟,只需要前端就可以搞定,但问题在于兼容性差,只支持IE浏览器,其他品牌浏览器均不支持ActiveX,且该插件具有系统级权限,有极高的安全风险,在安装时浏览器会弹出红色预警提示,大概率会使用户对产品安全性产生质疑。且目前主流浏览器的最新几个版本都不支持插件方案,在未来,插件方案极大概率会被浏览器厂商彻底抛弃。
第二种是建立一个中转服务器,就后台拉取的RTSP流转发给前端的websocket,Websocket在2011年成为国际标准,2011年之后的浏览器全部支持,能复用http协议,可在tcp上面快速建立长连接,性能高且能发送二进制数据,是解决视频直播、点播问题的有效方案,这种方案需要稳定的网络传输环境,并不适合嵌入式设备。所以我们应用在了云平台上,详见。
第三种就是后端将H264转成jpeg和amr码流推送给前端websocket,这可以有效解决嵌入式设备中网络不稳定的问题,下面讲一讲该方案在实现过程中我遇到的典型问题:
一、异常重连机制
首先各种浏览器内核对websocket的网页刷新处理是不一样的,比如在类Chrome浏览器,对websocket进程管理使用的是单例模式,每次刷新会使用一个新的进程代替原有的进程,原有的进程会被挂起。
谷歌websock.png
而在类IE浏览器中每次刷新会新建一个进程,一个websocket连接最多能并发连接五个进程,超过的话则失去响应。
IEwebsock.png
由于设备硬件资源有限,不稳定,偶尔会出现异常重启,所以我区分webkit内核(断线刷新)和非webkit内核(断线不刷新尝试重连)进行了重连设计,核心都是onMessage和onClose函数中设计一个状态变量wsIsOpened,当断线时,将该状态置为0,然后尝试重连
注意,在重连时,websocket首先进行TCP握手连接,成功之后此时websocket的open状态为true,但是由于websocket还要发送一次通信协议升级协议,因此此时send函数不能调用,强行调用会报错,应当与后端商定,添加一条升级成功的消息
我在这里设计的成功消息,自定义了一个2001状态码,同时在该消息中还发送了一个后端生成的用户消息签名,用于生成密钥,伪代码如下:
const onClose = function(evt) {
wsIsOpened = 0
/*
* TODO 重连代码
*/
}
const onMessage = function(evt) {
if (evt && evt.data) {