|
|
|
<template>
|
|
|
|
|
|
|
|
<div id="videoWrapper">
|
|
|
|
<!-- <video ref="videoPlayer" id="videoPlayer" disablePictureInPicture autoplay muted
|
|
|
|
@loadedmetadata="changeVideoCanvasSize" @mouseover="mouseOverVideo" @mouseout="mouseOutVideo"
|
|
|
|
@mousedown="mouseDownVideo">
|
|
|
|
</video> -->
|
|
|
|
<div id="videoPlayer" @mousedown="mouseDownVideo"></div>
|
|
|
|
<div v-for="item in labelList" :class="`labels ${item.groupName == '视频标签' ? 'lavels-video' : 'lavels-build'}`"
|
|
|
|
:key="item.id" :id="item.id"
|
|
|
|
:style="`top:${canvasHeight * item.canvasTopRatio}px;left:${canvasWidth * item.canvasLeftRatio}px`">
|
|
|
|
<a-dropdown :trigger="['contextmenu']">
|
|
|
|
<div class="labels-item" v-if="item.groupName == '视频标签'">
|
|
|
|
<!-- <div class="labels-item" v-if="item.inFlag"> -->
|
|
|
|
<div>
|
|
|
|
<img src="@/assets/images/dialog.png" alt="">
|
|
|
|
<div class="label-content">{{ item.name }}</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="labels-item" alt="" v-else>
|
|
|
|
<img src="@/assets/images/buildLabelLine.png">
|
|
|
|
<div>
|
|
|
|
<img src="@/assets/images/buildLabelBg.png">
|
|
|
|
<div class="label-content">{{ item.name }}</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<template #overlay>
|
|
|
|
<a-menu @click="({ key: menuKey }) => onContextMenuClick(item, menuKey)">
|
|
|
|
<a-menu-item key="edit">编辑</a-menu-item>
|
|
|
|
<a-menu-item key="delete">删除</a-menu-item>
|
|
|
|
</a-menu>
|
|
|
|
</template>
|
|
|
|
</a-dropdown>
|
|
|
|
</div>
|
|
|
|
<div class="labels lavels-build" v-if="addLabel.isAddLabel && addLabel.labelType == '建筑标签'"
|
|
|
|
:style="` pointer-events: none;top:${addLabelTop}px;left:${addLabelLeft}px`">
|
|
|
|
|
|
|
|
<div class="labels-item" alt="">
|
|
|
|
<img src="@/assets/images/buildLabelLine.png">
|
|
|
|
<div>
|
|
|
|
<img src="@/assets/images/buildLabelBg.png">
|
|
|
|
<div class="label-content">{{ addLabel.labelType }}</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="labels lavels-video" v-if="addLabel.isAddLabel && addLabel.labelType == '视频标签'"
|
|
|
|
:style="`top:${addLabelTop}px;left:${addLabelLeft}px`">
|
|
|
|
<div class="labels-item">
|
|
|
|
<!-- <div class="labels-item" v-if="item.inFlag"> -->
|
|
|
|
<div>
|
|
|
|
<img src="@/assets/images/dialog.png" alt="">
|
|
|
|
<div class="label-content">{{ addLabel.labelType }}</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<CameraLeftMenu></CameraLeftMenu>
|
|
|
|
<CameraRightMenu></CameraRightMenu>
|
|
|
|
<!-- 云台控制 -->
|
|
|
|
<VideoControlSimple></VideoControlSimple>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang='ts'>
|
|
|
|
////@ts-nocheck
|
|
|
|
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
|
|
|
import { Modal } from 'ant-design-vue';
|
|
|
|
import { ref, h, onMounted, getCurrentInstance, ComponentInternalInstance, onUnmounted, watch, createVNode } from "vue";
|
|
|
|
import * as markLabelApi from '@/axios/cameraMark/markLabelApi';
|
|
|
|
import * as markSearchApi from '@/axios/core/markSearchApi';
|
|
|
|
import { useStore } from '@/store/index';
|
|
|
|
import { apiUrl } from "@/axios";
|
|
|
|
import { storeToRefs } from 'pinia';
|
|
|
|
import Msg from "@/utils/message";
|
|
|
|
import CameraLeftMenu from '@/views/page/Aside/cameraLeftMenu.vue'
|
|
|
|
import CameraRightMenu from '@/views/page/Aside/cameraRightMenu.vue'
|
|
|
|
import LabelEditModel from '@/views/page/Model/LabelEditModel.vue'
|
|
|
|
import popup from "@/utils/popup";
|
|
|
|
import VideoControlSimple from './VideoControl/VideoControlSimple.vue';
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
|
let piniaStore = useStore();
|
|
|
|
let player = <HTMLVideoElement>document.querySelector('#videoPlayer')
|
|
|
|
let canvasWidth = ref(0)
|
|
|
|
let canvasHeight = ref(0)
|
|
|
|
let curSelectKey = storeToRefs(piniaStore).curSelectKey
|
|
|
|
let closeVideoKey = storeToRefs(piniaStore).closeVideoKey
|
|
|
|
let cameraMap = storeToRefs(piniaStore).cameraMap
|
|
|
|
let isActiveChoose = ref(false)
|
|
|
|
let labelList = ref<any[]>([])
|
|
|
|
let addLabel = storeToRefs(piniaStore).addLabel
|
|
|
|
let addLabelLeft = ref(0);
|
|
|
|
let addLabelTop = ref(0);
|
|
|
|
var jessibuca = ref();
|
|
|
|
const curVideoKey = ref()
|
|
|
|
onMounted(() => {
|
|
|
|
{
|
|
|
|
player = <HTMLVideoElement>document.querySelector('#videoPlayer');
|
|
|
|
loadVideoPlayer();
|
|
|
|
loadVideoCanvas();
|
|
|
|
changeVideoCanvasSize();
|
|
|
|
create()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
watch(curSelectKey, (newVal, oldVal) => {
|
|
|
|
switchCamera(newVal)
|
|
|
|
})
|
|
|
|
watch(closeVideoKey, (newVal, oldVal) => {
|
|
|
|
if (curVideoKey.value == newVal) {
|
|
|
|
destroyVideo()
|
|
|
|
piniaStore.updateCloseVideoKey("")
|
|
|
|
piniaStore.updateCurSelectKey("")
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
watch(addLabel, (newVal, oldVal) => {
|
|
|
|
if (newVal.isAddLabel) {
|
|
|
|
isActiveChoose.value = true
|
|
|
|
document.body.onmousemove = (e) => {
|
|
|
|
addLabelTop.value = e.clientY
|
|
|
|
addLabelLeft.value = e.clientX
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
function destroyVideo() {
|
|
|
|
jessibuca.value.destroy();
|
|
|
|
create()
|
|
|
|
labelList.value = []
|
|
|
|
}
|
|
|
|
//创建流媒体
|
|
|
|
function create() {
|
|
|
|
var showOperateBtns = false; // 是否显示按钮
|
|
|
|
var forceNoOffscreen = true; //
|
|
|
|
|
|
|
|
jessibuca.value = new Jessibuca({
|
|
|
|
container: player,
|
|
|
|
videoBuffer: 1, // 缓存时长
|
|
|
|
isResize: false,
|
|
|
|
text: "",
|
|
|
|
loadingText: "",
|
|
|
|
useMSE: false,
|
|
|
|
debug: false,
|
|
|
|
showBandwidth: showOperateBtns, // 显示网速
|
|
|
|
operateBtns: {
|
|
|
|
fullscreen: showOperateBtns,
|
|
|
|
screenshot: showOperateBtns,
|
|
|
|
play: showOperateBtns,
|
|
|
|
audio: false,
|
|
|
|
recorder: false
|
|
|
|
},
|
|
|
|
forceNoOffscreen: forceNoOffscreen,
|
|
|
|
isNotMute: false,
|
|
|
|
},);
|
|
|
|
|
|
|
|
jessibuca.value.onLog = (msg: any) => console.error(msg);
|
|
|
|
jessibuca.value.onRecord = (status: any) => console.log('onRecord', status);
|
|
|
|
jessibuca.value.onPause = () => console.log('onPause');
|
|
|
|
jessibuca.value.onPlay = () => console.log('onPlay');
|
|
|
|
jessibuca.value.onFullscreen = (msg: any) => console.log('onFullscreen', msg);
|
|
|
|
jessibuca.value.onMute = (msg: any) => console.log('onMute', msg);
|
|
|
|
|
|
|
|
}
|
|
|
|
//删除标签
|
|
|
|
function deleteLabel(id: number) {
|
|
|
|
|
|
|
|
markLabelApi.Delete({ id }).then((res: any) => {
|
|
|
|
console.log(res.data);
|
|
|
|
if (res.data.code == 200) {
|
|
|
|
let msg = res.data.data == true ? "删除成功" : "删除失败"
|
|
|
|
if (res.data.data) {
|
|
|
|
labelList.value = labelList.value.filter((element: any) => {
|
|
|
|
return element.id != id
|
|
|
|
});
|
|
|
|
Msg.success("删除成功")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Msg.error("删除失败")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function loadVideoPlayer() {
|
|
|
|
let elmId = 'videoPlayer';
|
|
|
|
let url = apiUrl.WebRtcUrl;
|
|
|
|
// webRtcServer.value = new WebRtcStreamer(elmId, url);
|
|
|
|
}
|
|
|
|
function loadVideoCanvas() {
|
|
|
|
window.addEventListener('resize', () => {
|
|
|
|
changeVideoCanvasSize();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
const onContextMenuClick = (item: any, menuKey: string,) => {
|
|
|
|
console.log(`labelId: ${item.id}, menuKey: ${menuKey}`);
|
|
|
|
// console.log(`item: ${item}`);
|
|
|
|
// console.log(item);
|
|
|
|
//防止添加多个key值,再次点击不能关闭。数组去重
|
|
|
|
if (menuKey == "edit") {
|
|
|
|
popup("编辑标签", [h(LabelEditModel, { modelData: item })], "editLabel");
|
|
|
|
} else if (menuKey == "delete") {
|
|
|
|
Modal.confirm({
|
|
|
|
zIndex: 10000,
|
|
|
|
title: () => "警示",
|
|
|
|
icon: () => createVNode(ExclamationCircleOutlined),
|
|
|
|
content: () =>
|
|
|
|
h(
|
|
|
|
"span",
|
|
|
|
{ style: { color: "black" } },
|
|
|
|
"是否确认删除该标签?"
|
|
|
|
),
|
|
|
|
okText:
|
|
|
|
// () => h('span', { style: { color: 'black' } }, '确定'),
|
|
|
|
"确定",
|
|
|
|
// okType: 'primary',
|
|
|
|
cancelText: () => "取消",
|
|
|
|
cancelButtonProps: {
|
|
|
|
type: "default",
|
|
|
|
danger: true,
|
|
|
|
},
|
|
|
|
okButtonProps: {
|
|
|
|
type: "primary",
|
|
|
|
danger: true,
|
|
|
|
},
|
|
|
|
onOk() {
|
|
|
|
deleteLabel(parseInt(item.id))
|
|
|
|
|
|
|
|
},
|
|
|
|
style: "top: 250px",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
function changeVideoCanvasSize() {
|
|
|
|
canvasWidth.value = player.clientWidth;
|
|
|
|
canvasHeight.value = player.clientHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
function mouseDownVideo(e: MouseEvent) {
|
|
|
|
|
|
|
|
if (!isActiveChoose.value) return;
|
|
|
|
//鼠标左键 e.buttons == 1
|
|
|
|
if (e.buttons == 1) {
|
|
|
|
let cameraId = cameraMap.value.get(curSelectKey.value).id;
|
|
|
|
let name = addLabel.value.labelType;
|
|
|
|
let videoWidth = player.videoWidth | 1920;
|
|
|
|
let videoHeight = player.videoHeight | 1080;
|
|
|
|
let canvasWidth = player.clientWidth;
|
|
|
|
let canvasHeight = player.clientHeight;
|
|
|
|
let canvasLeft = addLabelLeft.value;
|
|
|
|
let canvasTop = addLabelTop.value;
|
|
|
|
let canvasLeftRatio = canvasLeft / canvasWidth;
|
|
|
|
let canvasTopRatio = canvasTop / canvasHeight;
|
|
|
|
let entity = {
|
|
|
|
CbCameraId: cameraId,
|
|
|
|
Name: name,
|
|
|
|
VideoWidth: videoWidth,
|
|
|
|
VideoHeight: videoHeight,
|
|
|
|
CanvasLeftRatio: canvasLeftRatio,
|
|
|
|
CanvasTopRatio: canvasTopRatio,
|
|
|
|
CmMarkGroupId: addLabel.value.cmMarkGroupId
|
|
|
|
}
|
|
|
|
markLabelApi.AddReturnId({
|
|
|
|
'entity': entity
|
|
|
|
}).then((res: any) => {
|
|
|
|
let markLabelId: number = res.data.data;
|
|
|
|
console.log(markLabelId);
|
|
|
|
|
|
|
|
if (markLabelId <= 0) return;
|
|
|
|
markSearchApi.ActivateMarkLabel({
|
|
|
|
'cameraId': cameraId,
|
|
|
|
'markLabelId': markLabelId
|
|
|
|
}).then((res: any) => {
|
|
|
|
|
|
|
|
let ret: boolean = res.data.data;
|
|
|
|
console.log(ret, 'ret');
|
|
|
|
if (ret) {
|
|
|
|
let obj = {
|
|
|
|
cbCameraId: cameraId,
|
|
|
|
videoWidth: videoWidth,
|
|
|
|
videoHeight: videoHeight,
|
|
|
|
canvasLeftRatio: canvasLeftRatio,
|
|
|
|
canvasTopRatio: canvasTopRatio,
|
|
|
|
id: markLabelId,
|
|
|
|
isDelete: false,
|
|
|
|
inFlag: true,
|
|
|
|
name: name,
|
|
|
|
groupName: name
|
|
|
|
}
|
|
|
|
labelList.value.push(obj)
|
|
|
|
isActiveChoose.value = false
|
|
|
|
piniaStore.updateIsAddLabel({ cmMarkGroupId: 0, labelType: "", isAddLabel: false })
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
//右键取消
|
|
|
|
isActiveChoose.value = false
|
|
|
|
piniaStore.updateIsAddLabel({ cmMarkGroupId: 0, labelType: "", isAddLabel: false })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function switchCamera(cameraId: string) {
|
|
|
|
console.log('camera switch.', cameraId, typeof cameraId);
|
|
|
|
|
|
|
|
// step1, get camera obj.
|
|
|
|
console.log('get camera obj.', cameraMap.value);
|
|
|
|
let cameraObj = cameraMap.value.get(cameraId);
|
|
|
|
console.log(cameraObj, 'cameraObj');
|
|
|
|
|
|
|
|
if (!cameraObj) {
|
|
|
|
console.log('camera obj not found.');
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
destroyVideo()
|
|
|
|
console.log('connect webrtc-steamer.');
|
|
|
|
jessibuca.value.play("ws://192.168.1.117:8080/jessica/live/test.flv")
|
|
|
|
curVideoKey.value = cameraId
|
|
|
|
// step4, active camera searcher.
|
|
|
|
markSearchApi.IsExistsSearcher({
|
|
|
|
'cameraId': cameraObj.id
|
|
|
|
}).then((res: any) => {
|
|
|
|
let flag: boolean = res.data.data;
|
|
|
|
if (!flag) {
|
|
|
|
console.log('not exist searcher.');
|
|
|
|
markSearchApi.ActiveSearcher({
|
|
|
|
'cameraId': cameraObj.id
|
|
|
|
}).then((res: any) => {
|
|
|
|
let flag: boolean = res.data.data;
|
|
|
|
console.log('activate searcher : ', flag);
|
|
|
|
if (flag) {
|
|
|
|
console.log('load camera labels');
|
|
|
|
getLabel(cameraObj.id)
|
|
|
|
loadMarkLabelsByLoop(cameraObj);
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
console.log('load camera labels');
|
|
|
|
getLabel(cameraObj.id)
|
|
|
|
loadMarkLabelsByLoop(cameraObj);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// step5, load camera labels.
|
|
|
|
}
|
|
|
|
function getLabel(cbCameraId: string | number) {
|
|
|
|
let str = JSON.stringify({ cbCameraId })
|
|
|
|
markLabelApi.GetList({ queryJson: str }).then((res: any) => {
|
|
|
|
if (res.data.code == 200) {
|
|
|
|
console.log(res, 'res');
|
|
|
|
labelList.value = res.data.data
|
|
|
|
labelList.value = labelList.value.map((item: any) => {
|
|
|
|
let labelGroupList = piniaStore.labelGroupList
|
|
|
|
labelGroupList.forEach((element: any) => {
|
|
|
|
if (element.id == item.cmMarkGroupId) {
|
|
|
|
item.groupName = element.name
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return item
|
|
|
|
})
|
|
|
|
console.log(labelList.value, 'labelList.value');
|
|
|
|
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
function loadMarkLabelsByLoop(cameraObj: any, ms: number = 1000) {
|
|
|
|
if (curSelectKey.value !== cameraObj.id) {
|
|
|
|
console.log('load camera labels end.');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
loadMarkLabels(cameraObj);
|
|
|
|
let timer = setTimeout(() => {
|
|
|
|
loadMarkLabelsByLoop(cameraObj, ms);
|
|
|
|
clearTimeout(timer)
|
|
|
|
}, ms);
|
|
|
|
}
|
|
|
|
function loadMarkLabels(cameraObj: any) {
|
|
|
|
markSearchApi.GetMarkLabelCalcResultList({
|
|
|
|
'cameraId': cameraObj.id
|
|
|
|
}).then((res: any) => {
|
|
|
|
let list: Array<any> = res.data.data;
|
|
|
|
console.log(list);
|
|
|
|
|
|
|
|
list.forEach((element: any) => {
|
|
|
|
labelList.value.forEach((item: any, index: number) => {
|
|
|
|
if (element.id == item.id) {
|
|
|
|
labelList.value[index].canvasLeftRatio = element.canvasLeftRatio
|
|
|
|
labelList.value[index].canvasTopRatio = element.canvasTopRatio
|
|
|
|
labelList.value[index].inFlag = element.inFlag
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
//刷新页面
|
|
|
|
window.onbeforeunload = () => {
|
|
|
|
jessibuca.value.destroy()
|
|
|
|
};
|
|
|
|
onUnmounted(() => {
|
|
|
|
{
|
|
|
|
jessibuca.value.destroy()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped lang='less'>
|
|
|
|
#videoWrapper {
|
|
|
|
height: 100vh;
|
|
|
|
position: relative;
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
|
|
|
|
|
|
#videoPlayer {
|
|
|
|
height: 100%;
|
|
|
|
width: 100%;
|
|
|
|
object-fit: fill;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
.labels {
|
|
|
|
position: absolute;
|
|
|
|
top: 0;
|
|
|
|
left: 0;
|
|
|
|
z-index: 2;
|
|
|
|
|
|
|
|
.labels-item {
|
|
|
|
position: relative;
|
|
|
|
cursor: pointer;
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
>div {
|
|
|
|
position: relative;
|
|
|
|
}
|
|
|
|
|
|
|
|
.label-content {
|
|
|
|
position: absolute;
|
|
|
|
top: 50%;
|
|
|
|
left: 50%;
|
|
|
|
transform: translate(-50%, -55%);
|
|
|
|
}
|
|
|
|
|
|
|
|
img {
|
|
|
|
width: 120px;
|
|
|
|
height: 50px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.lavels-video {
|
|
|
|
position: fixed;
|
|
|
|
transform: translate(-50%, -100%);
|
|
|
|
}
|
|
|
|
|
|
|
|
.lavels-build {
|
|
|
|
|
|
|
|
transform: translate(-10px, -10px);
|
|
|
|
position: fixed;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
:deep(.ant-dropdown-menu-title-content) {
|
|
|
|
color: #000 !important;
|
|
|
|
}
|
|
|
|
</style>
|