feat:视频列表、预览
This commit is contained in:
1 parent
daec591c07
commit
ff139a452c
120 files changed
+5064
-3
No files matched your search
@@ -0,0 +1,17 @@
|
||||
import request from "/@/utils/request"
|
||||
|
||||
export function getRtspVideo(query: any) {
|
||||
return request({
|
||||
url: '/admin/third/camera/getRtspVideo',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getRtspVideoList(query: any) {
|
||||
return request({
|
||||
url: '/admin/third/camera/getRtspVideoList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,13 @@
|
||||
import request from "/@/utils/request"
|
||||
|
||||
export function list(query: any) {
|
||||
return request({
|
||||
url: '/admin/deviceChannel/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchList(query?: Object) {
|
||||
return request({
|
||||
url: '/admin/deviceChannel/page',
|
||||
|
||||
@@ -239,7 +239,7 @@ defineExpose({
|
||||
|
||||
<style>
|
||||
.splitpanes {
|
||||
/* height: initial !important; */
|
||||
height: initial !important;
|
||||
}
|
||||
|
||||
.splitpanes--vertical>.splitpanes__splitter {
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
<template>
|
||||
<el-dialog v-model="state.dialogVisible" title="预览" width="60%" :before-close="handleClose">
|
||||
<div class="flex justify-center items-start">
|
||||
<div class="min-w-[20%]">
|
||||
<el-tree :data="channel.treeList" :props="{
|
||||
label: 'name',
|
||||
children: 'children',
|
||||
value: 'id',
|
||||
}" ref="orgTreeRef" :loading="state.treeLoading" node-key="id" highlight-current
|
||||
:default-expand-all="true" :expand-on-click-node="false" @node-click="handleNodeClick">
|
||||
<template #default="{ node, data }" v-if="$slots.default">
|
||||
<slot :node="node" :data="data"></slot>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
<div class="flex-1 overflow-hidden video-container">
|
||||
<div id="ws-real-player" style="height: 500px; margin-bottom: 20px" />
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<div />
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="PreviewVideo">
|
||||
import { useMessage } from "/@/hooks/message";
|
||||
|
||||
import * as DeviceManageApi from "/@/api/device/deviceManage"
|
||||
import * as DeviceChannelApi from "/@/api/device/deviceChannel"
|
||||
import * as DeviceCameraApi from "/@/api/device/deviceCamera"
|
||||
|
||||
// @ts-ignore
|
||||
import PlayerManager from "./lib/icc/PlayerManager";
|
||||
|
||||
let realPlayer: any = null;
|
||||
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
channelList: [],
|
||||
loading: false,
|
||||
treeLoading: false,
|
||||
videoNum: 0,
|
||||
})
|
||||
|
||||
const detail = reactive<any>({
|
||||
|
||||
})
|
||||
|
||||
const channel = reactive<any>({
|
||||
treeList: []
|
||||
})
|
||||
|
||||
const handleClose = () => {
|
||||
state.dialogVisible = false
|
||||
destroy()
|
||||
}
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = async (id: string) => {
|
||||
state.dialogVisible = true
|
||||
// 获取deviceManage信息
|
||||
if (!id) return
|
||||
await getdeviceManageData(id)
|
||||
await getDeviceChannelList()
|
||||
};
|
||||
|
||||
// 点击树
|
||||
const handleNodeClick = (e: any) => {
|
||||
destroy()
|
||||
if (e.id == -1) {
|
||||
initVideo()
|
||||
playReal()
|
||||
return
|
||||
}
|
||||
initVideo(1, e.channelCode)
|
||||
}
|
||||
|
||||
// 初始化表单数据
|
||||
const getdeviceManageData = async (id: string) => {
|
||||
// 获取数据
|
||||
state.loading = true
|
||||
try {
|
||||
const { data, code } = await DeviceManageApi.getObj(id)
|
||||
if (code != 0) throw new Error()
|
||||
Object.assign(detail, data)
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
useMessage().error(error?.msg ?? "意外的错误");
|
||||
} finally {
|
||||
state.loading = false
|
||||
}
|
||||
};
|
||||
|
||||
// 查询通道列表
|
||||
const getDeviceChannelList = async () => {
|
||||
state.treeLoading = true
|
||||
try {
|
||||
const { data, code } = await DeviceChannelApi.list({ deviceCode: detail.deviceCode })
|
||||
if (code != 0) throw new Error()
|
||||
channel.treeList = data
|
||||
channel.list = data
|
||||
// 处理树
|
||||
if (data && data.length) {
|
||||
const tree = data.map((v: any) => {
|
||||
return {
|
||||
...v,
|
||||
name: `通道${v.channelSeq}`,
|
||||
children: v.children
|
||||
}
|
||||
})
|
||||
channel.treeList = [
|
||||
{
|
||||
id: "-1",
|
||||
name: '全部通道',
|
||||
children: tree
|
||||
}
|
||||
]
|
||||
}
|
||||
// 播放视频
|
||||
initVideo()
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
useMessage().error(error?.msg ?? "意外的错误");
|
||||
} finally {
|
||||
state.treeLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化视频
|
||||
function initVideo(num: number = 4, channelCode?: string) {
|
||||
if (realPlayer) {
|
||||
realPlayer = null;
|
||||
}
|
||||
realPlayer = new PlayerManager({
|
||||
el: "ws-real-player" /** 实时预览容器id,创建多个播放器,传入不同的容器id即可 **/,
|
||||
type: "real" /** real - 实时预览 record - 录像回放 **/,
|
||||
maxNum: 25 /** 一个播放器上限能播放的路数,可根据实际情况设置,支持 1 4 9 16 25 **/,
|
||||
num: num /** 初始化,页面显示的路数 **/,
|
||||
showControl: true /** 是否显示工具栏,默认显示 **/,
|
||||
prefixUrl: "lib" /** 解码库所在位置的前缀 **/,
|
||||
isRTPStream: true /** true-标准码流拉流 false-私有码流拉流 **/,
|
||||
receiveMessageFromWSPlayer: (methods: any, data: any, err: any) => {
|
||||
/* 回调函数,可以在以下回调函数里面做监听 */
|
||||
switch (methods) {
|
||||
case "initializationCompleted":
|
||||
console.log("初始化完成");
|
||||
realPlayer.setPlayerAdapter("stretching");
|
||||
// 初始化完成,可调用播放方法(适用于动态加载解码库)
|
||||
// 若回调未触发时就使用实时预览/录像回放,则无法播放。
|
||||
// 此时我们可以调用一个
|
||||
playReal(channelCode);
|
||||
break;
|
||||
case "changeStreamType": // 主/辅码流切换回调
|
||||
console.log(data.channelData); // 通道数据
|
||||
console.log(data.streamType); // 码流类型 0-主码流 1-辅码流1 2-辅码流2
|
||||
console.log(data.selectIndex); // 窗口序号,从 0
|
||||
break;
|
||||
case "realSuccess": // 实时预览成功
|
||||
console.log("实时预览成功");
|
||||
break;
|
||||
case "realError": // 实时预览失败
|
||||
console.log("实时预览失败", err);
|
||||
break;
|
||||
case "notifyTalk":
|
||||
// 点击窗口上的对讲按钮触发的事件回调
|
||||
console.log("notifyTalk", data);
|
||||
case "talkError": // 对讲失败
|
||||
console.log("对讲失败");
|
||||
break;
|
||||
case "selectWindowChanged": // 选中的窗口发生改变
|
||||
console.log(data, "返回窗口信息");
|
||||
break;
|
||||
case "windowNumChanged": // 播放器显示的路数发生改变
|
||||
console.log(data, "返回显示的窗口数量");
|
||||
break;
|
||||
case "closeVideo": // 视频关闭回调
|
||||
// 点击关闭按钮引发的视频关闭进行提示
|
||||
// 切换视频引发的视频关闭不进行提示
|
||||
if (!data.changeVideoFlag) {
|
||||
console.log(`窗口${data.selectIndex}的视频已关闭`);
|
||||
}
|
||||
break;
|
||||
case "statusChanged": // 视频状态发生改变
|
||||
break;
|
||||
case "errorInfo": // 错误信息汇总
|
||||
console.log(data, "可打印查看错误消息");
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 预览
|
||||
async function playReal(channelCode?: string) {
|
||||
if (!channel.list || !channel.list.length) return
|
||||
try {
|
||||
// 获取流地址
|
||||
const _data = {
|
||||
channelIdList: channel.list.map((v: any) => v.channelCode)
|
||||
}
|
||||
if (channelCode) {
|
||||
_data.channelIdList = [channelCode]
|
||||
}
|
||||
const { code, data: channelList } = await DeviceCameraApi.getRtspVideoList({
|
||||
..._data
|
||||
})
|
||||
if (code != 0) throw new Error()
|
||||
if (!channel.list || !channel.list.length) return
|
||||
state.videoNum = channel.list.length
|
||||
for (let index = 0; index < channelList.length; index++) {
|
||||
const element = channelList[index]
|
||||
const targetChannel = channel.list.find((v: any) => v.channelCode == element.channelId)
|
||||
realPlayer.realByUrl({
|
||||
rtspURL: element.fullUrl, // 必传
|
||||
wsURL: element.wsURL, // 必传
|
||||
selectIndex: index, // 必传, 播放窗口号
|
||||
channelId: element.channelId, // 必传, 通道id
|
||||
streamType: element.streamType, // 必传, 码流类型,
|
||||
playerAdapter: "stretching", // 选传, selfAdaption 自适应 | stretching 拉伸
|
||||
channelData: {
|
||||
// 建议传, 通道树信息
|
||||
id: targetChannel.channelCode, // {String} 通道编码
|
||||
deviceCode: targetChannel.deviceCode, // {String} 设备编码
|
||||
deviceType: targetChannel.deviceType, // {String} 设备类型
|
||||
channelSeq: targetChannel.channelSeq, // {String|Number} 通道序号
|
||||
}, // 选传
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
useMessage().error(error?.msg ?? "视频播放错误");
|
||||
}
|
||||
}
|
||||
|
||||
// 销毁
|
||||
function destroy() {
|
||||
if (realPlayer) {
|
||||
if (realPlayer) {
|
||||
for (let index = 0; index < state.videoNum; index++) {
|
||||
realPlayer.close(index)
|
||||
}
|
||||
}
|
||||
realPlayer = null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.video-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 60vh;
|
||||
}
|
||||
|
||||
#ws-real-player {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-content: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,681 @@
|
||||
// WSPlayer源码调试时使用
|
||||
// import WSPlayer from '../WSPlayer/WSPlayer'
|
||||
import API from "./api.js";
|
||||
/**
|
||||
* WSPlayer是核心组件
|
||||
* API 封装了接口
|
||||
*/
|
||||
class PlayerManager {
|
||||
constructor(opt) {
|
||||
if(!opt.el) {
|
||||
console.warn(`请初始化播放器时传入必传参数:
|
||||
new PlayerManager({
|
||||
el: "wsplayer_id", // 播放器所在的容器ID
|
||||
...
|
||||
})`)
|
||||
}
|
||||
// 播放器所在的容器ID
|
||||
this.el = opt.el;
|
||||
// 实时预览播放器
|
||||
this.realPlayer = null;
|
||||
// 录像回放播放器
|
||||
this.recordPlayer = null;
|
||||
this.player = null;
|
||||
// 实时预览还是录像回放播放器
|
||||
this.type = "real";
|
||||
// 窗口的数量
|
||||
this.playNum = 1;
|
||||
// 当前选中的窗口的索引
|
||||
this.playIndex = 0;
|
||||
// 当前选中窗口正在播放视频的通道
|
||||
this.currentChannelId = "";
|
||||
// 索引对应窗口,保存当日录像信息
|
||||
this.recordList = [];
|
||||
|
||||
/**
|
||||
* 兼容老版本字段 【已废弃】
|
||||
* 在 ws/wss 直连模式下, serverIp 表示MTS流媒体服务 IP
|
||||
* 在 代理 模式下, serverIp 表示的是 代理服务器的IP 地址
|
||||
*/
|
||||
this.serverIp = opt.serverIp
|
||||
|
||||
/**
|
||||
* V1.2.8 版本新增字段
|
||||
* 解码库资源文件前缀
|
||||
*/
|
||||
this.prefixUrl = opt.prefixUrl || './static'
|
||||
/**
|
||||
* V1.2.8 版本新增字段
|
||||
* 优先级最高,该方法传入后
|
||||
* 结构如下:
|
||||
* eg:
|
||||
* (rtspUrl) => {
|
||||
* return "wss://{ip}:9102"
|
||||
* }
|
||||
* 配置后:protocol, isIntranet, proxyServerIp, streamServerIp
|
||||
*/
|
||||
this.setWSUrl = opt.setWSUrl
|
||||
/**
|
||||
* V1.2.8 版本新增字段
|
||||
* websocket协议,http支持wss协议拉流,因此增加protocol配置
|
||||
*/
|
||||
this.protocol = opt.protocol
|
||||
/**
|
||||
* V1.2.8 版本新增字段[组合使用]
|
||||
* @desc 注意事项: 用于有内外网场景和分布式场景
|
||||
* @param isIntranet 是否内网, 用于有内外网环境,多台大华服务器[分布式]拉流场景配置
|
||||
* @param intranetMap 映射关系,结构 { "内网ip": "外网ip", ... }
|
||||
*
|
||||
*/
|
||||
this.isIntranet = opt.isIntranet
|
||||
this.intranetMap = opt.intranetMap
|
||||
/**
|
||||
* V1.2.7 版本新增字段
|
||||
* 用于只有内网
|
||||
* @desc 代理 模式下,代理服务器的IP 地址
|
||||
* 如果代理服务有端口则拼接端口
|
||||
*
|
||||
*/
|
||||
this.proxyServerIp = opt.proxyServerIp
|
||||
/**
|
||||
* V1.2.7 版本新增字段
|
||||
* 用于只有内网
|
||||
* @desc 流媒体服务 IP 地址
|
||||
* 如果拉流的 9100 9320 端口发生修改,则需要后面拼接修改后的端口
|
||||
*/
|
||||
this.streamServerIp = opt.streamServerIp
|
||||
/**
|
||||
* V1.2.7 版本新增字段
|
||||
* @desc 模式类型 是直连模式/代理模式
|
||||
*/
|
||||
this.useNginxProxy = opt.useNginxProxy || false
|
||||
|
||||
if(opt.importLoad) {
|
||||
this.loadWSPlayer(() => {
|
||||
this.initPlayer(opt)
|
||||
})
|
||||
return
|
||||
}
|
||||
this.initPlayer(opt)
|
||||
}
|
||||
|
||||
loadWSPlayer(callback) {
|
||||
let dom = document.createElement("script");
|
||||
dom.src = `${this.prefixUrl}/WSPlayer/WSPlayer.js`;
|
||||
document.head.appendChild(dom);
|
||||
var loaded = false;
|
||||
if(typeof callback === 'function') {
|
||||
dom.onload = dom.onreadystatechange = function() {
|
||||
if(!loaded && (!dom.readyState || /loaded|complete/.test(dom.readyState))) {
|
||||
dom.onload = dom.onreadystatechange = null;
|
||||
loaded = true;
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化播放器
|
||||
initPlayer(opt) {
|
||||
// 初始化播放器
|
||||
if(window.WSPlayer.WSPlayer) {
|
||||
window.WSPlayer = window.WSPlayer.WSPlayer
|
||||
}
|
||||
switch(opt.type) {
|
||||
case "real":
|
||||
this.initRealPlayer(opt);
|
||||
break;
|
||||
case "record":
|
||||
this.initRecordPlayer(opt);
|
||||
break;
|
||||
default:
|
||||
console.warn(`请初始化播放器时传入必传参数:
|
||||
new PlayerManager({
|
||||
type: "real", // real-实时预览 record-录像回放
|
||||
...
|
||||
})`)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化实时预览播放器
|
||||
*/
|
||||
initRealPlayer(opt) {
|
||||
this.playNum = opt.num;
|
||||
this.type = "real";
|
||||
this.realPlayer = new WSPlayer({
|
||||
el: this.el,
|
||||
type: 'real',
|
||||
importLoad: opt.importLoad, // 是否动态加载库
|
||||
serverIp: this.serverIp, // V1.2.7 版本已废弃[兼容老版本]
|
||||
prefixUrl: this.prefixUrl, // 解码库资源前缀
|
||||
protocol: this.protocol, // 协议 ws wss
|
||||
isIntranet: this.isIntranet, // 传入当前是 内网还是外网, true-内网 false-外网. 内外网 + 分布式下, 使用该模式
|
||||
intranetMap: this.intranetMap, // 传入当前的内外网关系映射 {"内网ip": "外网ip"}. 内外网 + 分布式下, 使用该模式
|
||||
proxyServerIp: this.proxyServerIp, // V1.2.7 版本新增该配置 代理服务器的ip 【ws/wss直连时不传】
|
||||
streamServerIp: this.streamServerIp, // V1.2.7 版本新增该配置 流媒体服务器的ip
|
||||
rtspResponseTimeout: opt.rtspResponseTimeout, // rtsp 拉流超时时间
|
||||
setWSUrl: this.setWSUrl, // 开放式的 传入函数, return websocket 连接地址,第三方自行处理
|
||||
config: {
|
||||
num: opt.num || 1,
|
||||
maxNum: opt.maxNum || 25,
|
||||
showControl: opt.showControl, // 默认是否显示底部工具栏
|
||||
showTopOperate: opt.showTopOperate, // 是否显示播放顶部操作
|
||||
useNginxProxy: this.useNginxProxy, // V1.2.7 版本新增该配置 直连时需要改为 false, 代理模式为 true
|
||||
showIcons: opt.showIcons || {
|
||||
streamChangeSelect: true, // 主辅码流切换
|
||||
replayIcon:true, // 刷新重播按钮
|
||||
ivsIcon: true, // 控制智能帧按钮
|
||||
talkIcon: true, // 对讲功能按钮
|
||||
localRecordIcon: true, // 本地录像功能按钮
|
||||
audioIcon: true, // 开启关闭声音按钮
|
||||
snapshotIcon: true, // 抓图按钮
|
||||
closeIcon: true, // 关闭视频按钮
|
||||
}, // V1.2.8 新增配置 顶部按钮自定义
|
||||
onlyLoadSingleLib: typeof opt.onlyLoadSingleLib === 'boolean' ? opt.onlyLoadSingleLib : false, // 是否走单线程
|
||||
isRTPStream: typeof opt.isRTPStream === 'boolean' ? opt.isRTPStream : false, // true-rtp标准拉流 false-私有拉流
|
||||
openIvs: typeof opt.openIvs === 'boolean' ? opt.openIvs : true, // 默认显示智能帧
|
||||
draggable: opt.draggable, // 1.3.0 新增,是否支持拖拽
|
||||
downloadMp4Record: typeof opt.downloadMp4Record === 'boolean' ? opt.downloadMp4Record : true,
|
||||
useH264MSE: typeof opt.useH264MSE === 'boolean' ? opt.useH264MSE : true,
|
||||
useH265MSE: typeof opt.useH265MSE === 'boolean' ? opt.useH265MSE : true,
|
||||
localeLang: opt.localeLang || 'zhCN', // zhCN 中文 enUS 英文
|
||||
localeI18n: opt.localeI18n, // 1.3.2新增配置
|
||||
cacheMode: opt.cacheMode || 1, //1.3.4新增 0-关闭实时流自适应缓冲模式 1-自适应缓冲 2-实时优先 3-流畅优先 4-智能缓冲模式
|
||||
nMSEFrameCount: opt.nMSEFrameCount, // MSE硬解码每个包的帧数(用于处理卡顿下,一次给video标签推的帧数,可以有效的防止画面过于卡顿,但是会影响实时性)
|
||||
ivsTypeArr: opt.ivsTypeArr || [1, 2], // 传入数组,支持显示的情况, 空表示没有智能信息,1-智能规则线 2-智能目标框
|
||||
isWebView: opt.isWebView, // 是否webview引擎 true-是 false-不是
|
||||
picCapCb: opt.picCapCb, // 是否需要抓图回调
|
||||
recordCb: opt.recordCb // 是否需要本地录制数据回调
|
||||
},
|
||||
receiveMessageFromWSPlayer: opt.receiveMessageFromWSPlayer || this.__receiveMessageFromWSPlayer.bind(this),
|
||||
getRealRtsp: API.getRealmonitor, // 获取实时预览rtsp接口 (不要做任何改动, 只传入方法, API.getRealmonitor() 是错误的, 会报错)
|
||||
getTalkRtsp: API.getTalkRtsp, // 获取对讲rtsp接口 (不要做任何改动, 只传入方法, API.getTalkRtsp() 是错误的, 会报错)
|
||||
stopTalk: API.stopTalk, // 关闭对讲rtsp接口 (不要做任何改动, 只传入方法, API.stopTalk() 是错误的, 会报错)
|
||||
})
|
||||
this.player = this.realPlayer;
|
||||
// 初始化云台控制组件
|
||||
if(opt.pztEl) {
|
||||
this.realPlayer.initPanTilt({
|
||||
// 云台控制容器的id
|
||||
el: opt.pztEl,
|
||||
// 云台区域控制接口
|
||||
setPtzDirection: API.setPtzDirection,
|
||||
// 云台镜头控制接口
|
||||
setPtzCamera: API.setPtzCamera,
|
||||
// 云台三维定位接口
|
||||
controlSitPosition: API.controlSitPosition
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化录像回放播放器
|
||||
*/
|
||||
initRecordPlayer(opt) {
|
||||
this.playNum = opt.num;
|
||||
this.type = "record";
|
||||
this.recordPlayer = new WSPlayer({
|
||||
el: this.el,
|
||||
type: 'record',
|
||||
importLoad: opt.importLoad, // 是否动态加载
|
||||
serverIp: this.serverIp, // V1.2.7 版本已废弃[兼容老版本]
|
||||
prefixUrl: this.prefixUrl, // 解码库资源前缀
|
||||
protocol: this.protocol, // 协议 ws wss
|
||||
isIntranet: this.isIntranet, // 传入当前是 内网还是外网, true-内网 false-外网. 内外网 + 分布式下, 使用该模式
|
||||
intranetMap: this.intranetMap, // 传入当前的内外网关系映射 {"内网ip": "外网ip"}. 内外网 + 分布式下, 使用该模式
|
||||
proxyServerIp: this.proxyServerIp, // V1.2.7 版本新增该配置 代理服务器的ip 【ws/wss直连时不传】
|
||||
streamServerIp: this.streamServerIp, // V1.2.7 版本新增该配置 流媒体服务器的ip
|
||||
setWSUrl: this.setWSUrl, // 开放式的 传入函数, return websocket 连接地址,第三方自行处理
|
||||
rtspResponseTimeout: opt.rtspResponseTimeout, // rtsp 拉流超时时间
|
||||
config: {
|
||||
num: opt.num,
|
||||
maxNum: opt.maxNum,
|
||||
showControl: opt.showControl, // 是否显示播放器底部工具栏,默认显示
|
||||
showTopOperate: opt.showTopOperate, // 是否显示播放顶部操作
|
||||
useNginxProxy: this.useNginxProxy, // V1.2.7 版本新增该配置 直连时需要改为 false, 【代理模式可不传】
|
||||
showIcons: opt.showIcons || {
|
||||
streamChangeSelect: true, // 主辅码流切换
|
||||
replayIcon:true, // 刷新重播按钮
|
||||
ivsIcon: true, // 控制智能帧按钮
|
||||
talkIcon: true, // 对讲功能按钮
|
||||
localRecordIcon: true, // 本地录像功能按钮
|
||||
audioIcon: true, // 开启关闭声音按钮
|
||||
snapshotIcon: true, // 抓图按钮
|
||||
closeIcon: true, // 关闭视频按钮
|
||||
}, // V1.2.8 新增配置 顶部按钮自定义
|
||||
openIvs: typeof opt.openIvs === 'boolean' ? opt.openIvs : true, // 默认显示智能帧
|
||||
onlyLoadSingleLib: typeof opt.onlyLoadSingleLib === 'boolean' ? opt.onlyLoadSingleLib : false, // 是否走单线程
|
||||
isRTPStream: typeof opt.isRTPStream === 'boolean' ? opt.isRTPStream : false, // true-rtp标准拉流 false-私有拉流
|
||||
showRecordProgressBar: typeof opt.showRecordProgressBar === 'boolean' ? opt.showRecordProgressBar : true, // V1.2.8 新增配置 是否显示回放进度条
|
||||
draggable: opt.draggable, // 1.3.0 新增,是否支持拖拽
|
||||
downloadMp4Record: typeof opt.downloadMp4Record === 'boolean' ? opt.downloadMp4Record : true,
|
||||
useH264MSE: typeof opt.useH264MSE === 'boolean' ? opt.useH264MSE : true,
|
||||
useH265MSE: typeof opt.useH265MSE === 'boolean' ? opt.useH265MSE : true,
|
||||
localeLang: opt.localeLang || 'zhCN', // zhCN 中文 enUS 英文
|
||||
localeI18n: opt.localeI18n, // 1.3.2新增配置
|
||||
ivsTypeArr: opt.ivsTypeArr || [1, 2], // 传入数组,支持显示的情况, 空表示没有智能信息,1-智能规则线 2-智能目标框
|
||||
isWebView: opt.isWebView, // 是否webview引擎 true-是 false-不是
|
||||
picCapCb: opt.picCapCb, // 是否需要抓图回调
|
||||
recordCb: opt.recordCb // 是否需要本地录制数据回调
|
||||
},
|
||||
receiveMessageFromWSPlayer: opt.receiveMessageFromWSPlayer || this.__receiveMessageFromWSPlayer.bind(this),
|
||||
getRecords: API.getRecords, // 获取录像列表接口 (不要做任何改动, 只传入方法, API.getRecords() 是错误的, 会报错)
|
||||
getRecordRtspByTime: API.getRecordRtspByTime, // 根据时间形式获取录像rtsp接口 (不要做任何改动, 只传入方法, API.getRecordRtspByTime() 是错误的, 会报错)
|
||||
getRecordRtspByFile: API.getRecordRtspByFile, // 根据文件形式获取录像rtsp接口 (不要做任何改动, 只传入方法, API.getRecordRtspByFile() 是错误的, 会报错)
|
||||
})
|
||||
this.player = this.recordPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放实时预览视频
|
||||
* @param opt.channelList: {Array<Object>} 必填,通道列表
|
||||
* @param opt.streamType: {Number|String} 选填,码流类型,不填默认播放辅码流1,若不存在辅码流1,则自动切换到主码流 1-主码流 2-辅码流1 3-辅码流2
|
||||
* @param opt.windowIndex: {Number} 选填,指定从哪个窗口开始播放。不填默认从选中的窗口开始播放
|
||||
*/
|
||||
// channelList: [{
|
||||
// id: channelCode, // {String} 通道编码 -- 用于预览,必填
|
||||
// deviceCode: deviceCode, // {String} 设备编码 -- 用于对讲,对讲必填,无对讲功能可不填
|
||||
// deviceType: deviceType, // {String} 设备类型 -- 用于对讲,对讲必填,无对讲功能可不填
|
||||
// channelSeq: channelSeq, // {String|Number} 通道序号 -- 用于对讲,对讲必填,无对讲功能可不填
|
||||
// cameraType: cameraType, // {String|Number} 摄像头类型 -- 用于云台,云台必填,无云台功能可不填
|
||||
// capability: capability, // {String} 能力集 -- 用于云台,云台必填,无云台功能可不填
|
||||
// }]
|
||||
playRealVideo(opt) {
|
||||
this.realPlayer && this.realPlayer.playRealVideo({
|
||||
channelList: opt.channelList,
|
||||
streamType: opt.streamType,
|
||||
windowIndex: opt.windowIndex
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 传入 wsUri 和 wsUrl 播放预览视频
|
||||
* @param { String } opt.rtspURL 必传 rtsp流地址
|
||||
* @param { String } opt.wsURL 必传 建立的websocket连接地址
|
||||
* @param { String } opt.channelId 必传 通道id
|
||||
* @param { String } opt.streamType 必传 码流类型 1-主码流 2-辅码流1 3-辅码流2
|
||||
* @param { String } opt.streamServerIp 选传 流媒体服务ip
|
||||
* @param { String } opt.playerAdapter 选传 是否拉伸窗口 selfAdaption 自适应 | stretching 拉伸
|
||||
* @param { Number } opt.selectIndex 必传 窗口号 从0开始
|
||||
* @param { Object } opt.channelData 选传 通道信息,若使用云台功能,则必传
|
||||
*/
|
||||
realByUrl(opt) {
|
||||
this.player && this.player.realByUrl(opt)
|
||||
}
|
||||
|
||||
/**
|
||||
* @name talkByUrl
|
||||
* @desc 通过流地址方式进行对讲
|
||||
* @param playTalk 流地址方式
|
||||
* @param rtspURL rtsp的地址
|
||||
* @param wsURL websocket地址
|
||||
* @param selectIndex 窗口号
|
||||
* @param audioType 音频类型
|
||||
*/
|
||||
talkByUrl(opt) {
|
||||
this.realPlayer && this.realPlayer.talkByUrl(opt)
|
||||
}
|
||||
|
||||
/**
|
||||
* @name 停止流地址对讲
|
||||
* @desc 流地址停止对讲
|
||||
*/
|
||||
stopUrlTalk(windowIndex) {
|
||||
this.realPlayer && this.realPlayer.stopUrlTalk(windowIndex)
|
||||
}
|
||||
|
||||
// 音量设置
|
||||
/**
|
||||
* 开启声音
|
||||
*/
|
||||
openVolume(windowIndex) {
|
||||
this.player.openVolume(windowIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭声音
|
||||
*/
|
||||
closeVolume(windowIndex) {
|
||||
this.player.closeVolume(windowIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置声音
|
||||
* @param volume 声音大小 传 0-1 小数点一位的小数
|
||||
*/
|
||||
setVolume(windowIndex, volume) {
|
||||
this.player.setVolume(windowIndex, volume)
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放录像回放
|
||||
* @param opt.channelList {Array<Object>} 通道集合 必填
|
||||
* @param opt.startTime {String|Number} 开始时间 必选 timestamp到秒
|
||||
* @param opt.endTime {String|Number} 结束时间 必选 timestamp到秒
|
||||
* @param opt.recordSource {String|Number} 录像来源 必选 2表示设备录像 3表示中心录像
|
||||
* @param opt.streamType {String|Number} 码流类型 可选
|
||||
* @param opt.recordType {String|Number} 录像类型 可选
|
||||
* @param {Boolean} opt.playRecordByFile 选传 是否根据文件播放,默认为false
|
||||
*/
|
||||
// channelList: [{
|
||||
// id: channelCode, // {String} 通道编码 -- 用于回放,必填
|
||||
// }]
|
||||
playRecordVideo(opt) {
|
||||
this.recordPlayer && this.recordPlayer.playRecordVideo(opt)
|
||||
}
|
||||
|
||||
/**
|
||||
* 传入 wsUri 和 wsUrl 播放回放视频
|
||||
* @param {Object} opt
|
||||
* @param {String} opt.wsURL 必传 拉流的websocket连接地址
|
||||
* @param {String} opt.rtspURL 必传 rtsp地址
|
||||
* @param {String} opt.channelId 必传 通道id
|
||||
* @param {String} opt.startTime 必传 开始时间 到秒的时间戳
|
||||
* @param {String} opt.endTime 必传 结束时间 到秒的时间戳
|
||||
* @param {String} opt.playerAdapter 选传 是否拉伸窗口 selfAdaption 自适应 | stretching 拉伸
|
||||
* @param {Number} opt.selectIndex 选传 窗口号
|
||||
* @param {Object} opt.channelData 选传 通道信息
|
||||
* @param {Array} opt.records 录像文件信息 [数组格式,用于渲染进度条]
|
||||
* @param {Boolean} opt.playRecordByTime 是否设备录像播放 true-设备录像播放,false-中心录像播放
|
||||
*/
|
||||
recordByUrl(opt) {
|
||||
this.player && this.player.recordByUrl(opt)
|
||||
}
|
||||
|
||||
// 更新窗口
|
||||
updatePlayerWindow() {
|
||||
this.player.__updatePlayerWindow()
|
||||
}
|
||||
|
||||
// 开始对讲
|
||||
playTalkAudio(selectIndex) {
|
||||
this.player.playTalkAudio(selectIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* 录像暂停
|
||||
* 只有正在播放的录像调用才有效
|
||||
* @param { number } index 窗口号
|
||||
*/
|
||||
pause(index) {
|
||||
this.recordPlayer && this.recordPlayer.pause(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 录像暂停后播放
|
||||
* 只有暂停后的录像调用才有效
|
||||
* @param { number } index 窗口号
|
||||
*/
|
||||
play(index) {
|
||||
this.recordPlayer && this.recordPlayer.play(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 倍速播放
|
||||
* @param { number } speed 速率 0.125 0.25 0.5 1 1.25 1.5 2 4 8 共7种速率
|
||||
* @param { number } index 窗口号
|
||||
*/
|
||||
playSpeed(speed, index) {
|
||||
this.recordPlayer && this.recordPlayer.playSpeed(speed, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭播放器
|
||||
* @param {number} index 可选,关闭指定索引的窗口的播放器,不传则表示关闭所有播放器
|
||||
*/
|
||||
close(index, closeReason="api") {
|
||||
this.player && this.player.close(index, closeReason);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置全屏
|
||||
*/
|
||||
setFullScreen() {
|
||||
this.player.setFullScreen();
|
||||
}
|
||||
/**
|
||||
* 设置退出全屏
|
||||
*/
|
||||
setExitFullScreen() {
|
||||
this.player.setExitFullScreen();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置窗口自适应还是拉伸
|
||||
* @param {string} playerAdapter selfAdaption 自适应 | stretching 拉伸
|
||||
*/
|
||||
setPlayerAdapter(playerAdapter) {
|
||||
this.player.setPlayerAdapter(playerAdapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制视频播放器显示的路数: 1 2 3 4 6 8 9 16 25,不会超过最大显示路数
|
||||
* @param {number} number
|
||||
*/
|
||||
setPlayerNum(number) {
|
||||
this.player.setPlayerNum(number);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置选中的播放器的索引
|
||||
* @param {number} index 设置选中的窗口的索引
|
||||
*/
|
||||
setSelectIndex(index) {
|
||||
this.player.setSelectIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 录像跳转播放
|
||||
* /**
|
||||
* 根据时间跳转播放
|
||||
* @param {String|Number} time
|
||||
* time格式支持情况如下:
|
||||
* new Date("2024-11-01 01:00:00") // 时间戳对象格式 (直接跳转到当前时间)
|
||||
* "2024-11-01 01:00:00" // 时间字符串 (同上)
|
||||
* "2024/11/01 01:00:00" // 时间字符串 (同上)
|
||||
* 1706979600 // 到秒时间戳, (同上)
|
||||
* "1706979600" // 到秒时间戳字符串 (同上)
|
||||
* 1706979600000 // 到毫秒时间戳 (同上)
|
||||
* "1706979600000" // 到毫秒时间戳字符串 (同上)
|
||||
* "01:00:00" // 当日时分秒跳转 (此值跳转到开始播放的当前的时间点, 如录像是 11-1号的,则跳转到 11-1号01:00:00)
|
||||
* "3600" // 1点的时间戳 (此值根据录像的开始时间,往后推1小时,跳转到这个时间点, 如开始时间为 11-1号2点,则跳转到 11-1号3点开始播放)
|
||||
* 3600 // 1点的时间戳字符串 (同上)
|
||||
* @param {number} selectIndex 窗口号
|
||||
*/
|
||||
jumpPlayByTime(time, selectIndex) {
|
||||
this.player.jumpPlayByTime(time, selectIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始本地录像
|
||||
* @param selectIndex 选择本地录像下载的窗口索引 必填
|
||||
* @param name 录像名称 必填
|
||||
* @param size 单个录像文件大小(单位M) 可选,默认值为 ./config/config.js 中 localRecordSize
|
||||
* @param downloadMp4Record 可选,默认值为全局配置。 true - 以MP4格式下载录像,false - 以dav格式下载录像
|
||||
*/
|
||||
startLocalRecord(selectIndex, name, size, downloadMp4Record) {
|
||||
this.player.startLocalRecord(selectIndex, name, size, downloadMp4Record)
|
||||
}
|
||||
/**
|
||||
* 停止本地录像下载
|
||||
* @param {Number} selectIndex 选择关闭本地录像下载的窗口索引
|
||||
*/
|
||||
stopLocalRecord(selectIndex) {
|
||||
this.player.stopLocalRecord(selectIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能规则线和目标框设置
|
||||
* @param {Boolean} showIvs 是否显示智能规则线和目标框,true-显示,false-隐藏
|
||||
* @param {Number} selectIndex 选择的窗口号
|
||||
* @param {Array} ivsType [1] - 智能规则线, [2] - 智能目标框, [1, 2] - 规则线和智能目标框都显示
|
||||
*/
|
||||
setIvs(showIvs, selectIndex, ivsType) {
|
||||
this.player.setIvs({
|
||||
showIvs,
|
||||
ivsType,
|
||||
selectIndex
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 抓图
|
||||
* @param {Number} index 抓图的窗口索引
|
||||
* @param {String} name 抓图的图片名称(选填)
|
||||
*/
|
||||
picCap(index, name) {
|
||||
this.player.picCap(index, name)
|
||||
}
|
||||
|
||||
/**
|
||||
* 在窗口里显示自定义信息
|
||||
* @param index 窗口索引
|
||||
* @param msg 显示的信息
|
||||
* @param channelData 通道信息
|
||||
*/
|
||||
showMsgInWindow(index, msg, channelData) {
|
||||
this.player.showMsgInWindow(index, msg, channelData)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建WSPlayer蒙层
|
||||
*/
|
||||
createWSPlayerLayer(layerId, callback) {
|
||||
this.player.createWSPlayerLayer(layerId, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建自定义dom(可自定义水印,智能帧等)
|
||||
* @param {*} domId
|
||||
*/
|
||||
createCustomDom(domId) {
|
||||
this.player.createCustomDom(domId)
|
||||
}
|
||||
|
||||
/**
|
||||
* destroy 销毁播放器
|
||||
* @param {*} method
|
||||
* @param {*} data
|
||||
*/
|
||||
destroy() {
|
||||
this.player.destroy();
|
||||
}
|
||||
|
||||
// ----------------- 播放器事件 ------------------------
|
||||
__receiveMessageFromWSPlayer(method, data) {
|
||||
switch(method) {
|
||||
// ------------- 公共事件 ---------------------
|
||||
case "initializationCompleted":
|
||||
// 初始化完成,可调用播放方法(适用于动态加载解码库)
|
||||
break;
|
||||
case "realSuccess": // 实时预览成功
|
||||
console.log("实时预览成功")
|
||||
break;
|
||||
case "realError": // 实时预览失败
|
||||
console.log("实时预览失败", err)
|
||||
break;
|
||||
case "talkSuccess": // 对讲成功 V1.3.4版本新增
|
||||
console.log("对讲成功");
|
||||
break;
|
||||
case "talkError": // 对讲失败
|
||||
console.log("对讲失败");
|
||||
break;
|
||||
case "recordSuccess": // 录像回放成功
|
||||
console.log("录像回放成功");
|
||||
break;
|
||||
case "recordError": // 录像回放失败
|
||||
console.log("录像回放失败", err);
|
||||
break;
|
||||
case 'recordFinish':
|
||||
console.log("当前录像回放完成", data)
|
||||
break;
|
||||
case "selectWindowChanged": // 选中的窗口发生改变
|
||||
this.currentChannelId = data.channelId;
|
||||
this.playIndex = data.playIndex;
|
||||
break;
|
||||
case "windowNumChanged": // 播放器显示的路数发生改变
|
||||
this.playNum = data;
|
||||
break;
|
||||
case "closeVideo": // 视频关闭
|
||||
// 点击关闭按钮引发的视频关闭进行提示
|
||||
// 切换视频引发的视频关闭不进行提示
|
||||
if(!data.changeVideoFlag) {
|
||||
console.log(`窗口${data.selectIndex}的视频已关闭`)
|
||||
}
|
||||
// 关闭原因: btnClick-按钮关闭 chromeMemoryOut-浏览器内存不足 changeStream-切换视频调用 playSDKError-解码库报错关闭 recordFinish-录像回放完成 api-调用API关闭 destroy-解码库销毁关闭 otherCustom-可自定义关闭内容
|
||||
// console.log(data.closeReason)
|
||||
break;
|
||||
case "statusChanged": // 视频状态发生改变
|
||||
break;
|
||||
case "getVideoFrameInfo":
|
||||
// 获取视频帧信息
|
||||
break;
|
||||
case "getAudioFrameInfo":
|
||||
// 获取视频内音频帧信息
|
||||
break;
|
||||
case "getPlayBackStreamRange":
|
||||
// 获取录像回放时长
|
||||
console.log("getPlayBackStreamRange", data)
|
||||
break;
|
||||
case "dragWindow":
|
||||
// 拖拽窗口
|
||||
console.log(`窗口${data.dragIndex}的视频拖拽到窗口${data.dropIndex}上`)
|
||||
break;
|
||||
case "errorInfo": // 错误信息提示
|
||||
console.log(data, "可打印查看错误消息");
|
||||
// data = {
|
||||
// errorCode: xxx,
|
||||
// errorMsg: "",
|
||||
// errorData: {
|
||||
// channelList: [],
|
||||
// apiErrorInfo: {},
|
||||
// method: "",
|
||||
// arguments: [],
|
||||
// },
|
||||
// }
|
||||
console.error(data)
|
||||
break;
|
||||
case "customDomInfo": // 窗口大小发生变化,视频播放时均会触发
|
||||
// data.customDomId 为每一个小窗口的自定义dom的id
|
||||
// data.width 为当前窗口的宽度
|
||||
// data.height 为当前窗口的高度
|
||||
// data.currentWindowPlaying 当前窗口是否正在播放视频
|
||||
// 根据上述内容 进行水印的添加,或其他操作
|
||||
break;
|
||||
case "chromeMemoryOut":
|
||||
console.warn("浏览器资源不足!")
|
||||
break;
|
||||
case "fullScreen":
|
||||
console.log("点击播放器底部的全屏按钮触发此回调")
|
||||
break;
|
||||
case 'picCap': // 抓图回调,配置在 new PlayerManager 中配置 picCapCb为true后即可触发
|
||||
// data.channelData
|
||||
// data.selectIndex 窗口索引
|
||||
// data.base64Img 图片base64字符串
|
||||
break;
|
||||
case 'localRecordBuf': // 本地录像回调,一帧一帧返回
|
||||
break;
|
||||
case 'localRecordFile': // 本地录像停止时返回Blob数据
|
||||
break;
|
||||
case "notifyTalk": // 点击顶部对讲按钮,触发此回调
|
||||
// 当采用 realByUrl 进行实时预览时,点击顶部对讲按钮,会触发此回调,需要在此回调内 获取对讲rtsp发起对讲
|
||||
case "switchStartTime":
|
||||
// 在调用 recordByUrl 中配置 playRecordByTime: false 后, 每次拖拽都会触发此回调,此回调需要重新获取rtsp拉流(不支持直接拖拽跳转的设备需要重新拉流,走此回调)
|
||||
break;
|
||||
case "setSeekTime":
|
||||
// 此回调不需要重新获取rtsp拉流,录像内部会自动跳转,只返回时间告知用户。如此方法跳转无效,请采用上述 switchStartTime方法
|
||||
// 在调用 recordByUrl 中配置 playRecordByTime: true 后, 录像进度条触发拖拽跳转,默认触发此回调。返回跳转后的时间
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
PlayerManager
|
||||
}
|
||||
|
||||
export default PlayerManager
|
||||
@@ -0,0 +1,358 @@
|
||||
// 注意: 本文接口均为 ICC 接口, 接口需要三方后端转发使用,每个方法对请求进行改写,固定格式请看注释
|
||||
|
||||
|
||||
// 跟平台相关的业务JS
|
||||
const API = {
|
||||
/**
|
||||
* 请求实时视频流地址
|
||||
* @returns Promise<>
|
||||
* @desc 只需改写 API.ajaxRequest 内容
|
||||
*
|
||||
* 固定格式如下
|
||||
* return new Promise ((resolve, reject) => {
|
||||
* ...
|
||||
* resolve({ resolve 返回格式示例如下
|
||||
* url: "",
|
||||
* token: 1,
|
||||
* ...
|
||||
* })
|
||||
* })
|
||||
*/
|
||||
getRealmonitor(realmonitorParam) {
|
||||
return new Promise((resolve,reject) => {
|
||||
API.ajaxRequest({
|
||||
url: '/evo-apigw/admin/API/MTS/Video/StartVideo',
|
||||
data: realmonitorParam,
|
||||
}).then(res => {
|
||||
resolve(res.data)
|
||||
}).catch(res => {
|
||||
reject(res)
|
||||
})
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 获取对讲的rtsp
|
||||
* @returns Promise<>
|
||||
* @desc 只需改写 API.ajaxRequest 内容
|
||||
*
|
||||
* 固定格式如下
|
||||
* return new Promise ((resolve, reject) => {
|
||||
* ...
|
||||
* resolve({ resolve 返回格式示例如下
|
||||
* url: "",
|
||||
* token: 1,
|
||||
* ...
|
||||
* })
|
||||
* })
|
||||
*/
|
||||
getTalkRtsp(talkPram) {
|
||||
return new Promise((resolve,reject) => {
|
||||
API.ajaxRequest({
|
||||
url: '/evo-apigw/admin/API/MTS/Audio/StartTalk',
|
||||
data: talkPram,
|
||||
}).then(res => {
|
||||
resolve(res.data)
|
||||
}).catch(res => {
|
||||
reject(res)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 停止对讲
|
||||
*/
|
||||
stopTalk(recordByTimeParam) {
|
||||
return new Promise((resolve,reject) => {
|
||||
API.ajaxRequest({
|
||||
url: '/evo-apigw/admin/API/MTS/Audio/StopTalk',
|
||||
data: recordByTimeParam,
|
||||
}).then(res => {
|
||||
resolve(res.data)
|
||||
}).catch(res => {
|
||||
reject(res)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 根据时间查询设备上的录像
|
||||
* @param recordByTimeParam.recordType 录像类型:1=一般录像,2=报警录像
|
||||
* @param recordByTimeParam.recordSource 录像来源:1=全部,2=设备,3=中心
|
||||
* @param recordByTimeParam.streamType 码流类型:1=主码流, 2=辅码流 使用辅码流 码率低更加流畅
|
||||
* @param recordByTimeParam.channelId 通道code
|
||||
* @param recordByTimeParam.startTime 开始时间
|
||||
* @param recordByTimeParam.endTime 结束时间
|
||||
|
||||
* @returns Promise<>
|
||||
* @desc 只需改写 API.ajaxRequest 内容
|
||||
*
|
||||
* 固定格式如下
|
||||
* return new Promise ((resolve, reject) => {
|
||||
* ...
|
||||
* resolve({ resolve 返回格式示例如下
|
||||
* url: "",
|
||||
* token: 1,
|
||||
* ...
|
||||
* })
|
||||
* })
|
||||
*/
|
||||
getRecordRtspByTime(recordByTimeParam) {
|
||||
return new Promise((resolve,reject) => {
|
||||
API.ajaxRequest({
|
||||
url: '/evo-apigw/admin/API/SS/Playback/StartPlaybackByTime',
|
||||
data: recordByTimeParam,
|
||||
}).then(res => {
|
||||
resolve(res.data)
|
||||
}).catch(res => {
|
||||
reject(res)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据文件形式回放录像
|
||||
* @param recordByFileParam.recordType 录像类型:1=一般录像,2=报警录像
|
||||
* @param recordByFileParam.streamType 码流类型:1=主码流, 2=辅码流 使用辅码流 码率低更加流畅
|
||||
* @param recordByFileParam.recordSource 录像来源:1=全部,2=设备,3=中心
|
||||
* @param recordByFileParam.channelId 通道code
|
||||
* @param recordByFileParam.startTime 开始时间 timestamp到秒
|
||||
* @param recordByFileParam.endTime 结束时间 timestamp到秒
|
||||
|
||||
* @returns Promise<>
|
||||
* @desc 只需改写 API.ajaxRequest 内容
|
||||
*
|
||||
* 固定格式如下
|
||||
* return new Promise ((resolve, reject) => {
|
||||
* ...
|
||||
* resolve({ resolve 返回格式示例如下
|
||||
* url: "",
|
||||
* token: 1,
|
||||
* ...
|
||||
* })
|
||||
* })
|
||||
*/
|
||||
getRecordRtspByFile(recordByFileParam) {
|
||||
return new Promise((resolve, reject) => {
|
||||
API.ajaxRequest({
|
||||
url: '/evo-apigw/admin/API/SS/Playback/StartPlaybackByFile',
|
||||
data: recordByFileParam,
|
||||
}).then(res => {
|
||||
resolve(res.data)
|
||||
}).catch(res => {
|
||||
reject(res)
|
||||
})
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 查询中心录像上的录像文件信息
|
||||
* @param recordsParam.recordType 录像类型:1=一般录像,2=报警录像
|
||||
* @param recordsParam.streamType 码流类型:1=主码流, 2=辅码流 使用辅码流 码率低更加流畅
|
||||
* @param recordsParam.recordSource 录像来源:1=全部,2=设备,3=中心
|
||||
* @param recordsParam.channelId 通道code
|
||||
* @param recordsParam.startTime 开始时间 timestamp到秒
|
||||
* @param recordsParam.endTime 结束时间 timestamp到秒
|
||||
*
|
||||
* @returns Promise<>
|
||||
* @desc 只需改写 API.ajaxRequest 内容
|
||||
*
|
||||
* 固定格式如下
|
||||
* return new Promise ((resolve, reject) => {
|
||||
* ...
|
||||
* resolve({ resolve 返回格式示例如下
|
||||
* url: "",
|
||||
* token: 1,
|
||||
* ...
|
||||
* })
|
||||
* })
|
||||
*/
|
||||
getRecords(recordsParam) {
|
||||
return new Promise((resolve,reject) => {
|
||||
API.ajaxRequest({
|
||||
url: '/evo-apigw/admin/API/SS/Record/QueryRecords',
|
||||
data: recordsParam,
|
||||
}).then(res => {
|
||||
resolve(res.data)
|
||||
}).catch(res => {
|
||||
reject(res)
|
||||
})
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 云台方向控制
|
||||
* @param directionParam
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
setPtzDirection(directionParam) {
|
||||
return new Promise((resolve,reject) => {
|
||||
API.ajaxRequest({
|
||||
url: '/evo-apigw/admin/API/DMS/Ptz/OperateDirect',
|
||||
data: directionParam,
|
||||
}).then(res => {
|
||||
resolve(res.data)
|
||||
}).catch(res => {
|
||||
reject(res)
|
||||
})
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 云台镜头控制
|
||||
* @param cameraParam
|
||||
* @returns {Promise<unknown>}
|
||||
*
|
||||
* @returns Promise<>
|
||||
* @desc 只需改写 API.ajaxRequest 内容
|
||||
*
|
||||
* 固定格式如下
|
||||
* return new Promise ((resolve, reject) => {
|
||||
* ...
|
||||
* resolve({ resolve 返回格式示例如下
|
||||
* url: "",
|
||||
* token: 1,
|
||||
* ...
|
||||
* })
|
||||
* })
|
||||
*/
|
||||
setPtzCamera(cameraParam) {
|
||||
return new Promise((resolve,reject) => {
|
||||
API.ajaxRequest({
|
||||
url: '/evo-apigw/admin/API/DMS/Ptz/OperateCamera',
|
||||
data: cameraParam,
|
||||
}).then(res => {
|
||||
resolve(res.data)
|
||||
}).catch(res => {
|
||||
reject(res)
|
||||
})
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 云台三维定位接口
|
||||
* @param positionParam
|
||||
|
||||
* @returns Promise<>
|
||||
* @desc 只需改写 API.ajaxRequest 内容
|
||||
*
|
||||
* 固定格式如下
|
||||
* return new Promise ((resolve, reject) => {
|
||||
* ...
|
||||
* resolve({ resolve 返回格式示例如下
|
||||
* url: "",
|
||||
* token: 1,
|
||||
* ...
|
||||
* })
|
||||
* })
|
||||
*/
|
||||
controlSitPosition(positionParam) {
|
||||
return new Promise((resolve,reject) => {
|
||||
API.ajaxRequest({
|
||||
url: '/evo-apigw/admin/API/DMS/Ptz/SitPosition',
|
||||
data: positionParam,
|
||||
}).then(res => {
|
||||
resolve(res.data)
|
||||
}).catch(res => {
|
||||
reject(res)
|
||||
})
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 请求
|
||||
* @param {*} opt
|
||||
* @param {*} opt.url 接口地址
|
||||
* @param {*} opt.data 参数
|
||||
|
||||
* 集成后,该方法请删除
|
||||
*/
|
||||
ajaxRequest(opt) {
|
||||
var accessToken = localStorage.getItem('accessToken')
|
||||
var userId = accessToken.split(':')[0]
|
||||
opt.headers = opt.headers ? opt.headers : {}
|
||||
return new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
url: opt.url,
|
||||
type: opt.type ? opt.type : 'post',
|
||||
dataType: opt.dataType ? opt.dataType : 'json',
|
||||
data: JSON.stringify(opt.data),
|
||||
headers: Object.assign({
|
||||
Authorization: "bearer " + accessToken,
|
||||
'User-Id': userId,
|
||||
'Content-Type': 'application/json',
|
||||
proxyip: localStorage['proxyIp']
|
||||
}, opt.headers),
|
||||
contentType: 'application/json;charset=utf-8',
|
||||
crossDomain: true,
|
||||
success: function (res) {
|
||||
if (res.code == 1000 || res.code == '0') {
|
||||
resolve(res)
|
||||
} else if (res.code == "27001007") {
|
||||
// token过期,刷新token
|
||||
// API.refreshToken().then(() => {
|
||||
// // token刷新后,重新请求
|
||||
// API.ajaxRequest(opt).then(data => {
|
||||
// // 重新请求后,需要处理原始请求的resolve
|
||||
// resolve(data)
|
||||
// })
|
||||
// })
|
||||
console.warn("登陆失效,请重新登陆")
|
||||
reject(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
// 刷新token
|
||||
// 集成后,该方法请删除
|
||||
refreshToken() {
|
||||
var refreshToken = localStorage.getItem('refreshToken')
|
||||
var clientId = localStorage.getItem('checked') ? 'web_client_remember' : 'web_client'
|
||||
var value = {
|
||||
'refresh_token': refreshToken,
|
||||
'grant_type': 'refresh_token',
|
||||
'client_id': clientId,
|
||||
"client_secret": 'web_client'
|
||||
}
|
||||
var param = ''
|
||||
for (var i in value) {
|
||||
param += '&' + i + "=" + value[i]
|
||||
}
|
||||
param = param.slice(1)
|
||||
return new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
url: '/evo-apigw/evo-oauth/oauth/token',
|
||||
type: 'post',
|
||||
dataType: 'json',
|
||||
data: param,
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
proxyip: localStorage['proxyIp']
|
||||
},
|
||||
success: function (res) {
|
||||
console.log(res)
|
||||
if (res.code === '0') {
|
||||
localStorage.setItem("accessToken", res.data.access_token)
|
||||
localStorage.setItem("refreshToken", res.data.refresh_token)
|
||||
localStorage.setItem('expiresTime', new Date().getTime() + res.data.expires_in * 1000)
|
||||
// 登录保活
|
||||
API.ajaxRequest({
|
||||
url: `/evo-apigw/evo-brm/1.2.0/user/keepalive`,
|
||||
data: {
|
||||
clientType: 1,
|
||||
magicId: localStorage.magicId,
|
||||
timeout: 10000
|
||||
}
|
||||
})
|
||||
resolve()
|
||||
} else {
|
||||
console.error(`refreshToken 失效,请登录平台`)
|
||||
window.open(location.origin)
|
||||
reject()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
}
|
||||
export default API
|
||||
@@ -77,8 +77,8 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="200">
|
||||
<template #default="scope">
|
||||
<el-button icon="VideoPlay" text type="primary" v-auth="'admin_deviceManage_edit'"
|
||||
@click="formDialogRef.openDialog(scope.row.id)">预览</el-button>
|
||||
<el-button icon="VideoPlay" text type="primary"
|
||||
@click="previewVideoRef.openDialog(scope.row.id)">预览</el-button>
|
||||
<el-button icon="edit-pen" text type="primary" v-auth="'admin_deviceManage_edit'"
|
||||
@click="formDialogRef.openDialog(scope.row.id)">编辑</el-button>
|
||||
<el-button icon="delete" text type="primary" v-auth="'admin_deviceManage_del'"
|
||||
@@ -93,6 +93,7 @@
|
||||
</div>
|
||||
<!-- 编辑、新增 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
<preview-video ref="previewVideoRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -104,10 +105,12 @@ import { useDict } from '/@/hooks/dict';
|
||||
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
const PreviewVideo = defineAsyncComponent(() => import("../components/preview-video/index.vue"));
|
||||
// 定义查询字典
|
||||
const { device_category, is_sync, is_nline, dh_device_manufacturer } = useDict("device_category", "is_sync", "is_nline", "dh_device_manufacturer")
|
||||
// 定义变量内容
|
||||
const formDialogRef = ref()
|
||||
const previewVideoRef = ref()
|
||||
// 搜索变量
|
||||
const queryRef = ref()
|
||||
const showSearch = ref(true)
|
||||
|
||||
Reference in new issue
Block a user