当前位置: 首页 > news >正文

vue 海康视频插件

背景:

在vue项目中,在pc端对接视频,播放的视频包括视频实时、视频回放等。

写文思路:海康视频插件,项目引入依赖,前端开发封装的组件

效果展示:

一、海康视频

二、海康视频插件

海康视频插件运行环境需要安装插件VideoWebPlugin.exe,对浏览器也有兼容性要求,具体看官方文档

插件使用步骤总结
1.new 一个WebControl 插件实例
2. 启动插件服务
3.创建视频播放窗口、绑定消息回调
4.初始化参数,其中secret参数需要通过RSA加密,加密公钥通过WebControl.JS_RequestInterface获取
5.通过WebControl 插件实例调用API方法操作功能(预览,回放,抓图、录像等)
6.离开页面断开与插件服务连接

海康视频插件下载地址:点击跳转

下载的压缩包并解压:

项目引入依赖:

必须在 vue项目 的 index.html 文件中引入, main.js 中引入无效;

三、代码

封装组件:

海康插件封装js,自定义命名为videoUtils.js

videoUtils.js

import { haikangVideoPlayStore } from "@/store/haikangVideoPlayStore";
import { JSEncrypt } from "jsencrypt";
// import axios from 'axios';class videoUtils {initCount = 0;pubKey = "";videoPlays = {};windowList = []; //所有窗口播放过的视频记录store = haikangVideoPlayStore();resCallMsg = null;layoutSting = "1x1";layoutInfo = { x: 1, y: 1 };wndNum = 1;myType = 100;//渡船摄像头widthOrHeight = { width: 0, height: 0 };common = toRaw(this.store.videoComParam);constructor(videoParam, controlParam, buttonIds) {// this.judgeIntranet();this.videoParam = videoParam; //需要初始化插件实例对象的id,类型为Arraythis.buttonIds = buttonIds; //视频对象需要展示的控件编码this.controlParam = controlParam; //视频窗口的其它配置}//初始化插件initPlugin(fn) {if (!Array.isArray(this.videoParam)) return;this.videoParam.forEach((el) => {this.createOWebControl(el, el.id, fn);});}createOWebControl(param, id, fn) {console.log("createOWebControl>>>", param.targetHeight);this.widthOrHeight = { width: param.targetWidth, height: param.targetHeight };let that = this;this.videoPlays[id] = new WebControl({szPluginContainer: param.target, // 指定容器idiServicePortStart: 15900, // 指定起止端口号,建议使用该值iServicePortEnd: 15900,szClassId: "23BF3B0A-2C56-4D97-9C03-0CB103AA8F11", // 用于IE10使用ActiveX的clsidcbConnectSuccess: function () {// 创建WebControl实例成功that.videoPlays[id].JS_StartService("window", {// WebControl实例创建成功后需要启动服务dllPath: "./VideoPluginConnect.dll", // 值"./VideoPluginConnect.dll"写死}).then(function () {// 启动插件服务成功that.videoPlays[id].JS_SetWindowControlCallback({// 设置消息回调_that: that,cbIntegrationCallBack: that.cbIntegrationCallBack,});that.videoPlays[id].JS_CreateWnd(param.target,param.targetWidth,param.targetHeight).then(function () {//JS_CreateWnd创建视频播放窗口,宽高可设定that.init(that.videoPlays[id], param, fn); // 创建播放实例成功后初始化});},function () {fn(true); // 启动插件服务失败});},cbConnectError: function () {// 创建WebControl实例失败try {Object.keys(that.videoPlays).forEach((el) => {that.videoPlays[el]?.JS_WakeUp("VideoWebPlugin://"); //程序未启动时执行error函数,采用wakeup来启动程序that.videoPlays[el] = null;});that.initCount = 0;fn(true);} catch (error) { }},cbConnectClose: function (bNormalClose) {// 异常断开:bNormalClose = false// JS_Disconnect正常断开:bNormalClose = trueconsole.log("cbConnectClose", bNormalClose);that.videoPlays[id] = null;fn(false, "暂未安装插件");setTimeout(() => {fn(true, "暂未安装插件");}, 5 * 1000);},});return this.videoPlays[id];}init(oWebControl, param, fn) {let that = this;this.getPubKey(oWebControl, () => {let appkey = this.common.appkey || '29671910'; //综合安防管理平台提供的appkey,必填let secret = this.setEncrypt(this.common.secret || 'SaW380VimBL8kYJX0DEJ'); //综合安防管理平台提供的secret,必填let ip = this.common.ip || '10.1.0.10'; //综合安防管理平台IP地址,必填let playMode = this.controlParam.playMode; //初始播放模式:0-预览,1-回放var port = this.common.port || 1443; //综合安防管理平台端口,若启用HTTPS协议,默认443var snapDir = "D:\\SnapDir"; //抓图存储路径var videoDir = "D:\\VideoDir"; //紧急录像或录像剪辑存储路径var layout = this.controlParam.layout; //playMode指定模式的布局var enableHTTPS = 1; //是否启用HTTPS协议与综合安防管理平台交互,这里总是填1var encryptedFields = "secret"; //加密字段,默认加密领域为secretvar showToolbar = this.controlParam.showToolbar; //是否显示工具栏,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 reconnectTimes = this.controlParam.reconnectTimes || 0;var reconnectDuration = this.controlParam.reconnectDuration || 5;oWebControl.JS_RequestInterface({funcName: "init",argument: JSON.stringify({appkey: appkey, //API网关提供的appkeysecret: secret, //API网关提供的secretip: ip, //API网关IP地址playMode: playMode, //播放模式(决定显示预览还是回放界面)port: port, //端口snapDir: snapDir, //抓图存储路径videoDir: videoDir, //紧急录像或录像剪辑存储路径layout: layout, //布局enableHTTPS: enableHTTPS, //是否启用HTTPS协议encryptedFields: encryptedFields, //加密字段showToolbar: showToolbar, //是否显示工具栏showSmart: showSmart, //是否显示智能信息buttonIDs: buttonIDs, //自定义工具条按钮reconnectTimes,reconnectDuration,}),}).then(function (oData) {oWebControl.JS_Resize(param.targetWidth, param.targetHeight); // 初始化后resize一次,规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题fn(false, oData); //初始化成功,返回消息参数,用于判断执行视频播放接口});});}setMyType(type) {this.myType = type}cbIntegrationCallBack(oData) {const { msg: resMsg, type } = oData.responseMsg;this._that.resCallMsg = { resMsg, type };if (type === 2) {//播放成功console.log("播放成功 123" + JSON.stringify(resMsg));console.log('播放768', this._that.myType, '222', resMsg.result, resMsg.wndId);if (this._that.myType === 100 && resMsg.result === 768) {//768开始播放this._that.callText(0, 0, resMsg.wndId);//调用这个方法}} else if (type === 6) {const layoutInfos = resMsg.layout.split('x');this._that.layoutSting = resMsg.layout;this._that.layoutInfo = { x: layoutInfos[0], y: layoutInfos[1] };this._that.wndNum = resMsg.wndNum;} else if (type === 7) {const layoutInfos = resMsg.layout.split('x');this._that.layoutSting = resMsg.layout;this._that.layoutInfo = { x: layoutInfos[0], y: layoutInfos[1] };this._that.wndNum = resMsg.wndNum;// this._that.callText(0, 0, resMsg.wndId,50,600);//调用这个方法}if (Object.keys(resMsg).length === 3 &&Object.keys(resMsg).join("") === "cameraIndexCoderesultwndId") {this._that.store.videoSelectInfo = oData.responseMsg.msg;}// showCBInfo(JSON.stringify(oData.responseMsg));this._that.store.inspectionStateRecordList.push(oData.responseMsg.msg);if (oData.responseMsg.msg.cameraIndexCode) {this._that.windowList.push(oData.responseMsg.msg);const removeDuplicates = (arr, prop) => {const map = {};arr = arr.reduceRight((result, item) => {const pid = item[prop];if (!map[pid]) {map[pid] = true;result.push(item);}return result;}, []);return (arr = arr.sort((a, b) => a.wndId - b.wndId));};if (!this._that.controlParam.playMode) {//视频预览} else {//视频回放this._that.store.playbackList = removeDuplicates(this._that.windowList,"wndId").map((item) => item.cameraIndexCode);}}}//获取公钥getPubKey(oWebControl, callback) {let that = this;oWebControl.JS_RequestInterface({funcName: "getRSAPubKey",argument: JSON.stringify({keyLength: 1024,}),}).then(function (oData) {if (oData.responseMsg.data) {that.pubKey = oData.responseMsg.data;callback();}});}//RSA加密setEncrypt(value) {let encrypt = new JSEncrypt();encrypt.setPublicKey(this.pubKey);return encrypt.encrypt(value);}//重新调整视频窗口尺寸videoWindowResize(resizeParam) {if (Object.keys(this.videoPlays).length != 0) {let videoPlayKeys = Object.keys(this.videoPlays);resizeParam.forEach((el) => {if (videoPlayKeys.includes(el.id)) {this.videoPlays[el.id] &&this.videoPlays[el.id].JS_Resize(el.targetWidth, el.targetHeight);}});}}//隐藏播放窗口hideVideoWindow() {if (Object.keys(this.videoPlays).length != 0) {Object.keys(this.videoPlays).forEach((key) => {this.videoPlays[key].JS_HideWnd();});}}//显示播放窗口showVideoWindow() {if (Object.keys(this.videoPlays).length != 0) {Object.keys(this.videoPlays).forEach((key) => {this.videoPlays[key].JS_ShowWnd();});}}/*** 预览功能* @param {*} param 接口请求的视频数据* @param {*} currentPlay 当前播放的视频窗口id*/playPreview(param, currentPlayId) {if (!this.videoPlays[currentPlayId]) return;const streamMode = param.networkStatus ?? this.store.networkStatus;// console.log(streamMode ? '子码流' : '主码流');return this.videoPlays[currentPlayId].JS_RequestInterface({funcName: "startPreview",argument: JSON.stringify({cameraIndexCode: param.cameraid, // 监控点编号streamMode, // 主子码流标识,0-主码流 1-子码流transMode: 1, // 传输协议,0-UDP 1-TCPgpuMode: 0, // 是否开启 GPU 硬解,不建议开启,0-不开启 1-开启wndId: param.wndId || 0 || -1,}),}).then(function (oData) {return oData;});}stopPreview(currentPlayId) {this.videoPlays[currentPlayId].JS_RequestInterface({funcName: "stopAllPreview",}).then(function (oData) {console.log("停止预览视频 结果:" + JSON.stringify(oData));});}playMultiPreview(param, currentPlayId, moreParam) {return new Promise(async (resolve, reject) => {try {let list = param.reduce((pre, cre) => {if (typeof cre === "string") {return [...pre, { cameraid: cre, wndId: -1 }];} else if (typeof cre === "object") {return [...pre,{ cameraid: cre.cameraIndexCode, wndId: cre.wndId },];}}, []);let resultInfo;await Promise.all(list.map(async (aitem) => {try {aitem = { ...aitem, ...moreParam };resultInfo = await this.playPreview(aitem, currentPlayId);} catch (error) {reject("播放失败");}}));resolve(resultInfo);} catch (error) {reject("播放失败");}});}getVideoLayout(currentPlayId) {return this.videoPlays[currentPlayId].JS_RequestInterface({funcName: "getLayout",}).then(function (oData) {console.log("获取窗口布局 结果:" + JSON.stringify(oData));return JSON.parse(oData.responseMsg.data);});}playBack(param, currentPlayId, timeRange) {if (!this.videoPlays[currentPlayId]) return;const that = this;return this.videoPlays[currentPlayId].JS_RequestInterface({funcName: "startPlayback",argument: JSON.stringify({cameraIndexCode: param.cameraid, // 监控点编号startTimeStamp: timeRange? Math.floor(timeRange[0] / 1000): Math.floor((Date.now() - 8.64e7) / 1000), // 录像查询开始时间戳,单位:秒endTimeStamp: timeRange? Math.floor(timeRange[1] / 1000): Math.floor(Date.now() / 1000), // 录像查询结束时间戳,单位:秒recordLocation: 1, // 录像存储类型 0-中心存储 1-设备存储transMode: 1, // 传输协议 ,0-UDP 1-TCPgpuMode: 0, // 是否开启 GPU 硬解,0-不开启 1-开启wndId: 0,}),}).then(function (oData) {console.log("回放视频 结果:" + JSON.stringify(oData));return oData;});}// 播放窗口批量预览playMultiWnd(currentPlayId, cameraInfo, fn) {if (!cameraInfo) return;const { cameraIndexCode, wndId } = cameraInfo,streamMode = this.store.networkStatus;console.log(streamMode ? '子码流' : '主码流');const that = this;this.videoPlays[currentPlayId]?.JS_RequestInterface({funcName: "startMultiPreviewByCameraIndexCode",argument: JSON.stringify({list: [{ cameraIndexCode, wndId, streamMode }],}),}).then(function (oData) {console.log("播放窗口批量播放 结果:", oData);return oData;});}// 播放窗口批量停止stopMultiWnd(currentPlayId, wndId) {this.videoPlays[currentPlayId].JS_RequestInterface({funcName: "stopMultiPlay",argument: JSON.stringify({list: [{ wndId }],}),}).then(function (oData) {console.log("播放窗口批量停止 结果:", oData);});}/*** 停止所有预览* @param opt*/stopAllPreview() {if (Object.keys(this.videoPlays).length != 0) {Object.keys(this.videoPlays).forEach((key) => {this.videoPlays[key].JS_RequestInterface({funcName: "stopAllPreview",}).then(function (oData) {console.log("销毁数据", oData);});});}}/*** 组件销毁时调用或者路由切换时调用* 释放资源*/destoryVideoWindow() {if (Object.keys(this.videoPlays).length != 0) {Object.keys(this.videoPlays).forEach((key) => {this.videoPlays[key].JS_HideWnd();this.videoPlays[key].JS_Disconnect().then(function () {// 断开与插件服务连接成功},function () {// 断开与插件服务连接失败});});}}/*** 窗口添加文字信息*/addWndText(totolNum = 0, unWearNum = 0, wid, x, y, wndNum) {if (!totolNum || !unWearNum) {totolNum = 0;unWearNum = 0;}const that = this;const { localX, localY } = that.drawFixedGlobalOSD(50, 600, that.widthOrHeight.width, that.widthOrHeight.height, that.layoutSting, "");const text = that.myType === 100 ? `总  人  数    :${totolNum} \n未穿救生衣  :${unWearNum}` : '';if (Object.keys(this.videoPlays).length != 0 && wid) {Object.keys(this.videoPlays).forEach((key) => {this.videoPlays[key].JS_RequestInterface({funcName: "drawOSD",argument: JSON.stringify({text: text, // 窗口布局x: Math.floor(localX),y: Math.floor(localY),// x: 50,// y: 600 / y,color: 16711680,wndId: wid,fontSize: 30,bold: 1,}),}).then(function (oData) {console.log("窗口文字信息 成功结果:" + JSON.stringify(oData));return oData;}).catch(function (oData) {console.log("窗口文字信息 失败结果:" + JSON.stringify(oData));return oData;});});}}callText(totolNum, unWearNum, wid, x, y) {const that = thisif (Object.keys(this.videoPlays).length != 0) {that.addWndText(totolNum, unWearNum, wid, that.layoutInfo.x, that.layoutInfo.y, that.wndNum);}}/*** 在固定全局位置绘制 OSD* @param {Number} globalX 插件整体坐标X(固定)* @param {Number} globalY 插件整体坐标Y(固定)* @param {Number} pluginW 插件初始化宽度* @param {Number} pluginH 插件初始化高度* @param {String} layout 当前布局,例如 "1x1", "2x2", "3x3"* @param {String} text 叠加文本*/drawFixedGlobalOSD(globalX, globalY, pluginW, pluginH, layout, text) {const [cols, rows] = layout.split("x").map(Number);const wndW = pluginW / cols;const wndH = pluginH / rows;const col = Math.floor(globalX / wndW);const row = Math.floor(globalY / wndH);const wndId = row * cols + col;const localX = globalX - col * wndW;const localY = globalY - row * wndH;console.log(`OSD 在布局 ${layout} 的窗口 ${wndId} 坐标 (${Math.floor(localX)}, ${Math.floor(localY)})`);return { localX, localY };}
}
export default videoUtils;

用户没有安装海康播放器的提示信息:

插件是后端部署在服务器上的,也可以自行去官网下载exe文件,例如:VideoWebPlugin.exe

VideoTipe.vue

<template><div class="tipe-box"><div style="text-align: center"><p>插件启动失败,请检查插件是否安装!未安装插件?点击<a :href="filePreUrl + '/VideoWebPlugin.exe'" style="color: #0085ff"download="VideoWebPlugin.exe">下载</a></p><p>插件已安装?请试试修改浏览器设置,在浏览器地址栏输入:<br /><spanstyle="color: #2678cd ; user-select: all;cursor: text;">chrome://flags/#block-insecure-private-network-</span>&nbsp;并按照如下设置</p></div></div>
</template><script setup>
import { BASEUrl } from '@/utils/request';
const filePreUrl = BASEUrl.replace('windfarms', 'download');
</script>
<style scoped lang="scss">
.tipe-box {height: 100%;width: 100%;background: #ffff;z-index: 9999999;>div {overflow: hidden;}
}
</style>

调用封装组件的示例,如下:

let myVideo = new videoUtils(initVideoParam(), {

    playMode: 0,

    showToolbar: 1,

    layout,

  });

const initVideoParam = () => {

  // 初始化视频插件设置相关参数(传入自己元素目标和位置)

  const { id, offsetWidth, offsetHeight } = videoPlaysRef.value;

  return [

    {

      id,

      target: id,

      targetWidth: offsetWidth * getScale(),

      targetHeight: offsetHeight * getScale(),

    },

  ];

};

const selectVideo_haikang = (data) => {

  videoPreview([{ cameraIndexCode: data.deviceId, wndId: haikangState.selectWndId || 1, ...data }]);

};

核心代码:

const handleNodeClick = (val) => {if (!(val.type === 100 || val.type === 101)) return;if (!haikangState.isHaiKang) {selectVideo(val);} else {console.log('选择摄像头>>>', val);selectVideo_haikang({ deviceId: val.deviceId || 'DS-2DC7223IW-A20190108AACHC86420496W' });// selectVideo_haikang({ deviceId: '7af2175a02c34e20bb923c706790013e' || 'DS-2DC7223IW-A20190108AACHC86420496W' });const wid = JSON.parse(JSON.stringify(haikangState.selectWndId));const type100 = JSON.parse(JSON.stringify(val.type));const isOk = timerIDS.includes(val.pid);if (val.type === 100) {if (isOk && aWindow.value !== 1) {//存在,清除這個定時器clearInterval(timeIntervalArr[val.pid]);timerIDS = timerIDS.filter(item => item !== val.pid);return}haikangState.video && haikangState.video.setMyType(type100);timeIntervalArr[val.pid] = setInterval(() => {getTextWdIfone(val, wid);}, 10 * 1000)timerIDS.push(val.pid);} else {if (aWindow.value === 1) {for (let i = 1; i < 1000; i++) {clearInterval(i);}}haikangState.video && haikangState.video.setMyType(type100);}}
};
const selectVideo_haikang = (data) => {const cacheInfoSetData = JSON.parse(JSON.stringify(haikangState.cacheInfoData));cacheInfoSetData.videoInfo[haikangState.selectWndId - 1] = {cameraIndexCode: data.deviceId,wndId: haikangState.selectWndId || 1,};haikangState.cacheInfoData = cacheInfoSetData;videoPreview([{ cameraIndexCode: data.deviceId, wndId: haikangState.selectWndId || 1, ...data }]);
};
const videoPreview = (videoList) => {let videoids = Object.keys(haikangState.video.videoPlays);videoids.forEach(async (el) => {// await haikangState.video.playMultiPreview([...videoList], el);haikangState.video.playMultiWnd(el, videoList[0])});
};//添加OSD到海康视频播放器,的接口请求
const getTextWdIfone = (data, wid) => {if (data.type === 100) {const params = {pid: data.pid,}api.videoApi.getTextWdIfone(params).then(res => {if (res.data.code == 200) {const resData = res.data.data;const { personnel: totalNum, jacket: wearNum, others: unWearNum } = resData;console.log("yes", wid, 22, data.type)haikangState.video && haikangState.video.callText(totalNum, unWearNum, wid);return resData}}).catch(err => {console.log(err);})}
}

完整代码1:html页面布局

<template>
<div class="channel-monitor"><div class="channel-left-box"><div class="tree-select-box"><el-tree style="max-width: 600px" :data="state.videoLists" :props="state.defaultProps"@node-click="handleNodeClick" node-key="pid" default-expand-all /></div></div><div v-loading="data.isLoading" class="channel-right-box"><VideoTipe v-show="haikangState.isShowTipe"></VideoTipe><div class="video-play-wrapper" id="videoPlayers" ref="videoPlaysRef" v-show="!haikangState.isShowTipe"></div></div>
</template><script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from "vue";
import videoUtils from '@/utils/video/videoUtils';
import api from "@/api/index";
import { haikangVideoPlayStore } from "@/store/haikangVideoPlayStore";
import { getScale } from '@/utils';
let timeInterval = null;
const store = videoPlayStore();
const haikangStore = haikangVideoPlayStore();
const layoutInfo = computed(() => {return {layout: haikangStore.layoutInfo.layout,wndNum: haikangStore.layoutInfo.wndNum,videoInfo: haikangStore.videoInfo}
})watch(layoutInfo, (newVal, oldValue) => {nextTick(() => {if (newVal) {const { layout, wndNum, videoInfo } = newValhaikangState.cacheInfoData = {layoutInfo: {layout,wndNum},videoInfo: videoInfo}}})
}, { immediate: true })
const haikangState = reactive({isHaiKang: true,isShowTipe: false, //是否下载视频插件video: null, //WebControl视频对象noClassVideoList: [], //没有分类的摄像头数据videoLists: [], //已按照tree组件分类处理的所有摄像头数据cacheInfoData: null,// currentVideoList: [],//在插件窗口播放的视频selectWndId: 1, //当前选中窗口
})
onMounted(() => {//海康视频nextTick(() => {videoInit(); //初始化视频对象_海康视频videoPlaysRef.value && addVideoObserver();});
});
onBeforeUnmount(() => {console.log("销毁视频插件");videoObserver && videoObserver.unobserve(videoPlaysRef.value);!haikangState.isShowTipe && haikangState.video.destoryVideoWindow(); //视频插件安装过,才能销毁视频插件timeInterval && clearInterval(timeInterval);for (let i = 1; i < 1000; i++) {clearInterval(i);}timeIntervalArr.forEach((item) => {clearInterval(item);})
});
const handleNodeClick = (val) => {if (!(val.type === 100 || val.type === 101)) return;if (!haikangState.isHaiKang) {selectVideo(val);} else {console.log('选择摄像头>>>', val);selectVideo_haikang({ deviceId: val.deviceId || 'DS-2DC7223IW-A20190108AACHC86420496W' });// selectVideo_haikang({ deviceId: '7af2175a02c34e20bb923c706790013e' || 'DS-2DC7223IW-A20190108AACHC86420496W' });const wid = JSON.parse(JSON.stringify(haikangState.selectWndId));const type100 = JSON.parse(JSON.stringify(val.type));const isOk = timerIDS.includes(val.pid);if (val.type === 100) {if (isOk && aWindow.value !== 1) {//存在,清除這個定時器clearInterval(timeIntervalArr[val.pid]);timerIDS = timerIDS.filter(item => item !== val.pid);return}haikangState.video && haikangState.video.setMyType(type100);timeIntervalArr[val.pid] = setInterval(() => {getTextWdIfone(val, wid);}, 10 * 1000)timerIDS.push(val.pid);} else {if (aWindow.value === 1) {for (let i = 1; i < 1000; i++) {clearInterval(i);}}haikangState.video && haikangState.video.setMyType(type100);}}
};
</script>

完整代码2:海康播放器

//海康视频
const videoPlaysRef = ref();
let videoObserver = null;
const initVideoParam = () => {// 初始化视频插件设置相关参数(传入自己元素目标和位置)const { id, offsetWidth, offsetHeight } = videoPlaysRef.value;return [{id,target: id,targetWidth: offsetWidth * getScale(),targetHeight: offsetHeight * getScale(),},];
};
const videoInitFun = async (isShowTipe, initSucessData) => {haikangState.isShowTipe = isShowTipe;if (!isShowTipe && initSucessData) videoListPlay();
};
const videoInit = () => {let layout = haikangState.cacheInfoData?.layoutInfo.layout;if (typeof layout === 'string') {} else if (typeof layout === 'number') {layout = layout + 'x' + layout;}haikangState.video = new videoUtils(initVideoParam(), {playMode: 0,showToolbar: 1,layout,});haikangState.video.initPlugin(videoInitFun);
};
//插件窗口尺寸刷新
const videoWindowResize = () => {haikangState.video.videoWindowResize(initVideoParam());
};
const addVideoObserver = () => {//添加窗口尺寸监听videoObserver = new ResizeObserver(() => {videoWindowResize();});videoObserver.observe(videoPlaysRef.value);
};
const videoListPlay = () => {const { videoInfo } = haikangState.cacheInfoData;Object.keys(videoInfo).length && videoPreview({ ...videoInfo }); //配合在当前用户有缓存视频信息情况下播放视频
};
const videoPreview = (videoList) => {let videoids = Object.keys(haikangState.video.videoPlays);videoids.forEach(async (el) => {// await haikangState.video.playMultiPreview([...videoList], el);if (videoList instanceof Array) {videoList.length === 1 &&haikangState.video.playMultiWnd(el, videoList[0], getTextWdIfone(videoList[0], JSON.parse(JSON.stringify(haikangState.selectWndId)))); //一个一个选择} else if (videoList instanceof Object) {const videoInfoData = objArrExchange(videoList);videoInfoData.forEach((aitem, aindex) => {//初始化时多个视频播放if (typeof aitem === 'string')haikangState.video.playMultiWnd(el, {cameraIndexCode: aitem,wndId: aindex + 1,});else if (typeof aitem === 'object')haikangState.video.playMultiWnd(el, aitem);});}});
};
//对象数组互换
const objArrExchange = (target) => {if (target instanceof Object) {return Object.values(target).map((aitem) => {return Object.assign({},...Object.entries(aitem).map(([key, value]) => ({[key]: value,})));});} else if (target instanceof Array) {return target.map((item) => ({ [item.key]: item.value })).reduce((acc, curr) => {return acc;}, {});}
};
const aWindow = ref();
watch(() => haikangState.video?.resCallMsg,(newval, oldval) => {if (!newval) return;if (newval.type) {const { resMsg, type } = newval;if (type === 1) {//窗口选中消息haikangState.selectWndId = resMsg.wndId;console.log('===============1', haikangState.selectWndId);} else if (type === 2) {// 预览/回放播放视频消息if (!resMsg.cameraIndexCode) return;if (resMsg.result === 768) {aWindow.value = resMsg.wndIdconsole.log('======!!!=========768', aWindow.value);} //开始播放if (resMsg.result === 816) {//结束播放const closeTarget = resMsg;const cacheInfoSetData = { ...haikangState.cacheInfoData };const videoInfo = objArrExchange(cacheInfoSetData.videoInfo);const target = videoInfo.find((aitem) =>aitem && aitem.wndId === closeTarget.wndId);if (videoInfo.length) {delete cacheInfoSetData.videoInfo[target.wndId - 1];haikangState.cacheInfoData = cacheInfoSetData}}} else if (type === 6) {//播放窗口布局改变消息const cacheInfoSetData = { ...haikangState.cacheInfoData };cacheInfoSetData.layoutInfo = resMsg;haikangState.cacheInfoData = cacheInfoSetData;videoListPlay();}}},{ deep: true }
);
const getTextWdIfone = (data, wid) => {console.log('inner>>>', data);if (data.type === 100) {const params = {pid: data.pid,}api.videoApi.getTextWdIfone(params).then(res => {if (res.data.code == 200) {const resData = res.data.data;const { personnel: totalNum, jacket: wearNum, others: unWearNum } = resData;console.log("yes", wid, 22, data.type)haikangState.video && haikangState.video.callText(totalNum, unWearNum, wid);return resData}}).catch(err => {console.log(err);})}
}

四、画面字符叠加OSD

海康播放插件本质是ActiveX控件,无法通过css控制位置和大小,当页面窗口大小变化或者遇到页面有滚动条情况就需要手动设置插件窗口位置和大小,可以通过JS_Resize (调整插件窗口大小、位置),JS_CuttingPartWindow (扣除部分插件窗口)两个api来实现。

背景:

海康播放器插件是浮在浏览器表面的,只要窗口大小或有页面滚动条的情况,就需要手动设置海康播放器插件的位置和大小。

核心代码:

必须是在海康视频播放成功才能添加画面字符OSD

所以在海康视频成功的回调函数里面,进行逻辑判断:

//

  cbIntegrationCallBack(oData) {

    const { msg: resMsg, type } = oData.responseMsg;

    this._that.resCallMsg = { resMsg, type };

    if (type === 2) {

      //播放成功

      console.log("播放成功 123" + JSON.stringify(resMsg));

      console.log('播放768', this._that.myType, '222', resMsg.result, resMsg.wndId);

      if (this._that.myType === 100 && resMsg.result === 768) {

        //768开始播放

        this._that.callText(0, 0, resMsg.wndId);//调用这个方法

      }

    } else if (type === 6) {

      const layoutInfos = resMsg.layout.split('x');

      this._that.layoutSting = resMsg.layout;

      this._that.layoutInfo = { x: layoutInfos[0], y: layoutInfos[1] };

      this._that.wndNum = resMsg.wndNum;

    } else if (type === 7) {

      const layoutInfos = resMsg.layout.split('x');

      this._that.layoutSting = resMsg.layout;

      this._that.layoutInfo = { x: layoutInfos[0], y: layoutInfos[1] };

      this._that.wndNum = resMsg.wndNum;

    }

}

相关文档:

视频文档:点击跳转

动态设置海康播放器的大小和位置:

/* 获取div大小及位置 */getDomInfo(oWebControl) {const { width, height, top, left } = document.getElementById('playWnd0').getBoundingClientRect()if (oWebControl) {oWebControl.JS_Resize(width, height)oWebControl.JS_CuttingPartWindow(left, top, 0, 0)}else{this.plug.example.forEach(item=>{item.JS_Resize(width, height)item.JS_CuttingPartWindow(left, top, 0, 0)})}},
//videoPlaysRef.value打印是vue中
//<div class="video-play-wrapper" id="videoPlayers" ref="videoPlaysRef"></div>
const getDomInfo = () => {// 初始化视频插件设置相关参数(传入自己元素目标和位置)const { id, offsetWidth, offsetHeight } = videoPlaysRef.value;return [{id,target: id,targetWidth: offsetWidth * getScale(),targetHeight: offsetHeight * getScale(),},];
};
export const getScale = () => {const width = 1920,height = 1080;let ww = width / (window.innerWidth * 0.6);let wh = height / (window.innerHeight * 0.6);if (window.innerWidth <= 4000) {// let ww = window.innerWidth / width;// let wh = window.innerHeight / height;return 1;} else {return ww < wh ? ww : wh;}// if (window.innerWidth <= 1920) {// 	let ww = window.innerWidth / width;// 	let wh = window.innerHeight / height;// 	return ww < wh ? ww : wh;// } else {// 	return ww < wh ? ww : wh;// }
};

http://www.dtcms.com/a/354298.html

相关文章:

  • Java Spring Boot 集成淘宝 SDK:实现稳定可靠的商品信息查询服务
  • AI鱼塘,有你画的鱼吗?
  • 代码随想录刷题Day44
  • IDEA连接阿里云服务器中的Docker
  • 嵌入式学习日志————DMA直接存储器存取
  • 微信开发者工具中模拟调试现场扫描小程序二维码功能
  • Centos 7.6离线安装docker
  • 元宇宙+RWA:2025年虚拟资产与真实世界的金融融合实验
  • aiohttp模块如何使用
  • 开发避坑指南(39):Java List全null元素引发的空判断无效处理方案
  • 用LightRAG打造智能问答系统:知识图谱与RAG的融合应用实践
  • 如何在升级到S/4HANA后关闭SAP旧系统?
  • 3-4〔OSCP ◈ 研记〕❘ WEB应用攻击▸Burp Suite工具
  • MySQL中的InnoDB
  • pikachu-xss通关(1-8)
  • qt5+mingw64+opencv4.5.2搭建调试环境
  • FOC算法第二节 克拉克变换
  • mybatis-plus实现苍穹外卖项目-分类操作,不定期更新-day2
  • Hadoop(五)
  • AI、人工智能基础: 模型剪枝的概念与实践(PyTorch版)
  • uvloop深度实践:从原理到高性能异步应用实战
  • 死锁产生的条件是什么? 如何进行死锁诊断?
  • 本地部署DeepSeek大模型的基本方法
  • 自定义命令行补全机制的核心工具之compgen
  • web服务组件
  • MII的原理
  • 软件设计师备考-(三)操作系统基本原理
  • leetcode28. 找出字符串中第一个匹配项的下标
  • VR党建工作站-红色教育基地
  • 路由基础(三):静态路由、动态路由、默认路由