vue+海康威视视频web插件开发
一、环境准备
下载地址:海康威视官网
下载页面
![](https://www.0419af.com/wp-content/uploads/2023/01/1672573614-46a309f1022ff0c.jpg)
文件解压后的目录
![](https://www.0419af.com/wp-content/uploads/2023/01/1672573617-46a309f1022ff0c.jpg)
运行bin文件下的VideoWebPlugin.exe
二、插件demo测试
demo/demo_window_integration.html 包含了插件所有功能的,可参照此demo来开发、验证功能、排查问题
demo/demo_window_simple_playback.html.html 视频回放,可在此基础上二次开发
demo/demo_window_simple_preview.html 实时预览,可在基础上二次开发
在浏览器中打开 demo/demo_window_integration.html 文件如下
![](https://www.0419af.com/wp-content/uploads/2023/01/1672573618-46a309f1022ff0c.png)
在进入官网运行管理平台获取到 Appkey、AppSecret、Ip、Port,就能连接到自己的摄像头进行调试开发了
三、api相关介绍
1、JS_StartService(szType,options) 启动插件服务接口 (返回值Promise)
szType:服务类型 固定填充 “window”(必填)
options:可选参数对象 固定填充{dllPath: “./VideoPluginConnect.dll”}(对象选填)
2、JS_Disconnect() 断开服务接口 (返回值Promise)
3、JS_CreateWnd(szId, iWidth, iHeight) 创建插件窗口接口 (返回值Promise)
szId:父窗口的id (必填)
iWidth:元素 ID 标识窗口的宽度 (必填)
iHeight:元素 ID 标识窗口的高度 (必填)
4、JS_SetWindowControlCallback(callback) 设置消息回调接口
callback(对象 必填) 示例
{
cbIntegrationCallBack(必要属性): function(oData){ // oData 是封装的视频 web 插件回调消息的消息体 console.log(JSON.stringify(oData)); // 打印消息体至控制台
}
5、JS_Resize(iWidth,iHeight) 调整插件窗口大小、位置接口
iWidth:DIV 窗口宽度 (必填)
iHeight:DIV 窗口高度(必填)
6、JS_CuttingPartWindow(iLeft, iTop, iWidth, iHeight) 扣除部分插件窗口接口
场景:插件窗口创建后,会始终置顶,因此当和其它组件一同使用时,会遮挡其它组件内容,该接口 的作用是,当部分插件窗口不需要置顶时,我们会隐藏部分插件窗口。也就是向上滚动的时候会隐藏
iLeft:距离插件窗口左边距(必填)
iTop:距离插件窗口上边距(必填)
iWidth:需要扣除的宽度(必填)
iHeight:需要扣除的高度(必填)
7、JS_RepairPartWindow(iLeft, iTop, iWidth, iHeight) 还原扣除部分窗口后的插件窗口
场景:一般联合 JS_CuttingPartWindow 一起使用,在还原部分窗口后 在扣除
iLeft:扣除窗口的顶点距离插件窗 口左边距 (必填)
iTop:扣除窗口的顶点距离插件窗 口上边距 (必填)
iWidth:扣除窗口的宽度(必填)
iHeight:扣除窗口的高度(必填)
8、JS_HideWnd() 插件窗口隐藏接口
9、JS_ShowWnd() 插件窗口显示接口
10、JS_DestroyWnd() 插件窗口销毁接口
11、JS_WakeUp(szProtocal) 唤醒 WebControl.exe 接口
场景 当 WebControl.exe 未启动时唤醒 WebControl.exe。若 WebControl.exe 已启动则忽略
szProtocal:唤醒协议 内容固定为(”VideoWebPlugin://”)
12、JS_RequestInterface({funcName,argument})通过请求响应接口 (返回值Promise)
funcName:功能标识(必填)
argument:功能参数 (object 格式的字符串)
例 申请 RSA 公钥 { funcName: “getRSAPubKey”, argument: “{ keyLength: 1024 }” } 初始化 { funcName: “init”, argument: “{ appkey: “afsgnhmj34567dgh”, // API 网关提供的 appkey secret: “vgkk3g0jaoj0igoigj”, // API 网关提供的 secret ip: “10.33.31.4”, // API 网关 IP 地址 port: 9016, // API 网关端口 playMode: 0, //播放模式(决定显示预览还是回放界面),0-预览 1-录像播放 encryptedFields: “appkey,secret”, //secret 和 appkey 已加密,对多个字段加密存在初始化耗时问题 snapDir: “D:\SnapDir”, // 抓图存储路径 layout: “2×2” // 初始化 2×2 布局 showToolbar: 1, // 显示工具栏 showIntelligent: 1, // 显示智能信息 buttonIDs: “0,16,256,257,258” }”} 根据监控点编号视频预览{ funcName: “startPreview”, argument: “{ cameraIndexCode: “afsgnhmj34567dgh”, // 监控点编号 streamMode: 0, // 主子码流标识,0-主码流 1-子码流 transMode: 1, // 传输协议,0-UDP 1-TCP gpuMode: 0 // 是否开启 GPU 硬解,不建议开启,0-不开启 1-开启 }”} 停止所有视频预览{ funcName: “stopAllPreview”} 根据监控点编号录像回放{ funcName: “startPlayback”, argument: “{ cameraIndexCode: “afsgnhmj34567dgh”, // 监控点编号 startTimeStamp: “10237898985”, // 录像查询开始时间戳,单位:秒 endTimeStamp: “10237899985”, // 录像查询结束时间戳,单位:秒 recordLocation: 0, // 录像存储类型 0-中心存储 1-设备存储 transMode: 1, // 传输协议 ,0-UDP 1-TCP gpuMode: 0 // 是否开启 GPU 硬解,0-不开启 1-开启 }”} 停止所有录像回放{ funcName: “stopAllPlayback”} 销毁播放实例{ funcName: “destroyWnd”} 获取当前布局{ funcName: “getLayout”} 设置当前布局 { funcName: ” setLayout”, argument: “{ layout: “2×2″ // 窗口布局 }”}// 布局可选值有“1×1”、 “2×2”、“3×3”、“4×4”、“5×5”、“1×2”、 “1+2”、“1+5”、“1+7”、 “1+8”、“1+9”、 “1+12”、“1+16”、“4+9”、“1+1+12”、 “3+4”、“1×4”、“4×6” 画面字符叠加{ “funcName”: “drawOSD”, “argument”: “{ text: “温度:50\n 湿度:38″, // 窗口布局 x: 5, // 相对播放窗口左上角的横坐标起点 y: 5, // 相对播放窗口左上角的纵坐标起点 color: red // 字体颜色 }”}
根据监控点编号批量视频预览
{“funcName”: “startMultiPreviewByCameraIndexCode”, “argument”: { “list”: [ { “cameraIndexCode”: “c633ef048fe141e1ac6dbeb36aaf21d3”, // 监控点编号 “ezvizDirect”: 0, // 是否直连萤石预览 未指定或为 0-非直连 其它值-直连 “gpuMode”: 0, // 是否启用 GPU 硬解 0-不启用 1-启用 “streamMode”: 0, // 主子码流标识 0-主码流 1-子码流 “transMode”: 1, // 传输协议 0-UDP 1-TCP “wndId”: 1 // 播放窗口序号 }, { “cameraIndexCode”: “c633ef048fe141e1ac6dbeb36aaf21d3”, “ezvizDirect”: 0, “gpuMode”: 0, “streamMode”: 0, “transMode”: 1, “wndId”: 2 } ] }} 根据监控点编号批量录像回放{ “funcName”: “startMultiPlaybackByCameraIndexCode”, “argument”: { “list”:[{ “cameraIndexCode”: “58e90452772a4d9da7c7ba4cef26dbf0”, // 监控点编号 “startTimeStamp”: “10237898985”, // 录像查询开始时间戳,单位:秒 “endTimeStamp”: “10237899985”, // 录像查询结束时间戳,单位:秒 “recordLocation”: 0, // 录像存储类型 0-中心存储 1-设备存储 “transMode”: 1, // 传输协议 ,0-UDP 1-TCP “wndId”: 1, // 窗口序号 “gpuMode”: 0 // 是否开启 GPU 硬解,0-不开启 1-开启 }] }} 批量停止播放{ “funcName”: “stopMultiPlay”, “argument”: { “list”:[{ “wndId”: 1 // 窗口序号 }, { “wndId”: 2 // 窗口序号 }] }} 四、开发流程示意图
图中灰色部分为可选步骤。其中申请 RSA 公钥可选。申请 RSA 公钥是为了加密初始化中 的一些敏感参数,对安全性要求高的用户可以考虑对敏感参数加密。但需要注意的是,使用 RSA 公 钥加密机制必然会导致初始化耗时。一般情况下不需要调 JS_DestroyWnd 来销毁插件窗口 (JS_Disconnect 中会自行销毁),但一些特殊的场景如浏览器页面上需随时启用和禁用视频播放时, 需调 JS_DestroyWnd 来禁用视频播放,需要调 JS_CreateWnd 来启用视频播放。JS_DestroyWnd 和 JS_Disconnect 中会反初始化插件,这里无需调 JS_RequestInterface/uninit 反初始化。
五、在vue中使用(实时预览完整示例)
1、在public目录下的index.html中引入文件(资源文件在demo目录下)
// 用于 RSA 公钥加密 // 用于前端与插件交互
2、线预览模块完整实例,其他的模块功能以此内推(如有疑惑,可参考doc目录下的文档,里面记载很清楚)
html 示例
js 示例
// 声明公用变量 摄像头实例对现象,为了方便引用 定义在全局let oWebControl = nullvar initCount = 0var pubKey = ”export default { name: ‘videoRuntime’, data() { return { playWndHeight: ”, // 视频盒子的高度 playWndWidth: ” // 视频盒子的宽度 } }, mounted() { this.playWndHeight = this.$refs.playWndBox.clientHeight // 首次加载时的到父容器的高度 this.playWndWidth = this.$refs.playWndBox.clientWidth // 首次加载时的到父容器的宽度 this.$nextTick(() => { // 初始化摄像头 this.initPlugin() }) // 监听resize事件,使插件窗口尺寸跟随DIV窗口变化 window.addEventListener(‘resize’, () => { if (oWebControl != null) { if (this.$refs.playWndBox) oWebControl.JS_Resize(this.$refs.playWndBox.clientWidth, this.$refs.playWndBox.clientHeight) } }) }, methods: { // 创建播放实例 initPlugin() { let that = this oWebControl = new window.WebControl({ szPluginContainer: ‘playWnd’, // 指定容器id iServicePortStart: 15900, // 指定起止端口号,建议使用该值 iServicePortEnd: 15909, szClassId: ’23BF3B0A-2C56-4D97-9C03-0CB103AA8F11′, // 用于IE10使用ActiveX的clsid cbConnectSuccess: () => { // 创建WebControl实例成功 oWebControl .JS_StartService(‘window’, { // WebControl实例创建成功后需要启动服务 dllPath: ‘./VideoPluginConnect.dll’ // 值”./VideoPluginConnect.dll”写死 }) .then( function() { // 启动插件服务成功 oWebControl.JS_SetWindowControlCallback({ // 设置消息回调 cbIntegrationCallBack: that.cbIntegrationCallBack }) oWebControl.JS_CreateWnd(‘playWnd’, 0,0).then(function() { //JS_CreateWnd创建视频播放窗口,宽高可设定 默认设置为0 消除初始化闪白问题 that.init() // 创建播放实例成功后初始化 }) }, function() { // 启动插件服务失败 } ) }, cbConnectError: function() { // 创建WebControl实例失败 oWebControl = null that.$message.warning(‘插件未启动,正在尝试启动,请稍候…’) window.WebControl.JS_WakeUp(‘VideoWebPlugin://’) // 程序未启动时执行error函数,采用wakeup来启动程序 initCount++ if (initCount < 3) { setTimeout(function() { that.initPlugin() }, 3000) } else { that.$message.warning('插件启动失败,请检查插件是否安装!') } }, cbConnectClose: () => { // 异常断开:bNormalClose = false // JS_Disconnect正常断开:bNormalClose = true console.log(‘cbConnectClose’) oWebControl = null } }) }, // 初始化 init() { let that = this this.getPubKey(() => { var appkey = ‘28730366’ //综合安防管理平台提供的appkey,必填 var secret = this.setEncrypt(‘HSZkCJpSJ7gSUYrO6wVi’) //综合安防管理平台提供的secret,必填 var ip = ‘10.19.132.75’ //综合安防管理平台IP地址,必填 var playMode = 0 //初始播放模式:0-预览,1-回放 var port = 443 //综合安防管理平台端口,若启用HTTPS协议,默认443 var snapDir = ‘D:\\SnapDir’ //抓图存储路径 var videoDir = ‘D:\\VideoDir’ //紧急录像或录像剪辑存储路径 var layout = ‘1×1’ //playMode指定模式的布局 var enableHTTPS = 1 //是否启用HTTPS协议与综合安防管理平台交互,这里总是填1 var encryptedFields = ‘secret’ //加密字段,默认加密领域为secret var showToolbar = 1 //是否显示工具栏,0-不显示,非0-显示 var showSmart = 1 //是否显示智能信息(如配置移动侦测后画面上的线框),0-不显示,非0-显示 var buttonIDs = ‘0,16,256,257,258,259,260,512,513,514,515,516,517,768,769’ //自定义工具条按钮 // var toolBarButtonIDs = “2049,2304” // 工具栏上自定义按钮 oWebControl .JS_RequestInterface({ funcName: ‘init’, argument: JSON.stringify({ appkey: appkey, //API网关提供的appkey secret: secret, //API网关提供的secret ip: ip, //API网关IP地址 playMode: playMode, //播放模式(决定显示预览还是回放界面) port: port, //端口 snapDir: snapDir, //抓图存储路径 videoDir: videoDir, //紧急录像或录像剪辑存储路径 layout: layout, //布局 enableHTTPS: enableHTTPS, //是否启用HTTPS协议 encryptedFields: encryptedFields, //加密字段 showToolbar: showToolbar, //是否显示工具栏 showSmart: showSmart, //是否显示智能信息 buttonIDs //自定义工具条按钮 }) }) .then(function(oData) { console.log(oData) oWebControl.JS_Resize(that.playWndWidth, that.playWndHeight) // 初始化后resize一次,规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题 // 一进来就隐藏 oWebControl.JS_HideWnd() // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题 }) }) }, // 获取公钥 getPubKey(callback) { oWebControl .JS_RequestInterface({ funcName: ‘getRSAPubKey’, argument: JSON.stringify({ keyLength: 1024 }) }) .then(function(oData) { console.log(oData) if (oData.responseMsg.data) { pubKey = oData.responseMsg.data callback() } }) }, // RSA 加密 setEncrypt(value) { var encrypt = new window.JSEncrypt() encrypt.setPublicKey(pubKey) return encrypt.encrypt(value) }, // 回调的消息 cbIntegrationCallBack(oData) { let { responseMsg: type, responseMsg: msg } = oData if (type === ‘error’) { console.log(type, msg, this.dateFormat(new Date(), ‘yyyy-MM-dd hh:mm:ss’)) } else { console.log(type, msg, this.dateFormat(new Date(), ‘yyyy-MM-dd hh:mm:ss’)) } }, // 预览 startPreview(cameraCode) { // 点击查询后显示 oWebControl.JS_ShowWnd() var cameraIndexCode = cameraCode //获取输入的监控点编号值,必填 console.log(cameraCode) var streamMode = 0 //主子码流标识:0-主码流,1-子码流 var transMode = 1 //传输协议:0-UDP,1-TCP var gpuMode = 0 //是否启用GPU硬解,0-不启用,1-启用 var wndId = -1 //播放窗口序号(在2×2以上布局下可指定播放窗口) cameraIndexCode = cameraIndexCode.replace(/(^\s*)/g, ”) cameraIndexCode = cameraIndexCode.replace(/(\s*$)/g, ”) oWebControl.JS_RequestInterface({ funcName: ‘startPreview’, argument: JSON.stringify({ cameraIndexCode: cameraIndexCode, //监控点编号 streamMode: streamMode, //主子码流标识 transMode: transMode, //传输协议 gpuMode: gpuMode, //是否开启GPU硬解 wndId: wndId //可指定播放窗口 }) }) }, // 停止全部预览 stopAllPreview() { oWebControl.JS_RequestInterface({ funcName: ‘stopAllPreview’ }) }, // 格式化时间 dateFormat(oDate, fmt) { var o = { “M+”: oDate.getMonth() + 1, //月份 “d+”: oDate.getDate(), //日 “h+”: oDate.getHours(), //小时 “m+”: oDate.getMinutes(), //分 “s+”: oDate.getSeconds(), //秒 “q+”: Math.floor((oDate.getMonth() + 3) / 3), //季度 “S”: oDate.getMilliseconds()//毫秒 }; if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (oDate.getFullYear() + “”).substr(4 – RegExp.$1.length)); } for (var k in o) { if (new RegExp(“(” + k + “)”).test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : ((“00” + o[k]).substr((“” + o[k]).length))); } } return fmt; } }, destroyed() { // 组件销毁后 if (oWebControl != null) { oWebControl.JS_HideWnd() // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题 oWebControl.JS_RequestInterface({funcName: “destroyWnd”}) // 销毁当前播放的视频 oWebControl.JS_Disconnect() // 断开与插件服务连接 } }}
注:由于本页面没有滚动条,不会出现因滚动条滚动导致窗口需要被遮住的情况,所以就没有添加scroll,如有滚动条,请参考demo文件下 demo_window_simple_preview.html 中的setWndCover事件