vue3使用flv.js播放推流視頻的示例代碼
前言
本人是在vue3中使用flv.js處理推流時(shí),遇到的一些問題,以及處理辦法,歸納總結(jié)為一個(gè)組件,僅限于推流使用。
目前只貼出部分關(guān)鍵代碼,若需要完整的代碼,請往github下載

1、構(gòu)建
/**
* @description: 構(gòu)建播放器
* @return {*}
* @Author: liuxin
*/
function flvCreated() {
try {
const videoElement = flvPlayerVideo.value;
if (flvjs.isSupported() && videoElement) {
addLog(`flv created,address:${prop.url}`);
const flvOption = {
url: prop.url, // 播放地址
hasAudio: false, // 是否有音頻
hasVideo: true, //是否有視頻
isLive: true, // 是否是直播流,默認(rèn) true
type: "flv", // 是否是直播流,默認(rèn) true
stashInitialSize: 128, // 減少首幀顯示等待時(shí)長
...prop.option,
};
state.flvPlayer = flvjs.createPlayer(flvOption, {
enableWorker: false, // 不啟用分離的線程進(jìn)行轉(zhuǎn)換,之前為true
enableStashBuffer: false, // 關(guān)閉IO隱藏緩沖區(qū)
stashInitialSize: 128, // 減少首幀顯示等待時(shí)長
autoCleanupSourceBuffer: true, // 打開自動(dòng)清除緩存
fixAudioTimestampGap: false, //false才會(huì)音視頻同步,新增
lazyLoad: false, // 去掉懶加載,新增
});
state.flvPlayer.attachMediaElement(videoElement);
state.flvPlayer.load();
state.flvPlayer.play();
state.endedReloadFlag = true; // 重置畫面停滯的播放狀態(tài),下次停滯了會(huì)再次打開
videoElementEvent(); // 手動(dòng)跳幀,防止延時(shí)
flvPlayerEvent(); // 斷流、卡頓處理
}
} catch (error) {
console.error("構(gòu)建錯(cuò)誤", error);
}
}2、銷毀
/**
* @description: 銷毀播放器
* @return {*}
* @Author: liuxin
*/
function flvDestory() {
if (state.delayTimer) {
clearTimeout(state.delayTimer); // 清除推遲打開播放器定時(shí)器
}
if (state.flvPlayer == null) return; // 空對象,不執(zhí)行銷毀
/* ----- 銷毀開始 ----- */
addLog(`flv destory,address:${prop.url}`);
try {
state.flvPlayer.off(flvjs.Events.ERROR, errorHandle);
if (state.flvPlayer._hasPendingLoad) {
state.flvPlayer.pause();
state.flvPlayer.unload();
}
state.flvPlayer.detachMediaElement();
state.flvPlayer.destroy();
state.flvPlayer = null;
} catch (error) {
console.error("銷毀錯(cuò)誤");
}
}3、斷流、卡頓重連
state.flvPlayer.on(flvjs.Events.STATISTICS_INFO, statisticsInfoHanle); // 斷流重連
/**
* @description: 視頻卡頓,銷毀后重建
* @param {*} errorType
* @param {*} errorDetail
* @param {*} errorInfo
* @return {*}
* @Author: liuxin
*/
function statisticsInfoHanle(res) {
// 初始化播放
if (state.lastDecodedFrame == 0) {
state.lastDecodedFrame = res.decodedFrames;
return;
}
// 正常播放
if (state.lastDecodedFrame != res.decodedFrames) {
state.lastDecodedFrame = res.decodedFrames;
state.loading = false; // 去掉loading動(dòng)畫
state.errorCount = 0; // 錯(cuò)誤連接次數(shù)歸0
}
// 播放異常
else {
if (state.player) {
addLog(`Reconnect after video freezes, address:${prop.url}`); // 添加日志
state.errorCount = 0; // 錯(cuò)誤連接次數(shù)歸0
state.lastDecodedFrame = 0; // 最后播放編碼號
flvDestory(); // 銷毀對象
flvCreated("statistics_info"); // 創(chuàng)建對象
}
}
}4、報(bào)錯(cuò)、停滯重連
state.flvPlayer.on(flvjs.Events.ERROR, errorHandle); // 監(jiān)聽出錯(cuò)消息后,銷毀后重連
state.flvPlayer.on(flvjs.Events.LOADING_COMPLETE, errorHandle); // ctrl+f5刷新,會(huì)莫名因?yàn)橥V筫nd不播放
/**
* @description: 錯(cuò)誤回調(diào)事件
* @param {*} errorType
* @param {*} errorDetail
* @param {*} errorInfo
* @return {*}
* @Author: liuxin
*/
function errorHandle() {
//視頻出錯(cuò)后銷毀重新創(chuàng)建 網(wǎng)絡(luò)錯(cuò)誤
if (state.flvPlayer && state.errorCount <= state.maxReconnectCount) {
addLog(`Video error ${state.errorCount} reconnection,
address:${prop.url}`); // 視頻報(bào)錯(cuò)N重連
state.loading = true; // 添加loading動(dòng)畫
state.errorCount++; //錯(cuò)誤重連次數(shù)+1
flvDestory();
flvCreated("ERROR");
}
if (state.errorCount > state.maxReconnectCount) {
state.loading = false; // 去掉loading
}
}5、累計(jì)延時(shí)處理
/**
* @description: 瀏覽器下載流事件,手動(dòng)跳幀,防止累計(jì)延時(shí)
* @return {*}
* @Author: liuxin
*/
videoElement.onprogress = (e) => {
// 不需要跳幀,如:異常視頻 或者沒有數(shù)據(jù)流,則不進(jìn)行跳幀
if (!prop.isBufferedEnd || state.flvPlayer.buffered.length <= 0) {
return;
}
state.loading = false;
/* ----- 跳幀操作 ----- */
let end = state.flvPlayer.buffered.end(0); //獲取當(dāng)前時(shí)間值
let diff = end - state.flvPlayer.currentTime; //獲取相差差值
// 延遲過大或幀率不正常,通過跳幀的方式更新視頻
if (diff > 20 || diff < 0) {
// addLog(`Manual frame skipping,address:${prop.url}`); // 添加日志
state.flvPlayer.currentTime = state.flvPlayer.buffered.end(0) - 0.5; // 手動(dòng)跳幀到最后
return;
}
// 正常幀率,正常播放
if (diff <= 1) {
videoElement.playbackRate = 1;
}
// 10秒內(nèi)的延時(shí),1.1倍速播放
else if (diff <= 10) {
// addLog(`Chase frames manually 1.1,address:${prop.url}`); // 手動(dòng)追幀
videoElement.playbackRate = 1.1;
}
// 20秒內(nèi)的延時(shí),1.2倍速播放
else if (diff <= 20) {
// addLog(`Chase frames manually 1.2,address:${prop.url}`); // 手動(dòng)追幀
videoElement.playbackRate = 1.2;
}
};6、手動(dòng)全屏
/**
* @description: 全屏 / 退出全屏
* @return {*}
* @Author: liuxin
*/
function fullscreenHandle() {
state.isFlullscreen = !state.isFlullscreen;
document.addEventListener("keydown", function (e) {
//此處填寫你的業(yè)務(wù)邏輯即可
if (e.key == "Escape") {
e.stopPropagation();
state.isFlullscreen = false;
}
});到此這篇關(guān)于vue3使用flv.js播放推流視頻的示例代碼的文章就介紹到這了,更多相關(guān)vue3 flv.js播放推流視頻內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue實(shí)現(xiàn)下載文件而非瀏覽器直接打開的方法
對于瀏覽器來說,文本、圖片等可以直接打開的文件,不會(huì)進(jìn)行自動(dòng)下載,下面這篇文章主要給大家介紹了關(guān)于Vue實(shí)現(xiàn)下載文件而非瀏覽器直接打開的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05
vue-cli項(xiàng)目中怎么使用mock數(shù)據(jù)
本篇文章主要介紹了vue-cli項(xiàng)目中怎么使用mock數(shù)據(jù) ,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09
簡單實(shí)現(xiàn)Vue的observer和watcher
這篇文章主要教大家如何簡單實(shí)現(xiàn)Vue的observer和watcher,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
vuex state及mapState的基礎(chǔ)用法詳解
這篇文章主要介紹了vuex state及mapState的基礎(chǔ)用法詳解,本文通過實(shí)例代碼相結(jié)合的形式給大家介紹的非常詳細(xì),需要的朋友跟隨腳本之家小編一起學(xué)習(xí)吧2018-04-04
vite+vue3項(xiàng)目解決低版本兼容性問題解決方案(Safari白屏)
這篇文章主要介紹了vite+vue3項(xiàng)目解決低版本兼容性問題(Safari白屏),使用官方插件 @vitejs/plugin-legacy 為打包后的文件提供傳統(tǒng)瀏覽器兼容性支持,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2024-03-03
vue.js實(shí)現(xiàn)選項(xiàng)卡切換
這篇文章主要為大家詳細(xì)介紹了vue.js實(shí)現(xiàn)選項(xiàng)卡切換功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03

