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>
|
<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="'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" ref="playerBox" style="width: 100%; height: 100%; margin: 0 auto;">
|
<div :id="'glplayer-' + _uid" ref="playerBox" style="width: 100%; height: 100%; margin: 0 auto;">
|
||||||
<div v-if="playerLoading" class="play-loading">
|
<div v-if="playerLoading" class="play-loading">
|
||||||
<i class="el-icon-loading" />
|
<i class="el-icon-loading" />
|
||||||
<span style="margin-left: 5px">视频加载中</span>
|
<span style="margin-left: 5px">视频加载中</span>
|
||||||
@ -122,7 +122,7 @@ export default {
|
|||||||
const options = {}
|
const options = {}
|
||||||
h265webPlayer[this._uid] = new window.new265webjs(url, Object.assign(
|
h265webPlayer[this._uid] = new window.new265webjs(url, Object.assign(
|
||||||
{
|
{
|
||||||
player: 'glplayer', // 播放器容器id
|
player: 'glplayer-' + this._uid,
|
||||||
width: this.playerWidth,
|
width: this.playerWidth,
|
||||||
height: this.playerHeight,
|
height: this.playerHeight,
|
||||||
token: token,
|
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-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-tab-pane v-for="p in playerList" :key="p.key" :label="p.label" :name="p.key"></el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<div class="player-video-area">
|
<div class="player-video-area" :style="{ height: showTab ? 'calc(100% - 36px)' : '100%' }">
|
||||||
<jessibucaPlayer
|
<jessibucaPlayer
|
||||||
v-if="activePlayer === 'jessibuca'"
|
v-if="activePlayer === 'jessibuca'"
|
||||||
ref="jessibuca"
|
ref="jessibuca"
|
||||||
@ -179,7 +179,7 @@ export default {
|
|||||||
}
|
}
|
||||||
.player-video-area {
|
.player-video-area {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height:calc(100% - 36px);;
|
height: 100%;
|
||||||
background: #000;
|
background: #000;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="rtcPlayer">
|
<div :id="'rtcPlayer-' + _uid" class="rtc-player-wrapper">
|
||||||
<video id="webRtcPlayerBox" :controls="showControls" autoplay style="text-align:left;">
|
<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.
|
Your browser is too old which doesn't support HTML5 video.
|
||||||
</video>
|
</video>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let webrtcPlayer = null
|
const webrtcPlayer = {}
|
||||||
import dragZoom from '../../mixins/dragZoom'
|
import dragZoom from '../../mixins/dragZoom'
|
||||||
export default {
|
export default {
|
||||||
name: 'RtcPlayer',
|
name: 'RtcPlayer',
|
||||||
@ -26,16 +26,17 @@ export default {
|
|||||||
mounted() {},
|
mounted() {},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
clearTimeout(this.timer)
|
clearTimeout(this.timer)
|
||||||
|
this.pause()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
play: function(url) {
|
play: function(url) {
|
||||||
if (webrtcPlayer != null) {
|
if (webrtcPlayer[this._uid]) {
|
||||||
this.pause()
|
this.pause()
|
||||||
}
|
}
|
||||||
webrtcPlayer = new ZLMRTCClient.Endpoint({
|
webrtcPlayer[this._uid] = new ZLMRTCClient.Endpoint({
|
||||||
element: document.getElementById('webRtcPlayerBox'), // video 标签
|
element: document.getElementById('webRtcPlayerBox-' + this._uid),
|
||||||
debug: true, // 是否打印日志
|
debug: true,
|
||||||
zlmsdpUrl: url, // 流地址
|
zlmsdpUrl: url,
|
||||||
simulecast: false,
|
simulecast: false,
|
||||||
useCamera: false,
|
useCamera: false,
|
||||||
audioEnable: true,
|
audioEnable: true,
|
||||||
@ -43,37 +44,37 @@ export default {
|
|||||||
recvOnly: true,
|
recvOnly: true,
|
||||||
usedatachannel: false
|
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 协商出错')
|
console.error('ICE 协商出错')
|
||||||
this.eventcallbacK('ICE 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)
|
console.log('播放成功', e.streams)
|
||||||
this.eventcallbacK('playing', '播放成功')
|
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)
|
console.error('offer anwser 交换失败', e)
|
||||||
this.eventcallbacK('OFFER ANSWER ERROR ', 'offer anwser 交换失败')
|
this.eventcallbacK('OFFER ANSWER ERROR ', 'offer anwser 交换失败')
|
||||||
if (e.code == -400 && e.msg == '流不存在') {
|
if (e.code == -400 && e.msg == '流不存在') {
|
||||||
console.log('流不存在')
|
console.log('流不存在')
|
||||||
this.timer = setTimeout(() => {
|
this.timer = setTimeout(() => {
|
||||||
this.webrtcPlayer.close()
|
player.close()
|
||||||
this.play(url)
|
this.play(url)
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, (s) => { // 获取到了本地流
|
player.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, (s) => {
|
||||||
// document.getElementById('selfVideo').srcObject=s;
|
|
||||||
this.eventcallbacK('LOCAL STREAM', '获取到了本地流')
|
this.eventcallbacK('LOCAL STREAM', '获取到了本地流')
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
pause: function() {
|
pause: function() {
|
||||||
if (webrtcPlayer != null) {
|
if (webrtcPlayer[this._uid]) {
|
||||||
webrtcPlayer.close()
|
webrtcPlayer[this._uid].close()
|
||||||
webrtcPlayer = null
|
webrtcPlayer[this._uid] = null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
stop: function() {
|
stop: function() {
|
||||||
@ -85,7 +86,7 @@ export default {
|
|||||||
console.log(message)
|
console.log(message)
|
||||||
},
|
},
|
||||||
getVideoElement() {
|
getVideoElement() {
|
||||||
return document.getElementById('webRtcPlayerBox')
|
return document.getElementById('webRtcPlayerBox-' + this._uid)
|
||||||
},
|
},
|
||||||
getVideoRect() {
|
getVideoRect() {
|
||||||
const video = this.getVideoElement()
|
const video = this.getVideoElement()
|
||||||
@ -121,12 +122,12 @@ export default {
|
|||||||
.LodingTitle {
|
.LodingTitle {
|
||||||
min-width: 70px;
|
min-width: 70px;
|
||||||
}
|
}
|
||||||
#rtcPlayer{
|
.rtc-player-wrapper{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
#webRtcPlayerBox{
|
.rtc-player-video{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-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-6fenpingshi btn" :class="{active: spiltIndex === 2}" @click="spiltIndex=2" />
|
||||||
<i class="iconfont icon-a-mti-9fenpingshi btn" :class="{active: spiltIndex === 3}" @click="spiltIndex=3" />
|
<i class="iconfont icon-a-mti-9fenpingshi btn" :class="{active: spiltIndex === 3}" @click="spiltIndex=3" />
|
||||||
</div>
|
</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">
|
<div class="fullscreen-control">
|
||||||
<i class="el-icon-full-screen btn" @click="fullScreen()" />
|
<i class="el-icon-full-screen btn" @click="fullScreen()" />
|
||||||
</div>
|
</div>
|
||||||
@ -30,15 +38,12 @@
|
|||||||
:class="getPlayerClass(spiltIndex, i)"
|
:class="getPlayerClass(spiltIndex, i)"
|
||||||
@click="playerIdx = (i-1)"
|
@click="playerIdx = (i-1)"
|
||||||
>
|
>
|
||||||
<div v-if="!videoUrl[i-1]" class="no-signal">{{ videoTip[i-1]?videoTip[i-1]:"无信号" }}</div>
|
<div v-if="!streamInfo[i-1]" class="no-signal">{{ videoTip[i-1]?videoTip[i-1]:"无信号" }}</div>
|
||||||
<player
|
<PlayerTabs
|
||||||
v-else
|
v-else
|
||||||
:ref="'player' + i"
|
:ref="'playerTabs' + i"
|
||||||
fluent
|
:show-tab="false"
|
||||||
autoplay
|
|
||||||
:show-button="true"
|
:show-button="true"
|
||||||
@screenshot="shot"
|
|
||||||
@destroy="destroy"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -49,20 +54,21 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import player from '../common/jessibuca.vue'
|
import PlayerTabs from '../common/playerTabs.vue'
|
||||||
import DeviceTree from '../common/DeviceTree.vue'
|
import DeviceTree from '../common/DeviceTree.vue'
|
||||||
import screenFull from 'screenfull'
|
import screenFull from 'screenfull'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Live',
|
name: 'Live',
|
||||||
components: {
|
components: {
|
||||||
player, DeviceTree
|
PlayerTabs, DeviceTree
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
videoUrl: [''],
|
streamInfo: [null],
|
||||||
videoTip: [''],
|
videoTip: [''],
|
||||||
|
globalPlayer: 'jessibuca',
|
||||||
spiltIndex: 2, // 分屏
|
spiltIndex: 2, // 分屏
|
||||||
playerIdx: 0, // 激活播放器
|
playerIdx: 0, // 激活播放器
|
||||||
|
|
||||||
@ -122,23 +128,33 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
spilt(newValue) {
|
spiltIndex(newValue) {
|
||||||
console.log('切换画幅;' + newValue)
|
console.log('切换画幅;' + newValue)
|
||||||
const that = this
|
const that = this
|
||||||
for (let i = 1; i <= newValue; i++) {
|
for (let i = 1; i <= this.layout[newValue].spilt; i++) {
|
||||||
if (!that.$refs['player' + i]) {
|
if (!that.$refs['playerTabs' + i]) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (that.$refs['player' + i] instanceof Array) {
|
const ref = that.$refs['playerTabs' + i]
|
||||||
that.$refs['player' + i][0].resize()
|
const instance = ref instanceof Array ? ref[0] : ref
|
||||||
} else {
|
if (instance && instance.resize) {
|
||||||
that.$refs['player' + i].resize()
|
instance.resize()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
window.localStorage.setItem('split', newValue)
|
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'
|
'$route.fullPath': 'checkPlayByParam'
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -156,27 +172,18 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleResize() {
|
handleResize() {
|
||||||
// Force update to recalculate responsive layout
|
|
||||||
this.$forceUpdate()
|
this.$forceUpdate()
|
||||||
|
|
||||||
// Resize any active players
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
for (let i = 0; i < this.layout[this.spiltIndex].spilt; i++) {
|
for (let i = 0; i < this.layout[this.spiltIndex].spilt; i++) {
|
||||||
const playerRef = this.$refs[`player${i + 1}`]
|
const ref = this.$refs[`playerTabs${i + 1}`]
|
||||||
if (playerRef) {
|
if (ref) {
|
||||||
if (playerRef instanceof Array) {
|
const instance = ref instanceof Array ? ref[0] : ref
|
||||||
playerRef[0].resize && playerRef[0].resize()
|
instance.resize && instance.resize()
|
||||||
} else {
|
|
||||||
playerRef.resize && playerRef.resize()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
destroy(idx) {
|
|
||||||
console.log(idx)
|
|
||||||
this.clear(idx.substring(idx.length - 1))
|
|
||||||
},
|
|
||||||
clickEvent: function(channelId) {
|
clickEvent: function(channelId) {
|
||||||
this.sendDevicePush(channelId)
|
this.sendDevicePush(channelId)
|
||||||
},
|
},
|
||||||
@ -194,17 +201,11 @@ export default {
|
|||||||
sendDevicePush: function(channelId) {
|
sendDevicePush: function(channelId) {
|
||||||
this.save(channelId)
|
this.save(channelId)
|
||||||
const idxTmp = this.playerIdx
|
const idxTmp = this.playerIdx
|
||||||
this.setPlayUrl('', idxTmp)
|
this.$set(this.streamInfo, idxTmp, null)
|
||||||
this.$set(this.videoTip, idxTmp, '正在拉流...')
|
this.$set(this.videoTip, idxTmp, '正在拉流...')
|
||||||
this.$store.dispatch('commonChanel/playChannel', channelId)
|
this.$store.dispatch('commonChanel/playChannel', channelId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
let videoUrl
|
this.setPlayStream(data.transcodeStream || data, idxTmp)
|
||||||
if (location.protocol === 'https:') {
|
|
||||||
videoUrl = data.wss_flv
|
|
||||||
} else {
|
|
||||||
videoUrl = data.ws_flv
|
|
||||||
}
|
|
||||||
this.setPlayUrl(videoUrl, idxTmp)
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
this.$set(this.videoTip, idxTmp, '播放失败: ' + err)
|
this.$set(this.videoTip, idxTmp, '播放失败: ' + err)
|
||||||
@ -213,18 +214,15 @@ export default {
|
|||||||
this.loading = false
|
this.loading = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
setPlayUrl(url, idx) {
|
setPlayStream(streamInfo, idx) {
|
||||||
this.$set(this.videoUrl, idx, url)
|
this.$set(this.streamInfo, idx, streamInfo)
|
||||||
const _this = this
|
|
||||||
setTimeout(() => {
|
|
||||||
window.localStorage.setItem('videoUrl', JSON.stringify(_this.videoUrl))
|
|
||||||
}, 100)
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
const refName = 'player' + (idx + 1)
|
const refName = 'playerTabs' + (idx + 1)
|
||||||
if (this.$refs[refName]) {
|
const ref = this.$refs[refName]
|
||||||
const player = this.$refs[refName] instanceof Array ? this.$refs[refName][0] : this.$refs[refName]
|
if (ref) {
|
||||||
if (player && player.play) {
|
const instance = ref instanceof Array ? ref[0] : ref
|
||||||
player.play(url)
|
if (instance && instance.setStreamInfo) {
|
||||||
|
instance.setStreamInfo(streamInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -235,28 +233,7 @@ export default {
|
|||||||
this.sendDevicePush(query.channelId)
|
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) {
|
save(item) {
|
||||||
const dataStr = window.localStorage.getItem('playData') || '[]'
|
const dataStr = window.localStorage.getItem('playData') || '[]'
|
||||||
const data = JSON.parse(dataStr)
|
const data = JSON.parse(dataStr)
|
||||||
@ -340,6 +317,13 @@ export default {
|
|||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.global-player-control {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.player-container {
|
.player-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -354,6 +338,7 @@ export default {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
max-height: calc(100vh - 180px);
|
max-height: calc(100vh - 180px);
|
||||||
aspect-ratio: 16/9;
|
aspect-ratio: 16/9;
|
||||||
|
border: 4px solid rgb(169, 168, 168);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
@ -370,7 +355,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.redborder {
|
.redborder {
|
||||||
border: 4px solid rgb(0, 198, 255) !important;
|
outline: 4px solid rgb(0, 198, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
.play-box {
|
.play-box {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user