You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

538 lines
12 KiB

5 days ago
<template>
<view class="container">
<u-loading-page :loading="loading" bg-color="#eef3f7" loadingText="FastBee.cn"></u-loading-page>
<view class="card">
<view class="player-wrapper">
<div class="container-shell">
<div id="container" @tap='operate' :ref="'container'+refId"></div>
</div>
<div class="player-display" />
<view class="kBps"
v-if="statisticsAll && (screensStats.abps || screensStats.buf || screensStats.fps || screensStats.ts || screensStats.vbps)">
<text class="iconfont icon-dian"></text>
<text class="li" v-if="screensStats.abps">{{ statisticsAll.abps }}abps</text>
<text class="li" v-if="screensStats.buf">{{ statisticsAll.buf }}buf</text>
<text class="li" v-if="screensStats.fps">{{ statisticsAll.fps }}fps</text>
<text class="li" v-if="screensStats.ts">{{ statisticsAll.ts }}ts</text>
<text class="li" v-if="screensStats.vbps">{{ statisticsAll.vbps }}vbps</text>
</view>
<view class="tabbar" v-if="isTabbar" @tap="tapTbabar">
<view class="title">
<text class="iconfont icon-fanhui"></text>
<view class="texts">{{ title }}</view>
</view>
</view>
<slot></slot>
</view>
<view style="display: flex; justify-content: space-between;">
<uni-data-select v-model="value" :localdata="range" :clear="false" placeholder="请选择通道"
style="border: none; background-color: #ffffff; width: 375rpx;"
@change="changeindex"></uni-data-select>
<uni-data-select v-model="zoomvalue" :localdata="zoom" :clear="false" placeholder="请选择变焦方式"
style="border: none; background-color: #ffffff; width: 375rpx;"
@change="changezoom"></uni-data-select>
</view>
<!-- <u-tabs class="tab_wrap" :list="list" @click="tabClick" lineWidth="60"></u-tabs> -->
<view class="opt_button_wrap">
<view class="live_str_wrap" v-if="tabIndex == 0">
<view class="play_btn_wrap">
<button class="btn_icon" @click="operate" :disabled="playing">
<u-icon name="play-circle" size="30"></u-icon>
</button>
<text class="btn_title">播放</text>
</view>
<view class="play_btn_wrap">
<button class="btn_icon" @click="operate" :disabled="!playing">
<u-icon name="pause-circle" size="30"></u-icon>
</button>
<text class="btn_title">暂停</text>
</view>
<view class="play_btn_wrap">
<button class="btn_icon" @click="handlePtzScale(1)" :disabled="zoomtype">
<u-icon name="plus" size="30"></u-icon>
</button>
<text class="btn_title">放大</text>
</view>
<view class="play_btn_wrap">
<button class="btn_icon" @click="handlePtzScale(2)" :disabled="zoomtype">
<u-icon name="minus" size="30"></u-icon>
</button>
<text class="btn_title">缩小</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import props from './props.js';
import {
ptzdirection,
ptzscale
} from '@/apis/modules/sip';
import {
startPlay,
closeStream,
} from '@/apis/modules/player';
export default {
name: 'devicePlayer',
mixins: [props],
props: {
device: {
type: Array,
default: null,
required: true
}
},
watch: {
// 兼容小程序
device: function(newVal, oldVal) {
console.log("传过来的通道数据是", newVal)
for (let i = 0; i < newVal.length; i++) {
this.range.push({
value: i,
text: newVal[i].channelName,
deviceId: newVal[i].deviceSipId,
channelId: newVal[i].channelSipId,
status: newVal[i].status
})
}
console.log('现在range的数据是:', this.range)
}
},
onLoad(option) {
if (option.deviceId) {
console.log(option, '携带参数')
this.deviceId = option.deviceId
this.channelId = option.channelSipId
}
},
data() {
return {
zoomtype: true,
zoomvalue: '',
zoom: [{
value: 0,
text: "物理变焦"
},
{
value: 1,
text: "数字变焦"
}
],
value: '',
range: [
],
// 设备升级模态窗
show: false,
showcalendar: false,
// 加载图标
loading: false,
// 控制模块标题
title: ' 设备通道 ',
// 选项卡索引
tabIndex: 0,
queryParams: {
deviceSipId: '', //设备sipid
},
// 通道集合
selectChannel: null,
// 设备信息
deviceId: '',
channelId: '',
streamId: '',
streaminfo: {
ssrc: '',
playurl: ''
},
jessibuca: null,
operateBtns: {
fullscreen: true,
play: true,
ptz: true,
zoom: true,
},
list: [{
name: '设备直播',
}],
statisticsAll: null,
playing: false,
pausing: false,
retryCount: 0,
}
},
mounted() {
window.onerror = (msg) => (this.err = msg);
},
methods: {
create() {
const jessibuca = new window.Jessibuca(
Object.assign({
container: this.$refs['container' + this.refId],
videoBuffer: Number(0.2),
decoder: this.decoder + 'pages_player/static/h5/js/jessibuca/decoder.js',
timeout: 20,
debug: false,
debugLevel: 'debug',
isResize: false,
useWCS: false,
useMSE: false,
useSIMD: true,
wcsUseVideoRender: false,
loadingText: '加载中...',
hasAudio: true,
isFlv: true,
showBandwidth: true,
supportDblclickFullscreen: true,
operateBtns: this.operateBtns,
ptzClickType: 'mouseDownAndUp',
forceNoOffscreen: true,
playbackForwardMaxRateDecodeIFrame: 4,
playbackCurrentTimeMove: false,
networkDelayTimeoutReplay: true
},
this.options
)
);
this.jessibuca = jessibuca
const _this = this
jessibuca.on('error', function(error) {
console.log('error')
console.log(error)
_this.destroy()
})
jessibuca.on('timeout', function() {
console.log('timeout')
if (_this.retryCount <= 2) {
_this.replay()
_this.retryCount++
}
})
let pre = 0
let cur = 0
jessibuca.on('timeUpdate', function(ts) {
cur = parseInt(ts / 60000)
if (pre !== cur) {
pre++
}
})
jessibuca.on("play", function(play) {
console.log('play success!')
_this.playing = true
})
jessibuca.on("pause", function(pause) {
console.log('pause success!')
console.log(pause)
_this.playing = false
})
// jessibuca.on('stats', function (s) {
// console.log('stats is', s)
// })
jessibuca.on("ptz", (arrow) => {
console.log('ptz arrow', arrow);
_this.handlePtz(arrow);
})
jessibuca.on("crashLog", (value) => {
console.error('crashLog: ', value);
_this.$u.toast({
type: 'error',
message: JSON.stringify(value)
})
})
jessibuca.on("close", () => {
!this.isDebug && console.log('jessibuca close');
setTimeout(() => {
_this.cleanPlayer(true);
}, 10)
})
},
changeindex(e) {
console.log("改变了选项", e)
this.range.forEach(item => {
if (item.value == e) {
if (item.status != 2) {
uni.showToast({
title: "设备离线!!",
icon: "none"
})
this.value = null
//该设备是离线的
return;
}
window.onerror = (msg) => (this.err = msg);
this.create();
this.deviceId = item.deviceId
this.channelId = item.channelId
if(this.zoomvalue == 0){
this.zoomtype = false
}
}
})
},
changezoom(e) {
console.log("改变了选项", e)
if(e==0){
if(this.deviceId){
this.zoomtype = false
}
}
else{
this.zoomtype = true
}
},
initUrl(data) {
if (data) {
this.streamId = data.streamId
this.streaminfo.ssrc = data.ssrc
this.streaminfo.playurl = data.playurl
} else {
this.streamId = ''
this.streaminfo.ssrc = ''
this.streaminfo.playurl = ''
}
},
cleanPlayer(stop) {
this.destroy()
if (stop) {
this.stopPlay()
}
this.playing = false
},
destroy() {
if (this.jessibuca) {
this.jessibuca.destroy()
}
switch (this.tabIndex) {
case 0:
this.operateBtns.ptz = true;
this.operateBtns.zoom = true;
break;
}
// this.create();
},
// 单击选显卡
tabClick(item) {
this.tabIndex = item.index;
if (this.playing) {
this.cleanPlayer(true);
}
switch (this.tabIndex) {
case 0:
this.sendDevicePush();
break;
}
},
//直播控制
sendDevicePush: function() {
console.log("通知设备推流1:" + this.deviceId + " : " + this.channelId);
if (this.channelId) {
startPlay(this.deviceId, this.channelId).then((response) => {
console.log("开始播放:" + this.deviceId + " : " + this.channelId);
let res = response.data;
console.log("playurl:" + res.playurl);
this.streamId = res.streamId;
this.streaminfo.playurl = res.playurl;
this.play();
});
}
},
play: function() {
this.playing = true;
this.jessibuca.play(this.streaminfo.playurl);
},
operate() {
if (this.streaminfo.playurl) {
if (this.playing) {
this.jessibuca.pause();
this.pausing = true;
} else {
this.play();
this.pausing = false;
}
} else {
this.sendDevicePush();
}
},
stopPlay() {
if (this.streamId && this.playing) {
closeStream(this.deviceId, this.channelId, this.streamId).then(res => {
this.pausing = false
this.playing = false
if (this.jessibuca) {
this.retryCount = 0
this.destroy()
}
this.streamId = '';
this.streaminfo = {
ssrc: '',
playurl: ''
};
})
}
},
handlePtz(arrow) {
let leftRight = 0;
let upDown = 0;
if (arrow === 'left') {
leftRight = 2;
} else if (arrow === 'right') {
leftRight = 1;
} else if (arrow === 'up') {
upDown = 1;
} else if (arrow === 'down') {
upDown = 2;
}
var data = {
leftRight: leftRight,
upDown: upDown,
moveSpeed: 125
};
ptzdirection(this.deviceId, this.channelId, data).then(async response => {
//console.log("云台方向控制:" + JSON.stringify(response));
});
},
handlePtzScale(inOut) {
if (this.deviceId) {
console.log('云台缩放:' + inOut);
var data = {
inOut: inOut,
scaleSpeed: 30
}
ptzscale(this.deviceId, this.channelId, data);
// 放大/缩小后自动关闭
setTimeout(() => {
ptzscale(this.deviceId, this.channelId, {
inOut: 0,
scaleSpeed: 30
})
}, 800);
}
},
replay() {
this.cleanPlayer(false);
this.play();
},
},
onUnload() {
this.cleanPlayer(true);
},
}
</script>
<style lang="scss">
.player-wrapper {
display: flex;
place-content: center;
font-size: 12px;
width: 100%;
height: 260px;
.player-display {
position: absolute;
left: 4px;
bottom: 48px;
display: flex;
width: 150px;
height: 30px;
justify-content: center;
color: #fff;
}
.container-shell {
position: relative;
width: 100%;
}
#container {
background: rgba(13, 14, 27, 0.7);
width: 100%;
height: 100%;
position: relative;
}
}
.tab_wrap {
margin-top: 10px;
}
.opt_button_wrap {
margin-top: 40px;
.play_btn_wrap {
display: inline-block;
text-align: center;
flex: 1;
.btn_icon-size {
display: inline-block;
padding: 8px;
background-color: #01a394;
// border-radius: 50px;
&::after {
border: transparent;
}
}
.btn_icon {
display: inline-block;
padding: 16px;
// background-color: #01a394;
border-radius: 50px;
&::after {
border: transparent;
}
}
.btn_title {
display: block;
font-size: 14px;
margin-top: 8px;
}
}
.live_str_wrap {
display: flex;
flex-direction: row;
}
.live_str_item {
display: flex;
flex-direction: row;
margin-top: 60px;
}
.playback_wrap {
padding: 0 20px;
.date_wrap {}
.button_wrap {
display: flex;
flex-direction: row;
margin-top: 56px;
}
}
}
</style>