mirror of
https://gitee.com/beecue/fastbee.git
synced 2025-12-19 01:15:54 +08:00
开源版本,视频直播功能
This commit is contained in:
183
vue/src/views/components/player/DeviceTree.vue
Normal file
183
vue/src/views/components/player/DeviceTree.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<div id="DeviceTree" style="width: 100%; height: 100%; background-color: #ffffff; overflow: auto">
|
||||
<div style="line-height: 3vh;margin-bottom: 10px;font-size: 17px;">设备列表</div>
|
||||
<el-tree ref="tree" :props="defaultProps" :current-node-key="selectchannelId" :default-expanded-keys="expandIds"
|
||||
:highlight-current="true" @node-click="handleNodeClick" :load="loadNode" lazy node-key="id"
|
||||
style="min-width: 100%; display: inline-block !important">
|
||||
<span class="custom-tree-node" slot-scope="{ node, data }" style="width: 100%">
|
||||
<span v-if="node.data.type === 0 && node.data.online" title="在线设备"
|
||||
class="device-online iconfont icon-jiedianleizhukongzhongxin2"></span>
|
||||
<span v-if="node.data.type === 0 && !node.data.online" title="离线设备"
|
||||
class="device-offline iconfont icon-jiedianleizhukongzhongxin2"></span>
|
||||
<span v-if="node.data.type === 3 && node.data.online" title="在线通道"
|
||||
class="device-online iconfont icon-shebeileijiankongdian"></span>
|
||||
<span v-if="node.data.type === 3 && !node.data.online" title="离线通道"
|
||||
class="device-offline iconfont icon-shebeileijiankongdian"></span>
|
||||
<span v-if="node.data.type === 4 && node.data.online" title="在线通道-球机"
|
||||
class="device-online iconfont icon-shebeileiqiuji"></span>
|
||||
<span v-if="node.data.type === 4 && !node.data.online" title="离线通道-球机"
|
||||
class="device-offline iconfont icon-shebeileiqiuji"></span>
|
||||
<span v-if="node.data.type === 5 && node.data.online" title="在线通道-半球"
|
||||
class="device-online iconfont icon-shebeileibanqiu"></span>
|
||||
<span v-if="node.data.type === 5 && !node.data.online" title="离线通道-半球"
|
||||
class="device-offline iconfont icon-shebeileibanqiu"></span>
|
||||
<span v-if="node.data.type === 6 && node.data.online" title="在线通道-枪机"
|
||||
class="device-online iconfont icon-shebeileiqiangjitongdao"></span>
|
||||
<span v-if="node.data.type === 6 && !node.data.online" title="离线通道-枪机"
|
||||
class="device-offline iconfont icon-shebeileiqiangjitongdao"></span>
|
||||
<span v-if="node.data.online" style="padding-left: 1px" class="device-online">{{ node.label }}</span>
|
||||
<span v-if="!node.data.online" style="padding-left: 1px" class="device-offline">{{ node.label }}</span>
|
||||
</span>
|
||||
</el-tree>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listSipDeviceChannel } from '@/api/iot/sipdevice';
|
||||
import { listDeviceShort } from '@/api/iot/device';
|
||||
|
||||
export default {
|
||||
name: 'DeviceTree',
|
||||
data() {
|
||||
return {
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 监控设备通道信息表格数据
|
||||
channelList: [],
|
||||
DeviceData: [],
|
||||
expandIds: [],
|
||||
selectData: {},
|
||||
selectchannelId: '',
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
isLeaf: 'isLeaf',
|
||||
},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 100,
|
||||
status: 3,
|
||||
deviceType: 3,
|
||||
},
|
||||
};
|
||||
},
|
||||
props: ['onlyCatalog', 'clickEvent'],
|
||||
mounted() {
|
||||
this.selectchannelId = '';
|
||||
this.expandIds = ['0'];
|
||||
},
|
||||
methods: {
|
||||
handleNodeClick(data, node, element) {
|
||||
this.selectData = node.data;
|
||||
this.selectchannelId = node.data.value;
|
||||
if (node.level !== 0) {
|
||||
let deviceNode = this.$refs.tree.getNode(data.userData.channelSipId);
|
||||
if (typeof this.clickEvent == 'function' && node.level > 1) {
|
||||
this.clickEvent(deviceNode.data.userData);
|
||||
}
|
||||
}
|
||||
},
|
||||
loadNode: function (node, resolve) {
|
||||
if (node.level === 0) {
|
||||
listDeviceShort(this.queryParams).then((response) => {
|
||||
const data = response.rows;
|
||||
if (data.length > 0) {
|
||||
let nodeList = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let node = {
|
||||
name: data[i].deviceName,
|
||||
isLeaf: false,
|
||||
id: data[i].serialNumber,
|
||||
type: 0,
|
||||
online: data[i].status === 3,
|
||||
userData: data[i],
|
||||
};
|
||||
nodeList.push(node);
|
||||
}
|
||||
resolve(nodeList);
|
||||
} else {
|
||||
resolve([]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let channelArray = [];
|
||||
listSipDeviceChannel(node.data.userData.serialNumber).then((res) => {
|
||||
if (res.data != null) {
|
||||
channelArray = channelArray.concat(res.data);
|
||||
this.channelDataHandler(channelArray, resolve);
|
||||
} else {
|
||||
resolve([]);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
channelDataHandler: function (data, resolve) {
|
||||
if (data.length > 0) {
|
||||
let nodeList = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let item = data[i];
|
||||
let channelType = item.id.substring(10, 13);
|
||||
console.log('channelType: ' + channelType);
|
||||
let type = 3;
|
||||
if (item.id.length <= 10) {
|
||||
type = 2;
|
||||
} else {
|
||||
if (item.id.length > 14) {
|
||||
let channelType = item.id.substring(10, 13);
|
||||
// 111-DVR编码;112-视频服务器编码;118-网络视频录像机(NVR)编码;131-摄像机编码;132-网络摄像机(IPC)编码
|
||||
if (channelType !== '111' && channelType !== '112' && channelType !== '118' && channelType !== '131' && channelType !== '132') {
|
||||
type = -1;
|
||||
// 1-球机;2-半球;3-固定枪机;4-遥控枪机
|
||||
} else if (item.basicData.ptztype === 1) {
|
||||
type = 4;
|
||||
} else if (item.basicData.ptztype === 2) {
|
||||
type = 5;
|
||||
} else if (item.basicData.ptztype === 3 || item.basicData.ptztype === 4) {
|
||||
type = 6;
|
||||
}
|
||||
} else {
|
||||
if (item.basicData.subCount > 0 || item.basicData.parental === 1) {
|
||||
type = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
let node = {
|
||||
name: item.name || item.id,
|
||||
isLeaf: true,
|
||||
id: item.id,
|
||||
deviceId: item.deviceId,
|
||||
type: type,
|
||||
online: item.status === 2,
|
||||
userData: item.basicData,
|
||||
};
|
||||
|
||||
if (channelType === '111' || channelType === '112' || channelType === '118' || channelType === '131' || channelType === '132') {
|
||||
nodeList.push(node);
|
||||
}
|
||||
}
|
||||
resolve(nodeList);
|
||||
} else {
|
||||
resolve([]);
|
||||
}
|
||||
},
|
||||
reset: function () {
|
||||
this.$forceUpdate();
|
||||
},
|
||||
},
|
||||
destroyed() { },
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.device-tree-main-box {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.device-online {
|
||||
color: #252525;
|
||||
}
|
||||
|
||||
.device-offline {
|
||||
color: #727272;
|
||||
}
|
||||
</style>
|
||||
203
vue/src/views/components/player/deviceLiveStream.vue
Normal file
203
vue/src/views/components/player/deviceLiveStream.vue
Normal file
@@ -0,0 +1,203 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row>
|
||||
<span style="margin-left: 10px" prop="channelName">通道名称:</span>
|
||||
<el-select v-model="channelId" placeholder="请选择" @change="changeChannel()" size="small">
|
||||
<el-option v-for="option in channelList" :key="option.value" :label="option.label" :value="option.value"></el-option>
|
||||
</el-select>
|
||||
<span style="margin: 10px 10px 10px 30px">开启拉流:</span>
|
||||
<el-switch v-model="pushStream" active-color="#13ce66" inactive-color="#c4c6c9" style="border-radius: 10px" :disabled="channelId === ''" @change="startPushStream"></el-switch>
|
||||
<span style="margin: 10px 10px 10px 30px">开启直播录像:</span>
|
||||
<el-switch v-model="playrecord" active-color="#13ce66" inactive-color="#c4c6c9" style="border-radius: 10px" :disabled="channelId === ''" @change="startPlayRecord"></el-switch>
|
||||
</el-row>
|
||||
<player ref="player" :playerinfo="playinfo" class="components-container"></player>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import player from '@/views/components/player/player.vue';
|
||||
import { startPlay, closeStream, listChannel } from '@/api/iot/channel';
|
||||
import { startPlayRecord } from '@/api/iot/record';
|
||||
|
||||
export default {
|
||||
name: 'device-live-stream',
|
||||
components: {
|
||||
player,
|
||||
},
|
||||
props: {
|
||||
device: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 获取到父组件传递的device后
|
||||
device: function (newVal, oldVal) {
|
||||
this.deviceInfo = newVal;
|
||||
if (this.deviceInfo.channelId) {
|
||||
this.channelId = this.deviceInfo.channelId;
|
||||
this.changeChannel();
|
||||
}
|
||||
if (this.deviceInfo && this.deviceInfo.deviceId !== 0) {
|
||||
this.queryParams.deviceSipId = this.deviceInfo.serialNumber;
|
||||
this.deviceId = this.device.serialNumber;
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
deviceInfo: {},
|
||||
deviceId: '',
|
||||
channelId: '',
|
||||
streamId: '',
|
||||
ssrc: '',
|
||||
playurl: '',
|
||||
playinfo: {},
|
||||
playrecord: false,
|
||||
playrecording: false,
|
||||
playing: false,
|
||||
pushStream: false,
|
||||
retrycount: 0,
|
||||
channelList: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
deviceSipId: null,
|
||||
channelSipId: null,
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.queryParams.deviceSipId = this.device.serialNumber;
|
||||
this.deviceId = this.device.serialNumber;
|
||||
this.getList();
|
||||
this.playinfo = {
|
||||
playtype: 'play',
|
||||
deviceId: this.device.serialNumber,
|
||||
};
|
||||
},
|
||||
destroyed() {
|
||||
console.log("destroyed");
|
||||
this.closeDestroy(false);
|
||||
},
|
||||
methods: {
|
||||
/** 查询监控设备通道信息列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listChannel(this.queryParams).then((response) => {
|
||||
this.channelList = response.rows.map((item) => {
|
||||
return { value: item.channelSipId, label: item.channelName };
|
||||
});
|
||||
console.log(this.channelList);
|
||||
});
|
||||
},
|
||||
changeChannel() {
|
||||
this.playinfo.channelId = this.channelId;
|
||||
this.startPlayer();
|
||||
},
|
||||
// 直播播放
|
||||
TimeoutCallback() {
|
||||
this.closeDestroy(false);
|
||||
this.retrycount = 0;
|
||||
setTimeout(() => {
|
||||
this.startPlayer();
|
||||
}, 1000);
|
||||
},
|
||||
startPushStream() {
|
||||
if (!this.channelId) {
|
||||
console.log('开始通道: [' + this.channelId + ']');
|
||||
return;
|
||||
}
|
||||
console.log('推流状态: [' + this.pushStream + ']');
|
||||
if (this.pushStream) {
|
||||
this.startPlayer();
|
||||
} else {
|
||||
this.closeDestroy(true);
|
||||
}
|
||||
},
|
||||
startPlayRecord() {
|
||||
console.log('推流状态: [' + this.pushStream + ']');
|
||||
this.closeDestroy(true);
|
||||
setTimeout(() => {
|
||||
this.startPlayer();
|
||||
}, 500);
|
||||
},
|
||||
// 开启直播播放器
|
||||
startPlayer() {
|
||||
if (!this.channelId) {
|
||||
console.log('直播录像通道: [' + this.channelId + ']');
|
||||
return;
|
||||
}
|
||||
this.deviceId = this.queryParams.deviceSipId;
|
||||
if (this.playing) {
|
||||
this.closeDestroy(false);
|
||||
}
|
||||
if (!this.$refs.player.isInit) {
|
||||
this.$refs.player.init();
|
||||
}
|
||||
this.$refs.player.registercallback('loadingTimeout', this.TimeoutCallback);
|
||||
this.$refs.player.registercallback('delayTimeout', this.TimeoutCallback);
|
||||
if (this.playrecord) {
|
||||
startPlayRecord(this.deviceId, this.channelId).then((response) => {
|
||||
console.log('开始录像:' + this.deviceId + ' : ' + this.channelId);
|
||||
const res = response.data;
|
||||
this.streamId = res.streamId;
|
||||
this.playurl = res.playurl;
|
||||
this.$refs.player.play(res.playurl);
|
||||
this.playing = true;
|
||||
this.playrecording = true;
|
||||
this.pushStream = true;
|
||||
});
|
||||
} else {
|
||||
startPlay(this.deviceId, this.channelId).then((response) => {
|
||||
console.log('开始推流: [' + this.streamId + ']');
|
||||
const res = response.data;
|
||||
this.streamId = res.streamId;
|
||||
this.playurl = res.playurl;
|
||||
this.$refs.player.play(res.playurl);
|
||||
this.playing = true;
|
||||
this.playrecording = false;
|
||||
this.pushStream = true;
|
||||
});;
|
||||
}
|
||||
},
|
||||
closeStream(force) {
|
||||
if (force) {
|
||||
if (this.playing && this.streamId) {
|
||||
console.log('关闭推流3: [' + this.streamId + ']');
|
||||
closeStream(this.deviceId, this.channelId, this.streamId).then((res) => {
|
||||
this.streamId = '';
|
||||
this.ssrc = '';
|
||||
this.playurl = '';
|
||||
this.pushStream = false;
|
||||
});
|
||||
this.playing = false;
|
||||
this.playrecording = false;
|
||||
}
|
||||
} else {
|
||||
if (this.playrecording === true) {
|
||||
return;
|
||||
}
|
||||
if (this.playing && this.streamId) {
|
||||
console.log('关闭推流3: [' + this.streamId + ']');
|
||||
closeStream(this.deviceId, this.channelId, this.streamId).then((res) => {
|
||||
this.streamId = '';
|
||||
this.ssrc = '';
|
||||
this.playurl = '';
|
||||
this.pushStream = false;
|
||||
});
|
||||
this.playing = false;
|
||||
this.playrecording = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
closeDestroy(force) {
|
||||
this.closeStream(force);
|
||||
this.$refs.player.destroy();
|
||||
},
|
||||
destroy() {
|
||||
this.$refs.player.destroy();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
291
vue/src/views/components/player/deviceVideo.vue
Normal file
291
vue/src/views/components/player/deviceVideo.vue
Normal file
@@ -0,0 +1,291 @@
|
||||
<template>
|
||||
<div style="display: block; width: 1000px">
|
||||
<div style="display: flex">
|
||||
<el-row>
|
||||
<span style="margin-left: 10px" prop="channelName">通道:</span>
|
||||
<el-select v-model="channelId" placeholder="请选择" @change="changeChannel" style="width: 200px; margin-right: 10px" size="small">
|
||||
<el-option v-for="option in channelList" :key="option.value" :label="option.label" :value="option.value"></el-option>
|
||||
</el-select>
|
||||
<span style="overflow: auto; margin-left: 10px">日期:</span>
|
||||
<el-date-picker v-model="queryDate" type="date" size="small" value-format="yyyy-MM-dd" clearable placeholder="选择日期" style="width: 180px; margin-right: 10px" />
|
||||
<el-button-group style="margin: 0">
|
||||
<el-button size="mini" type="success" title="查看录像" @click="loadDevRecord()" :disabled="channelId === '' || !queryDate">
|
||||
<i class="el-icon-video-camera" />
|
||||
查看
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
|
||||
<span style="margin-left: 82px; overflow: auto">时间:</span>
|
||||
<el-button-group>
|
||||
<el-time-picker
|
||||
size="small"
|
||||
is-range
|
||||
align="left"
|
||||
v-model="timeRange"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
placeholder="选择时间范围"
|
||||
@change="timePickerChange"
|
||||
style="width: 200px"
|
||||
:disabled="channelId === '' || !queryDate"
|
||||
></el-time-picker>
|
||||
</el-button-group>
|
||||
<el-button-group style="margin: 0 0 0 10px">
|
||||
<el-button size="mini" type="primary" title="下载选定录像" @click="downloadRecord()" :disabled="channelId === '' || !timeRange">
|
||||
<i class="el-icon-download" />
|
||||
转存
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</el-row>
|
||||
</div>
|
||||
<player ref="playbacker" :playerinfo="playinfo" class="components-container"></player>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import player from '@/views/components/player/player.vue';
|
||||
import { listChannel, playback, closeStream, playbackSeek } from '@/api/iot/channel';
|
||||
import { getDevRecord, startDownloadRecord } from '@/api/iot/record';
|
||||
|
||||
export default {
|
||||
name: 'DeviceVideo',
|
||||
components: {
|
||||
player,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
deviceId: '',
|
||||
channelId: '',
|
||||
streamId: '',
|
||||
ssrc: '',
|
||||
playurl: '',
|
||||
queryDate: '',
|
||||
playing: false,
|
||||
vodData: {},
|
||||
hisData: [],
|
||||
playinfo: {},
|
||||
channelList: [],
|
||||
playbackinfo: {},
|
||||
timeRange: null,
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
deviceSipId: null,
|
||||
channelSipId: null,
|
||||
},
|
||||
};
|
||||
},
|
||||
props: {
|
||||
device: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 获取到父组件传递的device后
|
||||
device: function (newVal, oldVal) {
|
||||
this.deviceInfo = newVal;
|
||||
if (this.deviceInfo && this.deviceInfo.deviceId !== 0) {
|
||||
this.queryParams.deviceSipId = this.deviceInfo.serialNumber;
|
||||
this.deviceId = this.device.serialNumber;
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.queryParams.deviceSipId = this.device.serialNumber;
|
||||
this.deviceId = this.device.serialNumber;
|
||||
this.getList();
|
||||
this.playinfo = {
|
||||
playtype: 'playback',
|
||||
deviceId: this.device.serialNumber,
|
||||
};
|
||||
},
|
||||
beforeDestroy() {},
|
||||
destroyed() {
|
||||
this.closeStream();
|
||||
},
|
||||
methods: {
|
||||
/** 查询监控设备通道信息列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listChannel(this.queryParams).then((response) => {
|
||||
this.channelList = response.rows.map((item) => {
|
||||
return { value: item.channelSipId, label: item.channelName };
|
||||
});
|
||||
});
|
||||
},
|
||||
// 改变通道
|
||||
changeChannel() {
|
||||
this.playinfo.channelId = this.channelId;
|
||||
},
|
||||
initUrl(data) {
|
||||
if (data) {
|
||||
this.streamId = data.ssrc;
|
||||
this.ssrc = data.ssrc;
|
||||
this.playurl = data.playurl;
|
||||
} else {
|
||||
this.streamId = '';
|
||||
this.ssrc = '';
|
||||
this.playurl = '';
|
||||
}
|
||||
},
|
||||
getBeijingTime(queryDate) {
|
||||
// 计算与UTC的时区差,对于北京时间来说是8小时
|
||||
let offset = 8 * 60 * 60 * 1000;
|
||||
// 加上时区差,得到北京时间
|
||||
let beijingTime = new Date(new Date(queryDate).getTime() - offset);
|
||||
return beijingTime.getTime();
|
||||
},
|
||||
// 录像控制
|
||||
loadDevRecord() {
|
||||
this.$refs.playbacker.registercallback('playbackSeek', this.seekPlay);
|
||||
if (this.queryDate === '' || this.queryDate === null){
|
||||
this.$message.error('请选择日期');
|
||||
return;
|
||||
}
|
||||
if (this.deviceId && this.channelId) {
|
||||
const date = this.getBeijingTime(this.queryDate);
|
||||
const start = date / 1000;
|
||||
const end = Math.floor((date + 24 * 60 * 60 * 1000 - 1) / 1000);
|
||||
const query = {
|
||||
start: start,
|
||||
end: end,
|
||||
};
|
||||
this.vodData = {
|
||||
start: start,
|
||||
end: end,
|
||||
base: start,
|
||||
};
|
||||
this.setTime(this.queryDate + ' 00:00:00', this.queryDate + ' 23:59:59');
|
||||
getDevRecord(this.deviceId, this.channelId, query).then((res) => {
|
||||
this.hisData = res.data.recordItems;
|
||||
if (res.data.recordItems) {
|
||||
const len = this.hisData.length;
|
||||
if (len > 0) {
|
||||
if (this.hisData[0].start < start) {
|
||||
this.hisData[0].start = start;
|
||||
this.vodData.start = start;
|
||||
} else {
|
||||
this.vodData.start = this.hisData[0].start;
|
||||
}
|
||||
if (this.hisData[0].end !== 0 &&this.hisData[0].end < end) {
|
||||
this.vodData.end = this.hisData[0].end;
|
||||
}
|
||||
this.playback();
|
||||
} else {
|
||||
this.$message({
|
||||
type: 'warning',
|
||||
message: '请确认设备是否支持录像,或者设备SD卡是否正确插入!',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.$message({
|
||||
type: 'warning',
|
||||
message: '请确认设备是否支持录像,或者设备SD卡是否正确插入!',
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
/**录像播放*/
|
||||
playback() {
|
||||
const query = {
|
||||
start: this.vodData.start,
|
||||
end: this.vodData.end,
|
||||
};
|
||||
if (this.ssrc) {
|
||||
closeStream(this.deviceId, this.channelId, this.ssrc).then((res) => {
|
||||
playback(this.deviceId, this.channelId, query)
|
||||
.then((res) => {
|
||||
this.initUrl(res.data);
|
||||
})
|
||||
.finally(() => {
|
||||
this.triggerPlay(this.hisData);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
playback(this.deviceId, this.channelId, query)
|
||||
.then((res) => {
|
||||
this.initUrl(res.data);
|
||||
})
|
||||
.finally(() => {
|
||||
this.triggerPlay(this.hisData);
|
||||
});
|
||||
}
|
||||
},
|
||||
/**触发播放*/
|
||||
triggerPlay(playTimes) {
|
||||
this.$refs.playbacker.playback(this.playurl, playTimes);
|
||||
this.playing = true;
|
||||
},
|
||||
/**录像播放*/
|
||||
seekPlay(s) {
|
||||
const curTime = this.vodData.base + s.hour * 3600 + s.min * 60 + s.second;
|
||||
const seekRange = curTime - this.vodData.start;
|
||||
if (this.ssrc) {
|
||||
const query = {
|
||||
seek: seekRange,
|
||||
};
|
||||
const _this = this;
|
||||
playbackSeek(this.deviceId, this.channelId, this.streamId, query).then((res) => {
|
||||
_this.$refs.playbacker.setPlaybackStartTime(curTime);
|
||||
});
|
||||
}
|
||||
},
|
||||
/**关闭播放流*/
|
||||
closeStream() {
|
||||
if (this.playing && this.streamId) {
|
||||
closeStream(this.deviceId, this.channelId, this.streamId).then((res) => {
|
||||
this.streamId = '';
|
||||
this.ssrc = '';
|
||||
this.playurl = '';
|
||||
this.playing = false;
|
||||
});
|
||||
// this.$refs.playbacker.destroy();
|
||||
}
|
||||
},
|
||||
/**销毁录像播放器*/
|
||||
destroy() {
|
||||
if (this.playing && this.streamId) {
|
||||
this.$refs.playbacker.destroy();
|
||||
}
|
||||
},
|
||||
closeDestroy() {
|
||||
this.closeStream();
|
||||
this.destroy();
|
||||
},
|
||||
/**设置时间*/
|
||||
timePickerChange: function (val) {
|
||||
this.setTime(val[0], val[1]);
|
||||
},
|
||||
setTime: function (startTime, endTime) {
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
this.timeRange = [startTime, endTime];
|
||||
},
|
||||
/**下载录像*/
|
||||
downloadRecord: function () {
|
||||
const start = new Date(this.startTime).getTime() / 1000;
|
||||
const end = new Date(this.endTime).getTime() / 1000;
|
||||
const query = {
|
||||
startTime: start,
|
||||
endTime: end,
|
||||
speed: '4',
|
||||
};
|
||||
startDownloadRecord(this.deviceId, this.channelId, query).then((res) => {
|
||||
console.log('开始转存到流服务器:' + this.deviceId + ' : ' + this.channelId);
|
||||
if (res.code === 200 ) {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '转存到流服务器,请前往视频中心->录像管理查看!',
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
62
vue/src/views/components/player/easyplayer.vue
Normal file
62
vue/src/views/components/player/easyplayer.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div id="easyplayer"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'player',
|
||||
data() {
|
||||
return {
|
||||
easyPlayer: null
|
||||
};
|
||||
},
|
||||
props: ['videoUrl', 'error', 'hasaudio', 'height'],
|
||||
mounted () {
|
||||
let paramUrl = decodeURIComponent(this.$route.params.url)
|
||||
this.$nextTick(() =>{
|
||||
if (typeof (this.videoUrl) == "undefined") {
|
||||
this.videoUrl = paramUrl;
|
||||
}
|
||||
console.log("初始化时的地址为: " + this.videoUrl)
|
||||
this.play(this.videoUrl)
|
||||
})
|
||||
},
|
||||
watch:{
|
||||
videoUrl(newData, oldData){
|
||||
this.play(newData)
|
||||
},
|
||||
immediate:true
|
||||
},
|
||||
methods: {
|
||||
play: function (url) {
|
||||
console.log(this.height)
|
||||
if (this.easyPlayer != null) {
|
||||
this.easyPlayer.destroy();
|
||||
}
|
||||
if (typeof (this.height) == "undefined") {
|
||||
this.height = false
|
||||
}
|
||||
this.easyPlayer = new WasmPlayer(null, 'easyplayer', this.eventcallbacK, {Height: this.height})
|
||||
this.easyPlayer.play(url, 1)
|
||||
},
|
||||
pause: function () {
|
||||
this.easyPlayer.destroy();
|
||||
this.easyPlayer = null
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
this.easyPlayer.destroy();
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.LodingTitle {
|
||||
min-width: 70px;
|
||||
}
|
||||
/* 隐藏logo */
|
||||
.iconqingxiLOGO {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
363
vue/src/views/components/player/jessibuca.vue
Normal file
363
vue/src/views/components/player/jessibuca.vue
Normal file
@@ -0,0 +1,363 @@
|
||||
<template>
|
||||
<div ref="container" @dblclick="fullscreenSwich"
|
||||
style="width:100%;height:100%;background-color: #000000;margin:0 auto;">
|
||||
<div class="buttons-box" id="buttonsBox">
|
||||
<div class="buttons-box-left">
|
||||
<i v-if="!playing" class="iconfont icon-play jessibuca-btn" @click="playBtnClick"></i>
|
||||
<i v-if="playing" class="iconfont icon-pause jessibuca-btn" @click="pause"></i>
|
||||
<i class="iconfont icon-stop jessibuca-btn" @click="destroy"></i>
|
||||
<i v-if="isNotMute" class="iconfont icon-audio-high jessibuca-btn" @click="mute()"></i>
|
||||
<i v-if="!isNotMute" class="iconfont icon-audio-mute jessibuca-btn" @click="cancelMute()"></i>
|
||||
</div>
|
||||
<div class="buttons-box-right">
|
||||
<span class="jessibuca-btn">{{ kBps }} kb/s</span>
|
||||
<i class="iconfont icon-camera1196054easyiconnet jessibuca-btn" @click="screenshot"
|
||||
style="font-size: 1rem !important"></i>
|
||||
<i class="iconfont icon-shuaxin11 jessibuca-btn" @click="playBtnClick"></i>
|
||||
<i v-if="!fullscreen" class="iconfont icon-weibiaoti10 jessibuca-btn" @click="fullscreenSwich"></i>
|
||||
<i v-if="fullscreen" class="iconfont icon-weibiaoti11 jessibuca-btn" @click="fullscreenSwich"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let jessibucaPlayer = {};
|
||||
export default {
|
||||
name: 'jessibuca',
|
||||
data() {
|
||||
return {
|
||||
playing: false,
|
||||
isNotMute: false,
|
||||
quieting: false,
|
||||
fullscreen: false,
|
||||
loaded: false, // mute
|
||||
speed: 0,
|
||||
performance: "", // 工作情况
|
||||
kBps: 0,
|
||||
btnDom: null,
|
||||
videoInfo: null,
|
||||
volume: 1,
|
||||
rotate: 0,
|
||||
vod: true, // 点播
|
||||
forceNoOffscreen: false,
|
||||
};
|
||||
},
|
||||
props: ['videoUrl', 'error', 'hasAudio', 'height'],
|
||||
mounted() {
|
||||
window.onerror = (msg) => {
|
||||
// console.error(msg)
|
||||
};
|
||||
console.log(this._uid)
|
||||
let paramUrl = decodeURIComponent(this.$route.params.url)
|
||||
this.$nextTick(() => {
|
||||
this.updatePlayerDomSize()
|
||||
window.onresize = () => {
|
||||
this.updatePlayerDomSize()
|
||||
}
|
||||
if (typeof (this.videoUrl) == "undefined") {
|
||||
this.videoUrl = paramUrl;
|
||||
}
|
||||
this.btnDom = document.getElementById("buttonsBox");
|
||||
console.log("初始化时的地址为: " + this.videoUrl)
|
||||
this.play(this.videoUrl)
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
videoUrl(newData, oldData) {
|
||||
this.play(newData)
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
methods: {
|
||||
updatePlayerDomSize() {
|
||||
let dom = this.$refs.container;
|
||||
let width = dom.parentNode.clientWidth
|
||||
let height = (9 / 16) * width
|
||||
|
||||
const clientHeight = Math.min(document.body.clientHeight, document.documentElement.clientHeight)
|
||||
if (height > clientHeight) {
|
||||
height = clientHeight
|
||||
width = (16 / 9) * height
|
||||
}
|
||||
|
||||
dom.style.width = width + 'px';
|
||||
dom.style.height = height + "px";
|
||||
},
|
||||
create() {
|
||||
let options = {};
|
||||
console.log("hasAudio " + this.hasAudio)
|
||||
|
||||
jessibucaPlayer[this._uid] = new window.JessibucaPro(Object.assign(
|
||||
{
|
||||
container: this.$refs.container,
|
||||
autoWasm: true,
|
||||
background: "",
|
||||
controlAutoHide: false,
|
||||
debug: false,
|
||||
decoder: "/js/jessibuca-pro/decoder-pro.js",
|
||||
forceNoOffscreen: true,
|
||||
hasAudio: typeof (this.hasAudio) == "undefined" ? true : this.hasAudio,
|
||||
hasVideo: true,
|
||||
heartTimeout: 5,
|
||||
heartTimeoutReplay: true,
|
||||
heartTimeoutReplayTimes: 3,
|
||||
hiddenAutoPause: false,
|
||||
hotKey: false,
|
||||
isFlv: false,
|
||||
isFullResize: false,
|
||||
isNotMute: this.isNotMute,
|
||||
isResize: false,
|
||||
keepScreenOn: false,
|
||||
loadingText: "请稍等, 视频加载中......",
|
||||
loadingTimeout: 10,
|
||||
loadingTimeoutReplay: true,
|
||||
loadingTimeoutReplayTimes: 3,
|
||||
openWebglAlignment: false,
|
||||
operateBtns: {
|
||||
fullscreen: false,
|
||||
screenshot: false,
|
||||
play: false,
|
||||
audio: false,
|
||||
record: false
|
||||
},
|
||||
recordType: "webm",
|
||||
rotate: 0,
|
||||
showBandwidth: false,
|
||||
supportDblclickFullscreen: false,
|
||||
timeout: 10,
|
||||
useMSE: location.hostname !== "localhost" && location.protocol !== "https:",
|
||||
useOffscreen: false,
|
||||
useWCS: location.hostname === "localhost" || location.protocol === "https",
|
||||
useWebFullScreen: false,
|
||||
videoBuffer: 0,
|
||||
wasmDecodeAudioSyncVideo: true,
|
||||
wasmDecodeErrorReplay: true,
|
||||
wcsUseVideoRender: true
|
||||
},
|
||||
options
|
||||
));
|
||||
let jessibuca = jessibucaPlayer[this._uid];
|
||||
let _this = this;
|
||||
jessibuca.on("load", function () {
|
||||
console.log("on load init");
|
||||
});
|
||||
|
||||
jessibuca.on("log", function (msg) {
|
||||
console.log("on log", msg);
|
||||
});
|
||||
jessibuca.on("record", function (msg) {
|
||||
console.log("on record:", msg);
|
||||
});
|
||||
jessibuca.on("pause", function () {
|
||||
_this.playing = false;
|
||||
});
|
||||
jessibuca.on("play", function () {
|
||||
_this.playing = true;
|
||||
});
|
||||
jessibuca.on("fullscreen", function (msg) {
|
||||
console.log("on fullscreen", msg);
|
||||
_this.fullscreen = msg
|
||||
});
|
||||
|
||||
jessibuca.on("mute", function (msg) {
|
||||
console.log("on mute", msg);
|
||||
_this.isNotMute = !msg;
|
||||
});
|
||||
jessibuca.on("audioInfo", function (msg) {
|
||||
console.log("audioInfo", msg);
|
||||
});
|
||||
|
||||
jessibuca.on("bps", function (bps) {
|
||||
// console.log('bps', bps);
|
||||
|
||||
});
|
||||
let _ts = 0;
|
||||
jessibuca.on("timeUpdate", function (ts) {
|
||||
// console.log('timeUpdate,old,new,timestamp', _ts, ts, ts - _ts);
|
||||
_ts = ts;
|
||||
});
|
||||
|
||||
jessibuca.on("videoInfo", function (info) {
|
||||
console.log("videoInfo", info);
|
||||
});
|
||||
|
||||
jessibuca.on("error", function (error) {
|
||||
console.log("error", error);
|
||||
});
|
||||
|
||||
jessibuca.on("timeout", function () {
|
||||
console.log("timeout");
|
||||
});
|
||||
|
||||
jessibuca.on('start', function () {
|
||||
console.log('start');
|
||||
})
|
||||
|
||||
jessibuca.on("performance", function (performance) {
|
||||
let show = "卡顿";
|
||||
if (performance === 2) {
|
||||
show = "非常流畅";
|
||||
} else if (performance === 1) {
|
||||
show = "流畅";
|
||||
}
|
||||
_this.performance = show;
|
||||
});
|
||||
jessibuca.on('buffer', function (buffer) {
|
||||
// console.log('buffer', buffer);
|
||||
})
|
||||
|
||||
jessibuca.on('stats', function (stats) {
|
||||
// console.log('stats', stats);
|
||||
})
|
||||
|
||||
jessibuca.on('kBps', function (kBps) {
|
||||
_this.kBps = Math.round(kBps);
|
||||
});
|
||||
|
||||
// 显示时间戳 PTS
|
||||
jessibuca.on('videoFrame', function () {
|
||||
|
||||
})
|
||||
|
||||
//
|
||||
jessibuca.on('metadata', function () {
|
||||
|
||||
});
|
||||
},
|
||||
playBtnClick: function (event) {
|
||||
this.play(this.videoUrl)
|
||||
},
|
||||
play: function (url) {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
this.destroy().then(() => {
|
||||
jessibucaPlayer[this._uid].on("play", () => {
|
||||
this.playing = true;
|
||||
this.loaded = true;
|
||||
});
|
||||
if (jessibucaPlayer[this._uid].hasLoaded()) {
|
||||
jessibucaPlayer[this._uid].play(url);
|
||||
} else {
|
||||
jessibucaPlayer[this._uid].on("load", () => {
|
||||
console.log("load 播放")
|
||||
jessibucaPlayer[this._uid].play(url);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.create();
|
||||
jessibucaPlayer[this._uid].on("play", () => {
|
||||
this.playing = true;
|
||||
this.loaded = true;
|
||||
});
|
||||
if (jessibucaPlayer[this._uid].hasLoaded()) {
|
||||
jessibucaPlayer[this._uid].play(url);
|
||||
} else {
|
||||
jessibucaPlayer[this._uid].on("load", () => {
|
||||
console.log("load 播放")
|
||||
jessibucaPlayer[this._uid].play(url);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
pause: function () {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].pause();
|
||||
}
|
||||
this.playing = false;
|
||||
this.err = "";
|
||||
this.performance = "";
|
||||
},
|
||||
screenshot: function () {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].screenshot();
|
||||
}
|
||||
},
|
||||
mute: function () {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].mute();
|
||||
}
|
||||
},
|
||||
cancelMute: function () {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].cancelMute();
|
||||
}
|
||||
},
|
||||
destroy: async function () {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
await jessibucaPlayer[this._uid].destroy().then(() => {
|
||||
this.create();
|
||||
});
|
||||
} else {
|
||||
this.create();
|
||||
}
|
||||
if (document.getElementById("buttonsBox") == null) {
|
||||
this.$refs.container.appendChild(this.btnDom)
|
||||
}
|
||||
jessibucaPlayer[this._uid] = null;
|
||||
this.playing = false;
|
||||
this.err = "";
|
||||
this.performance = "";
|
||||
|
||||
},
|
||||
eventcallbacK: function (type, message) {
|
||||
// console.log("player 事件回调")
|
||||
// console.log(type)
|
||||
// console.log(message)
|
||||
},
|
||||
fullscreenSwich: function () {
|
||||
let isFull = this.isFullscreen()
|
||||
jessibucaPlayer[this._uid].setFullscreen(!isFull)
|
||||
this.fullscreen = !isFull;
|
||||
},
|
||||
isFullscreen: function () {
|
||||
return document.fullscreenElement ||
|
||||
document.msFullscreenElement ||
|
||||
document.mozFullScreenElement ||
|
||||
document.webkitFullscreenElement || false;
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].destroy();
|
||||
}
|
||||
this.playing = false;
|
||||
this.loaded = false;
|
||||
this.performance = "";
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import '../css/iconfont.css';
|
||||
.buttons-box {
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
background-color: rgba(43, 51, 63, 0.7);
|
||||
position: absolute;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
user-select: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.jessibuca-btn {
|
||||
width: 20px;
|
||||
color: rgb(255, 255, 255);
|
||||
line-height: 27px;
|
||||
margin: 0px 10px;
|
||||
padding: 0px 2px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
font-size: 0.8rem !important;
|
||||
}
|
||||
|
||||
.buttons-box-right {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
347
vue/src/views/components/player/player.vue
Normal file
347
vue/src/views/components/player/player.vue
Normal file
@@ -0,0 +1,347 @@
|
||||
<template>
|
||||
<div class="root">
|
||||
<div class="container-shell">
|
||||
<div id="container" ref="container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let jessibucaPlayer = {};
|
||||
import { ptzdirection, ptzscale } from '@/api/iot/sipdevice';
|
||||
export default {
|
||||
name: 'player',
|
||||
props: {
|
||||
playerinfo: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
console.log(this._uid);
|
||||
},
|
||||
watch: {
|
||||
playerinfo: function (newVal, oldVal) {
|
||||
console.log('playerinfo 发生变化');
|
||||
this.playinfo = newVal;
|
||||
if (this.playinfo && this.playinfo.playtype !== '') {
|
||||
this.playtype = this.playinfo.playtype;
|
||||
}
|
||||
},
|
||||
},
|
||||
jessibuca: null,
|
||||
data() {
|
||||
return {
|
||||
isPlaybackPause: false,
|
||||
useWebGPU: false,
|
||||
isInit: false,
|
||||
playinfo: {},
|
||||
playtype: 'play',
|
||||
operateBtns: {},
|
||||
};
|
||||
},
|
||||
beforeDestroy() {},
|
||||
created() {
|
||||
this.playinfo = this.playerinfo;
|
||||
if (this.playinfo && this.playinfo.playtype !== '') {
|
||||
this.playtype = this.playinfo.playtype;
|
||||
}
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
var isSupportWebgpu = 'gpu' in navigator;
|
||||
if (isSupportWebgpu) {
|
||||
console.log('支持webGPU');
|
||||
this.useWebGPU = true;
|
||||
} else {
|
||||
console.log('暂不支持webGPU,降级到webgl渲染');
|
||||
this.useWebGPU = false;
|
||||
}
|
||||
|
||||
const useVconsole = this.isMobile() || this.isPad();
|
||||
if (useVconsole && window.VConsole) {
|
||||
new window.VConsole();
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.initplayer();
|
||||
});
|
||||
},
|
||||
initplayer() {
|
||||
this.isPlaybackPause = false;
|
||||
this.initconf();
|
||||
jessibucaPlayer[this._uid] = new window.JessibucaPro({
|
||||
container: this.$refs.container,
|
||||
decoder: '/js/jessibuca-pro/decoder-pro.js',
|
||||
videoBuffer: Number(0.2), // 缓存时长
|
||||
isResize: false,
|
||||
useWCS: false,
|
||||
useMSE: false,
|
||||
useSIMD: true,
|
||||
wcsUseVideoRender: false,
|
||||
loadingText: '加载中',
|
||||
debug: false,
|
||||
debugLevel: "debug",
|
||||
showBandwidth: true, // 显示网速
|
||||
showPlaybackOperate: true,
|
||||
operateBtns: this.operateBtns,
|
||||
forceNoOffscreen: true,
|
||||
isNotMute: false,
|
||||
showPerformance: false,
|
||||
// playFailedAndReplay: true,
|
||||
// networkDelayTimeoutReplay: true,
|
||||
playbackForwardMaxRateDecodeIFrame: 4,
|
||||
useWebGPU: this.useWebGPU, // 使用WebGPU
|
||||
});
|
||||
let jessibuca = jessibucaPlayer[this._uid];
|
||||
this.initcallback(jessibuca);
|
||||
this.isInit = true;
|
||||
},
|
||||
initconf() {
|
||||
if (this.playtype === 'play') {
|
||||
//直播按钮配置
|
||||
this.operateBtns = {
|
||||
fullscreen: true,
|
||||
zoom: true,
|
||||
ptz: true,
|
||||
play: true,
|
||||
};
|
||||
} else {
|
||||
//录像回放按钮配置
|
||||
this.operateBtns = {
|
||||
fullscreen: true,
|
||||
zoom: true,
|
||||
play: true,
|
||||
ptz: false,
|
||||
};
|
||||
}
|
||||
},
|
||||
initcallback(jessibuca) {
|
||||
const _this = this;
|
||||
jessibuca.on('error', function (error) {
|
||||
console.log('jessibuca error');
|
||||
console.log(error);
|
||||
//_this.destroy();
|
||||
});
|
||||
jessibuca.on("playFailedAndPaused", function (reason, lastFrameInfo, msg) {
|
||||
console.log('playFailedAndPaused', reason, msg);
|
||||
// lastFrameInfo 是最后一帧的画面,可以用来重播的时候,显示最后一帧画面。
|
||||
// msg 具体的错误信息。
|
||||
});
|
||||
jessibuca.on('visibilityChange', (value) => {
|
||||
if (value === true) {
|
||||
// 窗口显示
|
||||
console.log('visibilityChange true');
|
||||
} else {
|
||||
// 窗口隐藏
|
||||
console.log('visibilityChange false');
|
||||
}
|
||||
});
|
||||
jessibuca.on('pause', function (pause) {
|
||||
console.log('pause success!');
|
||||
console.log(pause);
|
||||
});
|
||||
jessibuca.on('loading', function (load) {
|
||||
console.log('loading success!');
|
||||
console.log(load);
|
||||
});
|
||||
jessibuca.on('stats', function (s) {
|
||||
console.log('stats is', s);
|
||||
});
|
||||
jessibuca.on('timeout', function (error) {
|
||||
console.log('timeout:', error);
|
||||
});
|
||||
jessibuca.on('playbackPreRateChange', (rate) => {
|
||||
jessibuca.forward(rate);
|
||||
});
|
||||
|
||||
let pre = 0;
|
||||
let cur = 0;
|
||||
jessibuca.on('timeUpdate', function (ts) {
|
||||
cur = parseInt(ts / 60000);
|
||||
if (pre !== cur) {
|
||||
pre++;
|
||||
}
|
||||
});
|
||||
jessibuca.on(JessibucaPro.EVENTS.ptz, (arrow) => {
|
||||
console.log('ptz arrow', arrow);
|
||||
_this.handlePtz(arrow);
|
||||
});
|
||||
jessibuca.on('crashLog', (data) => {
|
||||
console.log('crashLog is', data);
|
||||
});
|
||||
},
|
||||
registercallback(events, func) {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].on(events, func);
|
||||
}
|
||||
},
|
||||
isMobile() {
|
||||
return /iphone|ipad|android.*mobile|windows.*phone|blackberry.*mobile/i.test(window.navigator.userAgent.toLowerCase());
|
||||
},
|
||||
isPad() {
|
||||
return /ipad|android(?!.*mobile)|tablet|kindle|silk/i.test(window.navigator.userAgent.toLowerCase());
|
||||
},
|
||||
play(url) {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].play(url);
|
||||
}
|
||||
},
|
||||
pause() {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].pause();
|
||||
}
|
||||
},
|
||||
replay(url) {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].destroy().then(() => {
|
||||
this.initplayer();
|
||||
this.play(url);
|
||||
});
|
||||
} else {
|
||||
this.initplayer();
|
||||
this.play(url);
|
||||
}
|
||||
},
|
||||
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,
|
||||
};
|
||||
if (this.playinfo && this.playinfo.playtype !== '') {
|
||||
ptzdirection(this.playinfo.deviceId, this.playinfo.channelId, data).then(async (response) => {
|
||||
//console.log("云台方向控制:" + JSON.stringify(response));
|
||||
});
|
||||
}
|
||||
},
|
||||
playback(url, playTimes) {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].playback(url, {
|
||||
playList: playTimes,
|
||||
fps: 25, //FPS(定频(本地设置)生效)
|
||||
showControl: true,
|
||||
showRateBtn: true,
|
||||
isUseFpsRender: true, // 是否使用固定的fps渲染,如果设置的fps小于流推过来的,会造成内存堆积甚至溢出
|
||||
isCacheBeforeDecodeForFpsRender: false, // rfs渲染时,是否在解码前缓存数据
|
||||
supportWheel: true, // 是否支持滚动轴切换精度。
|
||||
rateConfig: [
|
||||
{ label: '正常', value: 1 },
|
||||
{ label: '2倍', value: 2 },
|
||||
{ label: '4倍', value: 4 },
|
||||
{ label: '8倍', value: 8 },
|
||||
],
|
||||
});
|
||||
this.isPlaybackPause = false;
|
||||
}
|
||||
},
|
||||
playbackPause() {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].playbackPause();
|
||||
this.isPlaybackPause = true;
|
||||
}
|
||||
},
|
||||
replayback(url, playTimes) {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].destroy().then(() => {
|
||||
this.initplayer();
|
||||
this.playback(url, playTimes);
|
||||
});
|
||||
} else {
|
||||
this.initplayer();
|
||||
this.playback(url, playTimes);
|
||||
}
|
||||
},
|
||||
setPlaybackStartTime(curTime) {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].setPlaybackStartTime(curTime);
|
||||
}
|
||||
},
|
||||
destroy() {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].destroy().then(() => {
|
||||
this.initplayer();
|
||||
});
|
||||
}
|
||||
},
|
||||
close() {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].close();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.root {
|
||||
display: flex;
|
||||
margin-right: 3rem;
|
||||
}
|
||||
|
||||
.container-shell {
|
||||
backdrop-filter: blur(5px);
|
||||
// background: hsla(0, 0%, 50%, 0.5);
|
||||
//background: #fff;
|
||||
//padding: 10px 4px 10px 4px;
|
||||
/* border: 2px solid black; */
|
||||
// width: auto;
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
// box-shadow: 0 5px 5px;
|
||||
}
|
||||
|
||||
.container-shell:before {
|
||||
//content: "设备播放器";
|
||||
position: absolute;
|
||||
color: darkgray;
|
||||
//top: 4px;
|
||||
left: 10px;
|
||||
//text-shadow: 1px 1px black;
|
||||
}
|
||||
|
||||
#container {
|
||||
background: rgba(13, 14, 27, 0.7);
|
||||
width: 1000px;
|
||||
height: 630px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.err {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
left: 10px;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.option {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 10px;
|
||||
display: flex;
|
||||
place-content: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.option span {
|
||||
color: white;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
#container {
|
||||
width: 90vw;
|
||||
height: 52.7vw;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
vue/src/views/components/player/public/web-player-pro.js
Normal file
1
vue/src/views/components/player/public/web-player-pro.js
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user