一.对接视频流的方法
既然要对接海康的视频,那自然少不了怎样去处理视频流。其实通过这几天的探索,我个人大概总结了如下几种方式。(如有解释不对,欢迎大佬指点)
首先 海康支持下图几种协议
1.就是用 video.js 、videojs-flash、vue-video-player来播放 ‘rtmp’协议的视频流,这种例子有很 多,大家可以自行查阅一下。但是这种方法有个很大的弊端,就是它需要依赖于浏览器的 flash 插件,但是谷歌已经明确的表示,2020年底将不再用flash,因此,你没有flash插件,去使用这种方式播放浏览器是会报错的。
2.用nginx搭建一个流媒体服务器,这种方式网上也很多例子,我对他的理解,就是通过ffmpe这个工具推流到nginx上,然后你去访问nginx,通过nginx监听的端口,他在将这个流转发给你,你再通过地址将流拉下来。然后播放,但是我们这海康安防平台貌似并没有开启推流功能。而且400多个人工摄像头推起来成本太大,所以这地方我就没在深入研究。感兴趣的可参考 ffmpeg+nginx-rtmp转发视频流_nginx 视频流-CSDN博客
3.通过@easydarwin/easyplayer插件(亲测有效),地址 @easydarwin/easyplayer - npm 里面文档很详细,根据文档部署在vue项目中即可(知道你们不一定看,那,这有手把手教的Easy-player.js在vue中的使用教程 - 知乎)但是话说回来,这个插件对于海康返回的接口协议而言,只能播放 m3u8/HLS 协议了,这种视频流需要你在查询海康视频地址的时候(/api/video/v2/cameras/previewURLs)将 protocol=“hls”这个参数加上
4.最简单,开发成本最少的。就是用人家海康的已经封装好的web插件了,如果没什么特别的项目需求的话,强烈推荐这种方式,下面会详细解说用法,以及踩坑
至于怎么获取海康的视频地址,以及接口安全认证, 参考我上篇文章vue对接海康安防平台接口,以及处理接口认证规则流程-CSDN博客
这里给大家一些现在视频流地址,迄今为止还有效,可供测试用,用Vlc打开即可
rtmp://ns8.indexforce.com/home/mystream
https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8
https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/master.m3u8
https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8
二、通过插件,实现视频播放、回放功能(末尾源码附上)
1.首先在海康平台下载 web插件 海康开放平台
2.解压下载好的压缩包,将bin目录下的程序安装好
3.可以将demo中的.html的必要参数修改,然后运行到浏览器中查看
测试没问题后,既可以将他整理到Vue项目中了,
4.将
这里面的Js文件通通放到vue的public目录下
并且在必须!必须!必须!vue 的index.html中引用
3.这样在我们的.vue组件中就可以直接使用了,不墨迹了,直接上代码了
根据自己业务对立面的数据进行修改
<template>
<div class="home">
<el-container style="height: 100vh; border: 1px solid #eee">
<el-aside width="200px">
<el-menu>
<div class="search-input">
<el-input
@keydown.enter="fuzzyRetrieval"
clearable
size="small"
v-model="searhKey"
placeholder="请输入关键字"
></el-input>
<el-button class="serachButton" size="small" @click="fuzzyRetrieval"
><i class="el-icon-search"></i
></el-button>
</div>
<el-menu-item-group v-if="cameraList.length" style="margin-top: 20px">
<el-menu-item
v-for="item in cameraList"
:key="item.cameraIndexCode"
:index="item.cameraIndexCode"
>
<div
@dblclick="videoDetails(item.cameraIndexCode)"
@click="cameraIndexCode = item.cameraIndexCode"
:title="item.cameraName"
class="cameraName-list"
>
{{ item.cameraName }}
</div>
</el-menu-item>
</el-menu-item-group>
<el-empty v-else description="暂无数据"></el-empty>
</el-menu>
</el-aside>
<el-container>
<el-header>
<div>
<el-button type="primary" plain>停止所有预览</el-button>
<el-button type="primary" plain>停止所有回放</el-button>
</div>
<div>
<el-radio-group v-model="radioType" @input="changeVideoType">
<el-radio-button label="0">实时预览</el-radio-button>
<el-radio-button label="1">录像回放</el-radio-button>
</el-radio-group>
<el-date-picker
v-model="playbackTime"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:unlink-panels="false"
value-format="timestamp"
:disabled="radioType === '0'"
@focus="focusEvent"
@blur="blurEvent"
@change="videoDetails(cameraIndexCode)"
>
</el-date-picker>
</div>
</el-header>
<el-main>
<div id="video_home"></div>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
import request from "@/utils/request.js";
import {
SIGNATURE,
SECRET,
VIDEO_IP,
VIDEO_PORT,
APPKEY,
} from "@/components/basicStaticData.js";
//声明公用变量
var initCount = 0;
var pubKey = "";
var oWebControl = null; //webc插件的实例对象
export default {
data() {
return {
//监控站点列表数据
cameraList: [],
//监控站点列表数据 备用
cameraList_standby: [],
//预览与回放的切换
radioType: "0",
//搜索框绑定的值
searhKey: "",
//日期输入框
playbackTime: [],
//当前用户点击的 监控点编号
cameraIndexCode: "",
//视频窗口初始的固定宽度
idWidth: "",
//视频窗口初始的固定高度
idHeight: "",
};
},
watch: {
searhKey(newValue, oldValue) {
if (!newValue) {
this.cameraList = this.cameraList_standby;
}
},
},
mounted() {
this.$nextTick(() => {
//视频窗口初始的固定宽度,当前屏幕父元素的宽度
this.idWidth = $("#video_home").width();
//视频窗口初始的固定高度,当前屏幕父元素的高度
this.idHeight = $("#video_home").height();
// 初始化web插件
this.initPlugin();
// 监听浏览器的变化
this.monitoringWindow();
});
//查询监控列表
this.getCamerasList();
},
destroyed() {
// 页面销毁后,将插件先隐藏,再销毁
oWebControl.JS_HideWnd();
oWebControl.JS_Disconnect();
},
methods: {
/**
* 分页查询查询的监控列表
*
*/
async getCamerasList() {
const res = await request({
url: "artemis/api/resource/v1/cameras",
headers: {
"x-ca-signature": SIGNATURE,
},
method: "POST",
data: {
pageNo: 1,
pageSize: 460,
},
});
const {
data: { list },
} = res.data;
this.cameraList = list;
this.cameraList_standby = list;
},
/**
* 创建WebControl实例与启动插件
*/
initPlugin() {
oWebControl = new WebControl({
szPluginContainer: "video_home", //指定容器id
iServicePortStart: 15900, //指定起止端口号,建议使用该值
iServicePortEnd: 15900,
cbConnectSuccess: () => {
//实例创建成功后需要启动服务
oWebControl
.JS_StartService("window", {
dllPath: "./VideoPluginConnect.dll",
})
.then(
() => {
oWebControl.JS_SetWindowControlCallback({
// 设置消息回调
cbIntegrationCallBack: this.cbIntegrationCallBack(),
});
oWebControl
.JS_CreateWnd("video_home", this.idWidth, this.idHeight)
.then(() => {
//JS_CreateWnd创建视频播放窗口,宽高可设定
this.init(); //创建播放实例成功后初始化
});
},
function () {}
);
},
cbConnectError: function () {
oWebControl = null;
$("#playWnd").html("插件未启动,正在尝试启动,请稍候...");
WebControl.JS_WakeUp("VideoWebPlugin://"); //程序未启动时执行error函数,采用wakeup来启动程序
initCount++;
if (initCount < 3) {
setTimeout(() => {
this.initPlugin();
}, 3000);
} else {
$("#playWnd").html("插件启动失败,请检查插件是否安装!");
}
},
});
},
/**
* 海康视频插件初始化,用于为之后的回放,预览视频做准备
*/
init() {
// 获取公钥
this.getPubKey(() => {
var appkey = APPKEY; //综合安防管理平台提供的appkey,必填
var secret = this.setEncrypt(SECRET); //综合安防管理平台提供的secret,必填
var ip = VIDEO_IP; //综合安防管理平台IP地址,必填
var playMode = Number(this.radioType); //初始播放模式:0-预览,1-回放
var port = VIDEO_PORT; //综合安防管理平台端口,若启用HTTPS协议,默认443
var snapDir = "D:\\SnapDir"; //抓图存储路径
var videoDir = "D:\\VideoDir"; //紧急录像或录像剪辑存储路径
var layout = "2x2"; //playMode指定模式的布局
var enableHTTPS = 1; //是否启用HTTPS协议与综合安防管理平台交互,这里总是填1
var encryptedFields = "secret"; //加密字段,默认加密领域为secret
var showToolbar = 1; //是否显示工具栏,0-不显示,非0-显示
var showSmart = 0; //是否显示智能信息(如配置移动侦测后画面上的线框),0-不显示,非0-显示
var buttonIDs =
"0,16,256,257,258,259,260,512,513,514,515,516,517,768,769"; //自定义工具条按钮
var reconnectTimes = 2; // 重连次数,回放异常情况下有效
//var reconnectTime = 4; // 每次重连的重连间>=reconnectTime
// ****** 请自行修改以上变量值********
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, //重连次数
//reconnectDuration:reconnectTime //重连间隔
}),
})
.then(() => {
// 初始化后resize一次,规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题
oWebControl.JS_Resize(this.idWidth, this.idHeight);
});
});
},
/**
* 监控视频实时预览
* @param {String} cameraIndexCode 监控站点的编号
*/
video_Preview(cameraIndexCode) {
oWebControl.JS_RequestInterface({
funcName: "startPreview",
argument: JSON.stringify({
cameraIndexCode, //监控点编号,必填
streamMode: 0, //主子码流标识:0-主码流,1-子码流,选填
transMode: 1, // 传输协议:0-UDP,1-TCP,选填
gpuMode: 0, //是否启用 GPU 硬解,0-不启用,1-启用,选填
// wndId: playback_wind, // 播放窗口序号(在 2x2 以上布局下可指定播放窗口)选填
}),
});
},
/**
* 监控视频回放
* @param {String} cameraIndexCode 监控站点的编号
* @param {Number} startTimeStamp 录像查询开始时间戳,单位:秒
* @param {Number} endTimeStamp 录像查询结束时间戳,单位:秒
*/
video_Playback(cameraIndexCode, startTimeStamp, endTimeStamp) {
oWebControl.JS_RequestInterface({
funcName: "startPlayback",
argument: JSON.stringify({
cameraIndexCode, //监控点编号,必填
startTimeStamp, // 录像查询开始时间戳,单位:秒
endTimeStamp, // 录像查询结束时间戳,单位:秒
recordLocation: 1, // 录像存储类型 0-中心存储 1-设备存储
transMode: 1, // 传输协议 ,0-UDP 1-TCP
gpuMode: 0, // 是否开启 GPU 硬解,0-不开启 1-开
// wndId: playback_wind, // 播放窗口序号(在 2x2 以上布局下可指定播放窗口)选填
}),
});
},
// 获取公钥
getPubKey(callback) {
oWebControl
.JS_RequestInterface({
funcName: "getRSAPubKey",
argument: JSON.stringify({
keyLength: 1024,
}),
})
.then(function (oData) {
// console.log(oData);
if (oData.responseMsg.data) {
pubKey = oData.responseMsg.data;
callback();
}
});
},
// RSA加密
setEncrypt(value) {
var encrypt = new JSEncrypt();
encrypt.setPublicKey(pubKey);
return encrypt.encrypt(value);
},
// 推送消息
cbIntegrationCallBack(oData) {
// console.log(55555, JSON.stringify(oData));
},
/**
* 用户点击左侧监控列表,调用安防平台接口,获取视频流
* @param {*} cameraIndexCode 当前点击的监控列表的唯一值,用于接口传参
*/
videoDetails(cameraIndexCode) {
if (this.radioType === "0") {
this.video_Preview(cameraIndexCode);
} else {
// 判断是否填写了回放时间
if (!this.playbackTime.length) {
this.$message.warning("请选择回放时间");
return;
}
this.video_Playback(
cameraIndexCode,
Number(this.playbackTime[0]) / 1000,
Number(this.playbackTime[1]) / 1000
);
}
},
/**
* 对监控列表进行模糊检索
*/
fuzzyRetrieval() {
const new_CameraList = this.cameraList_standby.filter((item) => {
return item.cameraName.indexOf(this.searhKey) > -1 ? true : false;
});
this.cameraList = new_CameraList;
},
/**
* 时间输入框focus事件
*/
focusEvent() {
oWebControl.JS_Resize(this.idWidth - 650, this.idHeight);
},
/**
* 时间输入框blur事件
*/
blurEvent() {
oWebControl.JS_Resize(this.idWidth, this.idHeight);
},
/**
* 回放与预览的切换事件
*/
changeVideoType() {
// 先销毁 在创建,如果不这么做,只是单纯调用this.init()方法修改里面的预览和回放的参数值是行不通的
oWebControl.JS_Disconnect().then(() => {
this.initPlugin();
});
},
/**
* 监听浏览器窗口变化,以及关闭标签页变,并对插件做出处理
*/
monitoringWindow() {
// 标签关闭
$(window).unload(function () {
if (oWebControl != null) {
oWebControl.JS_HideWnd(); // 先让窗口隐藏,规避插件窗口滞后于浏览器消失问题
oWebControl.JS_Disconnect().then(
function () {},
function () {}
);
}
// 监听resize事件,使插件窗口尺寸跟随DIV窗口变化
$(window).resize(() => {
if (oWebControl != null) {
oWebControl.JS_Resize(
$("#video_home").width(),
$("#video_home").height()
);
}
});
});
},
},
};
</script>
<style lang="scss" scoped>
#video_home {
width: 100%;
height: 92vh;
}
.cameraName-list {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
-webkit-user-select: none; /*webkit浏览器*/
-moz-user-select: none; /*火狐*/
-ms-user-select: none; /*IE10*/
user-select: none;
}
.el-main {
padding: 0.3vh;
}
.el-header {
background-color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
}
::v-deep .el-radio-button__inner,
.el-radio-group {
vertical-align: baseline;
}
.search-input {
width: 180px;
display: flex;
align-items: center;
position: fixed;
z-index: 99;
box-shadow: 0px 5px 5px #d6d2d2;
}
.serachButton {
font-size: 15px;
padding: 7px 11px;
background-color: #ecf1f8;
}
</style>
来看效果吧
回放
4.注意点
1.这个插件会置于浏览器的最上层,也就是说他会挡住你所有的页面元素,导致我日期没办法在 插件上边选择,如有办法,还请不吝赐教
2. 回放与预览切换时,通过直接改数值是没有用的,要先销毁,再创建
3.有不懂得多看看看插件开发指南
ok 就这样,如再发现问题会及时更新的,后续更新uni.app播放海康 欢迎评论区指正