template中关键代码
<div class="page-item-box rel">
<div v-if="!state.left.videos.isFullScream" class="video-box overflow_auto" id='fullscreen-container'>
<div class="videos-item rel" v-for="(item,index) in state.list" :key="index">
<div class="rel wh-100">
<span class="video-fullScreen-btn fullScreen-btn-common pointer">
<img @click.stop="state.fullScreenClick(item,$event)" src="/public/img/video/fullScreen.png" alt="全屏">
</span>
<MyMonitor
:videoObj="item"
src="liveAddress"
/>
</div>
</div>
</div>
<div class="video-fullScreen-box wh-100" v-else>
<div class="rel wh-100">
<span class="video-nofullScreen-btn fullScreen-btn-common btn pointer">
<img @click.self="state.isFullScream=false" src="/public/img/video/no-fullScreen.png" alt="退出全屏">
</span>
<MyMonitor
:videoObj="state.fullVideoItem"
src="liveAddress"
/>
</div>
</div>
</div></div>
js关键代码
import MyMonitor from "./component/MyMonitor.vue";
export default defineComponent({
name:'home',
components:{MyTable,detailLayer,warning,envDetail,login,MyMonitor},
setup(){
let state = reactive({
isFullScream :false,
fullVideoItem:{},
list:[
{
name:'1',
muted:true,
liveAddress:'ws://192.***.***.***:***/live?url=rtmp://***.***.com/live/***'
}
],
fullScreenClick(item,event){
event.stopPropagation();
state.fullVideoItem = item
state.isFullScream = true
}
})
onActivated(async ()=>{
state.left.videos.isFullScream=false
})
return{
state
}
})
MyMonitor组件
<template>
<div class="videoContainer my-video" id="videoDom">
<div class="flex-center video-box bg-black" v-show="!videoObj[src] || showErrorInfo"><span>该视频无法播放!</span></div>
<video
:autoplay="true"
v-show="videoObj[src] && !showErrorInfo"
ref="videoPlayerContainer"
class="centeredVideo"
controls
controlslist="nofullscreen nodownload"
:muted="videoObj.muted"></video>
</div>
</template>
<script>
import {defineComponent, onMounted, onUnmounted, onDeactivated, reactive, ref, toRefs, watch} from 'vue';
import 'video.js/dist/video-js.css';
import 'videojs-flvjs-es6'
import flvjs from 'flv.js'
export default defineComponent({
name: 'MyMonitor',
emits: [],
props: {
videoObj: {
type: Object,
default: {
/*name:'测试21',
muted:true,
liveAddress:'http:/***.168.10.***:8000/flwis-1.5.0/demo/xsy3.flv'*/
}
},
src: {
type: String,
default: ''
},
},
setup(props, {emit}) {
let videoPlayerContainer = ref();
const state = reactive({
videoId: 0,
flvPlayer: null,
lastDecodedFrame:0,
connectCount:0,
showErrorInfo:false,
createPlayer(url){
state.showErrorInfo=false
state.flvPlayer = flvjs.createPlayer(
{
type: "flv", //媒体类型
url: url || '', //flv格式媒体URL
isLive: true, //数据源是否为直播流
hasAudio: true, //数据源是否包含有音频
hasVideo: true, //数据源是否包含有视频
enableStashBuffer: false, //是否启用缓存区
muted: props.videoObj.muted,
duration: 1,
},
{
enableWorker: false, // 是否启用分离的线程进行转换
enableStashBuffer: true, //关闭IO隐藏缓冲区
autoCleanupSourceBuffer: true, //自动清除缓存
fluid: true
}
);
},
flv_load(url) {
state.destoryVideo();
if (flvjs.isSupported()) {
state.createPlayer(url);
state.flvPlayer.attachMediaElement(videoPlayerContainer.value); //将播放实例注册到节点
state.flvPlayer.load(); //加载数据流
// state.flvPlayer.play(); //播放数据流
state.flvPlayer.on('error', function (err) {
state.flvPlayer.pause()
state.flvPlayer.unload()
})
state.flvPlayer.on("statistics_info", function (res) {
if(state.connectCount >= 10){
state.destoryVideo()
state.showErrorInfo=true
state.connectCount = 0;
return;
}
if (state.lastDecodedFrame === 0) {
state.lastDecodedFrame = res.decodedFrames;
state.connectCount++;
return;
}
});
}
},
destoryVideo() {
if (state.flvPlayer) {
state.flvPlayer?.pause();
state.flvPlayer?.unload();
state.flvPlayer?.detachMediaElement();
state.flvPlayer.destroy();
state.flvPlayer = null;
}
},
})
watch(() => props.videoObj, (val) => {
props.videoObj[props.src] && state.flv_load(val[props.src]);
})
onMounted(() => {
state.flv_load(props.videoObj[props.src]);
})
onUnmounted(() => {
state.destoryVideo()
})
onDeactivated(() => {
state.destoryVideo()
})
return {
...toRefs(state),
videoPlayerContainer,
}
}
})
</script>
<style lang="less" scoped>
.my-video, .video-box, .centeredVideo {
width: 100%;
height: 100%;
background: rgba(0,0,0,.6);
.flex_center;
/* @keyframes ani-color{
from {color:#333;}
to {color:#fff;}
}
>span{
animation: ani-color 5s;
}*/
:deep(.vjs-modal-dialog-content), .bg-black {
background: linear-gradient(#323232,#000);
}
:deep(.video-js .vjs-control) {
width: 2em;
font-size: 12px;
}
:deep(.video-js .vjs-modal-dialog) {
overflow: hidden;
}
:deep(.video-js .vjs-time-control) {
margin-right: 25px;
}
:deep(.video-js .vjs-picture-in-picture-control) {
margin-right: 10px;
}
:deep(.vjs-fullscreen-control.vjs-control.vjs-button) {
//display: none!important;
}
:deep(.video-js .vjs-progress-control .vjs-progress-holder) {
}
:deep(.vjs-progress-control.vjs-control) {
//max-width: 40px;
}
:deep(.vjs_video_3-dimensions.vjs-fluid:not(.vjs-audio-only-mode)), :deep(.vjs-fluid:not(.vjs-audio-only-mode)) {
padding-top: unset;
}
:deep(.video-js) {
height: 100%;
width: 100%;
overflow: hidden;
}
:deep(#flvjs-video-player .video-control-bar .fullscreen) {
display: none !important;
}
:deep(.vjs-fullscreen-control.vjs-control.vjs-button){
display: none!important;
}
/* 隐藏控制条 */
.flvjs-control-bar {
display: none;
}
}
video::-webkit-media-controls-fullscreen-button {
display:none !important;
}
video::-moz-media-controls-fullscreen-button {
display:none !important;
}
video::-moz-full-screen-ancestor::-moz-full-screen-controls {
display: none !important;
}
</style>