VUE项目开发,使用jessibuca——实现直播流播放器

2023-10-28

VUE项目开发,使用jessibuca——实现直播流播放器

效果图

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

插件目录

首先在项目的根目录下找到public文件夹,然后再public文件夹下方的index.html,引入jessibuca.js,jessibuca压缩包: 云盘地址。即可使用该播放器。项目目录如下:
在这里插入图片描述

public/index.html页面引入

在这里插入图片描述

组件一

SupVideoPlayer.vue

<template>
  <div class="sup-video-player">
    <div :class="['video-list', 'video-split-' + splitNum]">
      <div
        v-for="n in 9"
        :key="n"
        @click="videoItemClick(n)"
        :class="[
          'video-item',
          'split-' + splitNum,
          'video-item-' + n,
          { active: active == n },
        ]"
      >
        <vue-player
          :ref="'player' + n"
          :id="'tc-player' + n"
        ></vue-player>
      </div>
    </div>
    <div class="video-tools">
      <ul>
        <li>
          <el-tooltip placement="top">
            <div slot="content">保存视图</div>
            <svg-icon icon-class="vt_save_scene" @click="saveScene"/>
          </el-tooltip>
        </li>
        <li>
          <el-tooltip placement="top">
            <div slot="content">关闭所有</div>
            <svg-icon icon-class="vt_close_all" @click="closeAll"/>
          </el-tooltip>
        </li>
        <li>
          <el-tooltip placement="top">
            <div slot="content">关闭选中单元格</div>
            <svg-icon icon-class="vt_close" @click="closeCurCell"/>
          </el-tooltip>
        </li>
        <li v-if="false">
          <el-tooltip placement="top">
            <div slot="content">全屏</div>
            <svg-icon icon-class="vt_fullscreen" @click="fullscreen"/>
          </el-tooltip>
        </li>
        <li>
          <el-tooltip placement="top">
            <div slot="content">抓拍</div>
            <svg-icon icon-class="vt_screenshot" @click="capture"/>
          </el-tooltip>
        </li>
        <slot name="extend-tools"></slot>
        <li style="margin-left: auto">
          <el-tooltip placement="top">
            <div slot="content">9分屏</div>
            <svg-icon icon-class="vt_split_9" @click="splitScreen(9)"/>
          </el-tooltip>
        </li>
        <li>
          <el-tooltip placement="top">
            <div slot="content">6分屏</div>
            <svg-icon icon-class="vt_split_6" @click="splitScreen(6)"/>
          </el-tooltip>
        </li>
        <li>
          <el-tooltip placement="top">
            <div slot="content">4分屏</div>
            <svg-icon icon-class="vt_split_4" @click="splitScreen(4)"/>
          </el-tooltip>
        </li>
        <li>
          <el-tooltip placement="top">
            <div slot="content">1分屏</div>
            <svg-icon icon-class="vt_split_1" @click="splitScreen(1)"/>
          </el-tooltip>
        </li>
      </ul>
    </div>
  </div>
</template>
<script>
//引入播放组件
import VuePlayer from "./VuePlayer";
export default {
  name: "",
  data() {
    return {
      splitNum: this.split,
      active: this.activeIndex,
      recorder: null,
      talkbackData: null,
      dataList: []
    };
  },
  created() {},
  components: { VuePlayer },
  props: {
    split: {
      type: Number,
      default: 4,
    },
    activeIndex: {
      type: Number,
      default: 1,
    },
    talkback: {
      type: Boolean,
      default: false,
    },
    activeName:String
  },
  computed: {
    currPlayer() { 
      var p = this.$refs["player" + this.active];
      if (p) return p[0];
      return null;
    },
  },
  methods: {
    videoItemClick(n) {
      this.active = n;
      console.log(n);
      this.talkbackData = this.currPlayer.data;
    },
    closeAll() {
      for (var i = 1; i <= 9; i++) {
        let players = this.$refs["player" + i];
        if (players && players.length > 0) {
          players[0].close();
        }
      }
      this.active = 1;
      this.dataList = [];
    },
    closeCurCell() {
      if (this.currPlayer) {
        this.currPlayer.close();
        this.dataList[this.active] = null;
      }
    },
    splitScreen(n) {
      console.log(n);
      this.splitNum = n;
      this.active = 1;
    },
    play(url, data) {
      this.dataList[this.active] = data;
      this.currPlayer.load(url, data);
      var n = (this.active + 1) % (this.splitNum + 1);
      this.active = n == 0 ? 1 : n;
    },
    playAtIndex(index, url, data) {
      this.dataList[index] = data;
      let players = this.$refs["player" + index];
      if (players && players.length > 0) {
        players[0].load(url, data);
      }
    },
    capture() {
      if (this.currPlayer) {
        this.currPlayer.screenShot();
      }
    },
    detail() {
      console.log(this.currPlayer.data);
    },
    fullscreen() {
      if (this.currPlayer) {
        this.currPlayer.fullscreen();
      }
    },
    ptzCtrl(cmd, param) {
      // 判断是否正在播放视频
      if (!this.currPlayer.status()) {
        console.warn('没有选中正在播放的设备视频!');
        return;
      }
      let params = {
        avObjName: "",
        id: this.dataList[this.active].objId,
        cmd: cmd,
        param: param
      };
      this.$api.controlPtz(params).then((response) => {
        if (!response.success) {
          this.$message({
            message: response.info,
            type: 'error'
          })
        }
      })
    },
    saveScene() {
      // 判断是否有视频正在播放
      let isPlaying = false;
      for (var i = 1; i <= this.splitNum; i++) {
        let players = this.$refs["player" + i];
        if (players && players.length > 0 && players[0].status()) {
          isPlaying = true;
          break;
        }
      }
      if (!isPlaying) {
        this.$message({
          message: "没有正在播放的视频",
          type: "warning",
          duration: 2000
        });
        return;
      }
      // 弹出输入框输入视图名称
      this.$prompt('请输入视图名称', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
      }).then(({ value }) => {
        if (value) {
          let details = [];
          for (var i = 1; i <= this.splitNum; i++) {
            if (this.dataList[i]) {
              details.push({
                index: i,
                objId: this.dataList[i].objId,
                flvUrl: ''
              });
            }
          }
          let param = {
            name: value,
            viewInfo: {
              cut: this.splitNum,
              viewInfoDetails: details
            }
          };
          this.$api.createUVView(param).then((response) => {
            if (response.success) {
              this.$message({
                type: 'success',
                message: '保存成功'
              });
              this.$emit('saveSceneSuccess');
            }
          });
        }
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '取消输入'
        });       
      });
    }
  },
  state: {
    count: 0,
  },
  mutations: {
    increment(state) {
      state.count++;
    },
  },
};
</script>

<style lang="scss" scoped>
.sup-video-player {
  display: grid;
  grid-template-columns: 100%;
  grid-template-rows: calc(100% - 48px) 48px;
  height: 100%;
  .video-list {
    flex: 1 1 auto;
    background: #98a5a5;
    display: grid;

    .video-item {
      background: black;
      border: 1px solid #052524;
      cursor: pointer;
      position: relative;
    }

    .video-item.active {
      border: 1px solid #009586;
      box-shadow: inset 0 0 2px 2px #ffba00;
    }

    &.video-split-6 {
      grid-template-columns: 33.33% 33.33% 33.33%;
      grid-template-rows: 33.33% 33.33% 33.33%;
    }

    .video-item.split-6.video-item-1 {
      grid-column-start: 1;
      grid-column-end: 3;
      grid-row-start: 1;
      grid-row-end: 3;
    }

    &.video-split-1 {
      grid-template-columns: 100%;
      grid-template-rows: 100%;
    }

    &.video-split-4 {
      grid-template-columns: 50% 50%;
      grid-template-rows: 50% 50%;
    }

    &.video-split-9 {
      grid-template-columns: 33.33% 33.33% 33.33%;
      grid-template-rows: 33.33% 33.33% 33.33%;
    }
  }

  .video-tools {
    // height: 48px;
    background: #95a0a0;

    ul {
      display: flex;
      list-style: none;
      padding: 0 10px;
      margin: 0;
      line-height: 48px;

      .icon {
        cursor: pointer;
      }
      li {
        margin: 0 8px;
      }
      .iconfont {
        font-size: 24px;
      }
    }
  }
}
</style>

组件二

VuePlayer.vue播放组件

<template>
  <div :id="id" class="player" style="width: 100%; height: 100%"  ref="container"></div>
</template>

<script>
export default {
  props: {
    id: {
      type: String,
      default: "tc-player",
    },
    width: {
      type: [Number, String],
      default: "100%",
    },
    height: {
      type: [Number, String],
      default: "100%",
    },
    listener: {
      type: Function,
      default() {
        return function (msg) {};
      },
    },
    live: {
      type: Boolean,
      default: true,
    },
    autoplay: {
      type: Boolean,
      default: true,
    },
    src: {
      type: String,
      default: "",
    },
    options: {
      type: Object,
    },
  },
  data() {
    return {
      jessibuca: null,
      version: '',
      wasm: false,
      vc: "ff",
      quieting: true,
      showOperateBtns: false,
      showBandwidth: false,
      err: "",
      speed: 0,
      performance: "",
      volume: 1,
      rotate: 0,
      recordType: 'webm',
      scale: 0,
      url: this.src,
      data: null,
      isNotMute: false
    };
  },
  mounted() {
    // this.create();
    // window.onerror = (msg) => (this.err = msg);
    // this.jessibuca.play("https://flvplayer.js.org/assets/video/weathering-with-you.flv");
    
  },
  unmounted() {
    this.jessibuca.destroy();
  },
  methods: {
    load(src,data) {
      this.data = data
      this.url = src;
      this.create()
    }, 
    create(options) {
      options = options || {};
      this.jessibuca = new window.Jessibuca(
          Object.assign(
              {
                container: this.$refs.container,
                videoBuffer: Number(0.2), // 缓存时长
                isResize: false,
                useWCS: this.useWCS,
                useMSE: this.useMSE,
                text: "",
                // background: "bg.jpg",
                loadingText: "加载中...",
                // hasAudio:false,
                debug: false,
                supportDblclickFullscreen: true,
                showBandwidth: true, // 显示网速
                operateBtns: {
                  fullscreen: true,
                  screenshot: true,
                  play: true,
                  audio: true,
                  
                },
                vod: this.vod,
                forceNoOffscreen: !this.useOffscreen,
                isNotMute: this.isNotMute,
                timeout: 10
              },
              options
          )
      );
      
      var _this = this;
      this.jessibuca.on("load", function () {});

      this.jessibuca.on("log", function (msg) {});

      this.jessibuca.on("record", function (msg) {
        console.log("on record:", msg);
      });

      this.jessibuca.on("pause", function () {
        // _this.playing = false;
      });

      this.jessibuca.on("fullscreen", function (msg) {});

      this.jessibuca.on("mute", function (msg) {
        _this.quieting = msg;
      });

      this.jessibuca.on("audioInfo", function (msg) {
        // console.log("audioInfo", msg);
      });

      // this.jessibuca.on("bps", function (bps) {
      //   // console.log('bps', bps);
      // });
      // let _ts = 0;
      // this.jessibuca.on("timeUpdate", function (ts) {
      //     console.log('timeUpdate,old,new,timestamp', _ts, ts, ts - _ts);
      //     _ts = ts;
      // });

      this.jessibuca.on("videoInfo", function (info) {
        // console.log("videoInfo", info);
      });

      this.jessibuca.on("error", function (error) {
        _this.$message.error("获取视频错误");
      });

      this.jessibuca.on("timeout", function () {
        _this.$message.error("获取视频超时");
      });

      this.jessibuca.on('start', function () {
        // console.log('frame start');
      })

      this.jessibuca.on("performance", function (performance) {
        var show = "卡顿";
        if (performance === 2) {
          show = "非常流畅";
        } else if (performance === 1) {
          show = "流畅";
        }
        _this.performance = show;
      });
      this.jessibuca.on('buffer', function (buffer) {
        // console.log('buffer', buffer);
      })

      this.jessibuca.on('stats', function (stats) {
        // console.log('stats', stats);
      })

      this.jessibuca.on('kBps', function (kBps) {
        // console.log('kBps', kBps);
      });

      this.jessibuca.on("play", () => {
        // this.playing = true;
        // this.loaded = true;
        _this.quieting = _this.jessibuca.isMute();
      });

      this.jessibuca.on('recordingTimestamp', (ts) => {
        // console.log('recordingTimestamp', ts);
      })

      this.jessibuca.play(this.url);
      // console.log(this.jessibuca);
    },
    // play() {
    //   // this.jessibuca.onPlay = () => (this.playing = true);


    //   // if (this.$refs.playUrl.value) {
    //     // this.jessibuca.play(this.$refs.playUrl.value);
    //     this.jessibuca.play("https://flvplayer.js.org/assets/video/weathering-with-you.flv");
    //   // }
    // },
    mute() {
      // this.isNotMute = false
      //jessibuca.isMute()
      this.jessibuca.mute();
    },
    cancelMute() {
      // this.isNotMute = true
      this.jessibuca.cancelMute();
    },
    pause() {
      this.jessibuca.pause();
      // this.playing = false;
      this.err = "";
      this.performance = "";
    },
    volumeChange() {
      this.jessibuca.setVolume(this.volume);
    },
    rotateChange() {
      this.jessibuca.setRotate(this.rotate);
    },
    destroy() {
      if (this.jessibuca) {
        this.jessibuca.destroy();
      }
      this.create();
      // this.playing = false;
      // this.loaded = false;
      this.performance = "";
    },
    fullscreen() {
      if (!this.jessibuca) return;
      return this.jessibuca.setFullscreen(true);
    },
    clearView() {
      this.jessibuca.clearView();
    },
    close() {
      if (!this.jessibuca) return;
      return this.reset();
    },
    // startRecord() {
    //   const time = new Date().getTime();
    //   this.jessibuca.startRecord(time, this.recordType);
    // },

    // stopAndSaveRecord() {
    //   this.jessibuca.stopRecordAndSave();
    // },
    reset() {
      if (this.jessibuca) {
        this.jessibuca.destroy()
      }
      this.jessibuca = null;
      this.url = "";
    },
    screenShot() {
      this.jessibuca.screenshot();
    },
    status() {
      return this.jessibuca && this. jessibuca.isPlaying();
    }
  },
};
</script>

<style lang="scss" scoped>
.player {
  background: url(../../../../assets/image/shexiangtou.png) center center no-repeat #353535;
  background-size: 60px 60px;
}
</style>

主页:index.vue

<template>
  <div class="app-container">
    <sup-video-player
      :split="6"
      ref="supPlayer"
    >
    </sup-video-player>
  </div>
</template>

<script>
import SupVideoPlayer from "./components/SupVideoPlayer";

export default {
  components: {
    SupVideoPlayer,
  },
  data() {
    return {
    };
  },
  mounted() {
  },
  methods: {
  
    onClick() {
       //url,就是播放地址 http://192.168.0.1*0:1***0/av/192_168_0_6_8**0/1_0.flv 
       //data,是通过点击设备树获取回来的参数,这个具体根据实际开发进行修改。
	  this.$refs.supPlayer.play(url, data);
	}
  },
};
</script>

<style lang="scss" scoped>
</style>

补充内容

2023.14额外补充一下:如果需要点击树或者初始化加载多个视频,那就必须返回多个flvUrl。

        let details = data.viewInfo.viewInfoDetails;
        if (details) {
          // 关闭当前所有播放视频
          this.$refs.supPlayer.closeAll();
          //显示窗口数
          this.$refs.supPlayer.splitScreen(data.viewInfo.cut);
          // 打开视图中视频
          details.forEach((view) => {
            this.$refs.supPlayer.playAtIndex(view.index, view.flvUrl, view);
          });
        }

		data:[{"name":"8画面",
		"viewInfo":{
		"cut":6,//这是显示几个窗口参数
		"id":"XXXXXXXXXXX"
		"viewInfoDetails":[{
				 "index":1,
				"objId":"3716",
				"flvUrl":"http://192.168.0.1:8080/avI/6_8000/1_0.flv"
			},{
				"index":2,
				"objId":"3718",
				"objName":null,
				"flvUrl":"http://192.168.0.1:8080/avI/7_8000/1_0.flv"
			},{
				"index":3,
				"objId":"3719",
				"flvUrl":"http://192.168.0.1:8080/avI/8_8000/1_0.flv"
			},{
				"index":4,
				"objId":"3717",
				"flvUrl":"http://192.168.0.1:8080/avI/10_8000/1_0.flv"
			},{
				"index":5,
				"objId":"3715",
				"flvUrl":"http://192.168.0.1:8080/avI/9_8000/1_0.flv"}]
			},
		}]

加载多个视频

这是 一次性加载多个视频的效果:
在这里插入图片描述

SupVideoPlayer.vue、VuePlayer.vue可以充当组件直接引入项目即可,url和data都需要根据实际的开发传需要的值。图片地址需要自己重新定义修改。decoder.js、decoder.wasm、jessibuca.js代码可以去github获取。

参考连接

jessibuca压缩包: 云盘地址
API文档: jessibuca-api-文档
demo代码: Vue
全部demo代码: 所有官方案例

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

VUE项目开发,使用jessibuca——实现直播流播放器 的相关文章

随机推荐

  • 雷电模拟器 服务器无响应,雷电模拟器怎么用脚本长时间运行未响应win10

    vbInformation ExitSub Err100 MsgBox 对不起 不能建立表 结果 保存文档共有二种方法 方法1 VB3 VB4之版本 可于Form Load 程序中加入下列程式码 Me Move Screen Width W
  • conda的安装

    Step1 下载miniconda的安装脚本 Step2 运行miniconda的安装脚本 Step3 查看并同意license Step3 选择安装位置 Step4 同意初始化 Step5 设置是否要显示base环境提示 Step6 重新
  • STM32-固件打包部署

    STM32 固件打包部署 Fang XS 1452512966 qq com STM32固件输出 工程上使用Keil开发STM32软件 在调试过程中 可直接编译下载 例如bootloader和APP 在调试时 可以直接下载2次 但是工程上
  • 【python】如何动态获取某个文件的绝对路径?

    导读 获取文件路径的方法诸如os getcwd os path abspath path 此类方法都是基于当前程序的启动目录来确定路径的 因此 当需要通过相对路径获取非当前目录下的文件时 就会出现类似No such file or dire
  • 【远程服务器】如何删除远程服务器中所有的特定类型的文件、如何在远程服务器中新建文件夹、如何逐行遍历文件夹中的图片等任务

    文章目录 一 如何删除远程服务器中所有的特定类型的文件 二 远程服务器上新建一个文件夹 三 如何逐行遍历文件夹中的图片 四 如何想办法读取某个文件夹中的照片 五 远程服务器如何清除一个很长的文件夹 六 删除所有文件 七 ssh远程连接的服务
  • 半导体设备通讯标准(SECS/GEM)

    目录 收起 前言 一 SECS GEM 简介 二 系统的硬件组成 三 搞清楚需求 搞明白是什么 基本玩法规则 1 secs是一个协议族
  • JAVA获取计算机的IP、名称、操作系统等信息

    java view plain copy print import java io BufferedReader import java io InputStream import java io InputStreamReader imp
  • leetcode 1817. 查找用户活跃分钟数【python3,哈希表的实现思路详解】

    题目 给你用户在 LeetCode 的操作日志 和一个整数k 日志用一个二维整数数组logs表示 其中每个logs i IDi timei 表示ID为IDi的用户在timei分钟时执行了某个操作 多个用户可以同时执行操作 单个用户可以在同一
  • C语言基础入门48篇_31_二维数组应用之游戏中的碰撞检测(二维数组初始化边界,利用边界值判断是否可移动)

    本篇在上篇C语言基础入门48篇 18 使用循环移动游戏人物 屏幕符号运动 while 1 进行实时响应 if 表达式 switch 表达式 windows的API及API进行自行封装使 的基础上增加了移动边界 并且游戏人物不会移动出所设计的
  • 跑通CogView教程

    入门小菜鸟 希望像做笔记记录自己学的东西 也希望能帮助到同样入门的人 更希望大佬们帮忙纠错啦 侵权立删 目录 一 代码下载 二 环境配置 1 输入命令 2 安装apex 三 下载 image tokenizer vqvae hard big
  • 测试知识点

    什么是软件测试 使用人工或自动手段 来运行或测试某个系统的过程 其目的在于检验它是否满足规定的需求或弄清预期结果与实际结果之间的差别 什么是测试用例 1 评价测试人员的标准主要有两个 即发现的有效Bug数和编写的有效测试用例数 2 测试用例
  • python免费课程全套-为了学习Python,我汇总了这10个免费的视频课程!

    毫无疑问 Python是目前世界上 的编程语言 它 的优势在于它将越多越多的人带入了编程领域 近年来 学习Python的人比学习其他任何语言的人都多 许多人学Python是为了它背后的数据科学和机器学习库 当然 也有一些人学习用于Web开发
  • Java Web 安全之CSRF

    概念 跨站请求伪造 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法 原理 用户登陆A网站 A网站确认身份 B网站页面向A网站发起请求 带A网站身份 危害 利用用户登陆态 盗取用户资金 转账 消费 用户不知情 冒充用户
  • R语言tidyr包数据变换函数(dplyr包组合使用,完美)

    日常的数据预处理中 不可避免的会遇到数据的各种变形 转换 R语言中的tidyr包函数解决了数据变形上的问题 变量 列的转换 1 宽数据变长数据 gather data key key value value na rm FALSE conv
  • win10 vs2015 freetype2编译

    01 FreeType FreeType库是一个完全免费 开源 的 高质量的且可移植的字体引擎 它提供统一的接口来访问多种字体格式文件 包括TrueType OpenType Type1 CID CFF Windows FON FNT X1
  • 从Linux内核源码到操作系统

    Linux源码只有运行起来才能成为操作系统 否则她只能静静的躺在存储介质上沉睡 本文就讲解如何将这个睡美人唤醒 唤醒后给他穿上旗袍她就成为RedHat 给她换上包臀裙她就成为SUSE 再或者给她换上超短裙 她就成为Ubuntu 总之就是你可
  • 你在用FastReport.Net报表工具做报表没

    博主文章仅用于学习 研究和交流目的 不足和错误之处在所难免 希望大家能够批评指出 博主核实后马上更改 一 FastReport 是什么 FastReport Net是一款适用于Windows Forms ASP NET和MVC框架的功能齐全
  • 3DMAX机械建模贴图教程

    1 大型机械模型 网上找图片 然后放进材质球 应用 查看图片 裁剪好 然后把材质球放在模型之上 然后点击绿色 讲材质放给制定对象 2 加VR 太阳光 0 02 3 贴图贴上不行 点击UV贴图 选择长方体啥的 想要好看 加V RAY渲染器 测
  • json序列化小驼峰格式(属性名首字母小写)

    引用Newtonsoft Json 只需要设置JsonSerializerSettings参数即可 using Newtonsoft Json using System namespace ItemDemo class Program st
  • VUE项目开发,使用jessibuca——实现直播流播放器

    VUE项目开发 使用jessibuca 实现直播流播放器 VUE项目开发 使用jessibuca 实现直播流播放器 效果图 插件目录 public index html页面引入 组件一 组件二 补充内容 加载多个视频 参考连接 VUE项目开