Использование flv.js в vue

Flv.js — это проигрыватель HTML5 Flash-видео (FLV), разработанный исключительно с использованием встроенного JavaScript и не использующий Flash. Открытый исходный код сайта bilibili. Он работает путем перекодирования и мультиплексирования потока файлов FLV во фрагменты ISO BMFF (фрагмент MP4), а затем подачи фрагментов MP4 в браузер через расширения источника мультимедиа.

инструкции

<template>
  <div class="video" :style="{ height: voidHeight }">
    <video ref="videoElement"  muted controls autoplay controlslist="nodownload noplaybackrate noremoteplayback" disablePictureInPicture="true" v-if="!imgError"></video>
    <div class="img_error" v-if="imgError">
      <p>无法连接相关设备</p>
    </div>
  </div>
</template>

<script>
import flvjs from "flv.js";
export default {
  name: "assemblyFlv",
  props: ["url", "height", "destroy","playon"], // 视频流路径,播放器高度,是否销毁播放器
  data() {
    return {
      flvPlayer: "",
      imgError: false,
      voidHeight: "",
      playOn:true
    };
  },
  mounted() {
    // 判断是否传入高度,如果没有,高度100%
    this.height ? (this.voidHeight = this.height) : (this.voidHeight = "100%");
    // 页面加载完成后,初始化
    this.$nextTick(() => {
      this.init(this.url);
    });
  },
  methods: {
    // 初始化
    init(source) {
      if (flvjs.isSupported()) {
        this.flvPlayer = flvjs.createPlayer(
          {
            type: "flv",
            url: source,
            isLive: true,
          },
          {
            enableWorker: false, //不启用分离线程
            enableStashBuffer: false, //关闭IO隐藏缓冲区
            reuseRedirectedURL: true, //重用301/302重定向url,用于随后的请求,如查找、重新连接等。
            autoCleanupSourceBuffer: true, //自动清除缓存
          }
        );
        var videoElement = this.$refs.videoElement;
        this.flvPlayer.attachMediaElement(this.$refs.videoElement);
        if (this.url !== "" && this.url !== null) {
            this.flvPlayer.load();
            //this.flvPlayer.play();
            setTimeout(() => { this.flvPlayer.play(); }, 100);
            // 加载完成
            this.flvPlayer.on(flvjs.Events.LOADING_COMPLETE, () => {
              this.imgError = false;
            });

            // 加载失败
            this.flvPlayer.on(
              flvjs.Events.ERROR,
              () => {
                if (this.flvPlayer) {
                    this.reloadVideo(this.flvPlayer);
                }else{
                  this.imgError = true;
                }
              },
              (error) => {
                console.log(error);
              }
            );

            this.flvPlayer.on(flvjs.Events.STATISTICS_INFO, (res) =>{
                if(this.playon != false){
                   if (this.lastDecodedFrame == 0) {
                     this.lastDecodedFrame = res.decodedFrames;
                     console.log(this.lastDecodedFrame)
                     return;
                   }
                   if (this.lastDecodedFrame != res.decodedFrames) {
                     this.lastDecodedFrame = res.decodedFrames;
                   } else {
                       this.lastDecodedFrame = 0;
                       console.log('卡住重连')
                       if (this.flvPlayer) {
                         this.reloadVideo(this.flvPlayer);
                         console.log('卡住重连完成')
                     }
                   }
                  }
                 });

            videoElement.addEventListener("progress", () => {
              if(videoElement.buffered.length != 0){
                let end = videoElement.buffered.end(0); //获取当前buffered值(缓冲区末尾)
                let delta = end - videoElement.currentTime; //获取buffered与当前播放位置的差值

                // 延迟过大,通过跳帧的方式更新视频
                if (delta > 10 || delta < 0) {
                  this.flvPlayer.currentTime = this.flvPlayer.buffered.end(0) - 1;
                  console.log('跳帧')
                  return;
                }
                // 追帧
                if (delta > 1) {
                  videoElement.playbackRate = 1.1;
                  console.log('追帧')
                } else {
                  videoElement.playbackRate = 1;
                  console.log('正常')
                }
              }
            });

            // 点击播放按钮后,更新视频
            videoElement.addEventListener("play", () => {
              if(videoElement.buffered.length > 0){
              let end = videoElement.buffered.end(0) - 1;
              this.flvPlayer.currentTime = end;
              console.log('播放最新')
              }
            });
            // 网页重新激活后,更新视频
            window.onfocus = () => {
              if(videoElement.buffered.length > 0){
                let end1 = videoElement.buffered.end(0) - 1;
                this.flvPlayer.currentTime = end1;
                console.log('页面切换')
              }
            };




        }

      } else {
        this.imgError = true;
      }
    },
    //断线重连
    reloadVideo(flvPlayer) {
          this.detachMediaElement();
          this.init(this.url);
          console.log('断线重连')
    },
    // 销毁
    detachMediaElement() {
      this.flvPlayer.pause();
      this.flvPlayer.unload();
      this.flvPlayer.detachMediaElement();
      this.flvPlayer.destroy();
      this.flvPlayer = "";
    },
  },
  watch: {
    url() {
      this.imgError = false;
      // 切换流之前,判断之前的流是否销毁
      this.flvPlayer == "" ? "" : this.detachMediaElement();
      // 初始化
      this.init(this.url);
    },
    destroy() {
      // 传入开关值
      if (this.destroy) {
        this.init(this.url);
      } else {
        this.flvPlayer == "" ? "" : this.detachMediaElement();
      }
    },
    playon() {
      this.reloadVideo(this.flvPlayer);
    }
  },
  beforeDestroy() {
    this.detachMediaElement();
  },
};
</script>

<style scoped>
.video {
  position: relative;
  height: 100%;
}
.video video {
  width: 100%;
  height: 100%;
  object-fit: fill;
}
.video video::-webkit-media-controls-play-button{
  display: none;
}
.video video::-webkit-media-controls-toggle-closed-captions-button {
    display: none;
}
.video video::-webkit-media-controls-timeline {
    display: none;
}
.video video::-webkit-media-controls-current-time-display {
    display: none;
}
.video video::-webkit-media-controls-time-remaining-display {
    display: none;
}
.img_error {
  position: absolute;
  top: 30%;
  left: 50%;
  margin-left: -120px;
  text-align: center;
}
.img_error > img {
  margin-bottom: 1em;
}
.img_error > p {
  color: #00fdff;
  font-weight: bold;
  font-size: 1.2em;
}
</style>

Упаковка:

 Инкапсуляция подкомпонентов:

<template>
    <div class="video-container">
      <video ref="videoElement" class="centeredVideo" controls autoplay muted></video>  
    </div>
</template>
 
<script>
import flvjs from "flv.js";  //引入flv
export default {
  props: {
    url : String,
  },
  data() {
    return {
      // src: ["http://172.21.1.111/live?port=1935&app=myapp&stream=streamname"],
    };
  },
  mounted() {
    this.flv_load(this.url);
  },
  methods: {
    flv_load(url) {
      if (flvjs.isSupported()) {
      let videoElement = this.$refs.videoElement;
        this.flvPlayer = flvjs.createPlayer(
          {
            type: "flv", //媒体类型
            url: url, //flv格式媒体URL
            isLive: true, //数据源是否为直播流
            hasAudio: false, //数据源是否包含有音频
            hasVideo: true, //数据源是否包含有视频
            enableStashBuffer: false, //是否启用缓存区
          },
          {
            enableWorker: false, // 是否启用分离的线程进行转换
            enableStashBuffer: false, //关闭IO隐藏缓冲区
            autoCleanupSourceBuffer: true, //自动清除缓存
          }
        );
        this.flvPlayer.attachMediaElement(videoElement); //将播放实例注册到节点
        this.flvPlayer.load(); //加载数据流
        this.flvPlayer.play(); //播放数据流
      }
    },
  },
};
</script>
 
<style scoped>
/* .video-container {
  display: inline-block;
  margin-right: 10px;
  width: 32%;
  height: 45%;
} */
.centeredVideo {
  width: 100%;
}
</style>

Вызовы родительского компонента:

<template>
  <el-card class="box-card">
    <div class="flvbox" v-for="(item,index) in src" :key="index">
    <!-- <VideoFlv url="http://172.21.1.111/live?port=1935&app=myapp&stream=streamname" /> -->
    <VideoFlv :url="item" />
    </div>
  </el-card>
</template>
 
<script>
import VideoFlv from "./VideoFlv.vue";
export default {
  components:{
    VideoFlv
  },
  data() {
    return {
      src: [
        "https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv",
        "https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv",
        "https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv",
        "https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv",
        "http://172.21.1.111/live?port=1935&app=myapp&stream=streamname",
        "https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv",
      ],
    };
  },
};
</script>
 
<style scoped>
.flvbox {
  display: inline-block;
  margin-right: 10px;
  width: 32%;
}
</style>

Поскольку видео должно быть в реальном времени, я обнаружил, что будет задержка после паузы и переключения страниц, как написано выше, поэтому разработку устраивает добавление новой кнопки обновления.Потом я попробовал много способов написания этот API, например, отслеживание кадров и обновление видео, но это не сработало.Родительский компонент был передан повторно.Поскольку значение не изменилось, повторный рендеринг не выполняется.Поэтому используется атрибут key.Vue будет сравнивать значение ключа каждый раз, когда оно отображается. Если значение ключа на этот раз отличается от последнего значения ключа, оно будет повторно отображено. dom элемент, в противном случае сохраняется последнее состояние элемента. Поэтому я использовал метод метки времени

Supongo que te gusta

Origin blog.csdn.net/irisMoon06/article/details/134370932
Recomendado
Clasificación