vue项目中使用flv.js实时播放 断流重连 关闭断流开发心得

第一次碰这个东西肯定会碰壁的,问百度人都问傻了(关键还骗人),其中心酸不必多描述,只为成长

后来项目更新采用的是EasyPlayer插件
文章地址: VUE项目中优雅使用EasyPlayer

后端是流媒体服务
**

用的是宇视的摄像头(对于前端就是拼接的地址不同)

**

简单介绍下:
Flv.js 是 HTML5 Flash 视频(FLV)播放器,纯原生 JavaScript 开发,没有用到 Flash。由 bilibili 网站开源。
开源地址: https://github.com/Bilibili/flv.js/

先看一下页面效果:
在这里插入图片描述

功能:点击右边菜单出现实时监控画面,页面有四个窗体,每个窗体播放不同的视频,当四个窗体都在播放时,点击下一个会依次替换每个窗体,随机点四个页面会出现对应的实时视频

开发问题1:离开页面视频会暂停(就没有实时性)
开发问题2:页面会疯狂报错(不影响功能,但是浏览器受不了)
开发问题3:怎么当四个窗体都在播放时,点击下一个会依次替换每个窗体
开发问题4:怎么断掉上一个视频的推流(视频会卡顿)
开发问题5:怎么断开重新连接

关于报错

Uncaught (in promise): The play() request was interrupted by a call to pause().

在调用 video的 play() 方法之后就立即被之后一次调用 pause() 方法中断了。错误提示中明确指出了调用 play 方法是返回了一个Promise对象。那么上述的问题就有了解决方法: 在 play() 执行成功后,播放视频,然后执行后续操作。

上才艺:

下载包 cnpm install --save flv.js

	页面结构
   <div class="video">
      <video
        v-for="i in 4"
        :id="'videoElement' + i"
        controls
        autoplay
        muted
      ></video>
    </div>

js部分

data() {
    
    
    return {
    
    
      flvPlayer: null,
      url: "",
      count: 1, // 当前点击标识
      flvPlayerList: []
    };
  },

创建实例

  createVideo() {
    
    
      if (flvjs.isSupported()) {
    
    
        var videoElement = document.getElementById("videoElement" + this.count);
        this.flvPlayer = flvjs.createPlayer(
          {
    
    
            type: "flv",
            isLive: true,
            hasAudio: false,
            url: this.url
          },
          {
    
    
            enableWorker: false, //不启用分离线程
            enableStashBuffer: false, //关闭IO隐藏缓冲区
            reuseRedirectedURL: true, //重用301/302重定向url,用于随后的请求,如查找、重新连接等。
            autoCleanupSourceBuffer: true //自动清除缓存
          }
        );

        this.flvPlayer.attachMediaElement(videoElement);
        // this.flvPlayer.load();

        if (this.url !== "" && this.url !== null) {
    
    
          this.flvPlayer.load();
          this.flvPlayer.play();
        }
      }

      //定时方法是为了用户离开页面视频是实时播放的,暂停按钮无用
      setInterval(function() {
    
    
        // console.log(videoElement.buffered,"idididid");
        if (videoElement.buffered.length > 0) {
    
    
          const end = videoElement.buffered.end(0); // 视频结尾时间
          const current = videoElement.currentTime; //  视频当前时间
          const diff = end - current; // 相差时间
          // console.log(diff);
          const diffCritical = 4; // 这里设定了超过4秒以上就进行跳转
          const diffSpeedUp = 1; // 这里设置了超过1秒以上则进行视频加速播放
          const maxPlaybackRate = 4; // 自定义设置允许的最大播放速度
          let playbackRate = 1.0; // 播放速度
          if (diff > diffCritical) {
    
    
            //  this.flvPlayer.load();
            // console.log("相差超过4秒,进行跳转");
            videoElement.currentTime = end - 1.5;
            playbackRate = Math.max(1, Math.min(diffCritical, 16));
          } else if (diff > diffSpeedUp) {
    
    
            // console.log("相差超过1秒,进行加速");
            playbackRate = Math.max(1, Math.min(diff, maxPlaybackRate, 16));
          }
          videoElement.playbackRate = playbackRate;
          if (videoElement.paused) {
    
    
            videoElement.play();
          }
        }
        //  if (videoElement.buffered.length) {
    
    
        //   let end = this.flvPlayer.buffered.end(0);//获取当前buffered值
        //   let diff = end - this.flvPlayer.currentTime;//获取buffered与currentTime的差值
        //   if (diff >= 0.5) {
    
    //如果差值大于等于0.5 手动跳帧 这里可根据自身需求来定
        //     this.flvPlayer.currentTime = this.flvPlayer.buffered.end(0);//手动跳帧
        //  }
        // }
      }, 1000);

      this.flvPlayer.on(flvjs.Events.ERROR, (errType, errDetail) => {
    
    
        // alert("网络波动,正在尝试连接中...");
        if (this.flvPlayer) {
    
    
          this.reloadVideo(this.flvPlayer);
        }
        // errType是 NetworkError时,对应errDetail有:Exception、HttpStatusCodeInvalid、ConnectingTimeout、EarlyEof、UnrecoverableEarlyEof
        // errType是 MediaError时,对应errDetail是MediaMSEError   或MEDIA_SOURCE_ENDED
      });
      this.flvPlayerList.push(this.flvPlayer);
    },

断开重连机制

   this.flvPlayer.on(flvjs.Events.ERROR, (errType, errDetail) => {
    
    
        // alert("网络波动,正在尝试连接中...");
        if (this.flvPlayer) {
    
    
          this.reloadVideo(this.flvPlayer);
        }
        // errType是 NetworkError时,对应errDetail有:Exception、HttpStatusCodeInvalid、ConnectingTimeout、EarlyEof、UnrecoverableEarlyEof
        // errType是 MediaError时,对应errDetail是MediaMSEError   或MEDIA_SOURCE_ENDED
      });

销毁断流方法

 destoryVideo(flvPlayer) {
    
    
      flvPlayer.pause();
      flvPlayer.unload();
      flvPlayer.detachMediaElement();
      flvPlayer.destroy();
      flvPlayer = null;
    },
 reloadVideo(flvPlayer) {
    
    
      this.destoryVideo(flvPlayer);
      this.createVideo();
    },

左边菜单点击的方法

clickhandleitem(data, index) {
    
    
  let ip = data.ipAddress;
  let admin = data.videoname;
  let password = data.videopas;

  this.url =
    "ws://服务地址:端口号/live?url=rtsp://" +
    admin +
    ":" +
    password +
    "@" +
    ip +
    "/media/video2/multicast";

  if (this.flvPlayerList.length > 3) {
    
    
    this.destoryVideo(this.flvPlayerList[0]);
    this.flvPlayerList.shift();
  }
  this.createVideo();
  this.count > 3 ? (this.count = 1) : this.count++;
},

代码没有封装,如有大佬有更好的办法,请指教

附上页面所有代码

<template>
  <div class="videobox">
    <div class="videolist">
      <div class="search">
        <el-input
          clearable
          v-model="inputvalue"
          placeholder="请输入内容"
          size="mini"
        ></el-input>
        <el-button type="primary" size="mini" @click="clicksearch"
          >查询</el-button
        >

        <ul>
          <li
            v-for="(item, index) in list"
            @click="clickhandleitem(item, index)"
          >
            <i class="el-icon-video-camera-solid"></i>
            {
    
    {
    
     item.devicename + item.deviceAddr }}
          </li>
        </ul>
      </div>
    </div>

    <div class="video">
      <video
        v-for="i in 4"
        :id="'videoElement' + i"
        controls
        autoplay
        muted
      ></video>
    </div>
  </div>
</template>

<script>
import flvjs from "flv.js";
import {
    
     mapGetters } from "vuex";

import {
    
     findAlllist } from "@/api/admin/equipmentlist";
export default {
    
    
  data() {
    
    
    return {
    
    
      flvPlayer: null,
      inputvalue: "",
      devicename: "60",
      url: "",
      list: [],
      count: 1, // 当前点击标识
      flvPlayerList: []
    };
  },
  computed: {
    
    
    ...mapGetters(["communityId"])
  },
  created() {
    
    
    this.findAlllistApi();
  },
  methods: {
    
    
    //查询
    clicksearch() {
    
    
      this.findAlllistApi();
    },
    clickhandleitem(data, index) {
    
    
      let ip = data.ipAddress;
      let admin = data.videoname;
      let password = data.videopas;

      this.url =
        "ws://服务地址:端口/live?url=rtsp://" +
        admin +
        ":" +
        password +
        "@" +
        ip +
        "/media/video2/multicast";

      if (this.flvPlayerList.length > 3) {
    
    
        this.destoryVideo(this.flvPlayerList[0]);
        this.flvPlayerList.shift();
      }
      this.createVideo();
      this.count > 3 ? (this.count = 1) : this.count++;
    },
    play(flvPlayer) {
    
    
      flvPlayer.play();
    },

    createVideo() {
    
    
      if (flvjs.isSupported()) {
    
    
        var videoElement = document.getElementById("videoElement" + this.count);
        this.flvPlayer = flvjs.createPlayer(
          {
    
    
            type: "flv",
            isLive: true,
            hasAudio: false,
            url: this.url
          },
          {
    
    
            enableWorker: false, //不启用分离线程
            enableStashBuffer: false, //关闭IO隐藏缓冲区
            reuseRedirectedURL: true, //重用301/302重定向url,用于随后的请求,如查找、重新连接等。
            autoCleanupSourceBuffer: true //自动清除缓存
          }
        );

        this.flvPlayer.attachMediaElement(videoElement);
        // this.flvPlayer.load();

        if (this.url !== "" && this.url !== null) {
    
    
          this.flvPlayer.load();
          this.flvPlayer.play();
        }
      }

      //定时方法是为了用户离开页面视频是实时播放的,暂停按钮无用
      setInterval(function() {
    
    
        // console.log(videoElement.buffered,"idididid");
        if (videoElement.buffered.length > 0) {
    
    
          const end = videoElement.buffered.end(0); // 视频结尾时间
          const current = videoElement.currentTime; //  视频当前时间
          const diff = end - current; // 相差时间
          // console.log(diff);
          const diffCritical = 4; // 这里设定了超过4秒以上就进行跳转
          const diffSpeedUp = 1; // 这里设置了超过1秒以上则进行视频加速播放
          const maxPlaybackRate = 4; // 自定义设置允许的最大播放速度
          let playbackRate = 1.0; // 播放速度
          if (diff > diffCritical) {
    
    
            //  this.flvPlayer.load();
            // console.log("相差超过4秒,进行跳转");
            videoElement.currentTime = end - 1.5;
            playbackRate = Math.max(1, Math.min(diffCritical, 16));
          } else if (diff > diffSpeedUp) {
    
    
            // console.log("相差超过1秒,进行加速");
            playbackRate = Math.max(1, Math.min(diff, maxPlaybackRate, 16));
          }
          videoElement.playbackRate = playbackRate;
          if (videoElement.paused) {
    
    
            videoElement.play();
          }
        }
        //  if (videoElement.buffered.length) {
    
    
        //   let end = this.flvPlayer.buffered.end(0);//获取当前buffered值
        //   let diff = end - this.flvPlayer.currentTime;//获取buffered与currentTime的差值
        //   if (diff >= 0.5) {
    
    //如果差值大于等于0.5 手动跳帧 这里可根据自身需求来定
        //     this.flvPlayer.currentTime = this.flvPlayer.buffered.end(0);//手动跳帧
        //  }
        // }
      }, 1000);

      this.flvPlayer.on(flvjs.Events.ERROR, (errType, errDetail) => {
    
    
        // alert("网络波动,正在尝试连接中...");
        if (this.flvPlayer) {
    
    
          this.reloadVideo(this.flvPlayer);
        }
        // errType是 NetworkError时,对应errDetail有:Exception、HttpStatusCodeInvalid、ConnectingTimeout、EarlyEof、UnrecoverableEarlyEof
        // errType是 MediaError时,对应errDetail是MediaMSEError   或MEDIA_SOURCE_ENDED
      });
      this.flvPlayerList.push(this.flvPlayer);
    },

    reloadVideo(flvPlayer) {
    
    
      this.destoryVideo(flvPlayer);
      this.createVideo();
    },
    destoryVideo(flvPlayer) {
    
    
      flvPlayer.pause();
      flvPlayer.unload();
      flvPlayer.detachMediaElement();
      flvPlayer.destroy();
      flvPlayer = null;
    },
    findAlllistApi() {
    
    
      findAlllist(this.communityId, this.inputvalue, this.devicename).then(
        res => {
    
    
          this.list = res.data;
        }
      );
    }
  }
};
</script>

<style lang="scss" scoped>
.videobox {
    
    
  display: flex;
  /* justify-content: space-between; */
  /* flex-wrap: wrap; */
}
.videolist {
    
    
  width: 550px;
  height: 906px;
  display: flex;
  .search {
    
    
    margin-left: 50px;
    .el-input {
    
    
      width: 300px;
      margin-top: 20px;
    }
    ul {
    
    
      width: 100%;
      height: 777px;
      overflow: hidden;
      overflow-y: auto;
      list-style: none;
      li {
    
    
        padding: 7px;
        margin: 1px 0;
        text-decoration: none;
        white-space: nowrap;
        font-size: 14px;
        cursor: pointer;
        &:hover {
    
    
          background: #fff;
        }
        .el-icon-video-camera-solid {
    
    
          font-size: 16px;
          color: #67c23a;
        }
      }
    }
  }
}
.video {
    
    
  display: flex;
  flex-wrap: wrap;
  width: 100%;
  justify-content: space-evenly;
  video {
    
    
    object-fit: contain;
    width: 600px;
    height: 390px;
  }
}
</style>

猜你喜欢

转载自blog.csdn.net/weixin_45906632/article/details/115031633
今日推荐