1.公司需要展示海康摄像头的预览画面
2.经过我两天的搜索资料加尝试,几种方式
2.1后端转码,前端调接口取太麻烦,对服务器负荷大
2.2 vlc插件对rtsp取流 高版本的各种浏览器不支持了vlc插件
2.3 WEB无插件开发包 V3.2 需要支持websocket,不是所有海康设备都支持,而且要自己去开启这个配置
2.4 WEB3.0控件开发包 V3.0 这个原理和vlc插件一模一样,谁会特意下个低版本谷歌
2.5 WEB3.3控件开发包 V3.3 从海康描述来说,非常适合,然后开始钻研,用了一个三天左右,成功迁移到vue项目,并实现预览,本来还要实现在预览画面上配置smart事件的区域,但是这个画面层级太高,我完全无法在画面上加个canvas,然后在canvas上作图.最终效果如下,切换通道可以切换画面
海康插件下载地址海康开放平台
1.把压缩包解压后,把
webVideoCtrl.js
jsVideoPlugin-1.0.0.min.js
jquery-1.7.1.min.js三个文件拷贝到自己项目的src/utils文件夹下,然后你准备在哪个页面做预览效果,就在这个页面引入这三个js文件,webVideoCtrl.js的代码是需要改动的(改动是因为它本身是针对html也就是压缩包那样的结构去做的,现在为了适配我的vue,需要改代码,如果你直接使用原html页面,那本文章就不需要参考,你可以demo文件夹直接放在public下,然后vue中跳转,地址就是/demo/index.html,最终会指向public/demo/index.html,然后把除了画面以外的所有样式都改为不可见,注意不能把多余的登陆预览回放组件删掉,否则会有很多调不通的报错),jsVideoPlugin-1.0.0.min.js的代码不需要改动,jquery-1.7.1.min.js也不需要改动
这边我为了修改webVideoCtrl.js先用idea打开然后格式化了这个文件,格式化之后,里面会有if else导致的报错 比如if(a) b else c;把它换行成
if(a) b
else c 就是如果不用大括号包起来,if和else代码不能在同一行
vue页面代码如下
<template> <div class='app-container'> <el-form ref='ruleForm' :model='ruleForm' :rules='ruleFormRules' label-width='80px' > <el-form-item label='通道' prop='enabled'> <template slot-scope="scope"> <el-select v-model="ruleForm.monitorPointId" placeholder="请选择通道" filterable size="small" @change="regetRule" > <el-option v-for="cameraChannel in points" :key="cameraChannel.id" :label="cameraChannel.name" :value="cameraChannel.id" /> </el-select> </template> </el-form-item> <el-tabs v-model="algorithmId" @tab-click="handleClick"> <el-tab-pane :label="item.name" :name="item.id" v-for="(item,index) in algorithms" :key="index" ></el-tab-pane> </el-tabs> <el-form-item label='是否启用' prop='enabled'> <el-switch v-model="ruleForm.enabled" active-color="#13ce66" inactive-color="#ff4949"> </el-switch> </el-form-item> <el-form-item label='区域规则' class="plugin" prop='coordinates'> <div style="position: relative"> <div id="divPlugin" width="250" height="250" class="plugin" style="position: relative;top: 0; left: 250px;z-index:-1;pointer-events: none"></div> <canvas ref="canvas0" class="plugin" width="250" height="250" @mousedown="mouseDown" style="pointer-events: none;z-index:3;position: absolute;top: 0; left: 0;background-color: rgba(255, 255, 255, 0.5);border: 1px solid rgb(255, 204, 0); cursor: default;"></canvas> </div> </el-form-item> <el-form-item label='绘制' prop='screenSize'> <el-button type="info" @click="startDraw">开始绘制</el-button> <el-button type="info" @click="stopDraw">停止绘制</el-button> <el-button type="info" @click="clearDraw">清除绘制</el-button> </el-form-item> <el-form-item label='目标检测' prop='detectionTarget'> <el-checkbox name="human" v-model="humanCheck">人类</el-checkbox> <el-checkbox name="vehicle" v-model="vehicleCheck">车辆</el-checkbox> </el-form-item> <el-form-item label='方向' prop='directionSensitivity' v-if="algCode==='LineDetection'"> <el-select v-model="ruleForm.directionSensitivity" placeholder="请选择"> <el-option v-for="item in directionList" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </el-form-item> <el-form-item label='灵敏度' prop='sensitivityLevel'> <el-slider v-model="ruleForm.sensitivityLevel" :min="1" show-input></el-slider> </el-form-item> <el-form-item label='时间阈值' prop='timeThreshold' v-if="algCode==='FieldDetection'||algCode==='LineDetection'"> <el-slider v-model="ruleForm.timeThreshold" show-input :max="10"></el-slider> </el-form-item> </el-form> <div slot='footer' class='dialog-footer'> <el-button :loading='buttonLoading' type='primary' @click='submitRuleForm'>确 定</el-button> <el-button @click='rulecancel'>取 消</el-button> </div> </div></template><script> import { addByServerIdAndAlgId,addByServerIdAndPointId,deleteByServerIdAndPointId,deleteByServerIdAndAlgId,setRule,addAlgorithmServer, delAlgorithmServer, getAlgorithmServer, listAlgorithmServer, updateAlgorithmServer,getRuleByServerIdAndAlgIdAndPointId } from '@/api/alg/algorithmServer' import { listMonitorPointAll } from '@/api/alg/monitorPoint'; import { listAlgorithmAll } from '@/api/alg/algorithm'; import $ from '@/utils/webVideoCtrl'; import a from '@/utils/jsVideoPlugin-1.0.0.min'; import b from '@/utils/jquery-1.7.1.min'; import axios from "axios";export default { name: 'smartEvent', data() { return { points:[], algorithms:[], directionList:[{label:'左->右',value:"left-right"},{label:'左<->右',value:"any"},{label:'左<-右',value:"right-left"}], algCode:'', humanCheck:false, vehicleCheck:false, id:null, algorithmId:'', activeName:0, channelFilterValue:'', algFilterValue:'', AllPointList:[], ShowPointList:[], AllAlgList:[], ShowAlgList:[], // 按钮loading buttonLoading: false, // 遮罩层 loading: true, // 导出遮罩层 exportLoading: false, // 选中数组 ids: [], // 非单个禁用 single: true, // 非多个禁用 multiple: true, // 显示搜索条件 showSearch: true, // 总条数 total: 0, // 算法服务器表格数据 algorithmServerList: [], // 弹出层标题 title: '', // 是否显示弹出层 open: false, // 查询参数 queryParams: { pageNum: 1, pageSize: 10, name: undefined, ip: undefined, }, // 表单参数 form: { id: undefined, name: undefined, ip: undefined, port: undefined, username: undefined, password: undefined, updateTime: undefined, createTime: undefined, updateBy: undefined, createBy: undefined, delFlag: undefined, algorithms: [], points:[], }, actionArr: [], ruleForm:{ sensitivityLevel:50, monitorPointId:undefined, detectionTarget:undefined, screenSize: undefined, timeThreshold: 5, coordinates: undefined, enabled:false }, drawFlag:false, openRule:false, ruleFormRules:{ monitorPointId: [ { required: true, message: '通道不能为空', trigger: 'blur' } ], }, // 表单校验 rules: { id: [ { required: true, message: '不能为空', trigger: 'blur' } ], name: [ { required: true, message: '名称不能为空', trigger: 'blur' } ], ip: [ { required: true, message: 'ip不能为空', trigger: 'blur' } ], port: [ { required: true, message: 'port不能为空', trigger: 'blur' } ], username: [ { required: true, message: '账号不能为空', trigger: 'blur' } ], password: [ { required: true, message: '密码不能为空', trigger: 'blur' } ], } } }, created() { }, destroyed(){ WebVideoCtrl.I_DestroyPlugin(); }, mounted(){ this.serverId=this.$route.query.serverId; getAlgorithmServer({id:this.serverId}).then((res)=>{ this.points=res.data.points; this.algorithms=res.data.algorithms; if (res.data.points.length!=0){ this.ruleForm.monitorPointId=res.data.points[0].id; } if(res.data.algorithms.length!=0){ this.algorithmId=res.data.algorithms[0].id; } this.$forceUpdate; WebVideoCtrl.I_InitPlugin({ bWndFull: false, //是否支持单窗口双击全屏,默认支持 true:支持 false:不支持 iWndowType: 1, bDebugMode: true, cbInitPluginComplete: function () { WebVideoCtrl.I_InsertOBJECTPlugin("divPlugin").then(() => { console.log("插件初始化") // 检查插件是否最新 WebVideoCtrl.I_CheckPluginVersion().then((bFlag) => { if (bFlag) { alert("检测到新的插件版本,双击开发包目录里的HCWebSDKPlugin.exe升级!"); } }); }, () => { alert("插件初始化失败,请确认是否已安装插件;如果未安装,请双击开发包目录里的HCWebSDKPlugin.exe安装!"); axios.get('/file/HCWebSDKPlugin.exe',{ responseType: 'blob' }).then(res => { console.log(res); const url = window.URL.createObjectURL(new Blob([res.data])); const link = document.createElement('a'); link.href = url; link.setAttribute('download', 'HCWebSDKPlugin.exe'); document.body.appendChild(link); link.click(); }) }); } }); if (this.algorithmId&&this.ruleForm.monitorPointId){ console.log("预览初始化") this.getRule(); }; }) }, methods: { handleClick(tab, event){ for(let i =0;i<this.algorithms.length;i++){ let item=this.algorithms[i]; if (item.id==this.algorithmId){ this.algCode=item.code; break; } } this.getRule(); }, startDraw(){ console.log("开启绘制") this.drawFlag=true; // this.rtsp(); }, clearDraw(){ const canvasDraw0 = this.$refs.canvas0; const ctx=canvasDraw0.getContext('2d'); ctx.clearRect(0, 0, canvasDraw0.width, canvasDraw0.height); this.actionArr=[]; }, stopDraw(){ if (this.drawFlag){ console.log(this.actionArr); console.log(this.algCode); if(this.actionArr.length!==2&&this.algCode==='LineDetection'){ alert("越界告警需要两个坐标"); return; }else if(this.actionArr.length!==4&&this.algCode!=='LineDetection'){ alert("区域需要四个坐标点"); return; } } this.drawFlag=false; }, drawCircle(x, y){ }, mouseDown(e){ console.log(e); if(e.buttons==1&&this.drawFlag){ if(this.actionArr.length>=4){ return; } this.actionArr.push({x:e.offsetX,y:e.offsetY}) this.drawActionArr(); } }, mouseMove(e){ console.log(e); }, filterAlg(){ this.ShowAlgList=this.AllAlgList.filter(function (element,index,array){ return element.name.search(e)!=-1; }) }, filterChannel(e){ this.ShowPointList=this.AllPointList.filter(function (element,index,array){ return element.name.search(e)!=-1; }) }, handleAlgAdd(index){ let element = this.ShowAlgList[index]; let find=this.form.algorithms.find((value => value.id==element.id)); if (!find){ addByServerIdAndAlgId({algorithmId:element.id,serverId:this.id}).then(response=>{ this.form.algorithms.push(element); }) } }, handleChannelAdd(index){ let element = this.ShowPointList[index]; let find=this.form.points.find((value => value.id==element.id)); if (!find){ addByServerIdAndPointId({pointId:element.id,serverId:this.id}).then(response=>{ this.form.points.push(element); }) } }, drawActionArr(){ if (this.actionArr.length>0){ const canvasDraw0 = this.$refs.canvas0; const ctx=canvasDraw0.getContext('2d'); ctx.fillStyle="black"; ctx.clearRect(0, 0, canvasDraw0.width, canvasDraw0.height) for (let i = 0; i <this.actionArr.length ; i++) { ctx.beginPath(); ctx.arc(this.actionArr[i].x,this.actionArr[i].y,5,0,2*Math.PI,true); ctx.fill(); ctx.closePath(); ctx.font="20px Georgia"; ctx.fillText(i+1,this.actionArr[i].x,this.actionArr[i].y) } ctx.beginPath(); ctx.moveTo(this.actionArr[0].x,this.actionArr[0].y); for (let i = 1; i <this.actionArr.length ; i++) { ctx.lineTo(this.actionArr[i].x,this.actionArr[i].y); ctx.moveTo(this.actionArr[i].x,this.actionArr[i].y); if (i==this.actionArr.length-1){ ctx.lineTo(this.actionArr[0].x,this.actionArr[0].y); } } ctx.stroke() ctx.closePath(); } }, regetRule(e){ this.getRule(); }, getRule(){ getRuleByServerIdAndAlgIdAndPointId({serverId:this.serverId,algorithmId:this.algorithmId,pointId:this.ruleForm.monitorPointId}).then(response => { this.clearDraw(); this.humanCheck=false; this.vehicleCheck=false; if (response.data){ this.ruleForm = response.data; this.actionArr=this.ruleForm.coordinates?this.ruleForm.coordinates.map(function(value) { return {x:parseInt(value.x)/4,y:parseInt(value.y)/4} }):[]; this.drawActionArr(); if (response.data.detectionTarget) { if (response.data.detectionTarget === 'human') { this.humanCheck = true; this.vehicleCheck = false; } else if (response.data.detectionTarget === 'vehicle') { this.humanCheck = false; this.vehicleCheck = true; } else if (response.data.detectionTarget === 'human,vehicle') { this.humanCheck = true; this.vehicleCheck = true; } } this.$forceUpdate(); console.log("ruleForm") console.log(this.ruleForm) this.startView(response.data.point); }else { console.log("没返回值") this.ruleForm ={ monitorPointId:this.ruleForm.monitorPointId, serverId:this.serverId, algorithmId:this.algorithmId, sensitivityLevel:50, detectionTarget:undefined, screenSize: undefined, timeThreshold: 5, coordinates: undefined, enabled:false }; } }); }, startView(option){ console.log("走到预览方法里") if (!option||!option.username||!option.password||!option.ip){ alert("监控点数据不全,无法预览") return; } //获取预览画面 var oLiveView = { iProtocol: 1, // protocol 1:http, 2:https szIP: option.ip, // protocol ip szPort: "80", // protocol port szUsername: option.username, // device username szPassword: option.password, // device password iStreamType: 1, // stream 1:main stream 2:sub-stream 3:third stream 4:transcode stream iChannelID: option.channelId?option.channelId:1, // channel no bZeroChannel: false // zero channel }; let isExists = WebVideoCtrl.findDeviceIndexByIP(option.ip); console.log("是否登陆过") console.log(isExists!==-1) if (isExists!==-1){ var szDeviceIdentify = oLiveView.szIP + "_" + oLiveView.szPort; var oWndInfo = WebVideoCtrl.I_GetWindowStatus(0); console.log("窗口是否在播放") console.log(oWndInfo != null) if (oWndInfo != null) {// 已经在播放了,先停止 WebVideoCtrl.I_Stop({iWndIndex:0, success: function () { console.log("停止成功") setTimeout(function () { WebVideoCtrl.I_StartRealPlay(szDeviceIdentify, { iWndIndex:0, iStreamType: oLiveView.iStreamType, iChannelID: oLiveView.iChannelID, bZeroChannel: oLiveView.bZeroChannel }); }, 1000); } }); } else { setTimeout(function () { WebVideoCtrl.I_StartRealPlay(szDeviceIdentify, { iWndIndex:0, iStreamType: oLiveView.iStreamType, iChannelID: oLiveView.iChannelID, bZeroChannel: oLiveView.bZeroChannel }); }, 1000); } return; } WebVideoCtrl.I_Login(oLiveView.szIP, oLiveView.iProtocol, oLiveView.szPort, oLiveView.szUsername, oLiveView.szPassword, { success: function (xmlDoc) { console.log("登陆成功") // 开始预览 var szDeviceIdentify = oLiveView.szIP + "_" + oLiveView.szPort; var oWndInfo = WebVideoCtrl.I_GetWindowStatus(0); console.log("窗口是否在播放") console.log(oWndInfo != null) if (oWndInfo != null) {// 已经在播放了,先停止 WebVideoCtrl.I_Stop({iWndIndex:0, success: function () { console.log("停止播放") setTimeout(function () { WebVideoCtrl.I_StartRealPlay(szDeviceIdentify, { iWndIndex:0, iStreamType: oLiveView.iStreamType, iChannelID: oLiveView.iChannelID, bZeroChannel: oLiveView.bZeroChannel }); }, 1000); } }); } else { setTimeout(function () { WebVideoCtrl.I_StartRealPlay(szDeviceIdentify, { iWndIndex:0, iStreamType: oLiveView.iStreamType, iChannelID: oLiveView.iChannelID, bZeroChannel: oLiveView.bZeroChannel }); }, 1000); } }, error: function (oError) { if (2001 === status) { var szDeviceIdentify = oLiveView.szIP + "_" + oLiveView.szPort; var oWndInfo = WebVideoCtrl.I_GetWindowStatus(0); if (oWndInfo != null) {// 已经在播放了,先停止 WebVideoCtrl.I_Stop({iWndIndex:0, success: function () { console.log("停止播放成功") setTimeout(function () { WebVideoCtrl.I_StartRealPlay(szDeviceIdentify, { iWndIndex:0, iStreamType: oLiveView.iStreamType, iChannelID: oLiveView.iChannelID, bZeroChannel: oLiveView.bZeroChannel, }); }, 1000); } }); } else { setTimeout(function () { WebVideoCtrl.I_StartRealPlay(szDeviceIdentify, { iWndIndex:0, iStreamType: oLiveView.iStreamType, iChannelID: oLiveView.iChannelID, bZeroChannel: oLiveView.bZeroChannel }); }, 1000); } }else { console.log("登陆失败") console.log(oLiveView) console.log(oError); console.log(status); } } }); }, handleAlgRemove(index){ this.form.algorithms.splice(index,1); }, handlePointRemove(index){ this.form.points.splice(index,1); }, showPointers(row){ this.algCode=row.code?row.code:""; this.algorithmId=row.id; this.openRule=true; this.rulereset(); }, // 取消按钮 cancel() { this.open = false this.reset() }, // 取消按钮 rulecancel() { this.rulereset() this.openRule = false }, // 表单重置 reset() { this.form = { id: undefined, name: undefined, ip: undefined, port: undefined, username: undefined, password: undefined, updateTime: undefined, createTime: undefined, updateBy: undefined, createBy: undefined, delFlag: undefined, algorithms: [], points:[], } this.resetForm('form') }, rulereset() { this.ruleForm = { sensitivityLevel:50, monitorPointId:undefined, detectionTarget:undefined, screenSize: undefined, timeThreshold: 5, coordinates: undefined, enabled:false } this.clearDraw(); this.resetForm('ruleForm') }, /** 修改按钮操作 */ handleUpdate(row) { this.id=row.id; this.loading = true this.reset() const id = row.id || this.ids getAlgorithmServer({ id: id }).then(response => { this.loading = false this.form = response.data this.open = true this.title = '修改算法服务器' }) }, /** 提交按钮 */ submitForm() { this.$refs['form'].validate(valid => { if (valid) { this.buttonLoading = true if (this.form.id != null) { updateAlgorithmServer(this.form).then(response => { this.$modal.msgSuccess('修改成功') this.open = false this.getList() }).finally(() => { this.buttonLoading = false }) } else { addAlgorithmServer(this.form).then(response => { this.$modal.msgSuccess('新增成功') this.open = false this.getList() }).finally(() => { this.buttonLoading = false }) } } }) }, submitRuleForm() { if(this.drawFlag){ alert("请先完成绘制"); return; } this.$refs['ruleForm'].validate(valid => { if (valid) { this.buttonLoading = true if (!this.humanCheck&&!this.vehicleCheck){ this.ruleForm.detectionTarget='all' } else if(this.humanCheck&&!this.vehicleCheck){ this.ruleForm.detectionTarget='human' }else if(!this.humanCheck&&this.vehicleCheck){ this.ruleForm.detectionTarget='vehicle' }else { this.ruleForm.detectionTarget='human,vehicle' } if (this.actionArr.length!==0){ this.ruleForm.coordinates= this.actionArr.map(function(value) { return {x:4*value.x,y:4*value.y} }) } setRule(this.ruleForm).then(response => { this.$modal.msgSuccess('新增成功') this.openRule = false this.getList() }).finally(() => { this.buttonLoading = false }) } }) }, /** 导出按钮操作 */ handleExport() { this.$download.excel('/alg/algorithmServer/export', this.queryParams) } }}</script><style scoped>.hide { display: none;}.item { height: 30px; line-height: 30px; margin: 0 15px;}.plugin { width: 250px; height: 250px;}.channel-list { max-height: 660px; overflow: scroll;}::-webkit-scrollbar { width: 5px; height: 5px;}::-webkit-scrollbar-track { width: 6px; background: none; -webkit-border-radius: 2em; -moz-border-radius: 2em; border-radius: 2em;}::-webkit-scrollbar-thumb { background-color: rgba(144, 147, 153, .5); background-clip: padding-box; min-height: 28px; -webkit-border-radius: 2em; -moz-border-radius: 2em; border-radius: 2em; transition: background-color .3s; cursor: pointer;}::-webkit-scrollbar-thumb:hover { background-color: rgba(144, 147, 153, .3);}.view { min-width: 1120px; padding: 10px 15px;}</style>
webVideoCtrl.js主要修改初始化插件的代码,原先有个获取dirName方法就是获取webVideoCtrl.js自己的文件路径前缀,通过这个路径拼接再去找到jsVideoPlugin.js,但是vue结构下打包后这个文件路径获取肯定不对,所有我把webVideoCtrl.js和jsVideoPlugin.js放到utils里,然后通过require([./xxx.js])的方法在webVideoCtrl.js中引入jsVideoPlugin.js,注意它是把整个jsVideoPlugin.js存到了自身的window.JSvideoPlugin变量中,方便之后调用,然后它这边还有个值得注意的是窗口号,因为我只需要单窗口,所有我的窗口号传的都0;
this.I_InitPlugin = function(options) { m_utilsInc.extend(m_options, options) if ('object' === typeof exports && typeof module !== 'undefined') { require([ './jsVideoPlugin-1.0.0.min.js'], function(o) { window.JSVideoPlugin = o.JSVideoPlugin if (options.cbInitPluginComplete) { options.cbInitPluginComplete() } }) } else if ('function' === typeof define && define.amd) { require([ './jsVideoPlugin-1.0.0.min.js'], function(o) { window.JSVideoPlugin = o.JSVideoPlugin if (options.cbInitPluginComplete) { options.cbInitPluginComplete() } }) } else { m_utilsInc.loadScript( './jsVideoPlugin-1.0.0.min.js', function() { if (options.cbInitPluginComplete) { options.cbInitPluginComplete() } }) } window.addEventListener('resize', function() { if (m_pluginOBJECT !== null) { var oElem = $('#' + m_options.szContainerID) m_pluginOBJECT.JS_Resize(oElem.width(), oElem.height()) } }) window.addEventListener('unload', function() { }) window.addEventListener('scroll', function() { }) }