VUE项目开发,使用jessibuca——实现直播流播放器
VUE项目开发,使用jessibuca——实现直播流播放器
效果图
插件目录
首先在项目的根目录下找到public文件夹,然后再public文件夹下方的index.html,引入jessibuca.js,jessibuca压缩包: 云盘地址。即可使用该播放器。项目目录如下:
public/index.html页面引入
组件一
SupVideoPlayer.vue
<template>
<div class="sup-video-player">
<div :class="['video-list', 'video-split-' + splitNum]">
<div
v-for="n in 9"
:key="n"
@click="videoItemClick(n)"
:class="[
'video-item',
'split-' + splitNum,
'video-item-' + n,
{ active: active == n },
]"
>
<vue-player
:ref="'player' + n"
:id="'tc-player' + n"
></vue-player>
</div>
</div>
<div class="video-tools">
<ul>
<li>
<el-tooltip placement="top">
<div slot="content">保存视图</div>
<svg-icon icon-class="vt_save_scene" @click="saveScene"/>
</el-tooltip>
</li>
<li>
<el-tooltip placement="top">
<div slot="content">关闭所有</div>
<svg-icon icon-class="vt_close_all" @click="closeAll"/>
</el-tooltip>
</li>
<li>
<el-tooltip placement="top">
<div slot="content">关闭选中单元格</div>
<svg-icon icon-class="vt_close" @click="closeCurCell"/>
</el-tooltip>
</li>
<li v-if="false">
<el-tooltip placement="top">
<div slot="content">全屏</div>
<svg-icon icon-class="vt_fullscreen" @click="fullscreen"/>
</el-tooltip>
</li>
<li>
<el-tooltip placement="top">
<div slot="content">抓拍</div>
<svg-icon icon-class="vt_screenshot" @click="capture"/>
</el-tooltip>
</li>
<slot name="extend-tools"></slot>
<li style="margin-left: auto">
<el-tooltip placement="top">
<div slot="content">9分屏</div>
<svg-icon icon-class="vt_split_9" @click="splitScreen(9)"/>
</el-tooltip>
</li>
<li>
<el-tooltip placement="top">
<div slot="content">6分屏</div>
<svg-icon icon-class="vt_split_6" @click="splitScreen(6)"/>
</el-tooltip>
</li>
<li>
<el-tooltip placement="top">
<div slot="content">4分屏</div>
<svg-icon icon-class="vt_split_4" @click="splitScreen(4)"/>
</el-tooltip>
</li>
<li>
<el-tooltip placement="top">
<div slot="content">1分屏</div>
<svg-icon icon-class="vt_split_1" @click="splitScreen(1)"/>
</el-tooltip>
</li>
</ul>
</div>
</div>
</template>
<script>
//引入播放组件
import VuePlayer from "./VuePlayer";
export default {
name: "",
data() {
return {
splitNum: this.split,
active: this.activeIndex,
recorder: null,
talkbackData: null,
dataList: []
};
},
created() {},
components: { VuePlayer },
props: {
split: {
type: Number,
default: 4,
},
activeIndex: {
type: Number,
default: 1,
},
talkback: {
type: Boolean,
default: false,
},
activeName:String
},
computed: {
currPlayer() {
var p = this.$refs["player" + this.active];
if (p) return p[0];
return null;
},
},
methods: {
videoItemClick(n) {
this.active = n;
console.log(n);
this.talkbackData = this.currPlayer.data;
},
closeAll() {
for (var i = 1; i <= 9; i++) {
let players = this.$refs["player" + i];
if (players && players.length > 0) {
players[0].close();
}
}
this.active = 1;
this.dataList = [];
},
closeCurCell() {
if (this.currPlayer) {
this.currPlayer.close();
this.dataList[this.active] = null;
}
},
splitScreen(n) {
console.log(n);
this.splitNum = n;
this.active = 1;
},
play(url, data) {
this.dataList[this.active] = data;
this.currPlayer.load(url, data);
var n = (this.active + 1) % (this.splitNum + 1);
this.active = n == 0 ? 1 : n;
},
playAtIndex(index, url, data) {
this.dataList[index] = data;
let players = this.$refs["player" + index];
if (players && players.length > 0) {
players[0].load(url, data);
}
},
capture() {
if (this.currPlayer) {
this.currPlayer.screenShot();
}
},
detail() {
console.log(this.currPlayer.data);
},
fullscreen() {
if (this.currPlayer) {
this.currPlayer.fullscreen();
}
},
ptzCtrl(cmd, param) {
// 判断是否正在播放视频
if (!this.currPlayer.status()) {
console.warn('没有选中正在播放的设备视频!');
return;
}
let params = {
avObjName: "",
id: this.dataList[this.active].objId,
cmd: cmd,
param: param
};
this.$api.controlPtz(params).then((response) => {
if (!response.success) {
this.$message({
message: response.info,
type: 'error'
})
}
})
},
saveScene() {
// 判断是否有视频正在播放
let isPlaying = false;
for (var i = 1; i <= this.splitNum; i++) {
let players = this.$refs["player" + i];
if (players && players.length > 0 && players[0].status()) {
isPlaying = true;
break;
}
}
if (!isPlaying) {
this.$message({
message: "没有正在播放的视频",
type: "warning",
duration: 2000
});
return;
}
// 弹出输入框输入视图名称
this.$prompt('请输入视图名称', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
}).then(({ value }) => {
if (value) {
let details = [];
for (var i = 1; i <= this.splitNum; i++) {
if (this.dataList[i]) {
details.push({
index: i,
objId: this.dataList[i].objId,
flvUrl: ''
});
}
}
let param = {
name: value,
viewInfo: {
cut: this.splitNum,
viewInfoDetails: details
}
};
this.$api.createUVView(param).then((response) => {
if (response.success) {
this.$message({
type: 'success',
message: '保存成功'
});
this.$emit('saveSceneSuccess');
}
});
}
}).catch(() => {
this.$message({
type: 'info',
message: '取消输入'
});
});
}
},
state: {
count: 0,
},
mutations: {
increment(state) {
state.count++;
},
},
};
</script>
<style lang="scss" scoped>
.sup-video-player {
display: grid;
grid-template-columns: 100%;
grid-template-rows: calc(100% - 48px) 48px;
height: 100%;
.video-list {
flex: 1 1 auto;
background: #98a5a5;
display: grid;
.video-item {
background: black;
border: 1px solid #052524;
cursor: pointer;
position: relative;
}
.video-item.active {
border: 1px solid #009586;
box-shadow: inset 0 0 2px 2px #ffba00;
}
&.video-split-6 {
grid-template-columns: 33.33% 33.33% 33.33%;
grid-template-rows: 33.33% 33.33% 33.33%;
}
.video-item.split-6.video-item-1 {
grid-column-start: 1;
grid-column-end: 3;
grid-row-start: 1;
grid-row-end: 3;
}
&.video-split-1 {
grid-template-columns: 100%;
grid-template-rows: 100%;
}
&.video-split-4 {
grid-template-columns: 50% 50%;
grid-template-rows: 50% 50%;
}
&.video-split-9 {
grid-template-columns: 33.33% 33.33% 33.33%;
grid-template-rows: 33.33% 33.33% 33.33%;
}
}
.video-tools {
// height: 48px;
background: #95a0a0;
ul {
display: flex;
list-style: none;
padding: 0 10px;
margin: 0;
line-height: 48px;
.icon {
cursor: pointer;
}
li {
margin: 0 8px;
}
.iconfont {
font-size: 24px;
}
}
}
}
</style>
组件二
VuePlayer.vue播放组件
<template>
<div :id="id" class="player" style="width: 100%; height: 100%" ref="container"></div>
</template>
<script>
export default {
props: {
id: {
type: String,
default: "tc-player",
},
width: {
type: [Number, String],
default: "100%",
},
height: {
type: [Number, String],
default: "100%",
},
listener: {
type: Function,
default() {
return function (msg) {};
},
},
live: {
type: Boolean,
default: true,
},
autoplay: {
type: Boolean,
default: true,
},
src: {
type: String,
default: "",
},
options: {
type: Object,
},
},
data() {
return {
jessibuca: null,
version: '',
wasm: false,
vc: "ff",
quieting: true,
showOperateBtns: false,
showBandwidth: false,
err: "",
speed: 0,
performance: "",
volume: 1,
rotate: 0,
recordType: 'webm',
scale: 0,
url: this.src,
data: null,
isNotMute: false
};
},
mounted() {
// this.create();
// window.onerror = (msg) => (this.err = msg);
// this.jessibuca.play("https://flvplayer.js.org/assets/video/weathering-with-you.flv");
},
unmounted() {
this.jessibuca.destroy();
},
methods: {
load(src,data) {
this.data = data
this.url = src;
this.create()
},
create(options) {
options = options || {};
this.jessibuca = new window.Jessibuca(
Object.assign(
{
container: this.$refs.container,
videoBuffer: Number(0.2), // 缓存时长
isResize: false,
useWCS: this.useWCS,
useMSE: this.useMSE,
text: "",
// background: "bg.jpg",
loadingText: "加载中...",
// hasAudio:false,
debug: false,
supportDblclickFullscreen: true,
showBandwidth: true, // 显示网速
operateBtns: {
fullscreen: true,
screenshot: true,
play: true,
audio: true,
},
vod: this.vod,
forceNoOffscreen: !this.useOffscreen,
isNotMute: this.isNotMute,
timeout: 10
},
options
)
);
var _this = this;
this.jessibuca.on("load", function () {});
this.jessibuca.on("log", function (msg) {});
this.jessibuca.on("record", function (msg) {
console.log("on record:", msg);
});
this.jessibuca.on("pause", function () {
// _this.playing = false;
});
this.jessibuca.on("fullscreen", function (msg) {});
this.jessibuca.on("mute", function (msg) {
_this.quieting = msg;
});
this.jessibuca.on("audioInfo", function (msg) {
// console.log("audioInfo", msg);
});
// this.jessibuca.on("bps", function (bps) {
// // console.log('bps', bps);
// });
// let _ts = 0;
// this.jessibuca.on("timeUpdate", function (ts) {
// console.log('timeUpdate,old,new,timestamp', _ts, ts, ts - _ts);
// _ts = ts;
// });
this.jessibuca.on("videoInfo", function (info) {
// console.log("videoInfo", info);
});
this.jessibuca.on("error", function (error) {
_this.$message.error("获取视频错误");
});
this.jessibuca.on("timeout", function () {
_this.$message.error("获取视频超时");
});
this.jessibuca.on('start', function () {
// console.log('frame start');
})
this.jessibuca.on("performance", function (performance) {
var show = "卡顿";
if (performance === 2) {
show = "非常流畅";
} else if (performance === 1) {
show = "流畅";
}
_this.performance = show;
});
this.jessibuca.on('buffer', function (buffer) {
// console.log('buffer', buffer);
})
this.jessibuca.on('stats', function (stats) {
// console.log('stats', stats);
})
this.jessibuca.on('kBps', function (kBps) {
// console.log('kBps', kBps);
});
this.jessibuca.on("play", () => {
// this.playing = true;
// this.loaded = true;
_this.quieting = _this.jessibuca.isMute();
});
this.jessibuca.on('recordingTimestamp', (ts) => {
// console.log('recordingTimestamp', ts);
})
this.jessibuca.play(this.url);
// console.log(this.jessibuca);
},
// play() {
// // this.jessibuca.onPlay = () => (this.playing = true);
// // if (this.$refs.playUrl.value) {
// // this.jessibuca.play(this.$refs.playUrl.value);
// this.jessibuca.play("https://flvplayer.js.org/assets/video/weathering-with-you.flv");
// // }
// },
mute() {
// this.isNotMute = false
//jessibuca.isMute()
this.jessibuca.mute();
},
cancelMute() {
// this.isNotMute = true
this.jessibuca.cancelMute();
},
pause() {
this.jessibuca.pause();
// this.playing = false;
this.err = "";
this.performance = "";
},
volumeChange() {
this.jessibuca.setVolume(this.volume);
},
rotateChange() {
this.jessibuca.setRotate(this.rotate);
},
destroy() {
if (this.jessibuca) {
this.jessibuca.destroy();
}
this.create();
// this.playing = false;
// this.loaded = false;
this.performance = "";
},
fullscreen() {
if (!this.jessibuca) return;
return this.jessibuca.setFullscreen(true);
},
clearView() {
this.jessibuca.clearView();
},
close() {
if (!this.jessibuca) return;
return this.reset();
},
// startRecord() {
// const time = new Date().getTime();
// this.jessibuca.startRecord(time, this.recordType);
// },
// stopAndSaveRecord() {
// this.jessibuca.stopRecordAndSave();
// },
reset() {
if (this.jessibuca) {
this.jessibuca.destroy()
}
this.jessibuca = null;
this.url = "";
},
screenShot() {
this.jessibuca.screenshot();
},
status() {
return this.jessibuca && this. jessibuca.isPlaying();
}
},
};
</script>
<style lang="scss" scoped>
.player {
background: url(../../../../assets/image/shexiangtou.png) center center no-repeat #353535;
background-size: 60px 60px;
}
</style>
主页:index.vue
<template>
<div class="app-container">
<sup-video-player
:split="6"
ref="supPlayer"
>
</sup-video-player>
</div>
</template>
<script>
import SupVideoPlayer from "./components/SupVideoPlayer";
export default {
components: {
SupVideoPlayer,
},
data() {
return {
};
},
mounted() {
},
methods: {
onClick() {
//url,就是播放地址 http://192.168.0.1*0:1***0/av/192_168_0_6_8**0/1_0.flv
//data,是通过点击设备树获取回来的参数,这个具体根据实际开发进行修改。
this.$refs.supPlayer.play(url, data);
}
},
};
</script>
<style lang="scss" scoped>
</style>
补充内容
2023.14额外补充一下:如果需要点击树或者初始化加载多个视频,那就必须返回多个flvUrl。
let details = data.viewInfo.viewInfoDetails;
if (details) {
// 关闭当前所有播放视频
this.$refs.supPlayer.closeAll();
//显示窗口数
this.$refs.supPlayer.splitScreen(data.viewInfo.cut);
// 打开视图中视频
details.forEach((view) => {
this.$refs.supPlayer.playAtIndex(view.index, view.flvUrl, view);
});
}
data:[{"name":"8画面",
"viewInfo":{
"cut":6,//这是显示几个窗口参数
"id":"XXXXXXXXXXX"
"viewInfoDetails":[{
"index":1,
"objId":"3716",
"flvUrl":"http://192.168.0.1:8080/avI/6_8000/1_0.flv"
},{
"index":2,
"objId":"3718",
"objName":null,
"flvUrl":"http://192.168.0.1:8080/avI/7_8000/1_0.flv"
},{
"index":3,
"objId":"3719",
"flvUrl":"http://192.168.0.1:8080/avI/8_8000/1_0.flv"
},{
"index":4,
"objId":"3717",
"flvUrl":"http://192.168.0.1:8080/avI/10_8000/1_0.flv"
},{
"index":5,
"objId":"3715",
"flvUrl":"http://192.168.0.1:8080/avI/9_8000/1_0.flv"}]
},
}]
加载多个视频
这是 一次性加载多个视频的效果:
SupVideoPlayer.vue、VuePlayer.vue可以充当组件直接引入项目即可,url和data都需要根据实际的开发传需要的值。图片地址需要自己重新定义修改。decoder.js、decoder.wasm、jessibuca.js代码可以去github获取。
参考连接
jessibuca压缩包: 云盘地址
API文档: jessibuca-api-文档
demo代码: Vue
全部demo代码: 所有官方案例