場景需求
- 需要畫面監(jiān)控設(shè)備實時播放,支持HTTP-FLV直播流,支持其他流后續(xù)可能會更換
- 需要類似于安防監(jiān)控多個視頻實時畫面同步
- 播放器可控制度強(qiáng),完全由我們來控制播放暫停進(jìn)行拉流斷流
實現(xiàn)過程
項目使用的是的vue3+ts
1. 使用flv.js
原本使用的video.js 但是不支持HTTP-FLV播放,改用flv.js
引入flv.js
npm install --save flv.js
開發(fā)問題和解決方案:
- flv.js視頻暫停時會有延遲增長,隨著暫停時間越久延遲越長幾秒幾分鐘或者更長
解決辦法:手動處理buffer時長,進(jìn)行刪減幀或加速播放 - 我需要當(dāng)暫停播放按鈕觸發(fā)時呼叫服務(wù)端拉流斷流,flv.js視頻播放時離開此頁面或者有遮擋此頁面會自動觸發(fā)video標(biāo)簽的暫停事件,回到此頁面時也會自動幫你播放,但不會通知你也不會觸發(fā)video標(biāo)簽的播放事件
解決辦法:執(zhí)行銷毀再創(chuàng)建flv.js
flv.js組件代碼(需要請自行修改監(jiān)聽部分邏輯)
<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沒有創(chuàng)建時是觸發(fā)不了播放,通過這個暫停畫面來創(chuàng)建實例/播放 -->
<!-- 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>
設(shè)備離線
</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//拉流斷流的狀態(tài)
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) //為了避免在銷毀時重復(fù)執(zhí)行暫停
// 清除緩存延遲
const buffered = () => {
timer = window.setInterval(() => {
if (videoElement.value && videoElement.value.buffered.length > 0) {
const end = videoElement.value.buffered.end(0) // 視頻結(jié)尾時間
const current = videoElement.value.currentTime // 視頻當(dāng)前時間
const diff = end - current // 相差時間
const diffCritical = 4 // 這里設(shè)定了超過4秒以上就進(jìn)行跳轉(zhuǎn)
const diffSpeedUp = 1 // 這里設(shè)置了超過1秒以上則進(jìn)行視頻加速播放
const maxPlaybackRate = 4 // 自定義設(shè)置允許的最大播放速度
let playbackRate = 1.0 // 播放速度
if (diff > diffCritical) {
// console.log("相差超過4秒,進(jìn)行跳轉(zhuǎn)");
videoElement.value.currentTime = end - 1.5
playbackRate = Math.max(1, Math.min(diffCritical, 16))
} else if (diff > diffSpeedUp) {
// console.log("相差超過1秒,進(jìn)行加速");
playbackRate = Math.max(1, Math.min(diff, maxPlaybackRate, 16))
}
videoElement.value.playbackRate = playbackRate
}
}, 1000)
}
// 創(chuàng)建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, //關(guān)閉IO隱藏緩沖區(qū)
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觸發(fā)puase
timer && window.clearInterval(timer)
if (!props.status && !pauseState.value) {
destoryVideo()
emit("onPause", false)
pauseState.value = true
}
})
// 重置
const reloadVideo = () => {
destoryVideo()
createVideo(props.sources)
}
// 銷毀/創(chuàng)建
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() //暫停播放數(shù)據(jù)流
pauseState.value = true
}
flvPlayer!.unload() //取消數(shù)據(jù)流加載
flvPlayer!.detachMediaElement() //將播放實例從節(jié)點中取出
flvPlayer!.destroy() //銷毀播放實例
flvPlayer = null
createEl.value = false
}
// 組件卸載/清除監(jiān)聽
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
2. 使用EasyPlayer.js
上面flv.js已經(jīng)解決了我一部分的使用需求,但是銷毀創(chuàng)建的方法,用戶體驗很差,只是暫停每次都要等個三秒左右才可以播放(因為要重新創(chuàng)建加拉流這個過程),并且不能支持其他直播協(xié)議,不能同時控制多個直播分屏畫面(flv.js 創(chuàng)建六個視頻共同維護(hù)起來很不方便),這些問題EasyPlayer都有做相應(yīng)的改善
引入EasyPlayer
npm install @easydarwin/easyplayer --save
Vue 集成調(diào)用
copy node_modules/@easydarwin/easyplayer/dist/component/EasyPlayer.swf 到 靜態(tài)文件 根目錄
copy node_modules/@easydarwin/easyplayer/dist/component/crossdomain.xml 到 靜態(tài)文件 根目錄
copy node_modules/@easydarwin/easyplayer/dist/component/EasyPlayer-lib.min.js 到 靜態(tài)文件 根目錄
注意: 沒有調(diào)用會出現(xiàn)無法加載對應(yīng)插件的報錯
在 html 中引用 dist/component/EasyPlayer-lib.min.js
###H.265 copy node_modules/@easydarwin/easyplayer/dist/component/EasyPlayer.wasm 到 靜態(tài)文件 根目錄
詳細(xì)引入可以查看官網(wǎng)文檔 https://github.com/tsingsee/EasyPlayer.js/?tab=readme-ov-file
gitHub打不開的可以看這個https://www.npmjs.com/package/@easydarwin/easyplayer
開發(fā)問題和解決方案:
- 引入時,因為官方針對vue的引入方式是npm ,通過npm安裝包import引入組件使用,引入組件后一直報錯,我以為是沒有將文件copy到根目錄的原因(官網(wǎng)有說copy node_modules的文件),我又根據(jù)文檔copy了文件并且在html中引入,還是會有報錯
解決方法:只要不用npm的引入即可,也就不用import的導(dǎo)入了,也就是npm安裝只是為了讓你能copy對應(yīng)的文件,具體的使用還是通過cdn的方式,copy之后就可以移除包,這么看像是文檔有誤導(dǎo)了
這篇文章就給了很好的解答 https://juejin.cn/post/7235908012673089573#comment
EasyPlayer組件代碼使用就很簡單了,暫時還沒有需要多屏實時播放,也不需要處理buffer,只需要暫停播放時觸發(fā)接口就好
<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>
3. 使用LivePlayer.js
EasyPlayer多屏實時播放 | 時差 | 延遲處理的很完善,EasyPlayer對于fvl.js的延遲問題采用的是追幀和加速播放,隨著你暫停時間越久他追幀速度越慢,并且在追到延遲在10s時就停止了,也就是直播畫面總是有10s的延遲(直播設(shè)備是測試過沒問題的,并且在銷毀重創(chuàng)畫面是實時的)
后來又發(fā)現(xiàn)了livePlayer,測試使用發(fā)現(xiàn)以上問題都不存在了,延遲也最慢控制在3s,也是我們項目中能接受的范圍
引入
vue2
npm install @liveqing/liveplayer
vue3
npm install @liveqing/liveplayer-v3
第一步 :復(fù)制依賴文件(示例 通過 webpack 插件自動復(fù)制依賴)
如果正在使用 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>
如果你上述有報錯或者一些問題可以轉(zhuǎn)變成手動復(fù)制文件
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的引入導(dǎo)致它們互相影響,本來是想做一下比較所以才沒有刪除EasyPlayer(這時候就要提醒一下引入任何相同插件時確保項目中只有一種此功能插件,可能會因為內(nèi)部使用相同東西導(dǎo)致沖突,真的會查不到問題在哪,也查不到遇到相關(guān)問題的??)文章來源:http://www.zghlxwxcb.cn/news/detail-791191.html
更多詳細(xì)了解可以看官方文檔https://www.liveqing.com/docs/manuals/LivePlayer.html#%E5%AE%89%E8%A3%85文章來源地址http://www.zghlxwxcb.cn/news/detail-791191.html
到了這里,關(guān)于vue項目中視頻實時播放時播放器遇到的問題和解決過程 flv.js - EasyPlayer - LivePlayer的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!