如何手动停止 videojs 直播视频流 m3u8 请求?

简介: 在可视化大屏项目中,多个VideoJS组件播放m3u8流时,即使暂停仍持续请求,导致页面卡顿。通过监听display属性,结合`dispose()`销毁实例并重建同ID的video DOM元素,有效释放资源且保留组件结构,解决性能问题,提升用户体验。

问题描述

在公司某个可视化大屏项目中,大屏页面会有多个 videojs 组件,每个组件都会对应一个视频流地址。每当视频开始播放,视频流m3u8 会不断请求,即便是暂停了播放,这个请求也不会终止。大量的请求会导致页面卡顿,长此以往会带来性能问题,导致浏览器卡死甚至崩溃。

而大屏操作中,经常会用到组件联动,点击百度地图的点位,出现一个视频弹窗,点击关闭视频,其实是隐藏了视频,而视频的请求还在继续。为了解决这个问题,我花了一些时间研究,找到了解决办法。

解决办法

从videojs官方文档可以查到,有一个 dispose 方法。这个方法是用来销毁 videojs 对象的。但这个方法不能直接使用,直接使用会导致一个新的问题,那就是销毁实例后 ,原本的 video 标签dom 元素也一并销毁了,这个特性从官方文档中可以看出。

Videojs Removing Players

请在此添加图片描述

其实我个人觉得,这个方法的操作 2 的特性非常不好,这样导致关闭后组件直接被销毁,导致下次触发视频弹窗(业务需求是点击百度地图图例,出现弹窗播放视频直播流),没有视频组件可以显示播放。

于是我针对这个项目组件,写了一个 Vue 的watch,用来监听监听 display 属性。在这个项目中,这个属性每个组件都有,display 为 false 是显示,true 是隐藏(别问为什么是反的,我也不知道)。代码示例如下:

<template>
  <div :style="styleObject" ref="myvideojs">
    <!-- <video
      :id="videojsId"
      class="video-js vjs-big-play-centered vjs-fluid"
      style="width: 100%; height: 100%; object-fit: fill"
    ></video> -->
    <!-- padding-top: 0,解决减小高度到一定数值后,高度不能自适应的问题 -->
    <video :id="videojsId" class="video-js vjs-big-play-centered vjs-fluid" style="width: 100%; height: 100%; object-fit: fill; padding-top: 0" ref="videoPlayer"></video>
  </div>
</template>
 watch: {
    ...
    display: {
      handler(newVal) {
        // true 代表隐藏
        if (newVal) {
          console.log("隐藏 :>> ")
          if (this.myVideo) {
            this.$nextTick(() => {
              // 必须先暂停,后销毁,销毁后 dom 元素也会被移除,所以需要手动添加相同 id 的 dom
              this.myVideo.dispose()
              // 下面这个 dom 跟 video 标签属性一致
              const videoElement = document.createElement("video")
              videoElement.setAttribute("id", this.videojsId) //注意 id 要一致
              videoElement.setAttribute("class", "video-js vjs-big-play-centered vjs-fluid")
              videoElement.setAttribute("style", "width: 100%; height: 100%; object-fit: fill; padding-top: 0")
              videoElement.setAttribute("ref", "videoPlayer")
              this.$refs.myvideojs.appendChild(videoElement) //添加相同 DOM
              this.myVideo = null
            })
          }
        } else {
          console.log("显示 :>> ")
        }
      },
    },
  }

下面是绘制组件的方法:

methods: {
    // 绘制方法
    drawChar(result) {
      let that = this
      if (result.length > 1) {
        console.log(this.MapPoiChange)
        console.log(this.option.Playtype)
        let key = this.MapPoiChange ? this.MapPoiChange : this.option.Playtype ? this.option.Playtype : "SXT"
        result = result.filter(v => {
          return v.key == key
        })[0]
      } else {
        result = result[0]
      }
      // 这些options属性也可直接设置在video标签上,见 muted
      // 实例化过,修改最新的url
      if (this.myVideo) {
        this.myVideo.src({ type: result.type, src: result.value })
      } else {
        let options = {
          autoplay: this.option.autoplay, // 设置自动播放
          controls: true, // 显示播放的控件
          width: this.component.width,
          height: this.component.height,
          sources: [
            {
              src: result.value,
              type: result.type, // 告诉videojs,这是一个hls流
            },
          ],
        } // videojs的第一个参数表示的是,文档中video的id
        this.myVideo = Videojs(this.videojsId, options, function onPlayerReady() {
          this.on("error", function () {
            // 报错信息
            var mediaError = this.error()
            console.log("mediaError", mediaError)
            // 异常处理
            that.updateData()
          })
        })
      }
    },
  },

以上代码的核心有3点:

  1. 关闭的时候,销毁 videojs;
  2. 销毁后立即创建一个与先前videojs 相同的 dom,尤其是 id 要保持一致;
  3. 显示时候重新初始化渲染 videojs(因为全局方法默认会调用绘制 drawChar,否则需要再显示逻辑里面新增绘制方法)

只要注意到这三个核心点,类似的问题也能迎刃而解。

注意事项

  1. 销毁要包裹在\$nextTick里面,不然会出现报错。

请在此添加图片描述

Error:Invalid target for nutl#on;must be a DOM node or evented object
  1. 需要通过 appendChild,添加一个跟之前 videojs 一样的根 dom,不然会报错找不到这个元素的 id:

请在此添加图片描述

TypeError: The element or ID supplied is not valid. (videojs)

总结

关于 videojs,实际项目用到的比较多,坑也是真的坑。文档不太好找,搜索查询了好长时间,才摸索出一套可行的解决方案。面对这类问题,需要善用搜索,从别人的文章和问答中寻找解决问题的思路和方案。查阅官方文档也是个不错的选择,但并不是每个类库框架的官方文档写的都易于理解。videojs 新版的文档和旧版本有些区别,很多 API 看起来并不十分直观,所以版本问题也要注意下。

以上是我解决这个问题的经验分享,欢迎评论区交流。

参考

vue使用videojs控制后台m3u8数据请求 - bomdeyada - 博客园

目录
相关文章
|
25天前
|
人工智能 JavaScript UED
如何实现两个下拉选择框 select选中联动效果?
本文通过一个公司与产品联动的下拉选择案例,详细讲解了Element UI中双向联动下拉框的实现方法。涵盖数据过滤、回显处理、重置功能及注意事项,结合Vue实战代码,帮助开发者提升表单交互体验,适用于各类关联选择场景的开发参考。(238字)
180 0
如何实现两个下拉选择框 select选中联动效果?
|
编解码 移动开发 JavaScript
html页面播放视频编码是265的m3u8的流媒体地址
html页面播放视频编码是265的m3u8的流媒体地址
913 0
使用ffmpeg工具下载m3u8类型的视频到本地
使用ffmpeg工具下载m3u8类型的视频到本地
1717 0
使用ffmpeg工具下载m3u8类型的视频到本地
|
25天前
|
文字识别 开发者 Windows
Windows 上值得推荐的软件(第一弹)
本文推荐两款提升Windows使用效率的神器:Listary,实现文件快速搜索与路径跳转;uTools,集快捷启动、剪贴板智能识别与丰富插件于一体,助力高效办公。
104 0
Windows 上值得推荐的软件(第一弹)
|
25天前
|
JavaScript 前端开发 数据可视化
如何优雅地处理Echarts环形图中的小数显示?
本文介绍了在数据可视化中处理数字格式的三种方法,重点解决保留两位小数并去除末尾多余0的问题。通过字符串操作、正则表达式和Number类型转换,实现简洁高效的数字格式化,推荐使用Number方法最优。
77 0
|
4月前
|
运维 监控 自动驾驶
低代码运维平台:是“运维福音”,还是“甩手掌柜”?
低代码运维平台:是“运维福音”,还是“甩手掌柜”?
158 29
|
5月前
|
编解码 JavaScript 前端开发
如何在网页播放英文的m3u8文件(基于Javascript搭建的在线网页工具)
什么是m3u8?又该如何在网页中高效、便捷地播放英文的m3u8文件呢?今天这篇文章就带你一起了解,并推荐一种基于Javascript搭建的在线网页工具,让你轻松解决播放问题。
1174 0
|
前端开发 JavaScript UED
React 滚动条组件 Scrollbar
本文介绍了在 React 中创建和使用滚动条组件的方法。首先,通过设置 `overflow: auto` 等 CSS 属性实现基础滚动功能。接着,推荐了如 `react-custom-scrollbars` 和 `react-perfect-scrollbar` 等第三方库以增强滚动条的功能与外观。针对常见问题,如样式不一致、无法正常工作及性能瓶颈,提供了相应的解决方案,包括自定义样式、确保容器高度和优化渲染逻辑。最后,强调了避免滚动事件绑定错误和不合理嵌套滚动的重要性,帮助开发者打造流畅、美观的用户界面。
756 16
|
前端开发 安全 测试技术
前端组件化有什么优势?
【10月更文挑战第4天】
484 56
阿里云实名认证api接口怎么调用
当我们注册一个购物网站,或者下载某个游戏,很多地方都需要做实名认证。那么作为购物网站,或者游戏公司,怎么才能判断客户提供的身份证号码是否真实呢?游戏玩家越来越多,我们可能人工去审核这个人提供个的身份证号码是否属实,或者是否是真人。根据国家规定,我们很多游戏都要对未成年游戏时长进行控制,也就是通常大家所谓的游戏防沉迷系统。我们只要把那些进行实名认证玩家的年龄给摘出来以后,就可以判断其是否成年。
阿里云实名认证api接口怎么调用