Vuejs集成海康威视web端视频插件问题。
目前公司有一个视频播放的需求:利用海康威视web端插件对摄像头视频进行播放(单播、多播)。。目前问题:
1、该web插件支持直接播放rtsp格式是视频流吗?(接口返回的视频流格式是rtsp格式的)。
基于海康web端视频插件的代码封住的VueJs组件代码如下:
<template>
<div class="play_wnd_box">
<div
id="playWnd"
class="play_wnd"
></div>
<span
v-if="videoPlayStatus === 1"
class="prompt_text"
>安装插件后刷新此页面。</span>
<span
v-else-if="videoPlayStatus === 2"
class="prompt_text"
>插件未启动,正在尝试启动,请稍候...</span>
</div>
</template>
<script>
import dayjs from 'dayjs'
import { mapState } from "vuex";
import { Modal } from 'ant-design-vue';
export default {
name: "HkVideo",
expose: ['startMultiPreview', 'stopVidePreview', 'snapPic', 'hiddenVidoe', 'showVideo', 'startPreview'],
data () {
return {
oWebControl: null,
initCount: 0,
pubKey: '',
divSizeWidth: 700,
divSizeHeight: 400,
oDiv: null,
iLastCoverLeft: 0,
iLastCoverTop: 0,
iLastCoverRight: 0,
iLastCoverBottom: 0,
setTimeoutClock: null,
videoPlayStatus: 0
}
},
props: {
pointData: {
required: true,
type: Array,
default: function () {
return [{
code: "11011500581314000911",
type: '1'
}]
}
},
initData: {
required: true,
type: Object
}
},
computed: {
...mapState({
inHomeSever: state => state.app.inHomeSever
})
},
mounted () {
this.oDiv = document.getElementById("playWnd")
this.divSizeWidth = this.oDiv.clientWidth || 700
this.divSizeHeight = this.oDiv.clientHeight || 400
//根据窗口和滚动设置插件大小
window.addEventListener('resize', this.handleScroll)
window.addEventListener('scroll', this.handleScroll)
//判断是否在主应用打开,在主应用打开时不执行 initScript()
if (this.inHomeSever) {
this.initPlugin()
} else {
this.initScript()
}
},
beforeUnmount () {
window.removeEventListener('resize', this.handleScroll)
window.removeEventListener('scroll', this.handleScroll)
this.destructionVideo()
if (this.setTimeoutClock) {
clearTimeout(this.setTimeoutClock)
}
},
methods: {
//初始化加载Script标签
initScript () {
const head = document.head || document.getElementsByTagName('head')[0];
const promise1 = new Promise((resolve, reject) => {
const script1 = document.createElement('script'); //创建一个script标签
script1.src = 'https://www.xxx.com/jsencrypt.min.js'
head.appendChild(script1)
script1.onload = script1.onreadystatechange = function () {
if (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") {
resolve()
}
};
});
const promise2 = new Promise((resolve, reject) => {
const script2 = document.createElement('script'); //创建一个script标签
script2.src = 'https://www.xxx.com/jsWebControl-1.0.0.min.js'
head.appendChild(script2)
script2.onload = script2.onreadystatechange = function () {
if (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") {
resolve()
}
};
});
Promise.all([
promise1,
promise2
]).then(res => {
/*初始化播放器插件*/
this.initPlugin()
})
},
//初始化播放器插件
initPlugin () {
const that = this
this.oWebControl = new WebControl({
szPluginContainer: "playWnd", // 指定容器id
iServicePortStart: 15900, // 指定起止端口号,建议使用该值
iServicePortEnd: 15909,
szClassId: "23BF3B0A-2C56-4D97-9C03-0CB103AA8F11", // 用于IE10使用ActiveX的clsid
cbConnectSuccess: function () {
console.warn('初始化播放器插件成功')
that.setCallbacks()
that.oWebControl.JS_StartService("window", { // WebControl实例创建成功后需要启动服务
dllPath: "./VideoPluginConnect.dll" // 值"./VideoPluginConnect.dll"写死
}).then(function () {
that.videoPlayStatus = 0 // 启动插件服务成功
that.oWebControl.JS_CreateWnd("playWnd", 700, 400).then(
function () { //JS_CreateWnd创建视频播放窗口,宽高可设定
that.initParameter(); // 创建播放实例成功后初始化
});
}, function () {
console.log('启动插件服务失败!')
});
},
cbConnectError: function () {
that.oWebControl = null
that.videoPlayStatus = 2
console.log('插件未启动,正在尝试启动,请稍候...')
WebControl.JS_WakeUp("VideoWebPlugin://"); // 程序未启动时执行error函数,采用wakeup来启动程序
that.initCount++;
if (that.initCount < 3) {
that.setTimeoutClock = setTimeout(function () {
that.initPlugin()
}, 3000)
} else {
that.videoPlayStatus = 1
console.log(that.initData.version, '插件版本')
Modal.confirm({
title: '下载提示',
content: '未检测到视频插件V' + that.initData.version + ',是否下载安装?',
okText: '确认',
cancelText: '取消',
wrapClassName: 'hkVideo-box',
onOk () {
window.open(that.initData.pluginUrl)
},
onCancel () {
//console.log('Cancel');
},
})
}
},
cbConnectClose: function (bNormalClose) {
// 异常断开:bNormalClose = false
// JS_Disconnect正常断开:bNormalClose = true
console.log("cbConnectClose", bNormalClose)
that.oWebControl = null
}
});
},
// 反初始化
uninit () {
this.oWebControl.JS_RequestInterface({
funcName: "uninit"
}).then(function () { });
},
// 初始化参数
initParameter () {
const that = this
this.getPubKey(function () {
const buttonIDs = "0,16,256,257,258,259,260,512,513,514,515,516,517,768,769"; //自定义工具条按钮
that.oWebControl.JS_RequestInterface({
funcName: "init",
argument: JSON.stringify({
appkey: that.initData.appkey, //API网关提供的appkey
secret: that.setEncrypt(that.initData.secret), //API网关提供的secret
ip: that.initData.ip, //API网关IP地址
playMode: that.initData.playMode || 0, //初始播放模式:0-预览,1-回放
port: that.initData.port, //端口
snapDir: "D:\\SnapDir", //抓图存储路径
videoDir: "D:\\VideoDir", //紧急录像或录像剪辑存储路径
layout: that.initData.layout, //布局
enableHTTPS: 1, //是否启用HTTPS协议
encryptedFields: 'secret', //加密字段
showToolbar: 1, //是否显示工具栏
showSmart: 1, //是否显示智能信息
buttonIDs: buttonIDs //自定义工具条按钮
})
}).then(function (oData) {
// 初始化后resize一次,规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题
that.oWebControl.JS_Resize(that.divSizeWidth, that.divSizeHeight)
// that.startMultiPreview(),这里先注释不调用看看
});
});
},
//窗口布局
setLayout (num = '1x1') {
const pram = {
funcName: "setLayout",
argument: {
layout: num
}
}
this.requestInterface(pram)
},
//单个预览
startPreview () {
const pram = {
funcName: "startPreview",
argument: JSON.stringify({
cameraIndexCode: this.pointData[0].code, //监控点编号
streamMode: 1, //主子码流标识:0-主码流,1-子码流
transMode: 0, //传输协议:0-UDP,1-TCP
gpuMode: 0, //是否启用GPU硬解,0-不启用,1-启用
wndId: -1, //播放窗口序号(在2x2以上布局下可指定播放窗口)
ezvizDirect: this.pointData[0].type // 未指定或者0 非直连,其他为直连
})
}
this.requestInterface(pram)
},
//单个回放
startPlayback (startTime, endTime, wndId) {
const pram = {
funcName: "startPlayback",
argument: JSON.stringify({
cameraIndexCode: this.pointData[0].code, //监控点编号
startTimeStamp: startTime, // 录像查询开始时间戳,单位:秒
endTimeStamp: endTime, // 录像查询结束时间戳,单位:秒
recordLocation: Number(this.initData.storageStrategy), // 录像存储类型 0-中心存储 1-设备存储
transMode: 0, // 传输协议 ,0-UDP 1-TCP
gpuMode: 0, // 是否开启 GPU 硬解,0-不开启 1-开启
wndId: wndId, //播放窗口序号(在2x2以上布局下可指定播放窗口)
})
}
this.requestInterface(pram)
},
//多个窗口播放
startMultiPreview () {
const listArr = [];
this.pointData.forEach((val, index, arr) => {
listArr.push({
cameraIndexCode: val.code,
ezvizDirect: val.type,
gpuMode: 0,
streamMode: 1,
transMode: 0,
wndId: index + 1
})
})
const pram = {
funcName: "startMultiPreviewByCameraIndexCode",
argument: {
list: listArr
}
}
this.requestInterface(pram)
},
requestInterface (value) {
this.oWebControl.JS_RequestInterface(value).then(function (oData) {
console.log('HkVideo请求成功:', oData)
}).catch(err => {
console.error('HkVideo网络请求失败:', err)
// 添加更详细的错误信息
if (err.message) {
console.error('错误信息:', err.message)
}
if (err.code) {
console.error('错误代码:', err.code)
}
// 向父组件发送错误事件
this.$emit('videoPlayEvent', {
type: 'error',
error: err,
message: '网络请求失败'
})
});
},
//窗口发生变化调用
handleScroll () {
this.divSizeWidth = this.oDiv.clientWidth
this.divSizeHeight = this.oDiv.clientHeight
if (this.oWebControl != null) {
setTimeout(() => {
this.oWebControl.JS_Resize(this.divSizeWidth, this.divSizeHeight)
this.setWndCover()
}, 200)
}
},
// 设置窗口遮挡
setWndCover () {
const iWidth = document.body.offsetWidth
const iHeight = document.body.offsetHeight
const oDivRect = document.getElementById("playWnd").getBoundingClientRect()
let iCoverLeft = (oDivRect.left < 0) ? Math.abs(oDivRect.left) : 0
let iCoverTop = (oDivRect.top < 0) ? Math.abs(oDivRect.top) : 0
let iCoverRight = (oDivRect.right - iWidth > 0) ? Math.round(oDivRect.right - iWidth) : 0
let iCoverBottom = (oDivRect.bottom - iHeight > 0) ? Math.round(oDivRect.bottom - iHeight) : 0
iCoverLeft = (iCoverLeft > this.divSizeWidth) ? this.divSizeWidth : iCoverLeft
iCoverTop = (iCoverTop > this.divSizeHeight) ? this.divSizeHeight : iCoverTop
iCoverRight = (iCoverRight > this.divSizeWidth) ? this.divSizeWidth : iCoverRight
iCoverBottom = (iCoverBottom > this.divSizeHeight) ? this.divSizeHeight : iCoverBottom
if (this.iLastCoverLeft != iCoverLeft) {
this.iLastCoverLeft = iCoverLeft
this.oWebControl.JS_SetWndCover("left", iCoverLeft)
}
if (this.iLastCoverTop != iCoverTop) {
this.iLastCoverTop = iCoverTop
this.oWebControl.JS_SetWndCover("top", iCoverTop)
}
if (this.iLastCoverRight != iCoverRight) {
this.iLastCoverRight = iCoverRight
this.oWebControl.JS_SetWndCover("right", iCoverRight)
}
if (this.iLastCoverBottom != iCoverBottom) {
this.iLastCoverBottom = iCoverBottom
this.oWebControl.JS_SetWndCover("bottom", iCoverBottom)
}
},
// 设置窗口控制回调
setCallbacks () {
const that = this
this.oWebControl.JS_SetWindowControlCallback({
cbIntegrationCallBack: that.cbIntegrationCallBack
});
},
cbIntegrationCallBack (oData) {
const data = oData.responseMsg
this.$emit('videoPlayEvent', data)
},
//截图
snapPic (num = 0, item = false) {
const that = this;
let snapName;
const wndId = parseInt(num, 10); // 0 选中窗口截图 1,、2、3...等数字为指定窗口
if (item) {
snapName = "D:\/SnapDir\/" + that.dateFormat(new Date(), "HH:mm") + item.name + ".jpg";
} else {
snapName = "D:\/SnapDir\/"
}
const code = new Promise((resolve, reject) => {
that.oWebControl.JS_RequestInterface({
funcName: "snapShot",
argument: JSON.stringify({
name: snapName,
wndId: wndId
})
}).then(function (oData) {
resolve(oData ? oData.responseMsg.code : '-1');
});
}).then(r => {
return r;
}).catch(e => {
console.log(e)
})
code.then((val) => {
if (val === 512) {
this.$emit('videoPlayEvent', {
type: 3,
msg: {
result: 512,
text: '截图失败'
}
})
}
})
},
//获取公钥
getPubKey (callback) {
const that = this
this.oWebControl.JS_RequestInterface({
funcName: "getRSAPubKey",
argument: JSON.stringify({
keyLength: 1024
})
}).then(function (oData) {
if (oData.responseMsg.data) {
that.pubKey = oData.responseMsg.data
callback()
}
})
},
//加密
setEncrypt (value) {
const that = this
const encrypt = new JSEncrypt()
encrypt.setPublicKey(that.pubKey)
return encrypt.encrypt(value)
},
//关闭,销毁标签
destructionVideo () {
if (this.oWebControl != null) {
this.uninit()
this.oWebControl.JS_HideWnd() // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题
this.oWebControl.JS_Disconnect().then(function () { }, function () { })
}
},
//隐藏
hiddenVidoe () {
if (this.oWebControl != null) {
this.oWebControl.JS_HideWnd() // 让窗口隐藏
}
},
//显示
showVideo () {
if (this.oWebControl != null) {
this.oWebControl.JS_ShowWnd() // 让窗口显示
}
},
//停止全部预览
stopVidePreview () {
const that = this;
that.oWebControl.JS_RequestInterface({
funcName: "stopAllPreview"
}).then(function (oData) {
//展示回调信息
//that.showCBInfo('停止预览',JSON.stringify(oData ? oData.responseMsg : ''));
});
},
// 格式化时间
dateFormat (oDate, fmt) {
const time = dayjs(oDate).format(fmt)
return time;
},
}
}
</script>
<style lang="less" scoped>
.play_wnd_box {
width: 100%;
height: 100%;
position: relative;
background: #000;
}
.play_wnd {
width: 100%;
height: 100%;
}
.prompt_text {
color: #fff;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
<style lang="less">
.hkVideo-box {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 100px;
.ant-modal {
top: 0px;
}
}
</style>通过集成海康web端视频插件,直接播放摄像头rtsp视频流播放不了。
低版本处理
浏览器兼容性处理
1、基础配置
@ 必须设置szBasePath指向插件文件路径
2、跨域配置
服务器需添加响应头
3、编码格式支持
低版本不支持H265时,需后端转码为H264
音频建议使用AAC格式
特殊场景解决方案
1、IE浏览器兼容
2、移动端适配
性能优化建议
1、使用硬件解码时需注意:
高级模式仅支持Chrome 80+/iOS Safari
普通模式建议限制分辨率至1080p以下
2、多画面播放时: