国标GB28181流媒体服务器支持多分屏操作
最近感觉使用国标协议的开发者有很多,我们国标GB28181流媒体服务器提供流转发服务,负责将GB28181设备/平台推送的PS流转成ES流,然后提供RTSP、RTMP、FLV、HLS多种格式进行分发,实现web浏览器、手机浏览器、微信等各种终端无插件播放。
之前有开发者说我们国标GB28181的流媒体服务器需要查看视频的时候,只能单独查看一路视频,想同时查看多路视频的时候,就不是很方便操作。
原本的界面如下:
我们的研发人员也对这个多分屏的问题进行了研究,在前几天实现了国标GB28181流媒体服务器的多分屏操作(我由衷觉得我的研发部同事们都很厉害啊!),界面如下图:
以下是实现代码,大家可以参考一下:
<template>
<div>
<div class="screen-main">
<el-row>
<el-col :xs="24" :sm="24" :md="24" :lg="4" :xl="4">
<div class="screen-tree" @scroll="scrollEvent">
<span class="screen-tree-title">设备列表</span>
<el-tree
v-loading="loading"
:data="terrData"
node-key="ID"
default-expand-all
:props="{children: 'Children', label: 'Name'}"
>
<span slot-scope="{ node, data }" class="custom-tree-node">
<div :class="['custom-tree-node', {'private-info': !data.Online&&data.Type===1}, {'private-info': data.ChannelCount===0} ]" :title="data.ID" @click="onSelection(data)">
<i :class="['fa', {'private-success': data.Online&&data.Type===1}, { 'fa-video-camera': data.Type===2},{'fa-desktop':data.Type===1} ]" />
{{ data.Name?data.Name:data.ID }}
</div>
</span>
</el-tree>
</div>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="16" :xl="16">
<div style="padding-top: 50px;">
<el-row class="screen-main-player" v-if="isShow">
<el-col :xs="24" :sm="colNum[3]" :md="colNum[2]" :lg="colNum[1]" :xl="colNum[0]" v-for="(item, index) in PlayerData" :key="index" :class="[{'active-shadow': indexType===index}]" >
<div class="screen-main-item" @click.stop="indexType=index">
<SereenPlayer :DeviceID="item.DeviceID" :ID="item.ID" :Index="index" @index="onIndex" :Radio="radio"/>
</div>
</el-col>
</el-row>
</div>
<div class="screen-radio">
<el-radio-group v-model="radio" size="small" @change="getList()">
<el-radio-button :label="item.type" v-for="(item, index) in radioData" :key="index">{{item.name}}</el-radio-button>
<el-radio-button :label="radio"><i class="fa fa-arrows-alt" @click.prevent="fullscreen"></i></el-radio-button>
</el-radio-group>
</div>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import SereenPlayer from './SereenPlayer'
export default {
components: { SereenPlayer },
data() {
return {
indexType: 0,
terrData: [],
isShow: true,
loading: true,
queryDev: undefined,
DeviceCount: 0,
radio: 4,
PlayerData: [],
PlayerDataName: '',
radioData: [
{ type: 1, name: '单屏' },
{ type: 4, name: '四分屏' },
{ type: 9, name: '九分屏' },
{ type: 16, name: '十六分屏' },
{ type: 36, name: '三十六分屏' },
{ type: 64, name: '六十四分屏' },
]
}
},
computed: {
colNum(){
if (this.radio === 1) {
return [24,24,24,24]
} else if (this.radio === 4) {
return [12,12,12,12]
}else if (this.radio === 9) {
return [8,8,12,12]
}else if (this.radio === 16) {
return [6,6,12,12]
}else if (this.radio === 36) {
return [4,4,12,12]
}else if (this.radio === 64) {
return [3,3,12,12]
}
}
},
created() {
this.getDeviceList()
},
mounted() {
this.getList()
},
methods: {
scrollEvent(e){
if(e.currentTarget.scrollTop+e.currentTarget.clientHeight>=e.currentTarget.scrollHeight){
console.log('t!');
}
},
fullscreen() {
this.$fullscreen.enter(this.$el.querySelector(`.screen-main-player`), {
wrap: false
})
},
onIndex(index) {
this.PlayerData[index].DeviceID = ''
this.PlayerData[index].ID = ''
},
getList() {
this.isShow = false
this.indexType = 0
this.PlayerData = []
for (let index = 0; index < this.radio; index++) {
this.PlayerData.push({
ID: '',
DeviceID: ''
})
}
this.isShow = true
},
onSelection(data) {
if (data.Type===1&&data.Online===false) {
this.$message({
message: '设备已离线!',
type: 'warning'
})
return
}
if (data.Type===2) {
if (this.indexType<this.radio) {
this.PlayerData[this.indexType].DeviceID = data.DeviceID
this.PlayerData[this.indexType].ID = data.ID
if ((this.indexType+1)<this.radio) {
this.indexType++
}
}
return
} else {
if (!data.Online) {
return
}
if (data.Children.length!==0) return
this.getChannels(data.ID, data)
}
},
getDeviceList() {
this.loading = true;
let _this = this;
$.get("/api/v1/device/list", {
q: this.queryDev
})
.then(res => {
_this.DeviceCount = res.DeviceCount
res.DeviceList.forEach(item => {
_this.terrData.push({
Name:item.Name,
ChannelCount:item.ChannelCount,
ID:item.ID,
Online:item.Online,
Children: [],
Type: 1,
Loading: false
})
})
})
.always(() => {
this.loading = false;
})
},
getChannels(ID, data) {
this.loading = true;
let _this = this
$.get("/api/v1/device/info", {
serial: ID
})
.then(res => {
if(res.ChannelCount == 0) {
_this.$message({
message: '设备下无通道!',
type: 'warning'
})
}else {
res.ChannelList.forEach(item => {
data.Children .push({
Name:item.Name,
DeviceID:item.DeviceID,
SnapURL:item.SnapURL,
ID:item.ID,
Online: true,
Children: [],
Type: 2,
Loading: false
})
})
}
this.loading = false;
})
.always(() => {
this.loading = false;
})
}
}
}
</script>
<style lang="less" scoped>
.screen-radio {
padding: 20px 15px;
padding-left: 0;
padding-bottom: 5px;
}
.screen-tree {
padding-right: 30px;
padding-top: 40px;
min-height: e("calc(100vh - 131px)");
max-height: e("calc(100vh - 131px)");
overflow:auto;
.el-tree {
background: transparent;
}
.screen-tree-title {
display: block;
font-size: 16px;
padding-bottom: 5px;
padding-left: 22px;
}
}
.screen-main {
.screen-main-item {
border: 1px solid #ccc;
}
}
.active-shadow{
.screen-main-item {
border: 1px solid red;
}
}
</style>
<style lang="less">
.screen-main {
>.el-row>.el-col {
padding: 0 !important;
}
}
.fullscreen {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.screen-radio .el-radio-button:last-child .el-radio-button__inner{
border-color: #dcdfe6;
border-left: 0px;
box-shadow: none !important;
background-color: #ffffff;
color: #606266;
&:hover {
color: #00a65a;
}
}
</style>