前端FLV视频直播解决方案

项目背景:

1. 后台给出一个地址,持续不断的推送flv视频流。

2.前端需要接收视频流,并寻找合适的播放插件。

一开始:

其实用的是xgplayer(西瓜视频)。

官网地址:西瓜播放器

使用的是直播,flv的插件:西瓜播放器 | xgplayer-flv。

但是无法播放,现象就是一直loading

后来,查了好多资料,发现一个issue:

流数据正常下载,xgplayer-flv无法播放 · Issue #604 · bytedance/xgplayer · GitHub。

和我情况一模一样,但是暂时没有什么解决方案。说明,此路不通

柳暗花明:

找了很久,找到一个万能播放插件 —— Jessibuca

官网地址:Jessibuca

如何使用:

前端如何使用?建议直接下载相关资源,静态引入。

需要下载三个资源,如下图:

怎么找到这三个资源?去官网的network里找找吧,不在多说。

vue中使用详情:

首先,上边的三个文件引入public。在index.html文件中只需要引入jessibuca.js。

<!-- 

public下的index.html 直接引入js文件

-->



....


<script src="<%= BASE_URL %>jessibuca.js"></script>





.....

然后,创建视频播放组件 LiveVideoPlay.vue:

<script>
export default {
  name: "LiveVideoPlay",
  props: {
    // 播放地址
    playUrl: {
      type: String,
      required: true,
    },
    // 目标domid
    id: {
      type: String,
      required: true,
    },
  },
  data() {
    return {
      player: null,
    };
  },
  methods: {
    // 初始化视频组件
    initPlayer() {
      if (Jessibuca) {
        this.player = new Jessibuca({
          container: document.getElementById(this.id), //jessibuca需要容器
          videoBuffer: 0.2, // 缓存时长
          isResize: false,
          loadingText: "疯狂加载中...",
          useMSE: true,
          useWCS: true,
          debug: true,
          background: "@/assets/icons/svg/no-video.svg",
          supportDblclickFullscreen: true,
          showBandwidth: true, // 显示网速
          operateBtns: {
            fullscreen: true,
            screenshot: true,
            play: true,
            audio: true,
          },
          forceNoOffscreen: true,
          isNotMute: false,
          timeout: 10,
        });
        //console.log("this.player ----- ", this.player, this.playUrl);
        this.player.play(this.playUrl);
      }
    },
  },
  beforeDestroy() {
    // 销毁视频
    if (this.player) {
      this.player.destroy();
      this.player = null;
    }
  },
  mounted() {
    this.initPlayer();
  },
};
</script>
<template>
  <div class="video-player-outer" :id="id"></div>
</template>
<style>
.video-player-outer {
  width: 100%;
  height: 100%;
}
</style>

最后,父组件中直接引用:

// 父组件中直接使用该组件

<script>
import LiveVideoPlay from "./LiveVideoPlay.vue";
export default {
    name: '',
    components: {
        LiveVideoPlay 
    },
    data () {
        return {
            playUrl1: null,
            playUrl2: null,
            showV1: false,
            showV2: false,
        }
    },
    methods: {
        handlePlayVideo(v) {
      if (v == 1) {
        this.playUrl1 =
          "https://xxxxxx/live/02.live.flv";
        this.showV1 = true;
      } else if (v == 2) {
        this.playUrl2 =
          "https://xxxxxx/live/02.live.flv";
        this.showV2 = true;
      }
    },
    StopPlayVideo(v) {
      if (v == 1) {
        this.showV1 = false;
      } else if (v == 2) {
        this.showV2 = false;
      }
    }, 
    },
}

</script>

<template>

    <div class="box">
          <div class="video-box-item">
            <el-button @click="handlePlayVideo(1)">播放视频1</el-button>
            <el-button @click="StopPlayVideo(1)">停止视频1</el-button>
            <LiveVideoPlay v-if="showV1" :playUrl="playUrl1" id="v1" />
          </div>

          <div class="video-box-item">
            <el-button @click="handlePlayVideo(2)">播放视频2</el-button>
            <el-button @click="StopPlayVideo(2)">停止视频2</el-button>
            <LiveVideoPlay v-if="showV2" :playUrl="playUrl2" id="v2" />
          </div>
    </div>

</template>

如上,可以试一试自己的播放地址是否可以成功播放视频.如果你的需求不那么高,不考虑延迟,到这里基本就够了,不需要卷下去.

但是,如果你的要求比较高,又不想买jecibuca的收费版本,那么请继续往下看, 有更好的解决方案.

优中选优:

现在这个功能是做出来了,但是视频延迟的问题比较突出.比如,我控制摄像头旋转一下位置,在上述解决方案中,会明显发现摄像头触发旋转进而回传视频明显延迟.那怎么办? 如果你要求不高,那么上边的解决方案足够使用,但是如果要求低延迟,那么就不得不祭出另个大杀器了 - LiveQing.

官网地址: LivePlayer H5播放器 | 青柿视频流媒体服务解决方案

如何使用? vue2中大概分为这几步骤(vue3+vite的可以参考官网):

1. 安装 Liveplayer:

npm install @liveqing/liveplayer

2. 安装 webpack 插件 copy-webpack-plugin

npm install copy-webpack-plugin@4.6.0

注意: 如果你的版本是vuecli4.0+, 那么以上这个版本就够用了.但是 如果你的vuecli5.0+, 你需要升级这个copywebpack-plugins的版本,比如 copy-webpack-plugin@11.0.0 .亲测有效,一定要注意!!!

3. 配置vue.config.js

/**  vue.config.js   */

// *****  注意 还是vuecli版本的问题
// *****  如果你的vuelci4.0+  就用下边这个配置

const CopyWebpackPlugin = require("copy-webpack-plugin");

module.exports = {

    ...


    configureWebpack: (config) => {
        config.devtool = "source-map";
        config.output.libraryExport =
                      "default"; /* 解决import UMD打包文件时, 组件install方法执行报错的问题!! */



        // 增加配置如下  主要是 plugins

        const plugins = [
          new CopyWebpackPlugin([
            {
              from: "node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml",
            },
            {
              from: "node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf",
            },
            {
              from: "node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js",
              to: "livePlayer/",
            },
      ]),
    ];
        config.plugins.push(...plugins);

      
    }
  },


    ...

}


// *****  如果你的vuelci5.0+  就用下边这个配置

const CopyWebpackPlugin = require("copy-webpack-plugin");


module.exports = {


    .....


    chainWebpack(config) {
        
        // 增加插件
        
        config.plugin('copy').use(CopyWebpackPlugin, [

           {
                 patterns: [
            {
              from: "node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml",
            },
            {
              from: "node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf",
            },
            {
              from: "node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js",
              to: "livePlayer/",
            },
      ]
            }
        ])

        
    }
    


}

4. public中的index.html中引入js文件

5. 封装播放视频组件

<template>
  <LivePlayer
    class="component-wrapper video-panel"
    :class="{ fullscreen: flag }"
    :videoUrl="options.url"
    :videoTitle="options.title"
    :poster="options.poster"
    :controls="options.controls"
    :autoplay="options.autoplay"
    :live="options.live"
    :hide-snapshot-button="options.hideSnapshot"
    :muted="options.muted"
    :fluent="options.fluent"
    :stretch="options.stretch"
    :aspect="options.aspect"
    :loading="options.loading"
    :hide-big-play-button="options.hideBigPlay"
    @fullscreen="onFullscreen"
  >
    <slot></slot>
  </LivePlayer>
</template>

<script>
import LivePlayer from "@liveqing/liveplayer";

export default {
  name: "LiveVideoRtcPlayer",
  components: {
    LivePlayer,
  },
  props: {
    params: {
      type: String,
    },
  },
  data() {
    return {
      flag: false,
    };
  },
  computed: {
    options() {
      return {
        // 播放地址
        url: this.params,
        // 视频标题
        title: "",
        // 视频封面图片
        poster: "",
        // 播放器控制栏
        controls: true,
        // 隐藏截图
        hideSnapshot: true,
        // 是否直播
        live: true,
        // 是否自动播放
        autoplay: true,
        // 是否静音
        muted: true,
        // 流畅模式
        fluent: true,
        // 是否拉伸
        stretch: true,
        // 全屏 - 适应div
        aspect: "fullscreen",
        // 指示加载状态
        loading: true,
        // 隐藏起播状态下的大播放按钮
        hideBigPlay: true,
      };
    },
  },
  created() {
    console.log("配置 ----- ", this.options);
  },
  beforeDestroy() {
    if (this.options) {
      this.options.url = "";
    }
    this.onExitFullscreen();
  },
  methods: {
    onExitFullscreen() {
      this.flag = false;
    },
    onFullscreen(status) {
      this.flag = status;
      if (!status) {
        this.onExitFullscreen();
        return;
      }
    },
  },
};
</script>

<style lang="scss" scoped>
.component-wrapper.video-panel {
  position: relative;
  width: 100%;
  height: 100%;

  .video-wrapper .video-js {
    background-color: rgba(32, 46, 71, 0.6);

    .video-title {
      top: 4px;
      right: unset;
      left: 4px;
      padding: 4px 6px;
      max-width: 80%;
      font-size: 16px;
    }

    .video-control {
      position: absolute;
      top: 100%;
      left: 50%;
      transform: translate(-50%, -140%);
      margin-top: 0;
    }
  }

  &.fullscreen .video-wrapper .video-js {
    .video-title {
      top: 60px;
      right: unset;
      left: 20px;
      padding: 5px 8px 6px;
      background: rgba(4, 16, 37, 0.6);
      border-radius: 4px;
    }
  }
}
</style>

6. 父组件中引用播放组件

<template>
  <div id="app">
    <!-- 视频 -->
    <div class="video-box">
      <el-button @click="handlePlayVideo(1)">播放视频1</el-button>
      <el-button @click="StopPlayVideo(1)">停止视频1</el-button>
      <div class="video-box-item">
        <LiveVideoRtcPlayer v-if="showV1" :params="playUrl1" id="v1" />
      </div>
      <el-button @click="handlePlayVideo(2)">播放视频2</el-button>
      <el-button @click="StopPlayVideo(2)">停止视频2</el-button>
      <div class="video-box-item">
        <LiveVideoRtcPlayer v-if="showV2" :params="playUrl2" id="v2" />
      </div>
    </div>
  </div>
</template>

<script>
//import Child from "./Child.vue";
//import LiveVideoPlay from "./LiveVideoPlay.vue";
import LiveVideoRtcPlayer from "./LiveVideoRtcPlayer.vue";
export default {
  name: "App",
  components: {
    //LiveVideoPlay,
    LiveVideoRtcPlayer,
  },
  data() {
    return {
      playUrl1: null,
      playUrl2: null,
      showV1: false,
      showV2: false,
    };
  },
  methods: {
    handlePlayVideo(v) {
      if (v == 1) {
        // this.playUrl1 =
        //   "https://xxxxxxxxxxxx/live/02.live.flv";
        this.playUrl1 =
          "webrtcs://xxxxxxxxxxxxx/live/index/api";
        this.showV1 = true;
      } else if (v == 2) {
        this.playUrl2 = 'xxxxx 播放地址';
        this.showV2 = true;
      }
    },
    StopPlayVideo(v) {
      if (v == 1) {
        this.showV1 = false;
      } else if (v == 2) {
        this.showV2 = false;
      }
    },
  },
};
</script>

<style lang="scss" scoped>
#app {
  height: 100%;
}

#app ::v-deep .box {
  font-size: 30px;
}

.video-box {
  width: 100%;
  height: 600px;
  overflow: hidden;

  &-item {
    width: 300px;
    height: 400px;
    float: left;
    overflow: hidden;
    background-color: #f1f2f3;
    margin-left: 50px;
    position: relative;
  }
}
</style>
<style lang="scss"></style>

测试一下,nice.

累了,就这样吧.

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

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

相关文章

开放式耳机怎么选?2023高人气品牌推荐:新手避坑必看!

自从开放式耳机风靡市场以来&#xff0c;大家对于开放式耳机的选购也越发摸不着头脑。价格从百元到千元不等&#xff0c;就连大品牌的产品口碑也褒贬不一。 不少人私信向我询问&#xff1a; 1、难道只有千元价位的开放式耳机才好吗&#xff1f;2、是否有价格更实惠且性价比更…

如何使用 Helm 在 K8s 上集成 Prometheus 和 Grafana|Part 1

本系列将分成三个部分&#xff0c;您将学习如何使用 Helm 在 Kubernetes 上集成 Prometheus 和 Grafana&#xff0c;以及如何在 Grafana 上创建一个简单的控制面板。Prometheus 和 Grafana 是 Kubernetes 最受欢迎的两种开源监控工具。学习如何使用 Helm 集成这两个工具&#x…

C#电源串口调试

目的 记录串口调试的遇到的一些问题以及相应的解决方法 1.串口定义:串口是计算机与其他硬件传输数据的通道&#xff0c;在计算机与外设通信时起到重要作用 2.串口通信的基础知识 C#中的串口通信类 C#使用串口通信类是SerialPort(),该类使用方法是 new 一个 SerialPort对象 为S…

Prometheus-JVM

一. JVM监控 通过 jmx_exporter 启动端口来实现JVM的监控 Github Kubernetes Deployment Java 服务&#xff0c;修改 wget https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.19.0/jmx_prometheus_javaagent-0.19.0.jar# 编写配置文件&#xff0…

JAVA判断两个时间之间的差

1.首先引入jar包 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.7</version> </dependency>2.计算差值 public static DateFormat getDateTimeFormat(){DateFormat dtf new Sim…

即将来临的2024年,汽车战场再起波澜?

我们来简要概况一下11月主流车企的销量表现&#xff1a; 根据数据显示&#xff0c;11月吉利集团总销量29.32万辆&#xff0c;同比增长28%。这在当月国内主流车企中综合实力凌厉&#xff0c;可谓表现得体。而与吉利直接竞争的比亚迪&#xff0c;尽管数据未公布&#xff0c;但我们…

华为二层交换机与防火墙配置实例

二层交换机与防火墙对接上网配置示例 组网图形 图1 二层交换机与防火墙对接上网组网图 二层交换机简介配置注意事项组网需求配置思路操作步骤配置文件相关信息 二层交换机简介 二层交换机指的是仅能够进行二层转发&#xff0c;不能进行三层转发的交换机。也就是说仅支持二层…

Flink系列之:Savepoints

Flink系列之&#xff1a;Savepoints 一、Savepoints二、分配算子ID三、Savepoint 状态四、算子五、触发Savepoint六、Savepoint 格式七、触发 Savepoint八、使用 YARN 触发 Savepoint九、使用 Savepoint 停止作业十、从 Savepoint 恢复十一、跳过无法映射的状态恢复十二、Resto…

22 3GPP在SHF频段基于中继的5G高速列车场景中的标准化

文章目录 信道模型实验μ参考信号初始接入方法波形比较 RRH&#xff1a;remote radio head 远程无线头 HTS&#xff1a;high speed train 高速移动列车 信道模型 考虑搭配RRH和车载中继站之间的LOS路径以及各种环境&#xff08;开放或峡谷&#xff09;&#xff0c;在本次实验场…

Postgresql源码(118)elog/ereport报错跳转功能分析

1 日志接口 elog.c完成PG中日志的生产、记录工作&#xff0c;对外常用接口如下&#xff1a; 1.1 最常用的ereport和elog ereport(ERROR,(errcode(ERRCODE_UNDEFINED_TABLE),errmsg("relation \"%s\" does not exist",relation->relname)));elog(ERRO…

如何粗暴地下载huggingface_hub指定数据文件

参考这里&#xff1a; https://huggingface.co/docs/huggingface_hub/guides/download 可见下载单个文件&#xff0c;下载整个仓库文件都是可行的。 这是使用snapshot_download下载的一个例子&#xff1a; https://qq742971636.blog.csdn.net/article/details/135150482 sn…

轻松管理TXT文本,高效批量内容调整,打造高效工作流程!

在数字时代&#xff0c;文本文件已经成为我们生活和工作中不可或缺的一部分。无论是简单的笔记、待办事项&#xff0c;还是复杂的项目报告、小说草稿&#xff0c;TXT文本都能为我们提供灵活的存储和编辑方式。但是&#xff0c;随着文本文件的增多&#xff0c;如何轻松管理、高效…

Java 并发编程中的线程池

7 并发编程中的线程池 自定义线程池 package com.rainsun.d7_thread_pool;import lombok.extern.slf4j.Slf4j;import java.util.ArrayDeque; import java.util.Deque; import java.util.HashSet; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Co…

vue3引入使用高德地图,不显示地图问题

将全局引入的mockjs去除&#xff0c;就可以了。

基于ChatGLM搭建专业领域问答机器人的思路

如果我们对ChatGLM进一步提出涉及专业领域的问题&#xff0c;而此方面知识是ChatGLM未经数据训练的&#xff0c;那么ChatGLM的回答效果如何呢&#xff1f;本节将考察ChatGLM在专业领域的问答水平&#xff0c;并尝试解决此方面的问题。 在使用ChatGLM制作专业领域问答机器人之前…

如何利用烛龙和谷歌插件优化CLS(累积布局偏移) | 京东云技术团队

简介 CLS 衡量的是页面的整个生命周期内发生的每次意外布局偏移的最大突发性_布局偏移分数_。布局变化的发生是因为浏览器倾向于异步加载页面元素。更重要的是&#xff0c;您的页面上可能存在一些初始尺寸未知的媒体元素。这种组合意味着浏览器在加载完成之前无法确定单个元素…

anconda常用命令

一、基础指令说明 1、查看anconda版本号 conda --version 2、查看当前已有虚拟环境 conda env list 3、创建新环境 conda create -n classify python3.9 创建一个叫做classify的虚拟环境&#xff0c;其中python等于3.9 4、进入虚拟环境 activate classify 5、安装包 接下来…

【Skynet 入门实战练习】事件模块 | 批处理模块 | GM 指令 | 模糊搜索

文章目录 前言事件模块批处理模块GM 指令模块模糊搜索最后 前言 本节完善了项目&#xff0c;实现了事件、批处理、模糊搜索模块、GM 指令模块。 事件模块 什么是事件模块&#xff1f;事件模块是用来在各系统之间传递事件消息的。 为什么需要事件模块&#xff1f;主要目的是…

由浅入深走进Python异步编程【多进程】(含代码实例讲解 || multiprocessing、异步进程池、进程通信)

写在前面 从底层到第三方库&#xff0c;全面讲解python的异步编程。这节讲述的是python的多线程实现&#xff0c;纯干货&#xff0c;无概念&#xff0c;代码实例讲解。 本系列有6章左右&#xff0c;点击头像或者专栏查看更多内容&#xff0c;陆续更新&#xff0c;欢迎关注。 …

群多多社群人脉H5-2.1.4多开插件+小程序独立前端+搭建教程

功能介绍&#xff1a; 1、群多多社群大全&#xff0c;是一个集发布、展示社群信息、人脉推广的裂变工具/平台。 2、通过人脉广场&#xff0c;将商家信息通过名片进行展示&#xff0c;让资源对接、人脉推广更加便捷高效。 3、行业群、兴趣群、知识付费群、交友群、商家活动推…
最新文章