vue3使用flv.js播放推流視頻的示例代碼
前言
本人是在vue3中使用flv.js處理推流時(shí),遇到的一些問題,以及處理辦法,歸納總結(jié)為一個(gè)組件,僅限于推流使用。
目前只貼出部分關(guān)鍵代碼,若需要完整的代碼,請(qǐng)往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í)長(zhǎng) ...prop.option, }; state.flvPlayer = flvjs.createPlayer(flvOption, { enableWorker: false, // 不啟用分離的線程進(jìn)行轉(zhuǎn)換,之前為true enableStashBuffer: false, // 關(guān)閉IO隱藏緩沖區(qū) stashInitialSize: 128, // 減少首幀顯示等待時(shí)長(zhǎng) 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; // 空對(duì)象,不執(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; // 最后播放編碼號(hào) flvDestory(); // 銷毀對(duì)象 flvCreated("statistics_info"); // 創(chuàng)建對(duì)象 } } }
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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue data的數(shù)據(jù)響應(yīng)式到底是如何實(shí)現(xiàn)的
這篇文章主要介紹了Vue data的數(shù)據(jù)響應(yīng)式到底是如何實(shí)現(xiàn)的,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02vue3中使用router路由實(shí)現(xiàn)跳轉(zhuǎn)傳參的方法
這篇文章主要介紹了vue3中使用router路由實(shí)現(xiàn)跳轉(zhuǎn)傳參的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03vue+egg+jwt實(shí)現(xiàn)登錄驗(yàn)證的示例代碼
這篇文章主要介紹了vue+egg+jwt實(shí)現(xiàn)登錄驗(yàn)證的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05vue實(shí)現(xiàn)導(dǎo)航菜單和編輯文本的示例代碼
這篇文章主要介紹了vue實(shí)現(xiàn)導(dǎo)航菜單和編輯文本功能的方法,文中示例代碼非常詳細(xì),幫助大家更好的參考和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07詳解vue-cli開發(fā)環(huán)境跨域問題解決方案
本篇文章主要介紹了vue-cli開發(fā)環(huán)境跨域問題解決方案,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-06-06vue如何集成raphael.js中國(guó)地圖的方法示例
最近的數(shù)據(jù)統(tǒng)計(jì)項(xiàng)目中要用到中國(guó)地圖,也就是在地圖上動(dòng)態(tài)的顯示某個(gè)時(shí)間段某個(gè)省份地區(qū)的統(tǒng)計(jì)數(shù)據(jù),我們不需要flash,僅僅依靠raphael.js以及SVG圖像就可以完成地圖的交互操作。本文就給大家介紹了關(guān)于利用vue集成raphael.js中國(guó)地圖的相關(guān)資料,需要的朋友可以參考下。2017-08-08