当前位置: 首页 > 知识库问答 >
问题:

vue.js - Vuejs集成海康威视web端视频插件问题?

邹祺然
2025-07-31

Vuejs集成海康威视web端视频插件问题。
目前公司有一个视频播放的需求:利用海康威视web端插件对摄像头视频进行播放(单播、多播)。image.png。目前问题:
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视频流播放不了。
image.png

共有1个答案

夏侯浩气
2025-07-31

低版本处理

// 通过动态加载指定版本
const script = document.createElement('script');
script.src = 'https://path/to/h5player1.5.5.min.js';
document.body.appendChild(script);

浏览器兼容性处理
‌1、基础配置

const player = new JSPlugin({
  szId: 'player',
  szBasePath: "/static/h5player/",
  iMaxSplit: 4,
  iCurrentSplit: 1,
  openDebug: true
});

@ 必须设置szBasePath指向插件文件路径‌

2、‌跨域配置
服务器需添加响应头

nginx

add_header Cross-Origin-Embedder-Policy require-corp;
add_header Cross-Origin-Opener-Policy same-origin;

3、编码格式支持‌

低版本不支持H265时,需后端转码为H264‌

音频建议使用AAC格式‌

特殊场景解决方案
1、IE浏览器兼容

if(navigator.userAgent.indexOf('Trident') > -1) {
  // 使用ActiveX控件模式
  const axPlayer = new ActiveXObject('H5Player.ActiveX');
  axPlayer.JS_StartPlay(url);
}

2、移动端适配

/* 响应式布局处理 */
#player {
  width: 100vw;
  height: calc(100vh - 60px);
}

性能优化建议

1、使用硬件解码时需注意:

高级模式仅支持Chrome 80+/iOS Safari‌

普通模式建议限制分辨率至1080p以下‌

2、多画面播放时:

// 动态调整解码路数
player.JS_SetDecodeRoute(routeId, 2); // 2表示双路解码
 类似资料:
  • 💻面试岗位:前端日常实习-一面 ❓面试问题: 1.自我介绍 2.js的基础数据类型 3.判别数据类型的方法 4.数组常用API 5.遍历循环的API 6.深浅拷贝 7.实现深拷贝-解决循环引用的情况 8.实现深拷贝还要注意哪些细节 9.闭包 10.setTimeout如何解决内存泄漏 11.拷打实习 12.promise 13.说一个promise代码询问输出情况 14.vue2/vue3生命周

  • 技术支持工程师-医疗方向-杭州 时间线: 9.04:测评 9.06:笔试 9.21:一面 1、一面(钉钉)(第一个面试) 1.01 自我介绍 1.02 问项目 1.03 项目中的主要工作 1.04 最有成就感的事情,详细说下 1.05 当班长的体会 1.06 对这个岗位的了解 1.07 课余爱好 1.08 长期坚持的爱好 1.09 为什么选这个城市 1.10 反问

  • 刚刚面的海康威视。找的是Java开发。一进是线下面的。然后他开始也不问,我技术啊,他就问我一些七七八八的学校做什么,你怎么自学的?你觉得你可算了多少嗯什么你最精通的什么框架?笑死了,我说我就会一个是公布的,其他不是很熟练。他说他这边要求必须精通三个以上的框架。真**笑死我了。然后说什么妮蔻,你这才刷至少得刷200道题啊?然后就是说你技术上不行。然后问我练了多少家公司了,怎么有没有考虑,为什么没有通

  • 下载示例链接:

  • 整体来说问的还是很简单的,感觉不符合想象 1.自我介绍 2.问了半天公司的事 3.数组方法 4.找数组最多出现次数 5.vue双向绑定 6.git解决冲突 7.微前端 8.webpack 9.工厂模式 10.反问 还有记不得了,问的奇少无比,而且都贼简单,不过听面试官的意思,招进来好像就是干外包的,原话叫"负责定制" 面试官声音挺好听的#我的实习求职记录#

  • 1、自我介绍 2、项目 3、项目功能上还可以有哪些优化 4、页面dom太多如何优化 5、图片懒加载 6、http与socket的区别 7、闭包、原型链 8、vue响应式原理,如何给一个普通变量加上响应式 9、说说es6 10、项目部署

  • 全程大概25min 1.自我介绍 2.围绕项目询问 3.vue的响应式原理 4.说一下flex布局 5.js事件循环 6.自己在前端方面还有什么不足 #前端面经##海康威视#

  • 1、内容:16道单选,4道多选,两道简答题,两道编程题 2、时长:90min 选择 前端基础知识:html、css考察居多,因为我才开始学js,所以也不清楚考察js哪些内容,但考察的不深。 数据结构知识:碰到的两道好像都是排序的 简答 第二道是让我实现一个圣杯布局,只能用txt写,很无语 编程 因为我第一次笔试,结果一进去没有代码编辑器,就只有txt写,还以为系统出问题了,搞得我都有点懵了。 第一