flv.js视频流出错,断流处理
可乐加冰515 2023年02月20日 17:45 · 阅读 274- 场景:前端使用flv.js播放视频流
Bug表现:
- 视频流播放两分钟左右video标签出现暂停按钮,控制台flv.js报错:Failed to execute 'appendBuffer' on 'SourceBuffer': The HTMLMediaElement.error attribute is not null
Bug原因:
- 百度出来是flv.js内部原因
Bug修复:
- 网上大部分解决方案是修改浏览器视频编解码配置(Chrome浏览器输入chrome://flags/,搜索video,选择disabled,如下图),但是此方法在我项目中无效,遂继续翻阅百度,终于找到破解之法。
- 修复之法:监听视频播放错误信息,出错时销毁重建,就是如此简单,我之前怎么没有想到
完整代码
js 复制代码<template>
<div v-if="propValue.rtspUrl === 'src'">
<span>视频加载中</span>
</div>
<video
v-else
muted
autoplay
controls
class="videoPlayer"
:id="propValue.id"
></video>
</template>
<script>
import flvjs from "flv.js";
export default {
props: {
propValue: {
type: Object,
default: () => {
return {
id: "player",
rtspUrl: "",
};
},
},
},
data() {
return {
player: null,
lastDecodedFrames: 0
};
},
watch: {
propValue: {
handler(newVal, oldVal) {
if (newVal && newVal != oldVal) {
this.init();
}
},
deep: true,
immediate: true,
},
},
mounted() {
this.init();
},
beforeDestroy() {
this.destroyFlv();
},
methods: {
init() {
if (!flvjs.isSupported()) return;
if (this.propValue.rtspUrl === "") return;
const videoContainer = document.getElementById(this.propValue.id);
if (videoContainer) {
this.player = flvjs.createPlayer(
{
type: "flv", // flv,mp4
isLive: true,
// url: `ws://localhost:80/rtsp/${this.id}/?url=${this.rtsp}`,
url: this.propValue.rtspUrl,
hasAudio: false,
},
{
autoCleanupSourceBuffer: true,
enableWorker: false, //不启用分离线程
enableStashBuffer: true, //关闭IO隐藏缓冲区
isLive: true,
lazyLoad: false,
}
);
this.player.attachMediaElement(videoContainer);
try {
this.player.load();
this.player.play();
this.listenVideo();
} catch (error) {
console.log(error);
}
}
},
destroyFlv() {
if (this.player) {
this.player.pause();
this.player.unload();
this.player.detachMediaElement();
this.player.destroy();
this.player = null;
}
},
// 监听视频流是否断流或者卡顿
listenVideo() {
const that = this;
this.player.on(
flvjs.Events.ERROR,
(errorType, errorDetail, errorInfo) => {
console.log("errorType", errorType);
console.log("errorDetail", errorDetail);
console.log("errorInfo", errorInfo);
// 视频出错后销毁重建
that.destroyFlv();
that.init();
}
);
// 视频断流
this.player.on("statistics_info", function (res) {
if(that.lastDecodedFrames === 0){
that.lastDecodedFrames = res.decodedFrames
return
}
if(that.lastDecodedFrames != res.decodedFrames){
that.lastDecodedFrames = res.decodedFrames
}else{
that.lastDecodedFrames = 0
that.destroyFlv()
that.init()
}
});
},
},
};
</script>
<style scoped lang='scss'>
div {
position: relative;
width: 100%;
height: 100%;
border: 1px solid #fff;
> span {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.videoPlayer {
width: 100%;
height: 100%;
}
</style>
总结
-
关键代码在于listenVideo这个函数