Hikvison connects to NVR to realize WEB plug-in-free development kit to realize front-end video preview (html, vue, nginx proxy)

Scenes

Preview the video of multiple channels (cameras) in HIKVSION's NVR (Network Disk Video Recorder) in Vue:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/124155295

The WEB control development kit is used for the NVR video preview above, which requires the computer to install the plug-in, and the browser needs to be in the

Preview in compatibility mode.

In addition to this, there is another way to develop packages without plugins

 

 

Up to now, it is WEB no plug-in development kit V3.2

WEB3.2 plug-in-free version development kit, supports high-level Google and Firefox browsers, and requires the device to support Websocket streaming. The no-plugin version requires the use of the nginx proxy server.

According to the instructions, the NVR needs to support websocket streaming.

Verify whether the NVR supports websocket streaming

The NVR model here is: DS-8664n-k16

 

 

Log in to the NVR's web page - configuration - system settings - network - advanced configuration - enable websocket

 

 

If there is an option to enable WebSokcet, it means you can. For further verification, download the official demo above.

 

 

 

Follow the official instructions to run the nginx proxy

 

 

Modify the configuration files in the nginx directory

 

 

Here is just a simple modification of the port number to 8000

Then click start.bat to start nginx and visit

http://127.0.0.1:8000/cn/demo.html

 

 

 

Enter the address, user name, password and other information corresponding to the NVR, then click Login and start previewing

 

 

If the official demo can be previewed successfully, then you can use the plug-in-free development kit.

Note:

Blog:
https://blog.csdn.net/badao_liumang_qizhi
Pay attention to the public account
domineering programmer
Get programming-related eBooks, tutorial pushes, and free downloads.

accomplish

1. On the basis that the preview can be realized in demo.html, you only need to modify it according to your own needs.

The corresponding demo and interface documentation are very clear.

So how to combine demo examples with existing projects, such as SpringBoot+Vue projects with front and back ends separated.

If you build a development environment and run the project according to the front-end and back-end separate versions:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108465662

Code retrofit on Vue-based projects

 

 

The relevant information of the camera is stored in the background. The most important thing is the channel number, which is used for reference when previewing.

 

 

Click the preview button when the camera is selected, and transfer the channel number parameters to the preview page.

The previous process can refer to

SpringBoot+Vue+HIKVSION realizes multiple camera selection and multi-window preview (plug-in version):

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/121180769

Then import the required js and other files according to the official demo

 

 

The following is mainly the code of the preview page

<template>
  <el-dialog
    title="Video Surveillance"
    :visible.sync="videoOpen"
    width="800px"
    :append-to-body=false
    @close="videoClose"
    class="video_box"
    :modal=false
  >
    <!-- Camera -->
    <!--Video window display-->
    <div id="playWnd" class="playWnd" ref="playWnd"></div>
  </el-dialog>
</template>
 
<script>
const g_iWndIndex = 0; //You don't need to set this variable. In the interface with window parameters, you don't need to pass a value. The development kit will use the currently selected window by default.
export default {
  name: "HkVideo1",
  components: {},
  props: {
    channelId: "",
  },
  watch: {},
  data() {
    return {
      isLogin: false,
      videoOpen: false,
      szDeviceIdentify: "", // Equipment Identity( IP_Port)
      ip: "NVR of ip",
      port: "80",
      username: "NVR username",
      password: "NVR password",
    };
  },
  created() {},
  mounted() {},
  destroyed() {},
  methods: {
    // Create a playback instance
    async initPlugin() {

      let iRet = window.WebVideoCtrl.I_CheckPluginInstall();

      if (-1 == iRet) {
        alert("You have not installed the plugin, please install it WebComponentsKit.exe!");
        this.$confirm("whether to download WebComponentsKit.exe plugin?", "hint", {
          confirmButtonText: "Sure",
          cancelButtonText: "Cancel",
          type: "warning",
        }).then(() => {
          window.location.href = "/static/HK_3.2/WebComponentsKit.exe";
        });

        return;
      }
      // Initialize plugin parameters and insert plugins
      window.WebVideoCtrl.I_InitPlugin(800, 600, {
        bWndFull: true, //Whether to support single-window double-click full screen, default support true:support false:not support
        iPackageType: 2,
        //szColorProperty:"plugin-background:0000ff; sub-background:0000ff; sub-border:00ffff; sub-border-select:0000ff",   //2:PS 11:MP4
        iWndowType: 1,
        bNoPlugin: true,
        // Window selected event callback
        cbSelWnd: (xmlDoc) => {
          var szInfo = "Currently selected window number:" + g_iWndIndex;
          console.log(szInfo);
        },
        // Window double click event callback
        cbDoubleClickWnd: (iWndIndex, bFullScreen) => {
          var szInfo = "The current zoomed-in window number:" + iWndIndex;
          if (!bFullScreen) {
            szInfo = "Current restored window number:" + iWndIndex;
          }
          console.log(szInfo);
        },
        // Plugin event callback
        cbEvent: (iEventType, iParam1, iParam2) => {
          if (2 == iEventType) {
            // Playback ends normally
            console.log("window" + iParam1 + "Playback is over!");
          } else if (-1 == iEventType) {
            console.log("equipment" + iParam1 + "Network Error!");
          } else if (3001 == iEventType) {
            this.clickStopRecord(g_szRecordType, iParam1);
          }
        },
        cbRemoteConfig: () => {
          console.log("Close the remote configuration repository!");
        },
        // Plugin initialization complete callback
        cbInitPluginComplete: () => {
          this.$nextTick(() => {
            console.log('window', this.$refs.playWnd)
            let isInit = window.WebVideoCtrl.I_InsertOBJECTPlugin('playWnd');
            console.log('isInit', isInit)
 
            // Check if the plugin is up to date
            if (-1 == window.WebVideoCtrl.I_CheckPluginVersion()) {
              alert("New plugin version detected, please correct WebComponentsKit.exe Make an upgrade!");
              return;
            } else this.clickLogin();
          })
        },
      });
    },

    // Log in
    clickLogin() {
      let { ip, port, username, password } = this;

      if ("" == ip || "" == port) {
        return;
      }

      this.szDeviceIdentify = ip + "_" + port;

      let iRet = window.WebVideoCtrl.I_Login(ip, 1, port, username, password, {
        success: (xmlDoc) => {
          setTimeout(() => {
            this.getChannelInfo();
            this.getDevicePort();
          }, 10);
        },
        error: (status, xmlDoc) => {
          console.log(" Login failed!", status, xmlDoc);
        },
      });

      if (-1 == iRet) {
        this.clickStartRealPlay();
      }
    },
    // get channel
    getChannelInfo() {
      if (null == this.szDeviceIdentify) {
        return;
      }

      // analog channel
      window.WebVideoCtrl.I_GetAnalogChannelInfo(this.szDeviceIdentify, {
        async: false,
        success: (xmlDoc) => {
        },
        error: (status, xmlDoc) => {
          console.log(" Failed to get analog channel!");
        },
      });
      // digital channel
      window.WebVideoCtrl.I_GetDigitalChannelInfo(this.szDeviceIdentify, {
        async: false,
        success: (xmlDoc) => {
        },
        error: (status, xmlDoc) => {
          console.log(" Failed to get digital channel!");
        },
      });
      // zero channel
      window.WebVideoCtrl.I_GetZeroChannelInfo(this.szDeviceIdentify, {
        async: false,
        success: (xmlDoc) => {
        },
        error: (status, xmlDoc) => {
          console.log(" Failed to get zero channel!");
        },
      });
    },
    // get port
    getDevicePort() {
      if (null == this.szDeviceIdentify) {
        return;
      }

      this.port = window.WebVideoCtrl.I_GetDevicePort(this.szDeviceIdentify);
      if (this.port != null) {
        this.clickStartRealPlay();
        return true
      } else {
        console.log(" Failed to get port!");
        return false
      }
    },
    // start preview
    clickStartRealPlay(iStreamType) {
      let wndInfo = window.WebVideoCtrl.I_GetWindowStatus(g_iWndIndex);

      let iChannelID = this.channelId; // Channel list
      let bZeroChannel = false; // Whether to play zero channel(drop down box)
      let szInfo = "";

      if ("undefined" === typeof iStreamType) {
        iStreamType = 2; // 1 Main stream 2 Sub-stream 3 Third stream 4 Transcoding stream
      }

      if (null == this.szDeviceIdentify) {
        return;
      }
      let startRealPlay = () => {
        window.WebVideoCtrl.I_StartRealPlay(this.szDeviceIdentify, {
          iRtspPort: 554,
          iStreamType: iStreamType,
          iChannelID: iChannelID,
          bZeroChannel: bZeroChannel,
          success: () => {
            szInfo = "Start previewing successfully!";
            console.log(szInfo);
          },
          error: (status, xmlDoc) => {
            if (403 === status) {
              szInfo = "device not supported Websocket Get the flow!";
            } else {
              szInfo = "Start preview failed!";
            }
            this.$message.error(szInfo);
          },
        });
      };

      if (wndInfo != null) {
        // It's already playing, stop it first
        window.WebVideoCtrl.I_Stop({
          success: () => {
            startRealPlay();
          },
        });
      } else {
        startRealPlay();
      }
    },
    // stop preview
    clickStopRealPlay() {
      let oWndInfo = window.WebVideoCtrl.I_GetWindowStatus(g_iWndIndex),
        szInfo = "";

      if (oWndInfo != null) {
        window.WebVideoCtrl.I_Stop({
          success: () => {
            szInfo = "Stop preview success!";
            console.log(szInfo);
          },
          error: () => {
            szInfo = "Stop preview failed!";
            console.log(szInfo);
          },
        });
      }
    },
    // full screen
    clickFullScreen() {
      window.WebVideoCtrl.I_FullScreen(true);
    },
    // stop recording
    clickStopRecord(szType, iWndIndex) {
      if ("undefined" === typeof iWndIndex) {
        iWndIndex = g_iWndIndex;
      }
      var oWndInfo = window.WebVideoCtrl.I_GetWindowStatus(iWndIndex),
        szInfo = "";

      if (oWndInfo != null) {
        window.WebVideoCtrl.I_StopRecord({
          success: () => {
            if ("realplay" === szType) {
              szInfo = "Stop recording successfully!";
            } else if ("playback" === szType) {
              szInfo = "Stop clipping successfully!";
            }
            showOPInfo(oWndInfo.szDeviceIdentify + " " + szInfo);
          },
          error: () => {
            if ("realplay" === szType) {
              szInfo = "Failed to stop recording!";
            } else if ("playback" === szType) {
              szInfo = "Failed to stop clipping!";
            }
            showOPInfo(oWndInfo.szDeviceIdentify + " " + szInfo);
          },
        });
      }
    },
    // View camera
    videoChange() {
      this.videoOpen = true;
      this.$nextTick(() => {
        if(!this.isLogin) {
          this.isLogin = true
          this.initPlugin()
        } else {
          this.clickStartRealPlay();
        }
      });
    },
    // Close the camera popup
    videoClose() {
      this.videoOpen = false;
      console.log(this.isLogin)
      this.clickStopRealPlay();
    },
  },
};
</script>
  <style scoped lang="scss">
.video_box {
  width: 100%;
  height: 100%;
}

.plugin {
  width: 100%;
  height: 100%;
}

.playWnd {
  width: 800px;
  height: 600px;
  margin: 0;
}

.video_box {
  ::v-deep .el-dialog__body {
    padding: 0 !important;
  }
}
</style>

2. About nginx proxy

Check out the official sample nginx configuration file

 

 

A two-part proxy is required. foremost

        location / {
            root   "../webs";
            index  index.html index.htm;
        }

It is the proxy of the static page of its example demo, corresponding to the proxy of its own Vue dist package

The corresponding online should be changed to

    location / {
      root C:\dist;
      try_files $uri $uri/ /index.html;
      index index.html index.htm;
    }

One of the remaining two proxies is an interface proxy, such as a reverse proxy when calling the login interface

 location ~ /ISAPI|SDK/ {
     if ($http_cookie ~ "webVideoCtrlProxy=(.+)") {
  proxy_pass http://$cookie_webVideoCtrlProxy;
  break;
     }
 }

This means that if it is a request at the beginning of ISAPI or SDK, the wavy line means that the case is not ignored, and it will be proxied to the following address.

But here I will ask why there is no configuration about the ip address of the nvr in the proxy configuration file, then how do I request the interface when I proxy.

Here is to add a nginx in the middle for forwarding, nginx finds the NVR's Ip address through the Cookie in the request for forwarding, including getting the video stream during preview

It is also in this way that the websocket proxy is performed and the video stream is obtained.

Similar to the following flow chart.

 

 

So just modify the nginx configuration file of your own project according to the official nginx configuration file.

The following provides an example of the nginx configuration file of the project after the transformation

worker_processes 1;
events {
  worker_connections 1024;
}
http {
  include mime.types;
  default_type application/octet-stream;
  sendfile on;
  keepalive_timeout 65;
  map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
  }
  server {
    listen 90;
    server_name localhost;

    client_max_body_size 300M;

    #websocket related configuration
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header X-real-ip $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;

   
    location / {
      root D:\font\dist;
      try_files $uri $uri/ /index.html;
      index index.html index.htm;
    }
    location /prod-api/ {
      proxy_set_header Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header REMOTE-HOST $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_pass http://server ip:8888/;
    }
    location ~ /ISAPI|SDK/ {
      if ($http_cookie ~ "webVideoCtrlProxy=(.+)") {
 proxy_pass http://$cookie_webVideoCtrlProxy;
 break;
      }
    }

    location ^~ /webSocketVideoCtrlProxy {
     #web socket
     proxy_http_version 1.1;
     proxy_set_header Upgrade $http_upgrade;
     proxy_set_header Connection "upgrade";
     proxy_set_header Host $host;

     if ($http_cookie ~ "webVideoCtrlProxyWs=(.+)") {
  proxy_pass http://$cookie_webVideoCtrlProxyWs/$cookie_webVideoCtrlProxyWsChannel?$args;
  break;
     }
     if ($http_cookie ~ "webVideoCtrlProxyWss=(.+)") {
  proxy_pass http://$cookie_webVideoCtrlProxyWss/$cookie_webVideoCtrlProxyWsChannel?$args;
  break;
     }
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
      root html;
    }
  }
}

Front-end and back-end projects using Nginx proxy can refer to

If the front-end and back-end versions are separated, use Nginx proxy for deployment under Windows (full process, graphic tutorial):

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108120070

Preview effect

 

 

Posted by YoussefSiblini on Sun, 06 Nov 2022 12:02:27 +0300