vue3使用flv.js播放推流视频的示例代码

2023-05-10 14:33:05 作者:admin
目录
  • 前言
  • 1、构建
  • 2、销毁
  • 3、断流、卡顿重连
  • 4、报错、停滞重连
  • 5、累计延时处理
  • 6、手动全屏

前言

本人是在vue3中使用flv.js处理推流时,遇到的一些问题,以及处理办法,归纳总结为一个组件,仅限于推流使用。

目前只贴出部分关键代码,若需要完整的代码,请往github下载

vue3使用flv.js播放推流视频的示例代码

1、构建

    /**     * @description: 构建播放器     * @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, // 是否是直播流,默认 true            type: "flv", // 是否是直播流,默认 true            stashInitialSize: 128, // 减少首帧显示等待时长            ...prop.option,          };          state.flvPlayer = flvjs.createPlayer(flvOption, {            enableWorker: false, // 不启用分离的线程进行转换,之前为true            enableStashBuffer: false, // 关闭IO隐藏缓冲区            stashInitialSize: 128, // 减少首帧显示等待时长            autoCleanupSourceBuffer: true, // 打开自动清除缓存            fixAudioTimestampGap: false, //false才会音视频同步,新增            lazyLoad: false, // 去掉懒加载,新增          });          state.flvPlayer.attachMediaElement(videoElement);          state.flvPlayer.load();          state.flvPlayer.play();          state.endedReloadFlag = true; // 重置画面停滞的播放状态,下次停滞了会再次打开          videoElementEvent(); // 手动跳帧,防止延时          flvPlayerEvent(); // 断流、卡顿处理        }      } catch (error) {        console.error("构建错误", error);      }    }

2、销毁

    /**     * @description: 销毁播放器     * @return {*}     * @Author: liuxin     */    function flvDestory() {      if (state.delayTimer) {        clearTimeout(state.delayTimer); // 清除推迟打开播放器定时器      }      if (state.flvPlayer == null) return; // 空对象,不执行销毁      /* ----- 销毁开始 ----- */      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("销毁错误");      }    }

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动画        state.errorCount = 0; // 错误连接次数归0      }      // 播放异常      else {        if (state.player) {          addLog(`Reconnect after video freezes, address:${prop.url}`); // 添加日志          state.errorCount = 0; // 错误连接次数归0          state.lastDecodedFrame = 0; // 最后播放编码号          flvDestory(); // 销毁对象          flvCreated("statistics_info"); // 创建对象        }      }    }

4、报错、停滞重连

      state.flvPlayer.on(flvjs.Events.ERROR, errorHandle); // 监听出错消息后,销毁后重连      state.flvPlayer.on(flvjs.Events.LOADING_COMPLETE, errorHandle); // ctrl+f5刷新,会莫名因为停止end不播放    /**     * @description: 错误回调事件     * @param {*} errorType     * @param {*} errorDetail     * @param {*} errorInfo     * @return {*}     * @Author: liuxin     */    function errorHandle() {      //视频出错后销毁重新创建 网络错误      if (state.flvPlayer && state.errorCount <= state.maxReconnectCount) {        addLog(`Video error ${state.errorCount} reconnection,            address:${prop.url}`); // 视频报错N重连        state.loading = true; // 添加loading动画        state.errorCount++; //错误重连次数+1        flvDestory();        flvCreated("ERROR");      }      if (state.errorCount > state.maxReconnectCount) {        state.loading = false; // 去掉loading      }    }

5、累计延时处理

     /**       * @description: 浏览器下载流事件,手动跳帧,防止累计延时       * @return {*}       * @Author: liuxin       */      videoElement.onprogress = (e) => {        // 不需要跳帧,如:异常视频   或者没有数据流,则不进行跳帧        if (!prop.isBufferedEnd || state.flvPlayer.buffered.length <= 0) {          return;        }        state.loading = false;        /* ----- 跳帧操作 ----- */        let end = state.flvPlayer.buffered.end(0); //获取当前时间值        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; // 手动跳帧到最后          return;        }        // 正常帧率,正常播放        if (diff <= 1) {          videoElement.playbackRate = 1;        }        // 10秒内的延时,1.1倍速播放        else if (diff <= 10) {          // addLog(`Chase frames manually 1.1,address:${prop.url}`); // 手动追帧          videoElement.playbackRate = 1.1;        }        // 20秒内的延时,1.2倍速播放        else if (diff <= 20) {          // addLog(`Chase frames manually 1.2,address:${prop.url}`); // 手动追帧          videoElement.playbackRate = 1.2;        }      };

6、手动全屏

    /**     * @description: 全屏 / 退出全屏     * @return {*}     * @Author: liuxin     */    function fullscreenHandle() {      state.isFlullscreen = !state.isFlullscreen;      document.addEventListener("keydown", function (e) {        //此处填写你的业务逻辑即可        if (e.key == "Escape") {          e.stopPropagation();          state.isFlullscreen = false;        }      });
原文地址:https://blog.csdn.net/liuxin00020/article/details/127496340
在线咨询 拨打电话