可以播放flv流
可以播放m3u8流
可以截屏并且自动下载到本地
<template>
<div
v-if="isWho === 'Nodata'"
style="
height: 300px;
line-height: 300px;
text-align: center;
font-size: 20px;
color: red;
background-color: #000000;
color: #fff;
"
>
播放地址错误</div
>
<div class="video-container" v-if="isWho === 'flv'">
<video
:id="`videoElement${index}`"
crossorigin="Anonymous"
class="centeredVideo"
controls
autoplay
muted
loop
preload="auto"
></video>
<div class="btn-wrap" @click="save" v-if="isScreenCapture"> 截屏 </div>
</div>
<div
v-if="isWho === 'm3u8'"
class="video-play video-container"
:class="{ 'video-bg-wrap': !`videoElement${props.index}` }"
>
<div
style="
font-size: 20px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
"
v-if="showloading"
>
<a-button
:loading="showloading"
type="link"
style="border: 0px solid transparent; color: #cccccc"
>
加载中...
</a-button></div
>
<video
v-show="!error"
:id="`videoElement${index}`"
autoplay
muted
@play="handleFullScreen"
></video>
<div class="btn-wrap" @click="save" v-if="isScreenCapture && !showloading"> 截屏 </div>
<div class="wrap_screen" @click="launchFullscreen" v-if="isScreenCapture && !showloading">
全屏
</div>
</div>
</template>
<script setup>
import flvjs from 'flv.js';
import { ref, nextTick, onMounted, defineProps, watch, onUnmounted } from 'vue';
import { message } from 'ant-design-vue';
const props = defineProps({
url: {
type: String,
default: '',
},
index: {
type: Number,
default: 0,
},
downFileName: {
type: String,
default: '截图',
},
isScreenCapture: {
type: Boolean,
default: true,
},
});
const hls = ref(null);
const error = ref(false);
let showloading = ref(true); //加载中预留控制
console.log(props.url);
let isWho = ref('flv');
if (props.url.indexOf('.m3u8') !== -1) {
// 此处不是m3u8数据流
isWho.value = 'm3u8';
} else if (props.url.indexOf('.flv') !== -1) {
// 此处不是m3u8数据流
isWho.value = 'flv';
} else {
// 此处不是数据流
isWho.value = 'Nodata';
}
if (isWho.value === 'flv') {
const flv_load = () => {
var videoElement = document.getElementById(`videoElement${props.index}`);
if (flvjs.isSupported()) {
var flvPlayer = flvjs.createPlayer(
{
type: 'flv', //媒体类型 mp4/flv
url: props.url, //flv格式媒体URL
isLive: true, //数据源是否为直播流
hasAudio: false, //数据源是否包含有音频
hasVideo: true, //数据源是否包含有视频
enableStashBuffer: true, //是否启用缓存区
},
{
enableWorker: false, // 是否启用分离的线程进行转换
enableStashBuffer: false, //关闭IO隐藏缓冲区
autoCleanupSourceBuffer: true, //自动清除缓存
},
);
flvPlayer?.attachMediaElement(videoElement); //将播放实例注册到节点
flvPlayer?.load(); //加载数据流
flvPlayer?.play().catch(() => {
flvPlayer?.play();
}); //播放数据流
}
};
onMounted(() => {
nextTick(() => {
flv_load();
});
});
}
if (isWho.value === 'm3u8') {
watch(
props,
() => {
setTimeout(() => {
playVideo(`videoElement${props.index}`, props.url);
}, 1000);
},
{
immediate: true,
},
);
onMounted(() => {
playVideo(`videoElement${props.index}`, props.url);
setTimeout(() => {
showloading.value = false;
}, 6000);
});
const playVideo = (id, url) => {
if (id && url) {
// showloading.value = true;
error.value = false;
let video = document.getElementById(id);
hls.value = new Hls();
hls.value.attachMedia(video);
hls.value.on(Hls.Events.MEDIA_ATTACHED, () => {
hls.value.loadSource(url);
});
hls.value.on(Hls.Events.MANIFEST_PARSED, () => {
// showloading.value = false;
});
hls.value.on(Hls.Events.ERROR, (event, data) => {
console.log('MANIFEST_ERROR', id, url, event, data);
if (data.fatal) {
// showloading.value = false;
this.error = true;
hls.value.stopLoad();
switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
//
break;
case Hls.ErrorTypes.MEDIA_ERROR:
//
break;
default:
hls.value.destroy();
break;
}
}
});
}
};
onUnmounted(() => {
if (hls.value) {
hls.value?.detachMedia();
hls.value?.destroy();
}
hls.value = null;
});
}
// -------------------------------截屏并且下载----------------------------------------
let mySrc = '';
const htmlToImage = () => {
var videoElement = document.getElementById(`videoElement${props.index}`);
var canvas = document.createElement('canvas');
canvas.width = videoElement.clientWidth;
canvas.height = videoElement.clientHeight;
if (!canvas.getContext) {
message.error('您的浏览器暂不支持canvas');
return false;
} else {
var context = canvas.getContext('2d');
var video = document.getElementById(`videoElement${props.index}`);
context.drawImage(video, 0, 0, canvas.width, canvas.height);
return (mySrc = canvas.toDataURL('image/png'));
}
};
const save = () => {
htmlToImage();
nextTick(() => {
var triggerDownload = document.createElement('a');
triggerDownload.setAttribute('href', mySrc);
triggerDownload.setAttribute('download', `${props.downFileName}.png`);
triggerDownload.click();
});
};
const myE = ref();
const handleFullScreen = (e) => {
myE.value = e.target;
};
const launchFullscreen = () => {
let element = myE.value;
//此方法不可以在異步任務中執行,否則火狐無法全屏
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen();
} else if (element.oRequestFullscreen) {
element.oRequestFullscreen();
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullScreen();
} else {
var docHtml = document.documentElement;
var docBody = document.body;
var videobox = element;
var cssText = 'width:100%;height:100%;overflow:hidden;';
docHtml.style.cssText = cssText;
docBody.style.cssText = cssText;
videobox.style.cssText = cssText + ';' + 'margin:0px;padding:0px;';
document.IsFullScreen = true;
}
};
</script>
<style scoped lang="less">
.video-play {
width: 100%;
height: 100%;
position: relative;
video {
object-fit: fill;
height: 100%;
width: 100%;
}
}
.video-container {
position: relative;
.centeredVideo {
width: 100%;
}
.btn-wrap,
.wrap_screen {
width: 40px;
height: 40px;
text-align: center;
line-height: 40px;
border-radius: 50%;
// border: 1px solid #cccccc;
font-size: 14px;
position: absolute;
bottom: 80px;
right: 10px;
color: #fff;
font-weight: 700;
cursor: pointer;
opacity: 0;
transition: opacity 0.5s ease;
}
.wrap_screen {
bottom: 30px;
}
}
.video-container:hover {
.btn-wrap {
opacity: 1;
background-color: #666666;
}
.wrap_screen {
opacity: 1;
background-color: #666666;
}
}
</style>
使用
//引入播放器
import VideoFlv from './flvAndM3u8Video.vue';
//使用
//url:视频流
//downFileName:指定截屏后,自动下载到本地的文件名的名称
//index:注意,此处类型为number类型,不可重复!(id的唯一性,不可重复),最好使用时间戳或者BigInt类型
<VideoFlv :url="XXXXXX" :downFileName="XXXXXX" :index="XXXXXX" />