Cenas
Visualize o vídeo de vários canais (câmera) no HIKVSION NVR da Hikvision (gravador de disco rígido de rede) em Vue:
Ao visualizar o vídeo NVR acima, é utilizado o pacote de desenvolvimento de controle WEB, que requer a instalação do plug-in e do navegador no computador.
Pré-visualização em modo de compatibilidade.
Além disso, existe outra maneira de desenvolver um pacote sem plug-ins
A partir de agora, é o kit de desenvolvimento sem plug-in WEB V3.2
O kit de desenvolvimento de versão sem plug-in WEB3.2 suporta versões superiores dos navegadores Google e Firefox, e o dispositivo precisa suportar streaming de Websocket. A versão sem plug-in requer o uso do servidor proxy nginx.
De acordo com as instruções, o NVR precisa suportar streaming de websocket.
Verifique se o NVR suporta streaming de websocket
O modelo NVR aqui é: DS-8664n-k16
Faça login na página da web do NVR-Configuração-Configurações do sistema-Rede-Configuração avançada-Habilitar websocket
Se houver uma opção para ativar o WebSokcet, significa que está OK. Para verificação adicional, baixe a demonstração oficial acima.
Siga as instruções oficiais para executar o agente nginx.
Modifique o arquivo de configuração no diretório nginx
Aqui simplesmente alteramos o número da porta para 8000
Em seguida, clique em start.bat para iniciar o nginx e visite
http://127.0.0.1:8000/cn/demo.html
Digite o endereço, nome de usuário, senha e outras informações correspondentes ao NVR e clique em Login e Iniciar Visualização
Se a demonstração oficial puder ser visualizada com sucesso, você poderá usar o pacote de desenvolvimento sem plug-in.
Observação:
Blog:
Um blog com temperamento desonesto e dominador_CSDN Blog-C#, Architecture Road, blogueiro SpringBoot.
Siga a conta pública
Domineering Programmer
para obter e-books, tutoriais e downloads gratuitos relacionados à programação.
concluir
1. Como a visualização pode ser realizada em demo.html, você só precisa modificá-la de acordo com suas necessidades.
Os documentos de demonstração e interface correspondentes são muito claros.
Então, como combinar os exemplos de demonstração com projetos existentes, como o projeto SpringBoot+Vue que separa o front e o backend.
Tutorial sobre como configurar um ambiente de desenvolvimento e executar o projeto com base em versões separadas de front-end e back-end:
Transformação de código em projetos baseados em Vue
As informações relacionadas à câmera são armazenadas em segundo plano, o mais necessário é o número do canal, que pode ser usado como referência na visualização.
Quando a câmera estiver selecionada, clique no botão de visualização e passe o parâmetro do número do canal para a página de visualização.
Consulte o processo anterior
SpringBoot+Vue+HIKVSION implementa seleção múltipla de câmera e visualização de múltiplas janelas (versão plug-in):
Em seguida, introduza os js necessários e outros arquivos de acordo com a demonstração oficial
A seguir está principalmente o código da página de visualização
<template>
<el-dialog
title="视频监控"
:visible.sync="videoOpen"
width="800px"
:append-to-body=false
@close="videoClose"
class="video_box"
:modal=false
>
<!-- 摄像头 -->
<!--视频窗口展示-->
<div id="playWnd" class="playWnd" ref="playWnd"></div>
</el-dialog>
</template>
<script>
const g_iWndIndex = 0; //可以不用设置这个变量,有窗口参数的接口中,不用传值,开发包会默认使用当前选择窗口
export default {
name: "HkVideo1",
components: {},
props: {
channelId: "",
},
watch: {},
data() {
return {
isLogin: false,
videoOpen: false,
szDeviceIdentify: "", // 设备标识(IP_Port)
ip: "NVR的ip",
port: "80",
username: "NVR的用户名",
password: "NVR的密码",
};
},
created() {},
mounted() {},
destroyed() {},
methods: {
// 创建播放实例
async initPlugin() {
let iRet = window.WebVideoCtrl.I_CheckPluginInstall();
if (-1 == iRet) {
alert("您还未安装过插件,请安装WebComponentsKit.exe!");
this.$confirm("是否下载WebComponentsKit.exe插件?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
window.location.href = "/static/HK_3.2/WebComponentsKit.exe";
});
return;
}
// 初始化插件参数及插入插件
window.WebVideoCtrl.I_InitPlugin(800, 600, {
bWndFull: true, //是否支持单窗口双击全屏,默认支持 true:支持 false:不支持
iPackageType: 2,
//szColorProperty:"plugin-background:0000ff; sub-background:0000ff; sub-border:00ffff; sub-border-select:0000ff", //2:PS 11:MP4
iWndowType: 1,
bNoPlugin: true,
// 窗口选中事件回调
cbSelWnd: (xmlDoc) => {
var szInfo = "当前选择的窗口编号:" + g_iWndIndex;
console.log(szInfo);
},
// 窗口双击事件回调
cbDoubleClickWnd: (iWndIndex, bFullScreen) => {
var szInfo = "当前放大的窗口编号:" + iWndIndex;
if (!bFullScreen) {
szInfo = "当前还原的窗口编号:" + iWndIndex;
}
console.log(szInfo);
},
// 插件事件回调
cbEvent: (iEventType, iParam1, iParam2) => {
if (2 == iEventType) {
// 回放正常结束
console.log("窗口" + iParam1 + "回放结束!");
} else if (-1 == iEventType) {
console.log("设备" + iParam1 + "网络错误!");
} else if (3001 == iEventType) {
this.clickStopRecord(g_szRecordType, iParam1);
}
},
cbRemoteConfig: () => {
console.log("关闭远程配置库!");
},
// 插件初始化完成回调
cbInitPluginComplete: () => {
this.$nextTick(() => {
console.log('窗口', this.$refs.playWnd)
let isInit = window.WebVideoCtrl.I_InsertOBJECTPlugin('playWnd');
console.log('isInit', isInit)
// 检查插件是否最新
if (-1 == window.WebVideoCtrl.I_CheckPluginVersion()) {
alert("检测到新的插件版本,请对WebComponentsKit.exe进行升级!");
return;
} else this.clickLogin();
})
},
});
},
// 登录
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(" 登录失败!", status, xmlDoc);
},
});
if (-1 == iRet) {
this.clickStartRealPlay();
}
},
// 获取通道
getChannelInfo() {
if (null == this.szDeviceIdentify) {
return;
}
// 模拟通道
window.WebVideoCtrl.I_GetAnalogChannelInfo(this.szDeviceIdentify, {
async: false,
success: (xmlDoc) => {
},
error: (status, xmlDoc) => {
console.log(" 获取模拟通道失败!");
},
});
// 数字通道
window.WebVideoCtrl.I_GetDigitalChannelInfo(this.szDeviceIdentify, {
async: false,
success: (xmlDoc) => {
},
error: (status, xmlDoc) => {
console.log(" 获取数字通道失败!");
},
});
// 零通道
window.WebVideoCtrl.I_GetZeroChannelInfo(this.szDeviceIdentify, {
async: false,
success: (xmlDoc) => {
},
error: (status, xmlDoc) => {
console.log(" 获取零通道失败!");
},
});
},
// 获取端口
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(" 获取端口失败!");
return false
}
},
// 开始预览
clickStartRealPlay(iStreamType) {
let wndInfo = window.WebVideoCtrl.I_GetWindowStatus(g_iWndIndex);
let iChannelID = this.channelId; // 通道列表
let bZeroChannel = false; // 是否播放零通道(下拉框)
let szInfo = "";
if ("undefined" === typeof iStreamType) {
iStreamType = 2; // 1主码流 2子码流 3第三码流 4转码码流
}
if (null == this.szDeviceIdentify) {
return;
}
let startRealPlay = () => {
window.WebVideoCtrl.I_StartRealPlay(this.szDeviceIdentify, {
iRtspPort: 554,
iStreamType: iStreamType,
iChannelID: iChannelID,
bZeroChannel: bZeroChannel,
success: () => {
szInfo = "开始预览成功!";
console.log(szInfo);
},
error: (status, xmlDoc) => {
if (403 === status) {
szInfo = "设备不支持Websocket取流!";
} else {
szInfo = "开始预览失败!";
}
this.$message.error(szInfo);
},
});
};
if (wndInfo != null) {
// 已经在播放了,先停止
window.WebVideoCtrl.I_Stop({
success: () => {
startRealPlay();
},
});
} else {
startRealPlay();
}
},
// 停止预览
clickStopRealPlay() {
let oWndInfo = window.WebVideoCtrl.I_GetWindowStatus(g_iWndIndex),
szInfo = "";
if (oWndInfo != null) {
window.WebVideoCtrl.I_Stop({
success: () => {
szInfo = "停止预览成功!";
console.log(szInfo);
},
error: () => {
szInfo = "停止预览失败!";
console.log(szInfo);
},
});
}
},
// 全屏
clickFullScreen() {
window.WebVideoCtrl.I_FullScreen(true);
},
// 停止录像
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 = "停止录像成功!";
} else if ("playback" === szType) {
szInfo = "停止剪辑成功!";
}
showOPInfo(oWndInfo.szDeviceIdentify + " " + szInfo);
},
error: () => {
if ("realplay" === szType) {
szInfo = "停止录像失败!";
} else if ("playback" === szType) {
szInfo = "停止剪辑失败!";
}
showOPInfo(oWndInfo.szDeviceIdentify + " " + szInfo);
},
});
}
},
// 查看摄像
videoChange() {
this.videoOpen = true;
this.$nextTick(() => {
if(!this.isLogin) {
this.isLogin = true
this.initPlugin()
} else {
this.clickStartRealPlay();
}
});
},
// 关闭摄像头弹窗
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. Sobre o proxy nginx
Veja o exemplo oficial do arquivo de configuração nginx
São necessárias duas partes da agência. a frente
location / {
root "../webs";
index index.html index.htm;
}
É o proxy da página estática do seu exemplo de demonstração, correspondente ao proxy do seu próprio pacote Vue dist.
O online correspondente deve ser alterado para
location / {
root C:\dist;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
Um dos dois proxies restantes é um proxy de interface, como um proxy reverso ao chamar a interface de login.
location ~ /ISAPI|SDK/ {
if ($http_cookie ~ "webVideoCtrlProxy=(.+)") {
proxy_pass http://$cookie_webVideoCtrlProxy;
break;
}
}
Isso provavelmente significa que se a solicitação começar com ISAPI ou SDK, a linha ondulada significa que esse caso não será ignorado e será enviado por proxy para o endereço a seguir.
Mas aqui vou perguntar por que não há configuração sobre o endereço IP do nvr no arquivo de configuração do proxy, então como faço para solicitar a interface quando sou um proxy.
Aqui, um nginx é adicionado no meio para encaminhamento. O nginx encontra o endereço IP do NVR através do cookie na solicitação e o encaminha, incluindo a obtenção do fluxo de vídeo durante a visualização.
É também assim que se executa o proxy websocket e se obtém stream de vídeo.
Semelhante ao fluxograma abaixo.
Portanto, basta modificar o arquivo de configuração nginx do seu projeto de acordo com o arquivo de configuração oficial do nginx.
A seguir está um exemplo do arquivo de configuração nginx do projeto após a transformação
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相关配置
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://服务器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;
}
}
}
Para projetos front-end e back-end usando proxy Nginx, consulte
Se as versões front-end e back-end estiverem separadas, implante usando o proxy Nginx no Windows (processo completo, tutorial gráfico):
Efeito de visualização