flv.js在vue中的使用

Flv.js 是 HTML5 Flash 视频(FLV)播放器,纯原生 JavaScript 开发,没有用到 Flash。由 bilibili 网站开源。它的工作原理是将 FLV 文件流转码复用成 ISO BMFF(MP4 碎片)片段,然后通过 Media Source Extensions 将 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每次渲染的时候会去拿这个key 值做对比,如果这一次的key 值和上一次的key值是不一样的才会重新渲染dom 元素,否则保持上一次的元素状态。所以我用了一个时间戳方法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/141954.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【vue实战项目】通用管理系统:封装token操作和网络请求

目录 1.概述 2.封装对token的操作 3.封装axios 1.概述 前文我们已经完成了登录页&#xff1a; 【vue实战项目】通用管理系统&#xff1a;登录页-CSDN博客 接下来我们要封装一下对token的操作和网络请求操作。之所以要封装这部分内容是因为token我们登陆后的所有请求都要携…

Python爬虫从基础到入门:认识爬虫

Python爬虫从基础到入门:认识爬虫 1. 认识爬虫2. 开始简单的爬虫操作(使用requests)3. 辨别“数据”是静态加载还是动态生成的1. 认识爬虫 爬虫用自己的话说其实就是利用一定的编程语言,到网络上去抓取一些数据为自己所用。那为什么要用爬虫呢?自己直接到网页上去copy数据它…

线性代数本质系列(二)矩阵乘法与复合线性变换,行列式,三维空间线性变换

本系列文章将从下面不同角度解析线性代数的本质&#xff0c;本文是本系列第二篇 向量究竟是什么&#xff1f; 向量的线性组合&#xff0c;基与线性相关 矩阵与线性相关 矩阵乘法与复合线性变换 三维空间中的线性变换 行列式 逆矩阵&#xff0c;列空间&#xff0c;秩与零空间 克…

Arthas(阿尔萨斯)--(二)

目录 一、Arthas学习 1、JVM相关命令一 1、dashboard 2、thread 3、jvm 4、sysprop 一、Arthas学习 Arthas(阿尔萨斯)--(一) Arthas代码开源地址 1、JVM相关命令一 1、dashboard dashboard:显示当前系统的实时数据面板&#xff0c;按q或ctrlc退出 ID: Java 级别的线…

vue3 ref 与shallowRef reactive与shallowReactive

ref 给数据添加响应式&#xff0c;基本类型采用object.defineProperty进行数据劫持&#xff0c;对象类型是借助reactive 实现响应式&#xff0c;采用proxy 实现数据劫持&#xff0c;利用reflect进行源数据的操作 let country ref({count:20,names:[河南,山东,陕西],objs:{key…

postman调用接口报{“detail“:“Method \“DELETE\“ not allowed.“}错误, 解决记录

项目是python代码开发, urls.py 路由中访问路径代码如下: urlpatterns [path(reportmanagement/<int:pk>/, views.ReportManagementDetail.as_view(), namereport-management-detail),] 对应view视图中代码如下: class ReportManagementDetail(GenericAPIView):"…

华为笔记本电脑原装win10/win11系统恢复安装教程方法

华为电脑matebook 14原装Win11系统带F10智能还原 安装恢复教程&#xff1a; 1.安装方法有两种&#xff0c;一种是用PE安装&#xff0c;一种是华为工厂包安装&#xff08;安装完成自带F10智能还原&#xff09; 若没有原装系统文件&#xff0c;请在这里获取&#xff1a;https:…

适配器模式 rust和java的实现

文章目录 适配器模式介绍何时使用应用实例优点缺点使用场景 实现java实现rust 实现 rust代码仓库 适配器模式 适配器模式&#xff08;Adapter Pattern&#xff09;是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式&#xff0c;它结合了两个独立接口的功能…

Javaweb之javascript事件的详细解析

1.6 JavaScript事件 1.6.1 事件介绍 如下图所示的百度注册页面&#xff0c;当我们用户输入完内容&#xff0c;百度可以自动的提示我们用户名已经存在还是可以使用。那么百度是怎么知道我们用户名输入完了呢&#xff1f;这就需要用到JavaScript中的事件了。 什么是事件呢&…

【电路笔记】-戴维南定理(Thevenin‘s Theorem)

戴维南定理&#xff08;Thevenin’s Theorem&#xff09; 文章目录 戴维南定理&#xff08;Thevenins Theorem&#xff09;1、概述与定义2、戴维南模型确定3、一些线性电路的戴维南模型3.1 单电压源3.2 单电流源3.3 多电流/电压源 4、结论 在本文中&#xff0c;我们将介绍一种强…

2023.11.12使用flask对图片进行黑白处理(base64编码方式传输)

2023.11.12使用flask对图片进行黑白处理&#xff08;base64编码方式传输&#xff09; 由前端输入图片并预览&#xff0c;在后端处理图片后返回前端显示&#xff0c;可以作为图片处理的模板。 关键点在于对图片进行base64编码的转化。 使用Base64编码可以更方便地将图片数据嵌入…

file2Udp增量日志转出Udp简介

https://gitee.com/tianjingle/file2udp 很多时候服务产生的日志需要进行汇总&#xff0c;这种统一日志处理的方式有elb&#xff0c;而且很多日志组件也支持日志转出的能力。但是从广义上来说是定制化的&#xff0c;我们需要一个小工具实现tail -f的能力&#xff0c;将增量日志…

[100天算法】-球会落何处(day 76)

题目描述 用一个大小为 m x n 的二维网格 grid 表示一个箱子。你有 n 颗球。箱子的顶部和底部都是开着的。箱子中的每个单元格都有一个对角线挡板&#xff0c;跨过单元格的两个角&#xff0c;可以将球导向左侧或者右侧。将球导向右侧的挡板跨过左上角和右下角&#xff0c;在网…

【java:牛客每日三十题总结-7】

java:牛客每日三十题总结 总结如下 总结如下 执行流程如下&#xff1a;创建HttpServlet时需要覆盖doGet()和doPost请求 2. request相关知识 request.getParameter()方法传递的数据&#xff0c;会从Web客户端传到Web服务器端&#xff0c;代表HTTP请求数据&#xff1b;request.…

C#中.NET 6.0控制台应用通过EF访问已建数据库

目录 一、新建.NET 6.0控制台应用并建立数据库连接 二、下载并安装EF程序包 三、自动生成EF模型和上下文 1.Blog类模型 2.Post类模型 3.数据库上下文 四、设计自己的应用 VS2022的.NET6.0、.NET7.0框架下默认支持EF7&#xff08;版本号7.0.13&#xff09;&#xff0c;除…

在 SQL 中,当复合主键成为外键时应该如何被其它表引用

文章目录 当研究一个问题慢慢深入时&#xff0c;一个看起来简单的问题也暗藏玄机。在 SQL 中&#xff0c;主键成为外键这是一个很平常的问题&#xff0c;乍一看没啥值得注意的。但如果这个主键是一种复合主键&#xff0c;而另一个表又引用这个键作为它的复合主键&#xff0c;问…

HTTP——

HTTP 请求报文的构成 如下图: 第一行:HTTP请求的方法,具体是POST方法还是GET方法,或是其它方法;URI就是你的HTTP请求的路径;后面是HTTP协议的版本; 第二行往下连续多行:这些是请求头部分,也就是请求的首部设置的一些信息,相当于对HTTP请求的一些设置; 空格行:在…

U-Mail邮件中继有效解决海外邮件发送不畅难题

相信不少企业都经历过类似的问题&#xff0c;在跟国外客户发送电子邮件的过程中&#xff0c;经常会遇到邮件发不过去、邮件隔了很久对方才收到&#xff0c;或者是邮件退信等情况出现。对此&#xff0c;U-Mail技术专家李工解释到&#xff0c;导致海外通邮不畅主要有以下三个原因…

数据结构哈希表(散列)Hash,手写实现(图文推导)

目录 一、介绍 二、哈希数据结构 三、✍️实现哈希散列 1. 哈希碰撞&#x1f4a5; 2. 拉链寻址⛓️ 3. 开放寻址⏩ 4. 合并散列 一、介绍 哈希表&#xff0c;也被称为散列表&#xff0c;是一种重要的数据结构。它通过将关键字映射到一个表中的位置来直接访问记录&#…

字符设备驱动基础框架

一、总体框架 1.Linux字符设备驱动工作原理图 2.驱动使用端 3.驱动实现端 二、各部分详解 1.VFS层 1) inode结构体 在Unix/Linux操作系统中&#xff0c;每个文件都由一个inode&#xff08;索引节点&#xff09;来索引。inode是特殊的磁盘块&#xff0c;它们在文件系统创建时…