mirror of
https://gitee.com/pan648540858/wvp-GB28181-pro.git
synced 2026-06-22 02:57:49 +08:00
支持多屏播放的播放器切换
This commit is contained in:
parent
1b7661039b
commit
9a52d29ded
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div id="h265Player" ref="container" style="background-color: #000000; position: relative; display: flex; align-items: center; justify-content: center;" @dblclick="fullscreenSwich" @mouseenter="showBar = true" @mouseleave="showBar = false">
|
||||
<div id="glplayer" ref="playerBox" style="width: 100%; height: 100%; margin: 0 auto;">
|
||||
<div :id="'h265Player-' + _uid" ref="container" style="background-color: #000000; position: relative; display: flex; align-items: center; justify-content: center;" @dblclick="fullscreenSwich" @mouseenter="showBar = true" @mouseleave="showBar = false">
|
||||
<div :id="'glplayer-' + _uid" ref="playerBox" style="width: 100%; height: 100%; margin: 0 auto;">
|
||||
<div v-if="playerLoading" class="play-loading">
|
||||
<i class="el-icon-loading" />
|
||||
<span style="margin-left: 5px">视频加载中</span>
|
||||
@ -122,7 +122,7 @@ export default {
|
||||
const options = {}
|
||||
h265webPlayer[this._uid] = new window.new265webjs(url, Object.assign(
|
||||
{
|
||||
player: 'glplayer', // 播放器容器id
|
||||
player: 'glplayer-' + this._uid,
|
||||
width: this.playerWidth,
|
||||
height: this.playerHeight,
|
||||
token: token,
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<el-tabs v-if="showTab && playerList.length > 1" v-model="activePlayer" type="card" :stretch="true" @tab-click="changePlayer">
|
||||
<el-tab-pane v-for="p in playerList" :key="p.key" :label="p.label" :name="p.key"></el-tab-pane>
|
||||
</el-tabs>
|
||||
<div class="player-video-area">
|
||||
<div class="player-video-area" :style="{ height: showTab ? 'calc(100% - 36px)' : '100%' }">
|
||||
<jessibucaPlayer
|
||||
v-if="activePlayer === 'jessibuca'"
|
||||
ref="jessibuca"
|
||||
@ -179,7 +179,7 @@ export default {
|
||||
}
|
||||
.player-video-area {
|
||||
width: 100%;
|
||||
height:calc(100% - 36px);;
|
||||
height: 100%;
|
||||
background: #000;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div id="rtcPlayer">
|
||||
<video id="webRtcPlayerBox" :controls="showControls" autoplay style="text-align:left;">
|
||||
<div :id="'rtcPlayer-' + _uid" class="rtc-player-wrapper">
|
||||
<video :id="'webRtcPlayerBox-' + _uid" class="rtc-player-video" :controls="showControls" autoplay style="text-align:left;">
|
||||
Your browser is too old which doesn't support HTML5 video.
|
||||
</video>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let webrtcPlayer = null
|
||||
const webrtcPlayer = {}
|
||||
import dragZoom from '../../mixins/dragZoom'
|
||||
export default {
|
||||
name: 'RtcPlayer',
|
||||
@ -26,16 +26,17 @@ export default {
|
||||
mounted() {},
|
||||
destroyed() {
|
||||
clearTimeout(this.timer)
|
||||
this.pause()
|
||||
},
|
||||
methods: {
|
||||
play: function(url) {
|
||||
if (webrtcPlayer != null) {
|
||||
if (webrtcPlayer[this._uid]) {
|
||||
this.pause()
|
||||
}
|
||||
webrtcPlayer = new ZLMRTCClient.Endpoint({
|
||||
element: document.getElementById('webRtcPlayerBox'), // video 标签
|
||||
debug: true, // 是否打印日志
|
||||
zlmsdpUrl: url, // 流地址
|
||||
webrtcPlayer[this._uid] = new ZLMRTCClient.Endpoint({
|
||||
element: document.getElementById('webRtcPlayerBox-' + this._uid),
|
||||
debug: true,
|
||||
zlmsdpUrl: url,
|
||||
simulecast: false,
|
||||
useCamera: false,
|
||||
audioEnable: true,
|
||||
@ -43,37 +44,37 @@ export default {
|
||||
recvOnly: true,
|
||||
usedatachannel: false
|
||||
})
|
||||
webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, (e) => { // ICE 协商出错
|
||||
const player = webrtcPlayer[this._uid]
|
||||
player.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, (e) => {
|
||||
console.error('ICE 协商出错')
|
||||
this.eventcallbacK('ICE ERROR', 'ICE 协商出错')
|
||||
})
|
||||
|
||||
webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, (e) => { // 获取到了远端流,可以播放
|
||||
player.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, (e) => {
|
||||
console.log('播放成功', e.streams)
|
||||
this.eventcallbacK('playing', '播放成功')
|
||||
})
|
||||
|
||||
webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, (e) => { // offer anwser 交换失败
|
||||
player.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, (e) => {
|
||||
console.error('offer anwser 交换失败', e)
|
||||
this.eventcallbacK('OFFER ANSWER ERROR ', 'offer anwser 交换失败')
|
||||
if (e.code == -400 && e.msg == '流不存在') {
|
||||
console.log('流不存在')
|
||||
this.timer = setTimeout(() => {
|
||||
this.webrtcPlayer.close()
|
||||
player.close()
|
||||
this.play(url)
|
||||
}, 100)
|
||||
}
|
||||
})
|
||||
|
||||
webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, (s) => { // 获取到了本地流
|
||||
// document.getElementById('selfVideo').srcObject=s;
|
||||
player.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, (s) => {
|
||||
this.eventcallbacK('LOCAL STREAM', '获取到了本地流')
|
||||
})
|
||||
},
|
||||
pause: function() {
|
||||
if (webrtcPlayer != null) {
|
||||
webrtcPlayer.close()
|
||||
webrtcPlayer = null
|
||||
if (webrtcPlayer[this._uid]) {
|
||||
webrtcPlayer[this._uid].close()
|
||||
webrtcPlayer[this._uid] = null
|
||||
}
|
||||
},
|
||||
stop: function() {
|
||||
@ -85,7 +86,7 @@ export default {
|
||||
console.log(message)
|
||||
},
|
||||
getVideoElement() {
|
||||
return document.getElementById('webRtcPlayerBox')
|
||||
return document.getElementById('webRtcPlayerBox-' + this._uid)
|
||||
},
|
||||
getVideoRect() {
|
||||
const video = this.getVideoElement()
|
||||
@ -121,12 +122,12 @@ export default {
|
||||
.LodingTitle {
|
||||
min-width: 70px;
|
||||
}
|
||||
#rtcPlayer{
|
||||
.rtc-player-wrapper{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
#webRtcPlayerBox{
|
||||
.rtc-player-video{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
|
||||
@ -13,6 +13,14 @@
|
||||
<i class="iconfont icon-a-mti-6fenpingshi btn" :class="{active: spiltIndex === 2}" @click="spiltIndex=2" />
|
||||
<i class="iconfont icon-a-mti-9fenpingshi btn" :class="{active: spiltIndex === 3}" @click="spiltIndex=3" />
|
||||
</div>
|
||||
<div class="global-player-control">
|
||||
播放器:
|
||||
<el-select v-model="globalPlayer" size="mini" style="width: 120px">
|
||||
<el-option label="Jessibuca" value="jessibuca" />
|
||||
<el-option label="WebRTC" value="webRTC" />
|
||||
<el-option label="H265web" value="h265web" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="fullscreen-control">
|
||||
<i class="el-icon-full-screen btn" @click="fullScreen()" />
|
||||
</div>
|
||||
@ -30,15 +38,12 @@
|
||||
:class="getPlayerClass(spiltIndex, i)"
|
||||
@click="playerIdx = (i-1)"
|
||||
>
|
||||
<div v-if="!videoUrl[i-1]" class="no-signal">{{ videoTip[i-1]?videoTip[i-1]:"无信号" }}</div>
|
||||
<player
|
||||
<div v-if="!streamInfo[i-1]" class="no-signal">{{ videoTip[i-1]?videoTip[i-1]:"无信号" }}</div>
|
||||
<PlayerTabs
|
||||
v-else
|
||||
:ref="'player' + i"
|
||||
fluent
|
||||
autoplay
|
||||
:ref="'playerTabs' + i"
|
||||
:show-tab="false"
|
||||
:show-button="true"
|
||||
@screenshot="shot"
|
||||
@destroy="destroy"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -49,20 +54,21 @@
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import player from '../common/jessibuca.vue'
|
||||
import PlayerTabs from '../common/playerTabs.vue'
|
||||
import DeviceTree from '../common/DeviceTree.vue'
|
||||
import screenFull from 'screenfull'
|
||||
|
||||
export default {
|
||||
name: 'Live',
|
||||
components: {
|
||||
player, DeviceTree
|
||||
PlayerTabs, DeviceTree
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
videoUrl: [''],
|
||||
streamInfo: [null],
|
||||
videoTip: [''],
|
||||
globalPlayer: 'jessibuca',
|
||||
spiltIndex: 2, // 分屏
|
||||
playerIdx: 0, // 激活播放器
|
||||
|
||||
@ -122,23 +128,33 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
spilt(newValue) {
|
||||
spiltIndex(newValue) {
|
||||
console.log('切换画幅;' + newValue)
|
||||
const that = this
|
||||
for (let i = 1; i <= newValue; i++) {
|
||||
if (!that.$refs['player' + i]) {
|
||||
for (let i = 1; i <= this.layout[newValue].spilt; i++) {
|
||||
if (!that.$refs['playerTabs' + i]) {
|
||||
continue
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (that.$refs['player' + i] instanceof Array) {
|
||||
that.$refs['player' + i][0].resize()
|
||||
} else {
|
||||
that.$refs['player' + i].resize()
|
||||
const ref = that.$refs['playerTabs' + i]
|
||||
const instance = ref instanceof Array ? ref[0] : ref
|
||||
if (instance && instance.resize) {
|
||||
instance.resize()
|
||||
}
|
||||
})
|
||||
}
|
||||
window.localStorage.setItem('split', newValue)
|
||||
},
|
||||
globalPlayer(newKey) {
|
||||
for (let i = 1; i <= this.layout[this.spiltIndex].spilt; i++) {
|
||||
const ref = this.$refs['playerTabs' + i]
|
||||
if (ref) {
|
||||
const instance = ref instanceof Array ? ref[0] : ref
|
||||
instance.switchPlayer(newKey)
|
||||
}
|
||||
}
|
||||
window.localStorage.setItem('globalPlayer', newKey)
|
||||
},
|
||||
'$route.fullPath': 'checkPlayByParam'
|
||||
},
|
||||
mounted() {
|
||||
@ -156,27 +172,18 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleResize() {
|
||||
// Force update to recalculate responsive layout
|
||||
this.$forceUpdate()
|
||||
|
||||
// Resize any active players
|
||||
this.$nextTick(() => {
|
||||
for (let i = 0; i < this.layout[this.spiltIndex].spilt; i++) {
|
||||
const playerRef = this.$refs[`player${i + 1}`]
|
||||
if (playerRef) {
|
||||
if (playerRef instanceof Array) {
|
||||
playerRef[0].resize && playerRef[0].resize()
|
||||
} else {
|
||||
playerRef.resize && playerRef.resize()
|
||||
}
|
||||
const ref = this.$refs[`playerTabs${i + 1}`]
|
||||
if (ref) {
|
||||
const instance = ref instanceof Array ? ref[0] : ref
|
||||
instance.resize && instance.resize()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
destroy(idx) {
|
||||
console.log(idx)
|
||||
this.clear(idx.substring(idx.length - 1))
|
||||
},
|
||||
clickEvent: function(channelId) {
|
||||
this.sendDevicePush(channelId)
|
||||
},
|
||||
@ -194,17 +201,11 @@ export default {
|
||||
sendDevicePush: function(channelId) {
|
||||
this.save(channelId)
|
||||
const idxTmp = this.playerIdx
|
||||
this.setPlayUrl('', idxTmp)
|
||||
this.$set(this.streamInfo, idxTmp, null)
|
||||
this.$set(this.videoTip, idxTmp, '正在拉流...')
|
||||
this.$store.dispatch('commonChanel/playChannel', channelId)
|
||||
.then(data => {
|
||||
let videoUrl
|
||||
if (location.protocol === 'https:') {
|
||||
videoUrl = data.wss_flv
|
||||
} else {
|
||||
videoUrl = data.ws_flv
|
||||
}
|
||||
this.setPlayUrl(videoUrl, idxTmp)
|
||||
this.setPlayStream(data.transcodeStream || data, idxTmp)
|
||||
})
|
||||
.catch(err => {
|
||||
this.$set(this.videoTip, idxTmp, '播放失败: ' + err)
|
||||
@ -213,18 +214,15 @@ export default {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
setPlayUrl(url, idx) {
|
||||
this.$set(this.videoUrl, idx, url)
|
||||
const _this = this
|
||||
setTimeout(() => {
|
||||
window.localStorage.setItem('videoUrl', JSON.stringify(_this.videoUrl))
|
||||
}, 100)
|
||||
setPlayStream(streamInfo, idx) {
|
||||
this.$set(this.streamInfo, idx, streamInfo)
|
||||
this.$nextTick(() => {
|
||||
const refName = 'player' + (idx + 1)
|
||||
if (this.$refs[refName]) {
|
||||
const player = this.$refs[refName] instanceof Array ? this.$refs[refName][0] : this.$refs[refName]
|
||||
if (player && player.play) {
|
||||
player.play(url)
|
||||
const refName = 'playerTabs' + (idx + 1)
|
||||
const ref = this.$refs[refName]
|
||||
if (ref) {
|
||||
const instance = ref instanceof Array ? ref[0] : ref
|
||||
if (instance && instance.setStreamInfo) {
|
||||
instance.setStreamInfo(streamInfo)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -235,28 +233,7 @@ export default {
|
||||
this.sendDevicePush(query.channelId)
|
||||
}
|
||||
},
|
||||
shot(e) {
|
||||
var base64ToBlob = function(code) {
|
||||
const parts = code.split(';base64,')
|
||||
const contentType = parts[0].split(':')[1]
|
||||
const raw = window.atob(parts[1])
|
||||
const rawLength = raw.length
|
||||
const uInt8Array = new Uint8Array(rawLength)
|
||||
for (let i = 0; i < rawLength; ++i) {
|
||||
uInt8Array[i] = raw.charCodeAt(i)
|
||||
}
|
||||
return new Blob([uInt8Array], {
|
||||
type: contentType
|
||||
})
|
||||
}
|
||||
const aLink = document.createElement('a')
|
||||
const blob = base64ToBlob(e) // new Blob([content]);
|
||||
const evt = document.createEvent('HTMLEvents')
|
||||
evt.initEvent('click', true, true) // initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为
|
||||
aLink.download = '截图'
|
||||
aLink.href = URL.createObjectURL(blob)
|
||||
aLink.click()
|
||||
},
|
||||
|
||||
save(item) {
|
||||
const dataStr = window.localStorage.getItem('playData') || '[]'
|
||||
const data = JSON.parse(dataStr)
|
||||
@ -340,6 +317,13 @@ export default {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.global-player-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.player-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
@ -354,6 +338,7 @@ export default {
|
||||
height: 100%;
|
||||
max-height: calc(100vh - 180px);
|
||||
aspect-ratio: 16/9;
|
||||
border: 4px solid rgb(169, 168, 168);
|
||||
}
|
||||
|
||||
.btn {
|
||||
@ -370,7 +355,7 @@ export default {
|
||||
}
|
||||
|
||||
.redborder {
|
||||
border: 4px solid rgb(0, 198, 255) !important;
|
||||
outline: 4px solid rgb(0, 198, 255);
|
||||
}
|
||||
|
||||
.play-box {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user