Bootstrap

Springboot+Vue3 整合海康获取视频流并展示

目录

1.后端

1.1 导入依赖

1.2 代码实战

2.前端

2.1 首先安装海康的web插件,前端vue3代码如下:


1.后端

1.1 导入依赖

        <dependency>
            <groupId>com.hikvision.ga</groupId>
            <artifactId>artemis-http-client</artifactId>
            <version>1.1.3</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.51</version>
        </dependency>

1.2 代码实战

新建ArtemisPostTest类,代码如下,设置认证信息,设置ArtemisConfig的host,appKey(AK),appSecret(SK)。设置接口URL。.设置接口入参。接口调用,调用doPostStringArtemis方法(该方法内部实现了登入认证逻辑),传入接口URL,接口入参,数据提交类型等信息,完成接口调用。

public class ArtemisPostTest {
    static {
        ArtemisConfig.host = "";// 代理API网关nginx服务器ip端口
        ArtemisConfig.appKey = "";// 秘钥appkey
        ArtemisConfig.appSecret = "";// 秘钥appSecret
    }
    /**
     * 能力开放平台的网站路径
     * TODO 路径不用修改,就是/artemis
     */
    private static final String ARTEMIS_PATH = "/artemis";

   public static String path="";
   public static JSONObject jsonBody=new JSONObject();

    /**
     * 调用POST请求类型(application/json)接口,这里以入侵报警事件日志为例
     * https://open.hikvision.com/docs/918519baf9904844a2b608e558b21bb6#e6798840
     *
     * @return
     */
    public static String callPostStringApi(){
        /**
         * http://10.33.47.50/artemis/api/scpms/v1/eventLogs/searches
         * 根据API文档可以看出来,这是一个POST请求的Rest接口,而且传入的参数值为一个json
         * ArtemisHttpUtil工具类提供了doPostStringArtemis这个函数,一共六个参数在文档里写明其中的意思,因为接口是https,
         * 所以第一个参数path是一个hashmap类型,请put一个key-value,query为传入的参数,body为传入的json数据
         * 传入的contentType为application/json,accept不指定为null
         * header没有额外参数可不传,指定为null
         *
         */
        final String getCamsApi = ARTEMIS_PATH +ArtemisPostTest.path;
        Map<String, String> path = new HashMap<String, String>(2) {
            {
                put("https://", getCamsApi);//根据现场环境部署确认是http还是https
            }
        };



        String body = jsonBody.toJSONString();

        String result = ArtemisHttpUtil.doPostStringArtemis(path,body,null,null,"application/json",null);// post请求application/json类型参数
        return result;
    }

   
   
}

新建实体类Camera代码如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Camera {
    String cameraIndexCode;
    String camereName;
    String url;
}

在Controller中进行调用,代码如下:

首先根据分页获取监控点列表,这里获取的是第三页,每一页为六个监控点。

查询监控点列表v2

接口说明

根据条件查询目录下有权限的监控点列表

接口版本

v2

接口地址

/api/resource/v2/camera/search

请求方法

POST

数据提交方式

application/json

 然后根据监控点唯一的标识获取预览url

接口说明

1.平台正常运行;平台已经添加过设备和监控点信息。
2.平台需要安装mgc取流服务。
3.三方平台通过openAPI获取到监控点数据,依据自身业务开发监控点导航界面。
4.调用本接口获取预览取流URL,协议类型包括:hik、rtsp、rtmp、hls。
5.通过开放平台的开发包进行实时预览或者使用标准的GUI播放工具进行实时预览。
6.为保证数据的安全性,取流URL设有有效时间,有效时间为5分钟。

接口版本

v2

接口地址

/api/video/v2/cameras/previewURLs

请求方法

POST

数据提交方式

application/json

最后就是进行属性的赋值,赋值给建立的Camera 实体类。

    @GetMapping("/test")
    public List<Camera> test(){
        ArtemisPostTest.path="/api/resource/v1/cameras";
        ArtemisPostTest.jsonBody.clear();
        ArtemisPostTest.jsonBody.put("pageNo", 3);
        ArtemisPostTest.jsonBody.put("pageSize", 6);
        String StringeResult = ArtemisPostTest.callPostStringApi();
        JSONObject result = (JSONObject) JSONObject.parse(StringeResult);
        String data = result.get("data").toString();
        JSONObject resultdata = (JSONObject) JSONObject.parse(data);
        JSONArray list = resultdata.getJSONArray("list");
//        System.out.println("StringeResult结果示例: "+JSONObject.toJSON(StringeResult));
//		callPostImgStringApi();
        ArtemisPostTest.path="/api/video/v2/cameras/previewURLs";
        List<Camera> cameraList=new ArrayList<>();
        for (Object i : list) {
            Camera camera=new Camera();
            JSONObject jsonObject = (JSONObject) JSONObject.parse(i.toString());
            String cameraIndexCode = jsonObject.get("cameraIndexCode").toString();
            String cameraName = jsonObject.get("cameraName").toString();
            ArtemisPostTest.jsonBody.clear();
            ArtemisPostTest.jsonBody.put("cameraIndexCode",cameraIndexCode);
            ArtemisPostTest.jsonBody.put("protocol","rtsp");
            String m = ArtemisPostTest.callPostStringApi();
            JSONObject jsonObject1 = (JSONObject) JSONObject.parse(m);
            JSONObject data1 = (JSONObject) jsonObject1.get("data");
            String url = data1.get("url").toString();
            camera.setCameraIndexCode(cameraIndexCode).setCamereName(cameraName).setUrl(url);
            cameraList.add(camera);
        }
        return cameraList;
    }

2.前端

2.1 首先安装海康的web插件,前端vue3代码如下:

<template>
    <div style="display: flex;flex-direction: row;flex-wrap: wrap">
    <div v-for="item in state.list">
        <el-button @click="go(item.cameraIndexCode)">预览{{item.camereName}}</el-button>
    </div>
    </div>
    <div class="main" ref="playWndBox">
        <div
                id="playWnd"
                class="playWnd"
                :style="{
        height: playWndHeight + 'px',
        width: playWndWidth + 'px',
      }"
        ></div>
    </div>
</template>

<script setup>
    import { ref, onMounted, onBeforeUnmount, getCurrentInstance, nextTick,reactive } from 'vue'
    import axios from "../utils/axios.js"
    const playWndBox = ref(null)
    let playWndHeight = ref('')
    let playWndWidth = ref('')
    let pubKey = ref('')
    let oWebControl = ref(null)
    let objData = ref({
        appkey: "",                 //海康提供的appkey
        ip: "",                     //海康提供的ip
        secret: "",                 //海康提供的secret
        port: 443,
        playMode: 0,        // 0 预览 1回放
        layout: "1x1",      //页面展示的模块数【16】
    })
    const go=(data)=>{
        previewVideo(data)
    }
    const state=reactive({
        list:[]
    })
    const load=()=>{
        axios.get("/test").then(res=>{
           state.list=res
            console.log(res)
        })

    }
    load()

    onMounted(() => {

        // 获取页面的实例对象
        const pageInstance = getCurrentInstance();
        // 获取dom节点对象
        const tagDomObj = pageInstance.refs.playWndBox;
        playWndHeight.value = tagDomObj.clientHeight;
        playWndWidth.value = tagDomObj.clientWidth;

        // 监听scroll事件,使插件窗口尺寸跟随DIV窗口变化
        window.addEventListener("scroll", () => {
            console.log(5);

            // return
            if (oWebControl.value != null) {
                oWebControl.JS_Resize(
                    tagDomObj.clientWidth,
                    tagDomObj.clientHeight
                );
                this.setWndCover();
            }
        });

        // 监听resize事件,使插件窗口尺寸跟随DIV窗口变化
        window.addEventListener("resize", (e) => {
            console.log(0);

            if (oWebControl.value != null) {
                oWebControl.JS_Resize(
                    tagDomObj.clientWidth,
                    tagDomObj.clientHeight
                );
                this.setWndCover();
            }
        });

        // 初始化播放器插件
        nextTick(() => {
            initPlugin();
        })
    })

    onBeforeUnmount(() => {
        if (oWebControl.value != null) {
            // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题
            oWebControl.JS_HideWnd();
            // 销毁当前播放的视频
            oWebControl.JS_RequestInterface({ funcName: "destroyWnd" });
            // 断开与插件服务连接
            oWebControl.JS_Disconnect();
        }
    })



    const initPlugin = () => {
        oWebControl = new WebControl({
            szPluginContainer: "playWnd", // 指定容器id
            iServicePortStart: 15900, // 指定起止端口号,建议使用该值
            iServicePortEnd: 15900,
            szClassId: "23BF3B0A-2C56-4D97-9C03-0CB103AA8F11", // 用于IE10使用ActiveX的clsid
            cbConnectSuccess: () => {
                // 创建WebControl实例成功
                oWebControl
                    .JS_StartService("window", {
                        // WebControl实例创建成功后需要启动服务
                        // 值"./VideoPluginConnect.dll"写死
                        dllPath: "./VideoPluginConnect.dll",
                    })
                    .then(
                        function () {
                            // 设置消息回调
                            oWebControl.JS_SetWindowControlCallback({
                                // cbIntegrationCallBack: cbIntegrationCallBack,
                            });
                            //JS_CreateWnd创建视频播放窗口,宽高可设定
                            oWebControl
                                .JS_CreateWnd("playWnd", 1152, 581, { bEmbed: true })//这一部分很重要,两个参数为你盒子的宽高,这样是写死是防止组件加载之前出现白屏;bEmbed: true 防止窗口闪烁
                                .then(function () {
                                    // 创建播放实例成功后初始化
                                    init();
                                });
                        },
                        function () {
                            // 启动插件服务失败
                        }
                    );
            },
            // 创建WebControl实例失败
            cbConnectError: function () {
                // 这里写创建WebControl实例失败时的处理步骤,下面的代码仅做参看,具体实现步骤根据个人需求进行编写!!!!!!!!


                // console.log(0);
                // oWebControl.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: () => {
                // 异常断开:bNormalClose = false
                // JS_Disconnect正常断开:bNormalClose = true
                // console.log("cbConnectClose");
                oWebControl.value = null;
            },
        });
        // oWebControl.JS_CuttingPartWindow(500, 500, 500, 500);

    }




    // 初始化
    const init = (callback) => {
        getPubKey(() => {
            let appkey = objData.value.appkey;                   //综合安防管理平台提供的appkey,必填
            let secret = setEncrypt(objData.value.secret); //综合安防管理平台提供的secret,必填
            let ip = objData.value.ip;                           //综合安防管理平台IP地址,必填
            let playMode = objData.value.playMode;                //初始播放模式:0-预览,1-回放
            let port = objData.value.port;                        //综合安防管理平台端口,若启用HTTPS协议,默认443
            let snapDir = "D:\\SnapDir";                        //抓图存储路径
            let videoDir = "D:\\VideoDir";                      //紧急录像或录像剪辑存储路径
            let layout = objData.value.layout;                   //playMode指定模式的布局
            let enableHTTPS = 1;                                //是否启用HTTPS协议与综合安防管理平台交互,这里总是填1
            let encryptedFields = "secret";                     //加密字段,默认加密领域为secret
            let showToolbar = 1;                                //是否显示工具栏,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
                .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.JS_Resize(playWndWidth.value, playWndHeight.value); // 初始化后resize一次,规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题
                    if (callback) {
                        callback();
                    }
                    // 隐藏
                    // oWebControl.JS_HideWnd()
                });
        });
    }

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



    // 调用这个函数可进行视频播放
    // 视频预览功能
    const previewVideo = (data) => {
        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以上布局下可指定播放窗口)

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

                });
            });
    }

</script>

<style scoped>
    .main {
        position: fixed;
        top: 50%;
        left: 20%;
        transform: translateY(-50%);
        width: 60vw;
        height: 60vh;
        margin: auto;
        background-color: #ccc;
    }
</style>

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;