vue3海康、大华实时视频插件对接

1,648 阅读7分钟

海康开发平台

官网地址:

插件下载地址:https://open.hikvision.com/download/5c67f1e2f05948198c909700?type=20
视频教程地址:https://open.hikvision.com/videoSupport?direction=%E7%BB%BC%E5%90%88%E5%AE%89%E9%98%B2%E7%AE%A1%E7%90%86%E5%B9%B3%E5%8F%B0&tagName=%E8%A7%86%E9%A2%91%E6%92%AD%E6%94%BE%E5%8F%8A%E6%8E%A7%E5%88%B6

img1.png

img2.png

实现步骤:

插件安装

image.png

代码实现

引入资源包

image.png image.png

公共组件

image.png

页面掉用

image.png

*注意当前页面:uid唯一

<template>
  <div class="main" ref="playWndBox">
    <div :id="props.uid" class="playWnd">
      {{ mesgText }}
    </div>
  </div>
</template>
   
<script setup>
import { ref, onMounted, onBeforeUnmount, getCurrentInstance} from 'vue'
const props = defineProps({
  uid: {
    type: String,
    default: 'playWnd'
  },
  initOption: {
    type: Object,
    default: () => {
      return{
        appkey: "****", //海康提供的appkey
        ip: "*****",    //海康提供的ip   
        secret: "*****",//海康提供的secret
        port: ***,     //端口号默认443
        playMode: 0,    // 0 预览 1回放
        layout: "1x1",  //页面展示的模块数【16】
      }
    }
  },
  playWndHeight: {
    type: Number,
    default: 200
  },
  playWndWidth: {
    type: Number,
    default: 400
  }
})
const playWndBox = ref(null)
let playWndHeight = ref(200)
let playWndWidth = ref(400)
let pubKey = ref('')
let oWebControl = ref(null);
const mesgText = ref('加载中...')
let tagDomObj;
const getWH = () => {
  // 获取页面的实例对象
  let pageInstance = getCurrentInstance();
  // 获取dom节点对象
  tagDomObj = playWndBox.value;
  playWndHeight.value = tagDomObj.clientHeight;
  playWndWidth.value = tagDomObj.clientWidth;
  console.log("盒子宽高",props.uid,playWndWidth.value,playWndHeight.value,tagDomObj);
}
onMounted(() => {
})
onBeforeUnmount(() => {
  if (oWebControl.value) {
    // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题
    oWebControl.value?.JS_HideWnd();
    // 销毁当前播放的视频
    oWebControl.value?.JS_RequestInterface({ funcName: "destroyWnd" });
    // 断开与插件服务连接
    oWebControl.value?.JS_Disconnect();
    console.log("销毁播放器");
  }
})
// ***注意*** 一个容器uid只能注册一次
const initPlugin = () => {
  getWH();
  oWebControl.value = new WebControl({
    szPluginContainer: props.uid, // 指定容器id
    iServicePortStart: 15900, // 指定起止端口号,建议使用该值
    iServicePortEnd: 15900,
    szClassId: "23BF3B0A-2C56-4D97-9C03-0CB103AA8F11", // 用于IE10使用ActiveX的clsid
    cbConnectSuccess: () => {
      // 创建WebControl实例成功
      oWebControl.value
        .JS_StartService("window", {
          // WebControl实例创建成功后需要启动服务
          // 值"./VideoPluginConnect.dll"写死
          dllPath: "./VideoPluginConnect.dll",
        })
        .then(
          function () {
            // 设置消息回调
            oWebControl.value.JS_SetWindowControlCallback({
              // cbIntegrationCallBack: cbIntegrationCallBack,
            });
            //JS_CreateWnd创建视频播放窗口,宽高可设定
            oWebControl.value
              .JS_CreateWnd(props.uid,0,0,{ bEmbed: true })//这一部分很重要,两个参数为你盒子的宽高,这样是写死是防止组件加载之前出现白屏;bEmbed: true 防止窗口闪烁
              .then(function () {
                // 创建播放实例成功后初始化
                console.log("HKvidoe 创建实例成功---");
                init();
              });
          },
          function () {
            // 启动插件服务失败
          }
        );
    },
    // 创建WebControl实例失败
    cbConnectError: function () {
      // 这里写创建WebControl实例失败时的处理步骤,下面的代码仅做参看,具体实现步骤根据个人需求进行编写!!!!!!!!

      mesgText.value = "插件启动失败,请检查插件是否安装!";
      // console.log(0);
      // oWebControl.value.value = null;
      // // 程序未启动时执行error函数,采用wakeup来启动程序
      // window.WebControl.JS_WakeUp("VideoWebPlugin://");
      // initCount++;
      // if (initCount < 3) {
      //   setTimeout(function () {
      //     initPlugin();
      //   }, 3000);
      // } else {
      //   setTimeout(function () {
      //     setTimeout(function () {
      //       $router.push('/home/PlugDown')
      //     }, 4000)
      //   }, 4000)
      // }
    },
    cbConnectClose: () => {
      mesgText.value = "连接已断开!";
      // 异常断开:bNormalClose = false
      // JS_Disconnect正常断开:bNormalClose = true
      // console.log("cbConnectClose");
      oWebControl.value = null;
    },
  });
  // oWebControl.value.JS_CuttingPartWindow(500, 500, 500, 500);

}

const emit = defineEmits(['createSuccess'])
// 初始化
const init = (callback) => {
  getPubKey(() => {
    let appkey = props.initOption.appkey;                   //综合安防管理平台提供的appkey,必填
    let secret = setEncrypt(props.initOption.secret); //综合安防管理平台提供的secret,必填
    let ip = props.initOption.ip;                           //综合安防管理平台IP地址,必填
    let playMode = props.initOption.playMode;                //初始播放模式:0-预览,1-回放
    let port = props.initOption.port;                        //综合安防管理平台端口,若启用HTTPS协议,默认443
    let snapDir = "D:\\SnapDir";                        //抓图存储路径
    let videoDir = "D:\\VideoDir";                      //紧急录像或录像剪辑存储路径
    let layout = props.initOption.layout;                   //playMode指定模式的布局
    let enableHTTPS = 1;                                //是否启用HTTPS协议与综合安防管理平台交互,这里总是填1
    let encryptedFields = "secret";                     //加密字段,默认加密领域为secret
    let showToolbar = 0;                                //是否显示工具栏,0-不显示,非0-显示
    let showSmart = 0;                                  //是否显示移动框线框,0-不显示,非0-显示
    let buttonIDs =
      "0,16,256,257,258,259,260,512,513,514,515,516,517,768,769"; //自定义工具条按钮
    // var toolBarButtonIDs = "2049,2304" // 工具栏上自定义按钮
    oWebControl.value
      .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,              //自定义工具条按钮
        }),
      })
      .then(function (oData) {
        oWebControl.value.JS_HideWnd();
        if(oData?.responseMsg?.msg == "success"){
          console.log(oData,"HKvidoe 初始化成功---");
          emit("createSuccess") 
        }
        oWebControl.value.JS_Resize(playWndWidth.value, playWndHeight.value); // 初始化后resize一次,规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题
        if (callback) {
          callback();
        }
      });
  });
}

// RSA 加密
let setEncrypt = (value) => {
  let encrypt = new window.JSEncrypt();
  encrypt.setPublicKey(pubKey);
  return encrypt.encrypt(value);
}
// 获取公钥
const getPubKey = (callback) => {
  oWebControl.value
    .JS_RequestInterface({
      funcName: "getRSAPubKey",
      argument: JSON.stringify({
        keyLength: 1024,
      }),
    })
    .then(function (oData) {
      if (oData.responseMsg.data) {
        pubKey = oData.responseMsg.data;
        callback();
      }
    });
}

// 调用这个函数可进行视频播放
// 视频预览功能
const previewVideo = (data='342fcd690e1441a9bef72b97ff7f8032') => {
  getWH()
  let cameraIndexCode = data;  // 获取输入的监控点编号值,必填
  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, "");
  oWebControl.value.JS_RequestInterface({
    funcName: "startPreview",
    argument: JSON.stringify({
      cameraIndexCode: cameraIndexCode, // 监控点编号
      streamMode: streamMode,                 // 主子码流标识
      transMode: transMode,                   // 传输协议
      gpuMode: gpuMode,                       // 是否开启GPU硬解
      wndId: wndId,                           // 可指定播放窗口
    }),
  })
    .then(function (data) {
      console.log("预览的回调",data);
      oWebControl.value.JS_SetWindowControlCallback({
      });
    });
}
// 视频回放功能
const previewBackVideo = (data='342fcd690e1441a9bef72b97ff7f8032',dateTime=[]) => {
  let cameraIndexCode = data;  // 获取输入的监控点编号值,必填
  var startTimeStamp = new Date(dateTime[0].replace('-', '/').replace('-', '/')).getTime();    //回放开始时间戳,必填
  var endTimeStamp = new Date(dateTime[1].replace('-', '/').replace('-', '/')).getTime();        //回放结束时间戳,必填
  var recordLocation = 0;                                     //录像存储位置:0-中心存储,1-设备存储
  var transMode = 1;                                          //传输协议:0-UDP,1-TCP
  var gpuMode = 0;                                            //是否启用GPU硬解,0-不启用,1-启用
  var wndId = -1;                                             //播放窗口序号(在2x2以上布局下可指定播放窗口)
  oWebControl.value.JS_RequestInterface({
        funcName: "startPlayback",
        argument: JSON.stringify({
            cameraIndexCode: cameraIndexCode,                   //监控点编号
            startTimeStamp: Math.floor(startTimeStamp / 1000).toString(),  //录像查询开始时间戳,单位:秒
            endTimeStamp: Math.floor(endTimeStamp / 1000).toString(),      //录像结束开始时间戳,单位:秒
            recordLocation: recordLocation,                     //录像存储类型:0-中心存储,1-设备存储
            transMode: transMode,                               //传输协议:0-UDP,1-TCP
            gpuMode: gpuMode,                                 //是否启用GPU硬解,0-不启用,1-启用
            wndId:wndId                                         //可指定播放窗口
        })
    })
    .then(function (data) {
      console.log("预览的回调",data);
      oWebControl.value.JS_SetWindowControlCallback({
      });
    });
}
// 窗口刷新问题
const resetWindow = () => {
  oWebControl.value.JS_Resize(playWndWidth.value, playWndHeight.value);
  oWebControl.value.JS_HideWnd();
  oWebControl.value.JS_ShowWnd();
}
// 隐藏窗口
const hideWindow = () => {
  oWebControl.value.JS_HideWnd();
}
// 显示窗口
const showWindow = () => {
  oWebControl.value.JS_ShowWnd();
}
defineExpose({previewVideo,resetWindow,hideWindow,showWindow,initPlugin,previewBackVideo});
</script>
<style lang="less" scoped>
.main {
  position: relative;
  height: 100%;
  width: 100%;
  background: transparent;
  border: 1px solid #C0C0C0;
  .playWnd{
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    height: 100%;
    width: 100%;
  }
}
</style>

最终效果

image.png

大华开发平台ICC

官网地址:

插件下载地址:https://open-icc.dahuatech.com/#/home

img3.png

实现步骤

插件安装

image.png image.png

代码实现

引入资源

image.png

公共组件

image.png

页面调用

image.png

代码实现

<template>
    <div :id="props.uid" class="v_box"></div>
</template>
<script setup>
import {onMounted,nextTick,onBeforeUnmount} from "vue"
let videoPlayer;
const props = defineProps({
    // 唯一标识
    uid:{
        type:String,
        default:"video-play-box"
    },
    // 初始化参数
    initOption:{
        type:Object,
        default:()=>{
            return{
                host: "***", // icc 平台ip
                port: "***", // icc 平台端口 https 默认 443
                username: "***", // icc 平台用户名
                password: "****", // icc 平台密码
            }
        }    
    },
    // 隐藏的Class
    shieldClass:{
        type:Array,
        default:()=>{
            return[]
        }    
    },
    uid:{
        type:String,
        default:"video-play-box"
    },
    // 0-实时预览 3-录像回放 7-录像回放(支持倒放)
    windowType:{
        type:String,
        default:"0"
    }
})
const emit = defineEmits(['createSuccess'])
// 初始化登录
const doInitVideo = () => {
  videoPlayer = new VideoPlayer({
    videoId: props.uid, // 唯一标识,必填,不能重复
    windowType: props.windowType, // 播放器类型,必传, 0 - 实时预览,3 - 录像回放,7- 录像回放(支持倒放)
    usePluginLogin: true, // 采用登录 (请默认传true,插件内部自动拉流)
    pluginLoginInfo: props.initOption,
    division: 1, // 默认展示的窗口数量, 必传
    draggable: false, // 窗口拖拽 【暂不支持】
    showBar: false, // 底部操作栏, 选传,【true - 显示, false - 隐藏】
    shieldClass:props.shieldClass, // 如果DOM元素被插件挡住了,把DOM元素的类名传入。
    coverShieldClass: [], // 如果插件要在dom内滚动,需要把DOM元素的类名传入,请查看案例-遮挡
    parentIframeShieldClass: [], // 有 iframe 时,top层 的 dom 元素被插件挡住了,把DOM元素的类名传入。
    // 下面均为回调函数,请按需使用,具体可在 API 内进行功能的体验和查看。

    // 创建播放器成功回调
    createSuccess: (versionInfo) => {
      // 该回调触发后,我们可以进行实时预览/录像回放等操作
      emit("createSuccess",versionInfo)
      console.log("创建成功11!!阿萨!");
      return
      type == 0 ? doPlay(channelId) : startPlayback(channelId);
      //[]: 隐藏顶部按钮 不传显示所有 回放不支持此功能
      videoPlayer.setTabControlBtn([]);
    },
    // 创建播放器失败回调
    createError: (err) => {
      // 有错误码,可打印查看错误信息
      console.log("创建失败 !!!", JSON.stringify(err));
    },
    // 插件公共回调
    dhPlayerMessage: (info, err) => { },
    // 实时预览成功回调
    realSuccess: (info) => { },
    // 实时预览失败回调
    realError: (info, err) => { },
    // 对讲成功回调
    talkSuccess: (info) => { },
    // 对讲失败回调
    talkError: (info, err) => { },
    // 录像播放成功回调
    playbackSuccess: (info) => { },
    // 录像播放失败回调
    playbackError: (info, err) => { },
    // 录像播放完成回调
    playbackFinish: (info) => { },
    // 抓图成功回调
    snapshotSuccess: ({ base64Url, path }, info) => {
      var byteCharacters = atob(
        base64Url.replace(/^data:image\/(png|jpeg|jpg);base64,/, "")
      );
      var byteNumbers = new Array(byteCharacters.length);
      for (var i = 0; i < byteCharacters.length; i++) {
        byteNumbers[i] = byteCharacters.charCodeAt(i);
      }
      var byteArray = new Uint8Array(byteNumbers);
      var blob = new Blob([byteArray], {
        type: undefined,
      });
      var aLink = document.createElement("a");
      aLink.download = "图片名称.jpg"; //这里写保存时的图片名称
      aLink.href = URL.createObjectURL(blob);
      aLink.click();
    },
    // 本地录像下载成功回调
    videoDownloadSuccess: (path, info) => {
      alert("本地录像地址:" + path);
    },
    // 关闭视频窗口回调
    closeWindowSuccess: ({ isAll, snum, channelList }) => { },
    // 鼠标单击窗口回调
    clickWindow: (snum) => { },
    // 鼠标双击窗口回调
    dbClickWindow: (snum) => { },
    // 播放器窗口的数量回调
    changeDivision: (division) => { },
    // rtsp 流下载录像成功回调
    downloadRecordSuccess: (info) => { },
    // rtsp 流下载录像失败回调
    downloadRecordError: (info, err) => { },
  });
  console.log("大华实时摄像头",videoPlayer);
};
// 实时播放
const doPlay = (id = "*****") => {
  videoPlayer.startReal([
    {
      channelId: id, // 通道id 【必传】
      channelName: "通道名称", // 通道名称 (用于本地录像下载)
      snum: 0, // 即将要播放的窗口序号,从0开始 【必传】
      streamType: 1, // 1-主码流  2-辅码流 (可不传,默认主码流)
      deviceType: 5, // 设备类别 (用于对讲使用)
      cameraType: "1", // 摄像头类型 (用于云台)
      capability: "00000000000000000000000000000001", // 能力集 (用于云台)
    },
  ]);
};
// 回放预览
const startPlayback = (id ="*****") => {
  let startTime = props.dateValue[0];
  let endTime = props.dateValue[1];
  videoPlayer.startPlayback([
    {
      channelId: id, // 通道id
      channelName: "通道名称", // 通道名称 (用于本地录像下载和错误提示)
      startTime: startTime, // 开始时间
      endTime: endTime, // 结束时间
      recordSource: 2, // 2-录像 3-中心
      streamType: 0, // 0-所有码流 1-主码流  2-辅码流
      snum: 0, //  播放的窗口序号 (从0开始计数,表示第一个窗口)
    },
  ]);
};
// 全屏
const setFullScreen = ()=>{
  videoPlayer.setFullScreen()
}
// 销毁
const doDestroy = () => {
  videoPlayer?.destroy().then((res) => {
    console.log("销毁成功");
  });
}
onMounted(() => {
  // doInitVideo()
})
onBeforeUnmount(() =>{
  doDestroy()
})
defineExpose({startPlayback,doPlay,setFullScreen,doInitVideo,doDestroy});
</script>
<style scoped lang="less">
.v_box{
    height: 100%;
    width: 100%;
}
</style>

最终效果

image.png

问题:海康、大华、华为的实时视频,在同一个项目里面,用到【海康的web插件、大华的web插件、HLS推流】,有什么比较好的方案,一套代码解决?