前言
简书应该不适合记录技术类文章换juej了
早前发现,有些记录涉及小程序带有微信,海康类似文案,疑似偏黑产风险词汇会概率被锁定,但实际是正常bug类记录,申诉过于浪费时间换个平台了,抓马吸眼球的奶头乐反门户大开 应该是情感类博文风华薛岳聚集地。
web视频插件,是指windos下安装海康官网的exe在网页中通过中间js,实现网页上预览监控画面的形式。
这是iframe接入任意网页账号密码接入的形式
优点:
1 不需要后台参与,指转wss流协议在网页播放,官网对照是h5视频播放器开发包
2 只要有超脑账号密码ip
缺点:
1 浏览器出现多窗口都有监控画面显示时,会快速切换时,重复启用海康插件窗口,出现多个画面叠加
2 关闭a页面打开b页面,可能虽然关闭了a但未及时释放监控画面导致b页上出现了a页的监控画面。
open.hikvision.com/download/5c…
本质是后台有个exe播放器,海康通过调整在电脑屏幕上的位置实现视觉上的引入网页,显示的不是video而是实打实的exe软件
<!DOCTYPE html>
<html>
<head>
<title>海康威视自动监控</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
body,
html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
#divPlugin {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="divPlugin"></div>
<script src="../../../public/static/lib/jquery-3.3.1.min.js"></script>
<!-- 海康账号登录视频插件 -->
<script src="../../../public/static/js/webVideoCtrl.js"></script>
<script>
function getQueryStringParam(paramName) {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get(paramName);
}
const CONFIG = {
IP: getQueryStringParam("ip"),
PORT: "80",
PROTOCOL: 1,
USER: getQueryStringParam("user"),
PASSWORD: getQueryStringParam("pwd"),
RTSP_PORT: "554",
iWndowType: getQueryStringParam("split") * 1, // 必须数字
// 1 单窗口;
// 2 4 宫格,即 2×2 布局;
// 3 6 宫格;
// 4 8 宫格;
// 5 9 宫格,即 3×3 布局;
// 6 16 宫格。
channels: getQueryStringParam("channel")
? getQueryStringParam("channel").split(",")
: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
STREAM_TYPE: 2,
};
let webVideoCtrl = null;
let isPreviewing = false;
let controller = null;
let initInProgress = false; // 跟踪初始化状态
class HikvisionController {
constructor() {
this.deviceId = `${CONFIG.IP}_${CONFIG.PORT}`;
this.init();
}
async init() {
if (initInProgress) return; // 防止重复初始化
initInProgress = true;
try {
closeHk();
await this.initPlugin();
await this.loginDevice();
await this.setupDisplay();
await this.startPreviewAllChannels();
} catch (error) {
console.error(1, error);
} finally {
initInProgress = false;
}
}
async initPlugin() {
return new Promise((resolve, reject) => {
if (!window.WebVideoCtrl) {
reject(new Error("WebVideoCtrl插件未加载"));
return;
}
WebVideoCtrl.I_InitPlugin({
bWndFull: true,
cbInitPluginComplete: () => {
console.log("插件初始化完成");
if (document.getElementById("divPlugin")) {
WebVideoCtrl.I_InsertOBJECTPlugin("divPlugin")
.then(() => {
// 调整插件位置
this.adjustPluginPosition();
resolve();
})
.catch((err) =>
reject(new Error(`插件插入失败: ${err.message}`))
);
} else {
reject(new Error("divPlugin容器不存在"));
}
},
});
});
}
adjustPluginPosition() {
// 获取iframe的DOM元素
const iframe = window.frameElement;
console.log("获取iframe的DOM元素");
console.log(iframe);
if (iframe) {
// 获取iframe相对于窗口的偏移量
console.log("获取iframe相对于窗口的偏移量");
const iframeRect = iframe.getBoundingClientRect();
const iframeOffsetTop = iframeRect.top;
const iframeOffsetLeft = iframeRect.left;
// 获取视频插件元素
const pluginElement = document.getElementById("divPlugin");
// 设置插件的定位方式
pluginElement.style.position = "absolute";
pluginElement.style.top = iframeOffsetTop + "px";
pluginElement.style.left = iframeOffsetLeft + "px";
// 调整插件大小
if (WebVideoCtrl) {
WebVideoCtrl.I_Resize(
pluginElement.offsetWidth,
pluginElement.offsetHeight
);
}
}
}
loginDevice() {
return new Promise((resolve, reject) => {
if (!window.WebVideoCtrl || !WebVideoCtrl.I_Login) {
reject(new Error("WebVideoCtrl.I_Login方法未定义"));
return;
}
WebVideoCtrl.I_Login(
CONFIG.IP,
CONFIG.PROTOCOL,
CONFIG.PORT,
CONFIG.USER,
CONFIG.PASSWORD,
{
success: (xmlDoc) => {
if ($(xmlDoc).find("statusString").text() === "OK") {
resolve();
} else {
reject(new Error("登录状态异常"));
}
},
error: (err) =>
reject(
new Error(`登录失败: ${err.errorCode} ${err.errorMsg}`)
),
}
);
});
}
setupDisplay() {
return new Promise((resolve) => {
// 设置为指定窗口布局
if (WebVideoCtrl && WebVideoCtrl.I_ChangeWndNum) {
WebVideoCtrl.I_ChangeWndNum(CONFIG.iWndowType);
}
if (WebVideoCtrl && WebVideoCtrl.I_Resize) {
WebVideoCtrl.I_Resize(window.innerWidth, window.innerHeight);
}
window.addEventListener("resize", () => {
if (WebVideoCtrl && WebVideoCtrl.I_Resize) {
WebVideoCtrl.I_Resize(window.innerWidth, window.innerHeight);
}
});
resolve();
});
}
async startPreviewAllChannels() {
if (isPreviewing) return;
isPreviewing = true;
try {
// 强制停止所有预览
if (WebVideoCtrl.I_StopRealPlayAll) {
WebVideoCtrl.I_StopRealPlayAll(this.deviceId);
}
const previewPromises = CONFIG.channels.map((channel, index) =>
this.playChannel(channel, index)
);
await Promise.allSettled(previewPromises);
} catch (error) {
console.error("预览启动失败:", error);
} finally {
isPreviewing = false;
}
}
playChannel(iChannelID, wndIndex) {
return new Promise((resolve, reject) => {
if (!window.WebVideoCtrl || !WebVideoCtrl.I_StartRealPlay) {
reject(new Error("WebVideoCtrl.I_StartRealPlay方法未定义"));
return;
}
WebVideoCtrl.I_StartRealPlay(this.deviceId, {
iWndIndex: wndIndex,
iChannelID: iChannelID,
iStreamType: CONFIG.STREAM_TYPE,
bZeroChannel: false,
iPort: CONFIG.RTSP_PORT,
success: () => resolve(),
error: (err) => reject(new Error(err.errorMsg)),
});
});
}
}
function closeHk() {
if (window.WebVideoCtrl.I_StopAllPlay) {
window.WebVideoCtrl.I_DestroyPlugin();
window.WebVideoCtrl.I_StopAllPlay();
} else {
console.log(0, window.WebVideoCtrl, "未检测到已有控件");
}
}
document.addEventListener("DOMContentLoaded", () => {
// 加载时关闭
closeHk();
// 确保DOM完全加载后再释放旧资源
// setTimeout(() => {
controller = new HikvisionController();
// }, 100); // 延迟初始化确保DOM稳定
});
window.addEventListener("beforeunload", () => {
closeHk();
});
// 监听iframe卸载
window.addEventListener("unload", () => {
closeHk();
});
</script>
</body>
</html>
使用方式
https://xx.com/index.html?ip=192.168.0.2&user=admin&pwd=xx&split=2&channel=3,2,6,7
超脑的ip账号密码
注意下载官网插件后的js,要两个放入对应,js是动态加载的会自行加载另外一个虽然html未引入
vue版未验证: juejin.cn/post/733393…
推荐个另外的vue版,同样public下index.html需要引入jq和官方的webVideo.js
<template>
<div ref="divPlugin" id="divPlugin" class="plugin-container"></div>
</template>
<script>
export default {
name: 'HikvisionMonitor',
props: {
config: {
type: Object,
required: true,
default: () => ({})
}
},
data() {
return {
webVideoCtrl: null,
isPreviewing: false,
controller: null,
initInProgress: false,
deviceId: '',
channels: []
}
},
watch: {
config: {
handler(newVal) {
// this.handleConfigChange(newVal)
},
deep: true
}
},
mounted() {
// 初始化插件容器
this.initPlugin()
},
beforeDestroy() {
// 组件销毁前清理插件资源
console.log('组件销毁前清理插件资源')
this.closeHk()
},
methods: {
// 获取查询参数(此处可根据实际需求调整)
getQueryStringParam(paramName) {
const urlParams = new URLSearchParams(window.location.search)
return urlParams.get(paramName)
},
handleConfigChange(newConfig) {
// 当配置改变时,重新初始化或更新预览
this.closeHk() // 先关闭当前预览
this.initPlugin() // 重新初始化
},
initPlugin() {
if (this.initInProgress) return
this.initInProgress = true
this.deviceId = `${this.config.ip}_${this.config.port || 80}`
this.channels = this.config.channel || [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // 根据传入配置解析通道
if (!window.WebVideoCtrl) {
console.error('WebVideoCtrl 插件未加载')
this.initInProgress = false
return
}
// 初始化插件
WebVideoCtrl.I_InitPlugin({
bWndFull: true,
cbInitPluginComplete: () => {
console.log('插件初始化完成')
this.$nextTick(() => {
if (this.$refs.divPlugin) {
// 插入插件到容器中
WebVideoCtrl.I_InsertOBJECTPlugin('divPlugin')
.then(() => {
this.loginDeviceAndSetupDisplay()
})
.catch(err => {
console.error(1, err)
})
} else {
console.error('未找到插件容器')
}
})
}
})
},
loginDeviceAndSetupDisplay() {
// 登录设备并设置显示
if (window.WebVideoCtrl && WebVideoCtrl.I_Login) {
WebVideoCtrl.I_Login(
this.config.ip,
1, // 假设协议为 1(可根据实际情况调整)
this.config.port || 80,
this.config.user,
this.config.pwd,
{
success: xmlDoc => {
if ($(xmlDoc).find('statusString').text() === 'OK') {
this.setupDisplay()
} else {
console.error('登录状态异常')
}
},
error: err => {
console.error('登录失败:', err)
}
}
)
} else {
console.error('WebVideoCtrl.I_Login 方法未定义')
}
},
setupDisplay() {
// 设置显示布局
if (window.WebVideoCtrl && WebVideoCtrl.I_ChangeWndNum) {
// 切割画面要求数字
WebVideoCtrl.I_ChangeWndNum(this.config.split || 2)
}
// window.addEventListener('resize', () => {
// if (window.WebVideoCtrl && WebVideoCtrl.I_Resize) {
// WebVideoCtrl.I_Resize(window.innerWidth, window.innerHeight)
// }
// })
this.startPreviewAllChannels() // 启动所有通道预览
},
startPreviewAllChannels() {
if (this.isPreviewing) return
this.isPreviewing = true
try {
if (window.WebVideoCtrl && WebVideoCtrl.I_StopRealPlayAll) {
WebVideoCtrl.I_StopRealPlayAll(this.deviceId)
}
this.channels.forEach((channel, index) => {
this.playChannel(channel, index)
})
} catch (error) {
console.error('预览启动失败:', error)
} finally {
this.isPreviewing = false
}
},
playChannel(iChannelID, wndIndex) {
if (!window.WebVideoCtrl || !WebVideoCtrl.I_StartRealPlay) {
console.error('WebVideoCtrl.I_StartRealPlay 方法未定义')
return
}
WebVideoCtrl.I_StartRealPlay(this.deviceId, {
iWndIndex: wndIndex,
iChannelID: parseInt(iChannelID), // 确保通道 ID 为数字类型
iStreamType: 2, // 假设流类型为 2(可根据实际情况调整)
bZeroChannel: false,
iPort: 554, // RTSP 端口(可根据实际情况调整)
success: () => {
console.log(`通道 ${iChannelID} 预览启动成功`)
},
error: err => {
console.error(`通道 ${iChannelID} 预览启动失败:`, err)
}
})
},
closeHk() {
if (window.WebVideoCtrl) {
console.log('关闭预览')
window.WebVideoCtrl.I_StopAllPlay()
console.log('销毁插件')
window.WebVideoCtrl.I_DestroyPlugin()
window.WebVideoCtrl.I_Logout(this.deviceId)
}
}
}
}
</script>
<style scoped>
.plugin-container {
width: 100%;
height: 100%;
}
</style>