多监控m3u8视频流,怎么获取每个监控的封面图(纯前端)

news2025/1/18 22:58:25

文章目录

  • 1.背景
  • 2.问题分析
  • 3.解决方案
    • 3.1解决思路
    • 3.2解决过程
      • 3.2.1 封装播放组件
      • 3.2.2 隐形的视频div
      • 3.2.3 截取封面图
    • 3.3 结束

1.背景

有这样一个需求:

  1. 给你一个监控列表,每页展示多个监控(至少12个,m3u8格式);
  2. 且展示每个监控的第一帧画面的封面图,但是后端没给你返回封面图;
  3. 点击监控时可以查看具体的监控弹框,支持全屏;

那么、你会怎么办呢?

后端说:后端获取封面图会比较麻烦,还要单独引入一个模块,所以丢给前端来做喽
备注:本例仅为思路分析,具体代码需要具体实现

实现效果如下:
在这里插入图片描述

2.问题分析

  • 首先,有的vue2的视频流播放组件,支持多个视频流同时播放,但那是定制的4宫格或9宫格,不够自由;
  • 或者,有的支持多视频流播放,但是本人暂未找到,其实也可以自定义实现多流播放;
  • 其次,即使有这种组件,但是毕竟是网页端,如果一直播放,性能肯定会扛不住,毕竟一直接收视频片段,这个是巨量的。
  • 所以,还是老老实实地想办法获取第一帧封面图吧!

备注:前端用的vue2

下图为多视频流播放的示例图:
在这里插入图片描述
但是这样会出现一个问题,那就是你会获取无止境的m3u8推流,不久,你的浏览器就会内存溢出(内存将不断增大,下图非只是初始时的截图):
在这里插入图片描述
作为对比,看下只用封面截图的网页资源占用:
在这里插入图片描述

所以,上述方案作废!

由于上述方案作废,所以此处不做多视频流同时播放的代码示例了。

3.解决方案

3.1解决思路

  1. 在监控列表页放一个隐藏的div,div中放的是视频播放组件;
  2. 然后循环播放,在播放的时候截取封面,用的html2canvas
  3. 将截取的封面处理进监控列表的数组中;
  4. 销毁隐藏的视频流播放的div;

3.2解决过程

3.2.1 封装播放组件

首先,你要有一个支持m3u8播放的vue组件,本例用的是hls.min.js,将这个js文件放在/public/js/hls.min.js下。

然后封装的组件示例如下hlsVideo.vue

<!-- 视频巡检专用,其他组件勿用 -->
<template>
  <div >
    <video
      id="hls"
      class="img-responsive video-js vjs-default-skin"
      controls
      ref="videoRef"
      preload="auto"
      :autoplay="autoplay"
      :muted="muted"
      :style="{ width: width, height: height }"
    >
    </video>
  </div>
</template>

<script>
let hlsPlayer
export default {
  props: {
    // 监控地址
    videoUrl: { type: String, default: '' },
    // 监控宽高
    width: { type: String, default: '100%' },
    height: { type: String, default: '100%' },
    // 自动播放
    autoplay: { type: Boolean, default: true },
    // 是否静音,默认静音
    muted: { type: Boolean, default: true }
  },
  watch: {
    videoUrl: {
      deep: true,
      handler(val) {
        if ( hlsPlayer != null ) {
          hlsPlayer.destroy()
        }
        this.handlePlayer()
      }
    }
  },
  mounted() {
    this.handlePlayer()
  },

  beforeDestroy() {
    if ( hlsPlayer!= null ) {
      hlsPlayer.destroy()
      hlsPlayer = null
    }
  },

  methods: {
    // emit 发送播放状态
    handlePlayer() {
      if(this.videoUrl) {
        const video = this.$refs.videoRef
        hlsPlayer = new Hls();
        hlsPlayer.loadSource(this.videoUrl);
        hlsPlayer.attachMedia(video);//将视频元素绑定到此 HLS 对象
        hlsPlayer.on(Hls.Events.MANIFEST_PARSED, () => {
          video.play();
          this.$emit("playing", true);
        });
        hlsPlayer.on(Hls.Events.ERROR, (eventName, data) => {
          if ( data.fatal && data.type !== "networkError" ) {
            video.pause();
            this.$emit("playing", false);
          }
        });
      } else {
        if ( hlsPlayer != null ) {
          hlsPlayer.destroy()
        }
      }
    },
  }
}
</script>
<style lang="scss" scoped>
::v-deep {
  // 隐藏视频监控进度条
  video::-webkit-media-controls-timeline {
    display: none;
  }
  // 隐藏视频监控剩余时间
  video::-webkit-media-controls-time-remaining-display {
    display: none;
  }
}
</style>

3.2.2 隐形的视频div

有了视频播放组件,我们只需要引入即可:

import HlsVideo from "@/componentsUser/m3u8Video/hlsVideo.vue";

然后在页面中写一个div元素来放置该组件:

 <!--      隐藏区域,用于截取封面图     -->
  <div class="hide-video">
    <hls-video
      v-if="tempVideoUrl"
      :video-url="tempVideoUrl"
      id="hiddenVideo"
      ref="hiddenRef"
      :autoplay="true"
      @playing="changePlayStatus"
    />
  </div>

再加上样式,让我们看不见它,让它自己截图玩去吧,CSS样式如下:

.hide-video {
  width: 300px;
  height: 180px;
  position: absolute;
  top: 0;
  left: 0;
  z-index: -2;
}

同时在data中声明以下变量:

 tempVideoUrl: null, // 隐藏的m3u8播放路径
 tempVideoIndex: 0, // 隐藏播放的当前下标,用户循环
 monitorList: [], // 监控列表

3.2.3 截取封面图

获取监控列表,然后进行截图处理

请确保已引入如下插件:
import html2canvas from ‘html2canvas’;

获取监控列表,也就是正常的接口请求:

 // 获取监控列表
    getList() {
      this.loading = true
      listDevice(this.queryParams).then(res => {
        const tempArr = res.rows.map(item => {
          return {
            deviceCode: item.deviceCode,
            type1Name: item.type1Name,
            type2Name: item.type2Name,
            name: item.name,
            deviceLocation: item.deviceLocation,
            status: item.status,
            videoUrl: null, // 监控地址,接口未返回,下面单独的接口循环获取,最好还是让后端返回吧
            posterImg: null, // 默认封面为空,下面循环截取
          }
        })
        this.total = res.total
        this.loading = false
        this.isFirstLoading = false
        this.requestAllVideoUrl(tempArr)
      })
    },

由于监控列表未返回m3u8的地址,所以此处需要再次循环请求获取地址:

你们最好让你们的后端把地址也返回,不然前端循环获取不太好!

 /** 循环请求,获取监控地址,todo:并且截取封面图 */
    requestAllVideoUrl(tempArr) {
      for(let i = 0; i < tempArr.length; i++) {
        const id = tempArr[i].deviceCode
        // 离线(值为2)的直接空值,省略请求
        if(!!id && tempArr[i].status != 2) {
          getVideo({
            channelId: id,
          }).then(res => {
            tempArr[i].videoUrl = res.data || null
          })
        }
      }

      this.monitorList = tempArr

	 // 初始设置第一个监控地址给隐藏的视频模仿组件,然后下面才可以循环截图
      let timer2 = setInterval(() => {
        if(tempArr[0].videoUrl) {
          this.tempVideoUrl = tempArr[0].videoUrl
          clearInterval(timer2)
        }
      }, 100)
    },

我们在上面的hlsVideo.vue组件中写了:

this.$emit("playing", true);
this.$emit("playing", false);

目的是当一个m3u8播放成功后,就告诉组件:我播放完了,你可以截图了,并且可以切换下一个m3u8地址了。

具体的截图代码如下:

 // todo:视频改变播放状态,执行截图功能
    changePlayStatus(e) {
      if(e) {
        setTimeout(() => {
          this.$nextTick(() => {
            if(this.$refs.hiddenRef) {
              const video_width = 396
              const video_height = 180
              const dom = document.getElementById("hiddenVideo");

              html2canvas(dom, {
                windowWidth: video_width * 1.78,
                windowHeight: video_height * 1.78,
                x: 0,
                y: 0,
                useCORS: true, //跨域
                scale: 0.8,
              }).then(canvas => {
                // 截图的base64位图片
                const imgData = canvas.toDataURL('image/jpeg', 1.0);
                // console.log('index', this.tempVideoIndex, imgData)
                this.monitorList[this.tempVideoIndex].posterImg = imgData
                if(this.tempVideoIndex < this.monitorList.length - 1) {
                  this.tempVideoIndex++
                  if(this.monitorList[this.tempVideoIndex].status != 0) {
                    this.tempVideoUrl = this.monitorList[this.tempVideoIndex].videoUrl
                  } else {
                    this.tempVideoIndex++
                    this.tempVideoUrl = this.monitorList[this.tempVideoIndex].videoUrl
                  }
                } else {
                  this.tempVideoUrl = null
                }
              })
            }
          })
        }, 500)
      }

3.3 结束

至此,截图完毕!截图就是最上面放的截图,此处不重复放了

  • 也成功地把封面图赋值给监控列表了,M3u8也销毁了,
  • 监控列表现在放的是图片,
  • 也不存在内存溢出等性能问题了

本例仅做思路,具体代码具体对待

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Golang Gin系列-2:搭建Gin 框架环境

开始网络开发之旅通常是从选择合适的工具开始的。在这个全面的指南中&#xff0c;我们将引导你完成安装Go编程语言和Gin框架的过程&#xff0c;Gin框架是Go的轻量级和灵活的web框架。从设置Go工作空间到将Gin整合到项目中&#xff0c;本指南是高效而强大的web开发路线图。 安装…

日志收集Day001

1.ElasticSearch 作用&#xff1a;日志存储和检索 2.单点部署Elasticsearch与基础配置 rpm -ivh elasticsearch-7.17.5-x86_64.rpm 查看配置文件yy /etc/elasticsearch/elasticsearch.yml&#xff08;这里yy做了别名&#xff0c;过滤掉空行和注释行&#xff09; yy /etc/el…

SW - 快捷键

文章目录 SW - 快捷键概述笔记视图缩放视图平移视图旋转END SW - 快捷键 概述 鼠标中键不好使了&#xff0c;导致鼠标滚轮的操作在SW中不好使。 已经拆过鼠标&#xff0c;清理过中键计数轮那里的灰尘。只好用几天&#xff0c;然后鼠标中键又不好使了。 先查一下能代替鼠标中键…

Freeswitch使用media_bug能力实现回铃音检测

利用freeswitch的media bug能力来在智能外呼时通过websocket对接智能中心的声音检测接口,来实现回铃音检测,来判断用户当前是否已响应,拒接,关机等。 1.回铃音处理流程 2.模块源码目录结构 首先新建一个freeswitch的源码的src/application目录下新建一个子目录mod_ringba…

【2024年华为OD机试】(B卷,100分)- 恢复数字序列 (Java JS PythonC/C++)

一、问题描述 题目解析 题目描述 对于一个连续正整数组成的序列&#xff0c;可以将其拼接成一个字符串&#xff0c;再将字符串里的部分字符打乱顺序。例如&#xff0c;序列 8 9 10 11 12 拼接成的字符串为 89101112&#xff0c;打乱一部分字符后得到 90811211&#xff0c;原…

ZNS SSD垃圾回收优化方案解读-2

四、Brick-ZNS 关键设计机制解析 Brick-ZNS 作为一种创新的 ZNS SSD 设计&#xff0c;聚焦于解决传统 ZNS SSDs 在垃圾回收&#xff08;GC&#xff09;过程中的数据迁移低效问题&#xff0c;其核心特色为存储内数据迁移与地址重映射功能。在应用场景中&#xff0c;针对如 Rock…

云消息队列 Kafka 版 V3 系列荣获信通院“云原生技术创新标杆案例”

2024 年 12 月 24 日&#xff0c;由中国信息通信研究院&#xff08;以下简称“中国信通院”&#xff09;主办的“2025 中国信通院深度观察报告会&#xff1a;算力互联网分论坛”&#xff0c;在北京隆重召开。本次论坛以“算力互联网 新质生产力”为主题&#xff0c;全面展示中国…

单元测试与unittest框架

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;薪资嘎嘎涨 单元测试的定义 1. 什么是单元测试&#xff1f; 单元测试是指&#xff0c;对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作&am…

EF Core全局查询筛选器

目录 概述 用法 添加全局查询筛选器 禁用全局查询筛选器 概述 全局查询筛选器&#xff1a;EF Core 会自动将这个查询筛选器应用于涉及这个实体类型的所有 LINQ 查询。 场景&#xff1a;软删除、多租户。 什么是软删除&#xff1f; 逻辑删除&#xff0c;并不是真正地从数…

【机器学习实战入门项目】MNIST数字分类机器学习项目

Python 深度学习项目&#xff1a;手写数字识别 为了使机器更加智能&#xff0c;开发者们正在深入研究机器学习和深度学习技术。人类通过不断练习和重复来学习执行某项任务&#xff0c;从而记住如何完成这些任务。然后&#xff0c;大脑中的神经元会自动触发&#xff0c;他们能够…

[Datawheel学习]用Llama-index创建Agent、数据库对话Agent和RAG接入Agent

1.Llama-index创建Agent 1.0 背景知识 什么是Llama-index? LlamaIndex&#xff08;原名GPT Index&#xff09;是一个专为大语言模型&#xff08;LLMs&#xff09;设计的数据框架&#xff0c;旨在帮助用户将外部数据与LLMs结合&#xff0c;实现更高效的数据检索和知识增强生成…

FPGA:Quartus软件与操作系统版本对照表

文章目录 1.软件概述2.软件版本3.设计流程4.支持的设备5.新特性6.版本对照 1.软件概述 Quartus软件是由英特尔&#xff08;Intel&#xff09;公司开发的一款功能强大的FPGA&#xff08;现场可编程逻辑门阵列&#xff09;设计工具&#xff0c;广泛应用于数字电路设计、仿真、综…

【网络协议】【http】【https】AES-TLS1.2

【网络协议】【http】【https】AES-TLS1.2 https并不是一个协议 而是在传输层之间添加了SSL/TLS协议TLS TLS 协议用于应用层协议&#xff08;如 HTTP&#xff09;和传输层&#xff08;如 TCP&#xff09;之间&#xff0c;增加了一层安全性来解决 HTTP 存在的问题&#xff0c;H…

数智化转型 | 星环科技Defensor 助力某银行数据分类分级

在数据驱动的金融时代&#xff0c;数据安全和隐私保护的重要性日益凸显。某银行作为数字化转型的先行者&#xff0c;面临着一项艰巨的任务&#xff1a;如何高效、准确地对分布在多个业务系统、业务库与数仓数湖中的约80万个字段进行数据分类和分级。该银行借助星环科技数据安全…

微信小程序:播放音频

在小程序开发中&#xff0c;音频播放是一个重要的功能。本文将详细介绍小程序音频播放的相关知识点&#xff0c;帮助开发者更好地掌握小程序音频播放的实现方法。 一、小程序音频播放的基本流程 在小程序中&#xff0c;音频播放的基本流程如下&#xff1a; 获取音频数据&#…

U盘被格式化后的数据救赎与防范策略

一、U盘格式化后的数据困境 在日常的工作与生活中&#xff0c;U盘作为数据传输与存储的重要工具&#xff0c;扮演着不可或缺的角色。然而&#xff0c;当U盘不幸遭遇格式化操作后&#xff0c;存储在其中的宝贵数据瞬间化为乌有&#xff0c;给用户带来极大的困扰。格式化后的U盘…

关于 Cursor 的一些学习记录

文章目录 1. 写在最前面2. Prompt Design2.1 Priompt v0.1&#xff1a;提示设计库的首次尝试2.2 注意事项 3. 了解 Cursor 的 AI 功能3.1 问题3.2 答案 4. cursor 免费功能体验5. 写在最后面6. 参考资料 1. 写在最前面 本文整理了一些学习 Cursor 过程中读到的或者发现的感兴趣…

基于Oracle与PyQt6的电子病历多模态大模型图形化查询系统编程构建

一、引言 1.1 研究背景阐述 在当今数字化时代,医疗行业正经历着深刻的变革,数字化转型的需求日益迫切。电子病历(EMR)作为医疗信息化的核心,其管理的高效性和数据利用的深度对于提升医疗服务质量、优化临床决策以及推动医学研究具有至关重要的意义。传统的电子病历管理系…

算法(蓝桥杯)贪心算法7——过河的最短时间问题解析

一、题目描述 在漆黑的夜里&#xff0c;N位旅行者来到了一座狭窄且没有护栏的桥边。他们只带了一只手电筒&#xff0c;且桥窄得只够让两个人同时过。如果各自单独过桥&#xff0c;N人所需的时间已知&#xff1b;若两人同时过桥&#xff0c;则所需时间是走得较慢的那个人单独行动…

《贪心算法:原理剖析与典型例题精解》

必刷的贪心算法典型例题&#xff01; 算法竞赛&#xff08;蓝桥杯&#xff09;贪心算法1——数塔问题-CSDN博客 算法竞赛&#xff08;蓝桥杯&#xff09;贪心算法2——需要安排几位师傅加工零件-CSDN博客 算法&#xff08;蓝桥杯&#xff09;贪心算法3——二维数组排序与贪心算…