flv.js视频流出错,断流处理

发布时间 2023-06-25 22:32:20作者: 漫思

flv.js视频流出错,断流处理

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内部原因

image.png

Bug修复:

  • 网上大部分解决方案是修改浏览器视频编解码配置(Chrome浏览器输入chrome://flags/,搜索video,选择disabled,如下图),但是此方法在我项目中无效,遂继续翻阅百度,终于找到破解之法。

image.png

  • 修复之法:监听视频播放错误信息,出错时销毁重建,就是如此简单,我之前怎么没有想到

完整代码

 
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>

总结