问题描述
之前已经把监控的画面实时在页面上显示出来了,但是发现有一个问题,因为我要做一个类似于点击某一个通道单独切换到这个通道的资源,因此我需要在开启这个通道之前,切断之前的通道,否则在没有使用资源的时候,也会占用端口,会造成不必要的内存使用以及后续通道无法正常开启(默认服务器只开启6条通道,需在后台配置)。
然后,出现的问题是,每次利用flvPlayer实例上的destroy方法时,发现只有最后一条通道被关闭了。排查后是因为实例丢失的原因,因为我将整个flvPlayer的实例都定义在data模块中,因此通过遍历后,每次最新的都会覆盖上一次的,因此实质上我只是在对最后一次的flvPlayer进行操作。
解决思路:
将这个flvPlayer放在数据列表中,然后循环时将这个实例当作参数传入该函数,即可实现效果,
代码:
<template>
<div class="video-container">
<div class="tree-select-container">
<h2 class="title">网络视频监控系统</h2>
<a-divider />
<a-tree
class="draggable-tree"
:defaultSelectedKeys="selectedKeys"
:default-expanded-keys="expandedKeys"
draggable
:tree-data="gData"
@dragenter="onDragEnter"
@drop="onDrop"
show-icon
@select="onSelect"
>
<a-icon slot="all" type="appstore"
/></a-tree>
</div>
<div class="video-page-container flex-column">
<div class="main-body">
<div class="carousel-container">
<a-carousel
dot-position="left"
v-show="isFullShow"
class="carousel-outside-box"
>
<div class="carousel">
<video
id="videoElement"
ref="videoElement"
muted
poster="/img/loading/loading-2.gif"
autoplay
></video>
</div>
</a-carousel>
<div
v-show="!isFullShow && !aloneVideoShow"
class="split-screen-container"
>
<!-- 分区显示(默认显示6块,做懒加载) -->
<video
v-for="(item, index) in videoName"
:id="item.key"
:key="item.key"
class="video-split-screen"
type="video/mp4"
poster="/img/loading/loading-1.gif"
muted
autoplay
></video>
</div>
<div
v-show="!isFullShow && aloneVideoShow"
style="background-color: #000; height: 100%"
class="flex-center-h-v"
>
<video
poster="/img/loading/loading-2.gif"
muted
autoplay
id="videoElement"
ref="videoElementSplit"
></video>
</div>
</div>
</div>
<a-collapse v-model="activeKey" class="log-report">
<a-collapse-panel key="1" header="日志监控">
<a-alert
v-for="(item, index) in alertInfoList"
:key="item.key"
:message="item.message"
:type="item.type"
class="alert"
/>
</a-collapse-panel>
</a-collapse>
</div>
</div>
</template>
<script>
// 模拟数据
import { GetCurrentTime } from "@/assets/js/utils/time";
const gData = [
{
title: "查看所有监控",
key: "all",
scopedSlots: { icon: "all" },
},
{
title: "工位一",
key: "工位一",
children: [
{
title: "实验区域一",
key: "technology1",
},
{
title: "实验区域二",
key: "technology",
},
],
},
{
title: "工位二",
key: "工位二",
children: [
{
title: "实验区域一",
key: "technology3",
},
{
title: "实验区域二",
key: "technology5",
},
{
title: "实验区域三",
key: "technology6",
},
{
title: "实验区域四",
key: "technology4",
},
],
},
{
title: "工位三",
key: "工位三",
children: [
{
title: "实验区域一",
key: "technology7",
},
{
title: "实验区域二",
key: "technology8",
},
{
title: "实验区域三",
key: "technology9",
},
{
title: "实验区域四",
key: "technology10",
},
],
},
];
export default {
data() {
return {
activeKey: "1",
gData,
expandedKeys: ["工位一", "工位二", "工位三"],
selectedKeys: ["all"],
currentTreeSelectKey: "technology1",
currentTime: "",
// 是否展示全屏
isFullShow: false,
videoName: [
{ key: "technology", value: "technology", flvPlayer: null },
{ key: "technology1", value: "technology1", flvPlayer: null },
{ key: "technology15", value: "technology15", flvPlayer: null },
{ key: "technology3", value: "technology3", flvPlayer: null },
{ key: "technology11", value: "technology11", flvPlayer: null },
{ key: "technology13", value: "technology13", flvPlayer: null },
],
// 单屏显示的flv信息列表
currentVideoList: {},
alertInfoList: [
{
key: 0,
message: "提示信息:当前工位有员工进行走动",
type: "warning",
},
{
key: 1,
message: "提示信息:当前工位有员工进行走动",
type: "warning",
},
{
key: 2,
message: "提示信息:当前工位有员工进行走动",
type: "warning",
},
{
key: 3,
message: "提示信息:当前工位有员工进行走动",
type: "warning",
},
{
key: 4,
message: "提示信息:当前工位有员工进行走动",
type: "warning",
},
],
// 是否销毁监控请求
isDestroyVideo: false,
aloneVideoShow: false,
};
},
mounted() {
this.init();
setTimeout(() => {
this.activeKey = null;
}, 3000);
this.videoDbClick();
},
// 每次离开之前关闭资源请求通道
beforeRouteLeave(from, to, next) {
this.isDestroyVideo = true;
next();
},
watch: {
isFullShow: {
handler(n) {
if (n) {
// 全屏
const treeELe = document.querySelector(".tree-select-container");
const logELe = document.querySelector(".log-report");
treeELe.classList.add("miss");
const videoElement = this.$refs["videoElement"];
// this.currentVideoList = {
// key: this.currentTreeSelectKey,
// value: this.currentTreeSelectKey,
// flvPlayer: null,
// };
this.loadVideoSource(this.currentVideoList, videoElement);
// logELe.classList.add("miss");
// this.loadVideoSource("technology5");
}
},
},
},
methods: {
init() {
this.videoName.forEach((item, index) => {
if (index < 6)
var videoElement = document.querySelector(`#${item.key}`);
this.loadVideoSource(item, videoElement);
});
},
// 点击拖动时触发的函数
onDragEnter(info) {
console.log(info);
},
// 松开时触发的函数
onDrop(info) {
console.log(info);
const dropKey = info.node.eventKey;
const dragKey = info.dragNode.eventKey;
const dropPos = info.node.pos.split("-");
const dropPosition =
info.dropPosition - Number(dropPos[dropPos.length - 1]);
const loop = (data, key, callback) => {
data.forEach((item, index, arr) => {
if (item.key === key) {
return callback(item, index, arr);
}
if (item.children) {
return loop(item.children, key, callback);
}
});
};
const data = [...this.gData];
// Find dragObject
let dragObj;
loop(data, dragKey, (item, index, arr) => {
arr.splice(index, 1);
dragObj = item;
});
if (!info.dropToGap) {
// Drop on the content
loop(data, dropKey, (item) => {
item.children = item.children || [];
// where to insert 示例添加到尾部,可以是随意位置
item.children.push(dragObj);
});
} else if (
(info.node.children || []).length > 0 && // Has children
info.node.expanded && // Is expanded
dropPosition === 1 // On the bottom gap
) {
loop(data, dropKey, (item) => {
item.children = item.children || [];
// where to insert 示例添加到尾部,可以是随意位置
item.children.unshift(dragObj);
});
} else {
let ar;
let i;
loop(data, dropKey, (item, index, arr) => {
ar = arr;
i = index;
});
if (dropPosition === -1) {
ar.splice(i, 0, dragObj);
} else {
ar.splice(i + 1, 0, dragObj);
}
}
this.gData = data;
},
// 点击树节点时触发
onSelect(selectedKeys, e) {
console.log(selectedKeys[0], "selectedKeys");
if (selectedKeys[0] && selectedKeys[0] === "all") {
this.init();
this.isFullShow = false;
this.aloneVideoShow = false;
}
if (selectedKeys[0] && selectedKeys[0] !== "all") {
this.isFullShow = false;
this.aloneVideoShow = true;
// 只要点击单独的就清空所有的通道
this.videoName.forEach((item) => (item.flvPlayer._isDestroy = true));
// 将当前的状态清空
if (this.currentVideoList.flvPlayer) {
this.destoryVideo(this.currentVideoList.flvPlayer);
}
this.currentTreeSelectKey = selectedKeys[0];
const videoElement = this.$refs["videoElementSplit"];
this.currentVideoList = {
key: this.currentTreeSelectKey,
value: this.currentTreeSelectKey,
flvPlayer: null,
};
this.loadVideoSource(this.currentVideoList, videoElement, 7999);
}
},
// 加日志(模拟)
addDate() {
setInterval(() => {
return () => {
const Time = GetCurrentTime();
console.log("触发了" + Time.hms);
this.currentTime = `${Time.YMD} ${Time.hms} 警告触发:${currentTreeSelectKey}`;
};
}, 1000);
},
// 读取监控资源
loadVideoSource(source, element, port = 8000) {
if (flvjs.isSupported()) {
source.flvPlayer = flvjs.createPlayer({
type: "flv",
url: `http://192.168.18.148:${port}/live/${source.key}.flv`, //你的地址
autoCleanupSourceBuffer: true, //自动清除缓存
});
source.flvPlayer.attachMediaElement(element);
source.flvPlayer.load();
// flvPlayer.play();
}
// 【重要事件监听】http请求建立好后,该事件会一直监听flvjs实例
// 注意,对于flv.js状态的更改只能在函数内部,因为外部循环的话,所访问到的flv实例只是最后一次的实例
source.flvPlayer.on(flvjs.Events.STATISTICS_INFO, (res) => {
try {
// console.log("请求数据信息", res);
if (this.isDestroyVideo || source.flvPlayer._isDestroy) {
// 离开路由或切换设备
console.warn("销毁实例");
// 销毁实例
this.destoryVideo(source.flvPlayer);
}
} catch (error) {
console.log(error);
}
});
},
destoryVideo(flvPlayer) {
flvPlayer.pause();
flvPlayer.unload();
flvPlayer.detachMediaElement();
flvPlayer.destroy();
flvPlayer = null;
},
videoDbClick() {
const videos = document.querySelectorAll("video"); //获取所有video标签
videos.forEach((video) => {
// 阻止单击暂停
video.addEventListener("click", (event) => {
event.preventDefault();
});
// 添加双击全屏放大功能
video.addEventListener("dblclick", () => {
if (!document.fullscreenElement) {
if (video.requestFullscreen) {
video.requestFullscreen();
} else if (video.mozRequestFullScreen) {
// 兼容火狐浏览器
video.mozRequestFullScreen();
} else if (video.webkitRequestFullscreen) {
// 兼容谷歌浏览器和Safari
video.webkitRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
// 兼容火狐浏览器
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
// 兼容谷歌浏览器和Safari
document.webkitExitFullscreen();
}
}
});
});
},
},
};
</script>
<style lang="less" scoped>
.miss {
display: none;
}
.active {
border: 1px solid red;
}
.video-container {
display: flex;
.tree-select-container {
flex-shrink: 0;
padding-top: 0.2rem;
border: 1px solid @primary-border-color-lv3;
width: 15%;
padding-left: 0.4rem;
.title {
display: flex;
color: @primary-color;
}
}
.video-page-container {
flex-grow: 1;
border: 1px solid @primary-border-color-lv1;
overflow-y: auto;
height: 100vh;
.main-body {
width: 100%;
overflow-y: auto;
border: 1px solid @primary-border-color-lv3;
flex-grow: 1;
.carousel-container {
height: 100%;
.carousel {
background-color: #000;
padding: 1rem;
}
.split-screen-container {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
overflow-y: auto;
.video-split-screen {
border: 1px solid #fff;
width: 33%;
height: 50%;
background-color: #000;
}
}
}
}
.log-report {
padding: 0.125rem;
max-height: 20%;
border: 1px solid @primary-border-color-lv3;
overflow: auto;
.alert {
margin-top: 0.1rem;
}
}
}
}
</style>
注意这只是一个demo,关于将监控的资源发布到服务器还需要后端的配合。