Commit b4194460 by 李乾广

Merge branch 'qxh-master' of http://git.keliuyun.com:55676/jiaxiuc123/miniProject into qxh-master

2 parents af56ea2f 3ecf4e47
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
"element-ui": "^2.15.14", "element-ui": "^2.15.14",
"less": "^4.2.0", "less": "^4.2.0",
"less-loader": "^11.1.3", "less-loader": "^11.1.3",
"lodash": "^4.17.21",
"moment": "^2.29.4", "moment": "^2.29.4",
"vant": "^2.12.54", "vant": "^2.12.54",
"vue": "^2.6.14", "vue": "^2.6.14",
......
const operateBtns = {
fullscreen: true,
screenshot: false,
play: true,
audio: true,
ptz: true, // 云台
zoom: true,// 电子放大
performance: true, // 视频流信息展示
record: true, // 录制
scale: true, // 显示模式:拉伸、缩放、正常
quality: false, // 视频清晰度
};
const extendOperateBtns = [];
export function getPlayerConfig () {
return {
decoder: '/jessibuca-pro/decoder-pro.js',
text: '', // TODO: 功能暂不清楚
keepScreenOn: true, // 屏幕常亮
isResize: false,
isNotMute: false,
debug: false, // 是否开启控制台调试打印
// 播放相关
videoBuffer: 0.1, // 缓存时长
videoBufferDelay: 0.2, // 缓存延迟(延迟超过会触发丢帧)
// TODO: 是否通过浏览器API判断是否支持后,再赋值true或false
// useMSE: false,
// useWCS: checkSupportWCSHevc(),
// useWCS: false,
// useSIMD: checkSupportSIMD(),
// wcsUseVideoRender: checkSupportWCSHevc(),
useWebFullScreen: false, // ios可能不支持系统级别全屏,可能要使用此配置
timeout: 10, // 在连接成功之前(loading)和播放中途(heart),如果超过设定时长无数据返回,则回调timeout事件
// 按钮和界面
loadingText: '加载中...',
controlAutoHide: true,
supportDblclickFullscreen: false,
showBandwidth: true, // 显示网速
// angle fontSize fontFamily
fullscreenWatermarkConfig: {
opacity: 0.3,
text: '',
},
operateBtns,
extendOperateBtns: [],
showPerformance: false, // 显示性能
qualityConfig: ['标清', '高清'],
// 云台控制
ptzClickType: 'mouseDownAndUp', // click mouseDownAndUp
ptzZoomShow: true,
ptzMoreArrowShow: true,
// ptzApertureShow: true, // 光圈
ptzFocusShow: true,
}
}
export function getRotateConfig() {
return {
name: 'rotate',
index: 1,
icon: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3QgeD0iMi40ODQzOCIgeT0iNi40ODQzOCIgd2lkdGg9IjEyLjAzMTIiIGhlaWdodD0iOC4wMzEyNSIgcng9IjAuODA3MjkyIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuOTY4NzUiLz4KPHBhdGggZD0iTTcuMzQ4NDMgMi42OTcwN0w0LjgzNTM0IDQuMTE3NTNMNC44NjE3MyAxLjIzMDlMNy4zNDg0MyAyLjY5NzA3WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTQuOTk2MjYgMi4xOTMzOEMzLjc5MTU2IDIuMjQwMDEgMi42NDQyNyAyLjcyMDM1IDEuNzY1NyAzLjU0NTkzQzAuODg3MTI2IDQuMzcxNTEgMC4zMzY0MzQgNS40ODY3NCAwLjIxNTA0OCA2LjY4NjIxTDEuNDA4OTUgNi44MDcwM0MxLjUwMTIgNS44OTU0MyAxLjkxOTczIDUuMDQ3ODYgMi41ODc0NCA0LjQyMDQyQzMuMjU1MTYgMy43OTI5OCA0LjEyNzEgMy40Mjc5MiA1LjA0MjY3IDMuMzkyNDhMNC45OTYyNiAyLjE5MzM4WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg==',
iconTitle: '旋转',
click: this.rotate,
}
}
export function getMirrorRotate() {
return {
name: 'mirrorRotate',
index: 2,
icon: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTAuNTIwOTcgMTQuNjY2N0w1LjY2NjY3IDMuNTE3NjZWMTQuNjY2N0gwLjUyMDk3WiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjY2NjY2NyIvPgo8cGF0aCBkPSJNMTUuNDc5IDE0LjY2NjdMMTAuMzMzMyAzLjUxNzY2VjE0LjY2NjdIMTUuNDc5WiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjY2NjY2NyIvPgo8cGF0aCBkPSJNOCAxVjE1IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuNTMzMzMzIi8+Cjwvc3ZnPgo=',
iconTitle: '镜像',
click: this.mirrorRotate,
}
}
export function getPlaybackConfig() {
return {
controlType: 'simple',
showRateBtn: true,
rateConfig: [
{ label: '0.5x', value: 0.5 },
{ label: '1x', value: 1 },
{ label: '2x', value: 2 },
{ label: '4x', value: 4 },
],
}
}
<template> <template>
<div ref="container" :class="containerClassName" ></div> <div ref="container"
:data-player-type="this.playerType"
:class="containerClassName"
@wheel="handleWheel"
>
</div>
</template> </template>
<script> <script>
import { merge } from 'lodash';
// checkSupportMSEHevc checkSupportSIMD, checkSupportWCSHevc // checkSupportMSEHevc checkSupportSIMD, checkSupportWCSHevc
import { getWatermarkCanvasImg } from './utils'; import { getWatermarkCanvasImg } from './utils';
import { getPlayerConfig, getRotateConfig, getMirrorRotate, getPlaybackConfig } from './config';
export default { export default {
name: 'VionPlayer', name: 'VionPlayer',
...@@ -25,6 +32,11 @@ export default { ...@@ -25,6 +32,11 @@ export default {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
isPlayback: {
type: Boolean,
default: false,
},
// 是否是小程序
isXCXPage: { isXCXPage: {
type: Boolean, type: Boolean,
default: false, default: false,
...@@ -35,6 +47,9 @@ export default { ...@@ -35,6 +47,9 @@ export default {
// eslint-disable-next-line // eslint-disable-next-line
_jessibuca: null, _jessibuca: null,
playOriginUrl: '', playOriginUrl: '',
rotateNum: 0,
mirrorIndex: 0,
playerType: 'live', // 播放器类型:live playback
player: { player: {
height: 0, height: 0,
...@@ -44,7 +59,11 @@ export default { ...@@ -44,7 +59,11 @@ export default {
}, },
computed: { computed: {
containerClassName() { containerClassName() {
return this.hideControls ? ['vion-player', 'hide-controls'] : 'vion-player'; // 样式不支持响应式,因为会覆盖掉播放器自身添加的样式
const list = ['vion-player'];
if (this.hideControls) list.push('hide-controls');
// if (this.playerType === 'playback') list.push('playback-status');
return list;
}, },
}, },
mounted() { mounted() {
...@@ -56,38 +75,13 @@ export default { ...@@ -56,38 +75,13 @@ export default {
}, },
methods:{ methods:{
init(url, params = {}) { init(url, params = {}) {
let options = Object.assign({ const options = merge(getPlayerConfig(), {
container: this.$refs.container, container: this.$refs.container,
decoder: '/jessibuca-pro/decoder-pro.js', useWebFullScreen: this.isXCXPage,
text: '', // TODO: 功能暂不清楚
keepScreenOn: true, // 屏幕常亮
isResize: false,
isNotMute: false,
debug: false, // 是否开启控制台调试打印
// 播放相关
videoBuffer: 0.1, // 缓存时长
videoBufferDelay: 0.2, // 缓存延迟(延迟超过会触发丢帧)
// TODO: 是否通过浏览器API判断是否支持后,再赋值true或false
// useMSE: false,
// useWCS: checkSupportWCSHevc(),
// useWCS: false,
// useSIMD: checkSupportSIMD(),
// wcsUseVideoRender: checkSupportWCSHevc(),
useWebFullScreen: this.isXCXPage, // ios可能不支持系统级别全屏,可能要使用此配置
timeout: 10, // 在连接成功之前(loading)和播放中途(heart),如果超过设定时长无数据返回,则回调timeout事件
// 按钮和界面
loadingText: '加载中...',
controlAutoHide: true,
supportDblclickFullscreen: false,
showBandwidth: true, // 显示网速
fullscreenWatermarkConfig: { fullscreenWatermarkConfig: {
text: this.watermarkText || '', text: this.watermarkText || '',
}, },
operateBtns: { operateBtns: {
fullscreen: true,
screenshot: false,
play: !this.isXCXPage, play: !this.isXCXPage,
audio: !this.isXCXPage, audio: !this.isXCXPage,
ptz: this.showPtz, // 云台 ptz: this.showPtz, // 云台
...@@ -95,23 +89,13 @@ export default { ...@@ -95,23 +89,13 @@ export default {
performance: !this.isXCXPage, // 视频流信息展示 performance: !this.isXCXPage, // 视频流信息展示
record: !this.isXCXPage, // 录制 record: !this.isXCXPage, // 录制
scale: !this.isXCXPage, // 显示模式:拉伸、缩放、正常 scale: !this.isXCXPage, // 显示模式:拉伸、缩放、正常
quality: false, // 视频清晰度
}, },
extendOperateBtns: [], extendOperateBtns: [getRotateConfig.call(this), getMirrorRotate.call(this)],
showPerformance: false, // 显示性能
qualityConfig: ['标清', '高清'],
// 云台控制
ptzClickType: 'click', // click mouseDownAndUp
ptzZoomShow: true,
ptzMoreArrowShow: true,
// ptzApertureShow: true, // 光圈
ptzFocusShow: true,
}); });
console.log('init-options', options); console.log('init-options', options);
this._jessibuca = new window.JessibucaPro(options); this._jessibuca = new window.JessibucaPro(options);
console.log('_jessibuca', this._jessibuca);
this.registerEvent(this._jessibuca); this.registerEvent(this._jessibuca);
...@@ -146,27 +130,73 @@ export default { ...@@ -146,27 +130,73 @@ export default {
// console.log('ptz', arrow); // console.log('ptz', arrow);
this.$emit('ptz-control', arrow); this.$emit('ptz-control', arrow);
}); });
// 改变倍速事件
playerIns.on('playbackPreRateChange', (rate) => {
// console.log('playbackPreRateChange', rate);
this.$emit('rate-change', rate, this.setForward);
});
// 点击时间轴跳转事件
playerIns.on('playbackSeek', (data) => {
console.log('playbackSeek', data);
// jessibuca.setPlaybackStartTime(data.ts);
});
// 电子放大
},
setForward(rate) {
this._jessibuca.forward(rate);
}, },
getWatermarkBase64() { getWatermarkBase64() {
const data = getWatermarkCanvasImg(150, 48, '文安智能'); const data = getWatermarkCanvasImg(150, 48, '文安智能');
console.log('getWatermarkBase64', data); console.log('getWatermarkBase64', data);
}, },
delayPlay(url) { delayPlay(url, isPlayback = false) {
setTimeout(() => { setTimeout(() => {
this._jessibuca.play(url); if (isPlayback) {
this._jessibuca.playback(url, getPlaybackConfig());
this.playerType = 'playback';
} else {
this._jessibuca.play(url);
this.playerType = 'live';
}
this.playOriginUrl = url; this.playOriginUrl = url;
}, 150); }, 150);
}, },
// 获取是否是回放状态
getPlaybackStatus(status) {
// 传入参数 优先级 大于属性
return status || this.isPlayback;
},
// true 放大,false 缩小
zoom(direction = true) {
// temp1.player.zoom.expandPrecision()
const targetPlayer = this._jessibuca.player;
// zooming 表示处于电子放大状态
if (targetPlayer && targetPlayer.zooming && targetPlayer.zoom) {
if (direction) targetPlayer.zoom.expandPrecision();
else targetPlayer.zoom.narrowPrecision();
}
},
handleWheel(event) {
console.log('handleWheel', event);
// TODO: 增加防抖动
event.stopPropagation();
const direction = event.deltaY < 0;
this.zoom(direction);
},
// 暴露方法 // 暴露方法
play(url) { play(url, isPlayback = false) {
if (url) { if (url) {
const playbackStatus = this.getPlaybackStatus(isPlayback);
// 播放地址不同,则先停止再播放。地址相同,相当于暂停后,继续播放 // 播放地址不同,则先停止再播放。地址相同,相当于暂停后,继续播放
if (this.playOriginUrl && this.playOriginUrl !== url) { // 若是回放,则直接播放
if (!playbackStatus && this.playOriginUrl && this.playOriginUrl !== url) {
// 切换视频流地址,先销毁当前实例,再创建新实例,避免内存溢出 // 切换视频流地址,先销毁当前实例,再创建新实例,避免内存溢出
this.replay(url); this.replay(url);
} else { } else {
this.delayPlay(url); this.delayPlay(url, playbackStatus);
} }
} else { } else {
console.error('The url is not valid'); console.error('The url is not valid');
...@@ -194,6 +224,23 @@ export default { ...@@ -194,6 +224,23 @@ export default {
return Promise.resolve(true); return Promise.resolve(true);
} }
}, },
rotate(num) {
let targetRotate = 0;
if (Number.isInteger(num)) {
targetRotate = num;
} else {
this.rotateNum++;
targetRotate = (this.rotateNum % 4) * 90;
}
this._jessibuca.setRotate(targetRotate);
},
mirrorRotate() {
// , 'vertical'
const typeList = ['', 'level'];
this.mirrorIndex++;
const targetType = typeList[this.mirrorIndex % 2];
this._jessibuca.setMirrorRotate(targetType);
},
screenshot(filename = '', format = 'jpeg', quality = 0.2, type = 'base64') { screenshot(filename = '', format = 'jpeg', quality = 0.2, type = 'base64') {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 如果是播放状态 // 如果是播放状态
...@@ -238,6 +285,20 @@ export default { ...@@ -238,6 +285,20 @@ export default {
height: 100%; height: 100%;
widows: 100%; widows: 100%;
background-color: rgba(13, 14, 27, 0.7); background-color: rgba(13, 14, 27, 0.7);
// 时间轴
::v-deep(.jessibuca-control-progress-simple) {
display: none;
}
// 电子放大
::v-deep(.jessibuca-zoom-controls) {
display: none !important;
}
// 云台控制
&[data-player-type="playback"] {
::v-deep(.jessibuca-ptz) {
display: none !important;
}
}
} }
.hide-controls { .hide-controls {
::v-deep(.jessibuca-controls) { ::v-deep(.jessibuca-controls) {
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
:datetime-range="datetimeRange" :datetime-range="datetimeRange"
watermarkText="文安智能678" watermarkText="文安智能678"
@ptz-control="handlePtz" @ptz-control="handlePtz"
@rate-change="handleRateChange"
/> />
</div> </div>
<div class="footer"> <div class="footer">
...@@ -19,8 +20,11 @@ ...@@ -19,8 +20,11 @@
<button @click="handlePause">暂停</button> <button @click="handlePause">暂停</button>
<button @click="handleStop">停止</button> <button @click="handleStop">停止</button>
<button @click="handleDestroy">销毁</button> <button @click="handleDestroy">销毁</button>
<button @click="handleRotate">旋转</button>
<button @click="handleScreenshot">抓拍</button> <button @click="handleScreenshot">抓拍</button>
<button @click="handleScreenshot2">抓拍(原)</button> <button @click="handleScreenshot2">抓拍(原)</button>
<button @click="handleZoom(true)">放大</button>
<button @click="handleZoom(false)">缩小</button>
<div class="record"> <div class="record">
<input type="text" v-model="recordUrl"> <input type="text" v-model="recordUrl">
<button @click="handleRecord">录像</button> <button @click="handleRecord">录像</button>
...@@ -50,9 +54,10 @@ export default { ...@@ -50,9 +54,10 @@ export default {
base64Url: '', base64Url: '',
// 播放器相关 // 播放器相关
// playUrl: 'https://rtmp01open.ys7.com:9188/v3/openlive/F16423875_4_1.flv?expire=1695696850&id=626370860302729216&t=8c5be77f3d74149d2cdb93f913baf0c171bc33f8d6b6507510e8869b3392a4da&ev=100&supportH265=1', playUrl: 'https://rtmp01open.ys7.com:9188/v3/openlive/F16423875_4_1.flv?expire=1695801507&id=626809821921091584&t=012b62efc1b531ada1d08aae3d374c4cb9cf999a87f38b298b52a14bbcc51883&ev=100&supportH265=1',
playUrl: '', // playUrl: 'http://flv.bdplay.nodemedia.cn/live/bbb_264.flv',
recordUrl: '', // playUrl: '',
recordUrl: 'https://rtmp01open.ys7.com:9188/v3/openpb/F16423875_1_1.flv?begin=20230925113136&end=20230925182952&expire=1695724192&id=626485541843505152&rec=local&t=1a3147ede9f0c48ed31df986f8e1a24b39ebcebf0325c134bdae5d8bbda078f5&ev=100&supportH265=1',
} }
}, },
methods: { methods: {
...@@ -68,7 +73,7 @@ export default { ...@@ -68,7 +73,7 @@ export default {
this.playerVisible = true; this.playerVisible = true;
}, },
handleRecord() { handleRecord() {
this.$refs.vionPlayer.play(this.recordUrl); this.$refs.vionPlayer.play(this.recordUrl, true);
}, },
handleScreenshot() { handleScreenshot() {
this.$refs.vionPlayer.screenshot().then(data => { this.$refs.vionPlayer.screenshot().then(data => {
...@@ -92,10 +97,22 @@ export default { ...@@ -92,10 +97,22 @@ export default {
handleDestroy() { handleDestroy() {
this.$refs.vionPlayer.destroy(); this.$refs.vionPlayer.destroy();
}, },
handleRotate() {
console.log('handleRotate');
this.$refs.vionPlayer.rotate();
},
handleZoom(direction) {
this.$refs.vionPlayer.zoom(direction);
},
handlePtz(arrow) { handlePtz(arrow) {
console.log('handlePtz', arrow); console.log('handlePtz', arrow);
}, },
handleRateChange(rate, done) {
console.log('handleRateChange', rate, done);
// 请求接口,获取流地址
done(rate);
},
}, },
} }
</script> </script>
...@@ -132,6 +149,7 @@ export default { ...@@ -132,6 +149,7 @@ export default {
> button { > button {
margin-left: 2px; margin-left: 2px;
} }
} }
} }
::v-deep(.el-dialog__body) { ::v-deep(.el-dialog__body) {
......
...@@ -4565,11 +4565,16 @@ lodash.uniqby@^4.7.0: ...@@ -4565,11 +4565,16 @@ lodash.uniqby@^4.7.0:
resolved "https://registry.npmmirror.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" resolved "https://registry.npmmirror.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302"
integrity sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww== integrity sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==
lodash@^4.17.14, lodash@^4.17.20, lodash@^4.17.21: lodash@^4.17.14, lodash@^4.17.20:
version "4.17.21" version "4.17.21"
resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log-symbols@^4.1.0: log-symbols@^4.1.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" resolved "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!