海康视频web插件简单使用

使用的海康插件版本1.2.5
个人将播放单个画面和多个画面分别抽离为两个组件,多个画面的也可以播放单个画面,但是单个画面的组件逻辑相对更好一点(如果要求是一次最多播放单个画面的话)

多个画面组件的使用:
注意:传入的'boxId', 'cameraIndexCode'这两个参数都是必传。cameraIndexCode为海康接口的监控列表返回值中的cameraIndexCode属性
多个播放画面组件

<!--
 * @Author: 
 * @LastEditTime: 2023-07-20 18:54:31
 * @Description: 多个监控画面播放
-->
<template>
  <div :id="boxId" class="box"></div>
</template>

<script>
export default {
  name: 'hkPlay',
  props: {
    boxId: {
      required: true,
    }, // 窗口容器id
    cameraIndexCodeList: {
      type: Array,
      required: true,
    }, // 监控摄像头唯一编码
    playCount: {
      type: Number,
      default: 1,
    }, // 正在播放监控画面的窗口数
    // layout: {
    //   type: String,
    //   default: '1x1',
    // }, // 同一画面中播放监控个数
  },
  watch: {
    cameraIndexCodeList: {
      handler() {
        this.$nextTick(() => {
          const closeWndIdList = [] // 需要关闭的窗口
          const openWndCodeList = [] // 需要新增的窗口(值为cameraIndexCode)
          _.forEach(this.lastCameraIndexCodeList, (lastItem) => {
            if (!this.cameraIndexCodeList.includes(lastItem)) {
              if (this.playWindowObj[lastItem]) {
                closeWndIdList.push({
                  wndId: this.playWindowObj[lastItem],
                })
                // 剩余窗口新增释放的窗口
                this.residueList.push(this.playWindowObj[lastItem])
                // 正在使用的窗口去掉释放的窗口
                const useIndex = this.usingList.indexOf(this.playWindowObj[lastItem])
                if (useIndex !== -1) {
                  this.usingList.splice(useIndex, 1)
                }
                // 正在播放的窗口信息去掉该项
                Reflect.deleteProperty(this.playWindowObj, lastItem)
              }
            }
          })
          _.forEach(this.cameraIndexCodeList, (item) => {
            if (!this.lastCameraIndexCodeList.includes(item)) {
              openWndCodeList.push(item)
            }
          })

          // 关闭窗口
          console.log('需关闭的窗口', closeWndIdList)
          if (closeWndIdList.length) {
            this.multiPlayStop(closeWndIdList)
          }

          // 播放
          this.$nextTick(() => {
            console.log('释放窗口完毕,需打开的窗口', openWndCodeList)
            _.forEach(openWndCodeList, (codeItem) => {
              if (!this.playWindowObj[codeItem] && this.residueList.length) {
                const usingWndId = this.residueList.shift()
                this.playWindowObj[codeItem] = usingWndId
                this.usingList.push(usingWndId)
                this.startPreview(codeItem, usingWndId)
              }
            })
            this.lastCameraIndexCodeList = _.cloneDeep(this.cameraIndexCodeList)
          })
        })
      },
      // immediate: true, 父组件中使用了v-if,数据进来时,组件还未初始化完成,不使用v-if提前初始化的情况下可使用该配置(去掉初始化完成后的cameraIndexCodeList遍历播放操作)
    },
    playCount(newValue) {
      // 重新建立窗口数量
      this.residueList = []
      for (let index = 1; index <= newValue; index += 1) {
        this.residueList.push(index)
      }

      // 排除掉已经使用的窗口
      this.residueList = _.filter(this.residueList, (item) => {
        return !this.usingList.includes(item)
      })
      console.log('this.residueList', this.residueList)
    },
  },
  data() {
    return {
      oWebControl: null, // 插件实例
      initCount: 0, // 启动程序次数
      playWindowObj: {}, // 正在播放的窗口信息
      residueList: [1], // 剩余窗口
      usingList: [], // 正在使用的窗口
      lastCameraIndexCodeList: [], // 上一刻状态的cameraIndexCodeList
    }
  },
  methods: {
    /**
     * @description 启动插件(创建监控播放实例)
     */
    initWebControl() {
      console.log('创建监控播放实例')
      const that = this
      this.oWebControl = new WebControl({
        szPluginContainer: that.boxId, // 指定DIV窗口标识
        iServicePortStart: 15900, // 指定服务的起止端口号,建议使用该值
        iServicePortEnd: 15909, // 起止端口号10个,只有一个会用上,所以会有其它9条报错
        szClassId: '23BF3B0A-2C56-4D97-9C03-0CB103AA8F11', // 用于IE10使用ActiveX的clsid
        cbConnectSuccess: () => {
          // 创建WebControl实例成功

          // 实例创建成功后启动服务
          that.oWebControl
            .JS_StartService('window', {
              dllPath: './VideoPluginConnect.dll', // 固定值
            })
            .then(
              () => {
                // 启动插件服务成功
                console.log('启动插件服务成功')
                // 设置消息回调
                that.oWebControl.JS_SetWindowControlCallback({
                  cbIntegrationCallBack: that.cbIntegrationCallBack,
                })

                // 创建视频播放窗口, 宽高可设定(第二个和第三个参数)
                that.oWebControl.JS_CreateWnd(that.boxId).then(() => {
                  // 创建播放实例成功后初始化
                  that.initPlugin()
                  console.log('创建播放实例成功后初始化')
                })
              },
              () => {
                // 启动插件服务失败
              }
            )
        },
        cbConnectError: () => {
          // 创建WebControl实例失败(当插件服务未启动或进程被结束,创建实例时会触发)
          that.oWebControl = null
          WebControl.JS_WakeUp('VideoWebPlugin://') // 程序未启动时执行error函数,采用wakeup来启动程序
          if (that.initCount < 3) {
            // 尝试启动插件3次
            that.initCount += 1
            setTimeout(() => {
              that.$message.warning('插件未启动,正在尝试启动,请稍候...')
              that.initWebControl()
            }, 3000)
          }
        },
        cbConnectClose: (bNormalClose) => {
          // bNormalClose = false 表示异常断开
          // bNormalClose = true 表示正常断开
          if (!bNormalClose) {
            that.$message.error('插件服务异常断开')
          }
          that.oWebControl = null
        },
      })
    },

    /**
     * @description 获取监控公钥
     */
    getPubKey(callback) {
      this.oWebControl
        .JS_RequestInterface({
          funcName: 'getRSAPubKey',
          argument: JSON.stringify({
            keyLength: 1024,
          }),
        })
        .then((oData) => {
          if (oData.responseMsg.data) {
            this.pubKey = oData.responseMsg.data
            callback()
          }
        })
    },

    /**
     * @description 接收插件消息回调(窗口选中消息,预览或回放播放消息,抓图结果消息,预览紧急录像或回放录像剪辑结果消息)
     * @param {Object} oData 是封装的视频web插件回调消息的消息体
     */
    cbIntegrationCallBack(oData) {
      console.log('oData', oData)
      // oData.responseMsg.type === 1 窗口选中消息(鼠标左键,右键,左键双击)
      // oData.responseMsg.type === 2 预览或回放播放消息
      // oData.responseMsg.type === 3 抓图结果消息
      // oData.responseMsg.type === 4 预览紧急录像或回放录像剪辑结果消息
      // oData.responseMsg.type === 5 进入全屏/退出全屏消息
      // oData.responseMsg.type === 6 切换布局信息
      // oData.responseMsg.type === 7 播放窗口双击消息
      // oData.responseMsg.type === 8 时间轴级别变化消息

      if (oData.responseMsg.type === 7) {
        // 全屏
        this.oWebControl.JS_RequestInterface({
          funcName: 'setFullScreen',
        })
      } else if (oData.responseMsg.type === 6) {
        // 切换播放数
        console.log('oData.responseMsg.wndNum', oData.responseMsg.msg.wndNum)
        this.$emit('layoutChange', oData.responseMsg.msg.wndNum)
      }
    },

    /**
     * @description 插件初始化
     */
    initPlugin() {
      console.log('插件初始化')
      const that = this
      that.getPubKey(function() {
        // 请自行修改以下变量值
        let appkey = `${currentEnv.VUE_APP_CAMERA_KEY}` //综合安防管理平台提供的appkey,必填
        let secret = `${currentEnv.VUE_APP_CAMERA_ENCRYPT}` //综合安防管理平台提供的secret,必填
        let ip = `${currentEnv.VUE_APP_CAMERAIP}` //综合安防管理平台IP地址,必填
        let playMode = 0 //初始播放模式:0-预览,1-回放
        let port = 443 //综合安防管理平台端口,若启用HTTPS协议,默认443
        let snapDir = 'D:\\SnapDir' //抓图存储路径
        let videoDir = 'D:\\VideoDir' //紧急录像或录像剪辑存储路径
        let layout = '1x1' //playMode指定模式的布局
        let enableHTTPS = 1 //是否启用HTTPS协议与综合安防管理平台交互,这里总是填1
        // let encryptedFields = 'secret' //加密字段,默认加密领域为secret
        let showToolbar = 1 //是否显示工具栏,0-不显示,非0-显示
        let showSmart = 1 //是否显示智能信息(如配置移动侦测后画面上的线框),0-不显示,非0-显示
        let buttonIDs = '' //自定义工具条按钮
        let reconnectTimes = 0 // 断流后无限次重连
        // 请自行修改以上变量值
        that.oWebControl
          .JS_RequestInterface({
            funcName: 'init',
            argument: JSON.stringify({
              appkey: appkey, //API网关提供的appkey
              secret: secret, //API网关提供的secret
              ip: 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: reconnectTimes, // 断流后无限次重连
            }),
          })
          .then(function(oData) {
            let height = document.getElementById(that.boxId).offsetHeight
            let width = document.getElementById(that.boxId).offsetWidth
            that.oWebControl.JS_Resize(width, height) // 初始化后resize一次,规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题
            _.forEach(that.cameraIndexCodeList, (item) => {
              // that.startPreview(item, index + 1)
              console.log('初始化播放', that.playWindowObj, that.residueList)
              if (!that.playWindowObj[item] && that.residueList.length) {
                const usingWndId = that.residueList.shift()
                that.playWindowObj[item] = usingWndId
                that.usingList.push(usingWndId)
                that.startPreview(item, usingWndId)
              }
            })
            console.log('插件初始化完成')
          })
      })
    },

    /**
     * @description 监控视频预览功能(批量播放)
     */
    // startPreview() {
    //   const indexCodeList = []
    //   _.forEach(this.cameraIndexCodeList, (item, index) => {
    //     indexCodeList.push({
    //       authUuid: '',
    //       cameraIndexCode: item,
    //       ezvizDirect: 0,
    //       gpuMode: 0,
    //       streamMode: 0,
    //       transMode: 1,
    //       wndId: index + 1, // 不要从0初始化, 从1开始
    //     })
    //   })

    //   this.oWebControl
    //     .JS_RequestInterface({
    //       funcName: 'startMultiPreviewByCameraIndexCode', // 批量播放
    //       argument: JSON.stringify({
    //         list: [...indexCodeList],
    //       }),
    //     })
    //     .then(
    //       (res) => {
    //         console.log('批量播放成功', res)
    //       },
    //       (err) => {
    //         console.log('批量播放失败', err)
    //       }
    //     )
    // },
    // 监控视频预览功能
    startPreview(cameraIndexCode, wndId) {
      console.log('监控视频预览功能')
      let _this = this
      // let cameraIndexCode = _this.cameraIndexCode //获取输入的监控点编号值,必填
      let streamMode = 0 //主子码流标识:0-主码流,1-子码流
      let transMode = 1 //传输协议:0-UDP,1-TCP
      let gpuMode = 0 //是否启用GPU硬解,0-不启用,1-启用
      // let wndId = -1 //播放窗口序号(在2x2以上布局下可指定播放窗口)

      cameraIndexCode = cameraIndexCode.replace(/(^\s*)/g, '')
      cameraIndexCode = cameraIndexCode.replace(/(\s*$)/g, '')

      _this.oWebControl.JS_RequestInterface({
        funcName: 'startPreview',
        argument: JSON.stringify({
          cameraIndexCode: cameraIndexCode, //监控点编号
          streamMode: streamMode, //主子码流标识
          transMode: transMode, //传输协议
          gpuMode: gpuMode, //是否开启GPU硬解
          wndId: wndId, //可指定播放窗口
        }),
      })
    },

    /**
     * @description 监控RSA加密
     */
    setEncrypt(value) {
      let encrypt = new JSEncrypt()
      encrypt.setPublicKey(this.pubKey)
      return encrypt.encrypt(value)
    },

    /**
     * @description 判断播放器是否销毁
     */
    initVideoView() {
      if (this.oWebControl == null) {
        this.initWebControl()
      } else {
        this.destroyedView()
        setTimeout(this.initVideoView, 1000)
      }
    },

    /**
     * @description 断开插件服务连接
     */
    destroyedView() {
      if (this.oWebControl != null) {
        this.stopPreview()
        this.oWebControl.JS_HideWnd() // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题
        this.oWebControl.JS_Disconnect().then(
          function() {
            // 断开与插件服务连接成功
          },
          function() {
            // 断开与插件服务连接失败
          }
        )
      }
    },

    /**
     * @description 批量停止播放
     */
    multiPlayStop(list) {
      this.oWebControl
        .JS_RequestInterface({
          funcName: 'stopMultiPlay', // 批量播放
          argument: JSON.stringify({
            list,
          }),
        })
        .then(
          (res) => {
            console.log('批量停止播放成功', res)
          },
          (err) => {
            console.log('批量停止播放失败', err)
          }
        )
    },

    /**
     * @description 停止监控预览
     */
    stopPreview() {
      this.oWebControl.JS_RequestInterface({
        funcName: 'stopPreview',
      })
    },
  },

  created() {
    console.log('created')
    this.lastCameraIndexCodeList = _.cloneDeep(this.cameraIndexCodeList)
    console.log('this.lastCameraIndexCodeList', this.lastCameraIndexCodeList)
  },
  mounted() {
    this.initVideoView()
  },
  beforeDestroy() {
    this.destroyedView()
  },
}

/**
 * 备注:
 * 1. JS_RequestInterface是插件实例上挂载的方法,通用请求响应接口,
 * 监控插件要完成某个功能,可以通过这个方法去调用已设定的插件方法就能实现这个功能
 * 例如全屏:
 * this.oWebControl.JS_RequestInterface({
 *  funcName: 'setFullScreen',
 * })
 */
</script>

<style lang="scss" scoped>
.box {
  width: 100%;
  height: 100%;
}
</style>

多个画面组件在父组件中的使用:
屏蔽掉目录树逻辑就行

<template>
  <el-row class="h-100">
    <!-- 播放视频start -->
    <el-col :span="20" class="h-100 p-r-20">
      <div class="h-100">
        <div class="video-box h-100" ref="videoBoxRef">
          <hikPlay
            boxId="indexCodeList"
            :cameraIndexCodeList="cameraIndexCodeList"
            :playCount="gridNumber"
            @downloadDialog="downloadDialog"
            @layoutChange="layoutChange"
            v-if="cameraIndexCodeList.length"
          ></hikPlay>
        </div>
      </div>
    </el-col>
    <!-- 播放视频end -->
    <!-- 列表strat -->
    <el-col :span="4" class="h-100">
      <el-tabs v-model="activeModule" class="h-100">
        <el-tab-pane label="监控点目录树" name="area" class="h-100">
          <div class="w-100 h-100 tree-container p-10">
            <el-input
              size="small"
              placeholder="请输入监控点名称"
              clearable
              v-model.trim="monitorKeyword"
            >
            </el-input>
            <el-tree
              class="filter-tree m-t-10"
              lazy
              highlight-current
              show-checkbox
              node-key="indexCode"
              :data="monitorTreeData"
              :props="defaultProps"
              :load="loadNode"
              @check="monitorCheck"
              ref="monitorTreeRef"
            >
            </el-tree>
          </div>
        </el-tab-pane>
      </el-tabs>
    </el-col>
    <!-- 列表end -->
  </el-row>
</template>

<script>
const hikPlay = () => {
  return import('@/components/monitor/hkPlay')
}
import MonitorService from '@/services/MonitorService'

export default {
  name: 'realTimeMonitor',

  components: {
    hikPlay,
  },

  watch: {
    monitorKeyword: {
      async handler(newValue) {
        if (newValue !== '') {
          this.$refs.monitorTreeRef.$data.store.lazy = false // 当输入框有值时关闭懒加载
          this.monitorTreeData = await this.searchMonitor()
          console.log('当输入框有值时关闭懒加载', this.monitorTreeData)
        } else {
          this.$refs.monitorTreeRef.$data.store.lazy = true // 开启懒加载
          this.getRoot()
          console.log('开启懒加载')
        }
      },
    },

    async selectedMonitorList(newValue) {
      if (this.lastSelectedMonitorList.length > newValue.length) {
        // 勾选减少

        // 查找变化项数据位置
        _.forEach(this.lastSelectedMonitorList, (lastItem) => {
          const existIndex = _.findIndex(
            this.selectedMonitorList,
            (listItem) => listItem.indexCode === lastItem.indexCode
          )
          if (existIndex === -1) {
            // 取消勾选项
            const deleteIndex = _.findIndex(
              this.playMonitorList,
              (playItem) => playItem.indexCode === lastItem.indexCode
            )
            if (deleteIndex !== -1) {
              this.playMonitorList = this.selectedMonitorList.slice(0, this.gridNumber)
            }
          }
        })
      } else {
        // 勾选增加
        if (this.playMonitorList.length < this.gridNumber) {
          this.playMonitorList = this.selectedMonitorList.slice(0, this.gridNumber)
        }
        console.log('勾选增加', this.playMonitorList)
      }
      this.lastSelectedMonitorList = [...this.selectedMonitorList]
    },

    async gridNumber(newValue, oldValue) {
      if (newValue > oldValue) {
        this.playMonitorList = this.selectedMonitorList.slice(0, newValue)
      } else if (newValue < oldValue) {
        this.playMonitorList = this.playMonitorList.slice(0, newValue)
      }
      console.log('this.playMonitorList', this.playMonitorList)
    },

    playMonitorList(newValue) {
      // this.cameraIndexCodeList = newValue
      this.indexCodeList = _.map(newValue, 'indexCode')
      console.log('this.indexCodeList', this.indexCodeList)
    },

    indexCodeList(newValue) {
      const requestList = []
      _.forEach(newValue, (item) => {
        requestList.push(
          MonitorService.getCameraDetail({
            cameraIndexCode: item,
          })
        )
      })
      Promise.all(requestList).then((resList) => {
        console.log('resList', resList)
        this.cameraIndexCodeList = []
        _.forEach(resList, (res) => {
          if (res && res.data && res.code === 0) {
            const result = JSON.parse(res.data)
            console.log('res', result)
            if (result.data && result.code === '0') {
              this.cameraIndexCodeList.push(result.data.cameraIndexCode)
            }
          }
        })
        console.log('this.cameraIndexCodeList', this.cameraIndexCodeList)
      })
    },
  },

  data() {
    return {
      activeModule: 'area', // tab页当前选中项
      gridNumber: 1, // 监控画面总窗口数
      areaKeyword: '', // 区域目录树查询关键字
      selectedDevice: [], // 目录树勾选的数据
      isPageFullscreen: false, // 是否全屏
      defaultProps: {
        label: 'name',
        isLeaf: 'isLeaf',
      }, // 目录树默认配置
      currentArea: null, // 当前点击的区域节点数据
      monitorKeyword: '', // 监控点列表查询关键字
      monitorList: [], // 监控点列表数据
      monitorLoading: false, // 监控点列表loading
      selectedMonitorList: [], // 监控点列表选中项
      lastSelectedMonitorList: [], // 上一个状态的监控点列表选中项
      playMonitorList: [], // 正在播放的监控列表
      indexCodeList: [], // 监控点唯一编码
      cameraIndexCodeList: [], // 视频播放所需参数
      monitorTreeData: [], // 监控点数据
    }
  },

  methods: {
    /**
     * @description 监控画面总数变化
     * @param {Number} layoutCount 监控画面总数
     */
    layoutChange(layoutCount) {
      this.gridNumber = layoutCount
    },

    /**
     * @description 勾选发生变化
     */
    monitorCheck() {
      this.selectedMonitorList = this.$refs.monitorTreeRef.getCheckedNodes(true)
      console.log('勾选发生变化', this.selectedMonitorList)
    },

    /**
     * @description 获取根区域节点
     * @returns {Array} 根区域节点
     */
    async getRoot() {
      const res = await MonitorService.getRegionsRoot({
        treeCode: '0',
      })
      console.log('获取根区域节点', res)
      if (res && res.data && res.code === 0) {
        const result = JSON.parse(res.data)
        console.log('result', result)
        if (result) {
          return [result.data]
        }
      }
    },

    /**
     * @description 根据查询条件获取区域列表
     * @param {String} parentCode 父节点编号
     * @param {String} keyword 查询条件
     * @returns {Array} 区域列表
     */
    async getNodes(parentCode, keyword = '') {
      const res = await MonitorService.getNodesByParams({
        resourceType: 'region', // 必填,资源类型
        regionName: keyword, // 非必填,区域名称,可模糊查询
        parentIndexCodes: parentCode ? [parentCode] : [], // 非必填,父编号集合
        pageNo: 1, // 必填
        pageSize: 1000, // 必填
      })
      console.log('根据查询条件获取区域列表', res)
      if (res && res.data && res.code === 0) {
        const result = JSON.parse(res.data)
        console.log('result', result)
        if (result) {
          _.forEach(result.data.list, (listItem) => {
            listItem.isLeaf = false
          })
          return result.data.list
        } else {
          return []
        }
      }
    },

    /**
     * @description 目录树节点懒加载
     * @param {Object} node 目录树节点
     * @param {Function} resolve 节点渲染回调方法
     */
    async loadNode(node, resolve) {
      console.log('目录树节点', node)
      if (node.level === 0) {
        const nodes = await this.getRoot()
        if (nodes) {
          return resolve(nodes)
        }
      } else if (node && node.data && node.data.leaf) {
        const nodes = await this.searchMonitor(node.parent.data.indexCode)
        if (nodes) {
          return resolve(nodes)
        }
      } else {
        if (node && node.data && node.data.indexCode) {
          const nodes = await this.getNodes(node.data.indexCode)
          if (nodes) {
            return resolve(nodes)
          }
        }
      }

      return resolve([])
    },

    /**
     * @description 监控点列表查询
     * @param {String} indexCode 区域节点编码
     */
    async searchMonitor(indexCode) {
      const res = await MonitorService.getCameraList({
        name: this.monitorKeyword, // 非必填,名称,模糊搜索
        regionIndexCodes: [indexCode], // 区域编号
        pageNo: 1, // 必填
        pageSize: 1000, // 必填
      })
      console.log('监控点列表查询', res)
      if (res && res.data && res.code === 0) {
        const result = JSON.parse(res.data)
        if (result) {
          _.forEach(this.monitorList, (listItem) => {
            listItem.isLeaf = true
          })
          return result.data.list
        } else {
          return []
        }
      }
    },

    /**
     * 监听是否安装海康播放器插件
     * 当用户未下载插件时,弹出弹框支持在线下载
     */
    downloadDialog() {
      if (this.showDialog) {
        this.$confirm('插件启动失败,请检查插件是否安装或点击下载按钮进行下载!', '提示', {
          confirmButtonText: '下载',
          cancelButtonText: '取消',
          type: 'warning',
        })
          .then(() => {
            window.location.href = '/static/hik/VideoWebPlugin.exe'
          })
          .catch(() => {
            this.$message({
              type: 'info',
              message: '已取消下载',
            })
          })
        this.showDialog = false
      }
    },
  },
}
</script>

<style lang="scss" scoped>
.video-box {
  // height: calc(100% - 44px);
  background: #fff;
}
.operation-box {
  height: 24px;
  line-height: 24px;
  background: #fff;
}
.icon-font {
  font-size: 19px;
}
.tree-container {
  border: 1px solid #ccc;
}
.filter-tree {
  height: calc(100% - 42px);
  overflow: auto;
}
/deep/ .el-tree-node > .el-tree-node__children {
  overflow: visible;
}
</style>

单个画面组件的使用:
注意:传入的'id', 'cameraIndexCode'这两个参数都是必传。cameraIndexCode为海康接口的监控列表返回值中的cameraIndexCode属性
单个播放画面组件

<!--
 * @Author: 
 * @LastEditTime: 2023-07-20 18:54:31
 * @Description: 单个监控画面播放
-->
<template>
  <div :id="id" class="w-100 h-100"></div>
</template>
<script>
export default {
  name: 'hikPlay',
  props: ['id', 'cameraIndexCode'],
  data() {
    return {
      oWebControl: null, // 播放器实例
      pubKey: '',
      initCount: 0, // 重连次数
      cameraIp: `${currentEnv.VUE_APP_CAMERAIP}`, //综合安防管理平台IP地址
      showVideo: false,
      timer: null,
    }
  },
  mounted() {
    this.initVideoView()
  },
  watch: {
    cameraIndexCode() {
      this.initVideoView()
    },
  },
  methods: {
    resizeVideo() {
      const node = document.getElementById(this.id)
      if (node.childNodes.length > 0) {
        node.removeChild(node.childNodes[0])
      }
      if (this.showVideo) {
        if (this.timer) clearInterval(this.timer)
        this.timer = setTimeout(() => {
          let height = document.getElementById(this.id).offsetHeight - 20
          let width = document.getElementById(this.id).offsetWidth
          this.oWebControl.JS_Resize(width, height)
          this.setWndCover()
        }, 100)
      }
    },
    // 设置窗口裁剪,当因滚动条滚动导致窗口需要被遮住的情况下需要JS_CuttingPartWindow部分窗口
    setWndCover() {
      let iWidth = $(window).width()
      let iHeight = $(window).height()
      console.log(iWidth, iHeight)
      let oDivRect = $(`#${this.id}`)
        .get(0)
        .getBoundingClientRect()
      console.log(oDivRect, $(this.id).get(0))
      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 > 1000 ? 1000 : iCoverLeft
      iCoverTop = iCoverTop > 600 ? 600 : iCoverTop
      iCoverRight = iCoverRight > 1000 ? 1000 : iCoverRight
      iCoverBottom = iCoverBottom > 600 ? 600 : iCoverBottom

      this.oWebControl.JS_RepairPartWindow(0, 0, 1001, 600) // 多1个像素点防止还原后边界缺失一个像素条
      if (iCoverLeft != 0) {
        this.oWebControl.JS_CuttingPartWindow(0, 0, iCoverLeft, 600)
      }
      if (iCoverTop != 0) {
        this.oWebControl.JS_CuttingPartWindow(0, 0, 1001, iCoverTop) // 多剪掉一个像素条,防止出现剪掉一部分窗口后出现一个像素条
      }
      if (iCoverRight != 0) {
        this.oWebControl.JS_CuttingPartWindow(1000 - iCoverRight, 0, iCoverRight, 600)
      }
      if (iCoverBottom != 0) {
        this.oWebControl.JS_CuttingPartWindow(0, 600 - iCoverBottom, 1000, iCoverBottom)
      }
    },
    // 判断播放器是否销毁
    initVideoView() {
      if (this.cameraIndexCode) {
        if (this.oWebControl == null) {
          this.initVideoPlugin()
        } else {
          this.destroyedView()
          setTimeout(this.initVideoView, 1000)
        }
      }
    },
    // 创建监控播放实例
    initVideoPlugin() {
      const _this = this
      _this.oWebControl = new WebControl({
        szPluginContainer: _this.id, // 指定容器id
        iServicePortStart: 15900, // 指定起止端口号,建议使用该值
        iServicePortEnd: 15909,
        szClassId: '23BF3B0A-2C56-4D97-9C03-0CB103AA8F11', // 用于IE10使用ActiveX的clsid
        cbConnectSuccess: function() {
          // 创建WebControl实例成功
          console.log('创建WebControl实例成功')
          _this.oWebControl
            .JS_StartService('window', {
              // WebControl实例创建成功后需要启动服务
              dllPath: './VideoPluginConnect.dll', // 值"./VideoPluginConnect.dll"写死
            })
            .then(
              function() {
                // 启动插件服务成功
                console.log('启动插件服务成功')
                _this.oWebControl.JS_SetWindowControlCallback({
                  // 设置消息回调
                  cbIntegrationCallBack: _this.cbIntegrationCallBack,
                })
                let height = document.getElementById(_this.id).offsetHeight - 20
                let width = document.getElementById(_this.id).offsetWidth
                _this.oWebControl.JS_CreateWnd(_this.id, width, height).then(function() {
                  //JS_CreateWnd创建视频播放窗口,宽高可设定
                  _this.init() // 创建区域监控播放实例成功后初始化
                })
              },
              function() {
                // 启动插件服务失败
              }
            )
        },
        cbConnectError: function() {
          // 创建WebControl实例失败
          _this.oWebControl = null
          WebControl.JS_WakeUp('VideoWebPlugin://') // 程序未启动时执行error函数,采用wakeup来启动程序
          _this.initCount++
          if (_this.initCount < 3) {
            setTimeout(function() {
              _this.$message.warning('插件未启动,正在尝试启动,请稍候...')
              _this.initVideoPlugin()
            }, 3000)
          } else {
            // 父组件提供下载弹框,避免组件触发多次下载弹框
            _this.$emit('downloadDialog')
          }
        },
        cbConnectClose: function(bNormalClose) {
          // 异常断开:bNormalClose = false
          // JS_Disconnect正常断开:bNormalClose = true
          if (!bNormalClose) {
            _this.$message.error('视屏链接异常中断!')
          }
          _this.oWebControl = null
        },
      })
    },
    init() {
      const _this = this
      _this.getPubKey(function() {
        // 请自行修改以下变量值
        let appkey = `${currentEnv.VUE_APP_CAMERA_KEY}` //综合安防管理平台提供的appkey,必填
        let secret = _this.setEncrypt(`${currentEnv.VUE_APP_CAMERA_ENCRYPT}`) //综合安防管理平台提供的secret,必填
        let ip = _this.cameraIp //综合安防管理平台IP地址,必填
        let playMode = 0 //初始播放模式:0-预览,1-回放
        let port = 8443 //综合安防管理平台端口,若启用HTTPS协议,默认443
        let snapDir = 'D:\\SnapDir' //抓图存储路径
        let videoDir = 'D:\\VideoDir' //紧急录像或录像剪辑存储路径
        let layout = '1x1' //playMode指定模式的布局
        let enableHTTPS = 1 //是否启用HTTPS协议与综合安防管理平台交互,这里总是填1
        let encryptedFields = 'secret' //加密字段,默认加密领域为secret
        let showToolbar = 0 //是否显示工具栏,0-不显示,非0-显示
        let showSmart = 1 //是否显示智能信息(如配置移动侦测后画面上的线框),0-不显示,非0-显示
        let buttonIDs = '' //自定义工具条按钮
        let reconnectTimes = 0 // 断流后无限次重连
        // 请自行修改以上变量值
        _this.oWebControl
          .JS_RequestInterface({
            funcName: 'init',
            argument: JSON.stringify({
              appkey: appkey, //API网关提供的appkey
              secret: secret, //API网关提供的secret
              ip: 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: reconnectTimes, // 断流后无限次重连
            }),
          })
          .then(function(oData) {
            let height = document.getElementById(_this.id).offsetHeight - 20
            let width = document.getElementById(_this.id).offsetWidth
            _this.oWebControl.JS_Resize(width, height) // 初始化后resize一次,规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题
            _this.startPreview()
          })
      })
    },
    // 监控视频预览功能
    startPreview() {
      let _this = this
      let cameraIndexCode = _this.cameraIndexCode //获取输入的监控点编号值,必填
      let streamMode = 0 //主子码流标识:0-主码流,1-子码流
      let transMode = 1 //传输协议:0-UDP,1-TCP
      let gpuMode = 0 //是否启用GPU硬解,0-不启用,1-启用
      let wndId = -1 //播放窗口序号(在2x2以上布局下可指定播放窗口)

      cameraIndexCode = cameraIndexCode.replace(/(^\s*)/g, '')
      cameraIndexCode = cameraIndexCode.replace(/(\s*$)/g, '')

      _this.oWebControl.JS_RequestInterface({
        funcName: 'startPreview',
        argument: JSON.stringify({
          cameraIndexCode: cameraIndexCode, //监控点编号
          streamMode: streamMode, //主子码流标识
          transMode: transMode, //传输协议
          gpuMode: gpuMode, //是否开启GPU硬解
          wndId: wndId, //可指定播放窗口
        }),
      })
      this.showVideo = true
    },
    //停止监控预览
    stopPreview() {
      this.oWebControl.JS_RequestInterface({
        funcName: 'stopPreview',
      })
    },
    // 摧毁播放器
    destroyedView() {
      if (this.oWebControl != null) {
        this.stopPreview()
        this.oWebControl.JS_HideWnd() // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题
        this.oWebControl.JS_Disconnect().then(
          function() {
            // 断开与插件服务连接成功
          },
          function() {
            // 断开与插件服务连接失败
          }
        )
      }
    },
    // 获取监控公钥
    getPubKey(callback) {
      this.oWebControl
        .JS_RequestInterface({
          funcName: 'getRSAPubKey',
          argument: JSON.stringify({
            keyLength: 1024,
          }),
        })
        .then((oData) => {
          if (oData.responseMsg.data) {
            this.pubKey = oData.responseMsg.data
            callback()
          }
        })
    },
    // 监控RSA加密
    setEncrypt(value) {
      let encrypt = new JSEncrypt()
      encrypt.setPublicKey(this.pubKey)
      return encrypt.encrypt(value)
    },
    // 设置窗口回调
    setCallBack() {
      this.oWebControl.JS_SetWindowControlCallback({
        cbIntegrationCallBack: this.cbIntegrationCallBack,
      })
    },
    // 推送消息
    cbIntegrationCallBack(oData) {
      // type =7  播放窗口双击事件
      if (oData.responseMsg.type === 7) {
        // 全屏
        this.oWebControl.JS_RequestInterface({
          funcName: 'setFullScreen',
        })
        this.$emit('videoFullScreen', {
          id: this.id,
          isFullScreen: true,
        })
      } else if (oData.responseMsg.type === 5) {
        /**
         * type = 5  插件窗口是否全屏
         *  向父组件传递是否退出全屏信息  控制其他播放器的显示和隐藏
         * result = 1024 进入全屏 result = 1025 退出全屏
         */
        let isFullScreen = oData.responseMsg.msg.result === 1025 ? false : true
        this.$emit('videoFullScreen', {
          id: this.id,
          isFullScreen,
        })
      } else if (oData.responseMsg.type === 2) {
        if (oData.responseMsg.msg.result === 769) {
          this.hideWnd()
          let height = document.getElementById(this.id).offsetHeight - 20
          let width = document.getElementById(this.id).offsetWidth
          this.oWebControl.JS_CreateWnd(this.id, width, height).then(() => {
            this.init()
          })
        } else if (oData.responseMsg.msg.result === 768) {
          this.showWnd()
        }
      }
      console.log(oData)
    },
    // 隐藏窗口
    hideWnd() {
      this.oWebControl.JS_HideWnd()
    },
    // 显示窗口
    showWnd() {
      this.oWebControl.JS_ShowWnd()
    },
  },
  beforeDestroy() {
    this.destroyedView()
    clearInterval(this.timer)
  },
}
</script>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容