赞
踩
项目使用的是的vue3+ts
原本使用的video.js 但是不支持HTTP-FLV播放,改用flv.js
引入flv.js
npm install --save flv.js
开发问题和解决方案:
flv.js组件代码(需要请自行修改监听部分逻辑)
<template> <div class="w-full h-full" v-if="deviceState !== 0"> <video v-show="createEl" id="videoElement" ref="videoElement" controls disablePictureInPicture class="FlvVideo w-full h-full" @click.prevent="onClick" @pause="onPause" muted @play="play" ></video> <!--下面部分html是暂停时的画面,flv.js没有创建时是触发不了播放,通过这个暂停画面来创建实例/播放 --> <!-- class="rounded-md flex justify-center items-center w-full h-full maskLayer" 这个是Tailwind Css语法--> <div v-show="!createEl" class="rounded-md flex justify-center items-center w-full h-full maskLayer"> <SvgIcon name="loading" size="30" class="rotate" v-if="loading" /> <div class="wrap" @click="onPlay" v-else> <SvgIcon name="play" size="20" fillColor="#ffffff" class="ml-0.8" /> </div> </div> </div> <div class="rounded-md flex justify-center items-center w-full h-full maskLayer text-light-50" v-else> 设备离线 </div> </template> <script setup name="videoFlv" lang="ts"> import { ref, onBeforeUnmount, watch } from "vue" import flvjs from "flv.js" import SvgIcon from "/@/components/Icon" import _ from "lodash-es" type VideoProps = { destroy: number | boolean//拉流断流的状态 sources: string//视频地址 status?: number | undefined loading?: boolean deviceState?: number } const emit = defineEmits(["onPlay", "onPause"]) const props = defineProps<VideoProps>() let flvPlayer: flvjs.Player | null = null let videoElement = ref<HTMLMediaElement | null>(null) let timer: any = null let createEl = ref<boolean | null>(null) const pauseState = ref<boolean>(true) //为了避免在销毁时重复执行暂停 // 清除缓存延迟 const buffered = () => { timer = window.setInterval(() => { if (videoElement.value && videoElement.value.buffered.length > 0) { const end = videoElement.value.buffered.end(0) // 视频结尾时间 const current = videoElement.value.currentTime // 视频当前时间 const diff = end - current // 相差时间 const diffCritical = 4 // 这里设定了超过4秒以上就进行跳转 const diffSpeedUp = 1 // 这里设置了超过1秒以上则进行视频加速播放 const maxPlaybackRate = 4 // 自定义设置允许的最大播放速度 let playbackRate = 1.0 // 播放速度 if (diff > diffCritical) { // console.log("相差超过4秒,进行跳转"); videoElement.value.currentTime = end - 1.5 playbackRate = Math.max(1, Math.min(diffCritical, 16)) } else if (diff > diffSpeedUp) { // console.log("相差超过1秒,进行加速"); playbackRate = Math.max(1, Math.min(diff, maxPlaybackRate, 16)) } videoElement.value.playbackRate = playbackRate } }, 1000) } // 创建flv.js实例 const createVideo = (url) => { if (flvjs.isSupported()) { flvPlayer = flvjs.createPlayer( { type: "flv", url, //你的url地址 isLive: true, hasVideo: true, hasAudio: true }, { enableWorker: false, //不启用分离线程 enableStashBuffer: true, //关闭IO隐藏缓冲区 reuseRedirectedURL: true, //重用301/302重定向url,用于随后的请求,如查找、重新连接等。 autoCleanupSourceBuffer: true, //自动清除缓存 lazyLoad: false, // 去掉懒加载,新增 fixAudioTimestampGap: false //false才会音视频同步,新增 } ) flvPlayer.attachMediaElement(videoElement.value as HTMLMediaElement) flvPlayer.load() flvPlayer.play() flvPlayer.on(flvjs.Events.ERROR, () => { flvPlayer && reloadVideo() }) } createEl.value = true } const antiShake = (Fn) => _.throttle(Fn, 2000, { leading: true }) const onClick = antiShake(() => { if (!videoElement.value || !flvPlayer) return if (videoElement.value.paused) { flvPlayer.play() } else { flvPlayer.pause() } }) // 这一步其实处理buffer有没有都可以,只不过防止拉流中卡顿的可能性 const play = () => { buffered() } // 自定义暂停页面的paly事件 const onPlay = antiShake(() => { emit("onPlay", true) }) const onPause = antiShake(() => { //初始化静音时页面被遮挡才会由flv.js触发puase timer && window.clearInterval(timer) if (!props.status && !pauseState.value) { destoryVideo() emit("onPause", false) pauseState.value = true } }) // 重置 const reloadVideo = () => { destoryVideo() createVideo(props.sources) } // 销毁/创建 const watchDestroy = watch( () => props.destroy, () => { if (props.destroy && createEl.value) { timer && window.clearInterval(timer) destoryVideo() } else if (props.destroy === 0) { pauseState.value = false createVideo(props.sources) } } ) // 销毁 const destoryVideo = () => { if (!pauseState.value) { flvPlayer!.pause() //暂停播放数据流 pauseState.value = true } flvPlayer!.unload() //取消数据流加载 flvPlayer!.detachMediaElement() //将播放实例从节点中取出 flvPlayer!.destroy() //销毁播放实例 flvPlayer = null createEl.value = false } // 组件卸载/清除监听 onBeforeUnmount(() => { watchDestroy() timer && window.clearInterval(timer) props.destroy && createEl.value && destoryVideo() }) </script> <style scoped> html[data-theme="dark"] .wrap, html[data-theme="dark"] .maskLayer { background-color: #000; } .maskLayer { background-color: var(--bg-dark-color); } @keyframes rotate { 0% { transform: rotate(0); } 25% { transform: rotate(90deg); } 50% { transform: rotate(180deg); } 75% { transform: rotate(270deg); } 100% { transform: rotate(360deg); } } .FlvVide { object-fit: fill; } video::-webkit-media-controls-timeline { display: none; } video::-webkit-media-controls-current-time-display { display: none; } .wrap { width: 65px; height: 65px; display: flex; justify-content: center; align-items: center; border-radius: 50%; background: rgb(154 154 154 / 0%); border: 2px solid #fff; cursor: pointer; } .rotate { animation: rotate 3s linear infinite; } </style>
我是借鉴这位博主的 https://blog.csdn.net/weixin_45906632/article/details/115031633
这篇分享的也很完整 https://juejin.cn/post/7050739831403446286
上面flv.js已经解决了我一部分的使用需求,但是销毁创建的方法,用户体验很差,只是暂停每次都要等个三秒左右才可以播放(因为要重新创建加拉流这个过程),并且不能支持其他直播协议,不能同时控制多个直播分屏画面(flv.js 创建六个视频共同维护起来很不方便),这些问题EasyPlayer都有做相应的改善
引入EasyPlayer
npm install @easydarwin/easyplayer --save
Vue 集成调用
copy node_modules/@easydarwin/easyplayer/dist/component/EasyPlayer.swf 到 静态文件 根目录
copy node_modules/@easydarwin/easyplayer/dist/component/crossdomain.xml 到 静态文件 根目录
copy node_modules/@easydarwin/easyplayer/dist/component/EasyPlayer-lib.min.js 到 静态文件 根目录
注意: 没有调用会出现无法加载对应插件的报错
在 html 中引用 dist/component/EasyPlayer-lib.min.js
###H.265 copy node_modules/@easydarwin/easyplayer/dist/component/EasyPlayer.wasm 到 静态文件 根目录
详细引入可以查看官网文档 https://github.com/tsingsee/EasyPlayer.js/?tab=readme-ov-file
gitHub打不开的可以看这个https://www.npmjs.com/package/@easydarwin/easyplayer
开发问题和解决方案:
EasyPlayer组件代码
使用就很简单了,暂时还没有需要多屏实时播放,也不需要处理buffer,只需要暂停播放时触发接口就好
<template> <div class="video-box"> <easy-player :video-url="sources" ref="videoEl" aspect="16:9" live autoplay stretch @pause="onPause" @play="onPlay" :video-title="title || ''" /> </div> </template> <script setup lang="ts" name="EasyPlayerFlv"> import { ref, onUnmounted } from "vue" type VideoProps = { sources: string//url title?: string//直播名称 } const emit = defineEmits(["onPlay", "onPause"]) defineProps<VideoProps>() let videoEl = ref<any>(null) const onPlay = () => { emit("onPlay", true) } const onPause = () => { emit("onPause", false) } onUnmounted(() => { console.log("videoEl.value :>> ", videoEl.value) // easyPlayer.value!.destroyPlayer() }) </script> <style scoped> .video-box { aspect-ratio: 16/9; width: 100%; } .video-box video { width: 100%; object-fit: cover; } </style>
EasyPlayer多屏实时播放 | 时差 | 延迟处理的很完善,EasyPlayer对于fvl.js的延迟问题采用的是追帧和加速播放,随着你暂停时间越久他追帧速度越慢,并且在追到延迟在10s时就停止了,也就是直播画面总是有10s的延迟(直播设备是测试过没问题的,并且在销毁重创画面是实时的)
后来又发现了livePlayer,测试使用发现以上问题都不存在了,延迟也最慢控制在3s,也是我们项目中能接受的范围
引入
vue2
npm install @liveqing/liveplayer
vue3
npm install @liveqing/liveplayer-v3
第一步 :复制依赖文件(示例 通过 webpack 插件自动复制依赖)
如果正在使用 vue2 + vue-cli, 编辑你的 vue.config.js
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
configureWebpack: {
plugins: [
new CopyWebpackPlugin([
{ from: 'node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml'},
{ from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf'},
{ from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js', to: 'js/'},
])
]
}
}
如果正在使用 vue3 + vite, 编辑你的 vite.config.js
import copy from 'rollup-plugin-copy'
export default defineConfig({
plugins: [vue(), copy({
targets: [
{src: 'node_modules/@liveqing/liveplayer-v3/dist/component/liveplayer-lib.min.js', dest: 'public/js'},
]
})]
})
第二步: html模板中引入依赖js
在 html 中引用 www 根目录 liveplayer-lib.min.js
<!DOCTYPE HTML>
<html>
<head>
<title>template</title>
......
<script src="js/liveplayer-lib.min.js"></script>
<!-- 如果正在使用 vue-cli:
<script src="<%= BASE_URL %>js/liveplayer-lib.min.js"></script>
-->
</head>
<body>
......
</body>
</html>
第三步 编辑你的 Vue 组件
import LivePlayer from '@liveqing/liveplayer' // vue2
// import LivePlayer from '@liveqing/liveplayer-v3' // vue3
components: {
LivePlayer
}//vue3,语法糖就不需要这一步了
<LivePlayer :videoUrl="videoUrl" fluent autoplay live stretch></LivePlayer>
如果你上述有报错或者一些问题可以转变成手动复制文件
copy node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf 到根目录
copy node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml 到根目录
copy node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js 到根目录
我遇到swf文件不能访问的错误,这个问题很简单,只是因为我在html引入LivePlayer文件时没有删除EasyPlayer的引入导致它们互相影响,本来是想做一下比较所以才没有删除EasyPlayer(这时候就要提醒一下引入任何相同插件时确保项目中只有一种此功能插件,可能会因为内部使用相同东西导致冲突,真的会查不到问题在哪,也查不到遇到相关问题的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。