vue项目中视频实时播放时 播放器遇到的问题和解决过程 flv.js - EasyPlayer - LivePlayer

这篇具有很好参考价值的文章主要介绍了vue项目中视频实时播放时 播放器遇到的问题和解决过程 flv.js - EasyPlayer - LivePlayer。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

场景需求

  1. 需要画面监控设备实时播放,支持HTTP-FLV直播流,支持其他流后续可能会更换
  2. 需要类似于安防监控多个视频实时画面同步
  3. 播放器可控制度强,完全由我们来控制播放暂停进行拉流断流

实现过程

项目使用的是的vue3+ts

1. 使用flv.js

原本使用的video.js 但是不支持HTTP-FLV播放,改用flv.js

引入flv.js

npm install --save flv.js

开发问题和解决方案:

  • flv.js视频暂停时会有延迟增长,随着暂停时间越久延迟越长几秒几分钟或者更长
    解决办法:手动处理buffer时长,进行删减帧或加速播放
  • 我需要当暂停播放按钮触发时呼叫服务端拉流断流,flv.js视频播放时离开此页面或者有遮挡此页面会自动触发video标签的暂停事件,回到此页面时也会自动帮你播放,但不会通知你也不会触发video标签的播放事件
    解决办法:执行销毁再创建flv.js

flv.js组件代码(需要请自行修改监听部分逻辑)

<template>
  <div class="w-full h-full" v-if="deviceState !== 0">
    <video
      v-show="createEl"
      id="videoElement"
      ref="videoElement"
      controls
      disablePictureInPicture
      class="FlvVideo w-full h-full"
      @click.prevent="onClick"
      @pause="onPause"
      muted
      @play="play"
    ></video>
     <!--下面部分html是暂停时的画面,flv.js没有创建时是触发不了播放,通过这个暂停画面来创建实例/播放 -->
     <!-- class="rounded-md flex justify-center items-center w-full h-full maskLayer" 这个是Tailwind Css语法-->
    <div v-show="!createEl" class="rounded-md flex justify-center items-center w-full h-full maskLayer">
      <SvgIcon name="loading" size="30" class="rotate" v-if="loading" />
      <div class="wrap" @click="onPlay" v-else>
        <SvgIcon name="play" size="20" fillColor="#ffffff" class="ml-0.8" />
      </div>
    </div>
  </div>
  <div class="rounded-md flex justify-center items-center w-full h-full maskLayer text-light-50" v-else>
    设备离线
  </div>
</template>
<script setup name="videoFlv" lang="ts">
import { ref, onBeforeUnmount, watch } from "vue"
import flvjs from "flv.js"
import SvgIcon from "/@/components/Icon"
import _ from "lodash-es"

type VideoProps = {
  destroy: number | boolean//拉流断流的状态
  sources: string//视频地址
  status?: number | undefined
  loading?: boolean
  deviceState?: number
}
const emit = defineEmits(["onPlay", "onPause"])
const props = defineProps<VideoProps>()

let flvPlayer: flvjs.Player | null = null
let videoElement = ref<HTMLMediaElement | null>(null)
let timer: any = null
let createEl = ref<boolean | null>(null)
const pauseState = ref<boolean>(true) //为了避免在销毁时重复执行暂停

// 清除缓存延迟
const buffered = () => {
  timer = window.setInterval(() => {
    if (videoElement.value && videoElement.value.buffered.length > 0) {
      const end = videoElement.value.buffered.end(0) // 视频结尾时间
      const current = videoElement.value.currentTime //  视频当前时间
      const diff = end - current // 相差时间
      const diffCritical = 4 // 这里设定了超过4秒以上就进行跳转
      const diffSpeedUp = 1 // 这里设置了超过1秒以上则进行视频加速播放
      const maxPlaybackRate = 4 // 自定义设置允许的最大播放速度
      let playbackRate = 1.0 // 播放速度

      if (diff > diffCritical) {
        // console.log("相差超过4秒,进行跳转");
        videoElement.value.currentTime = end - 1.5
        playbackRate = Math.max(1, Math.min(diffCritical, 16))
      } else if (diff > diffSpeedUp) {
        // console.log("相差超过1秒,进行加速");
        playbackRate = Math.max(1, Math.min(diff, maxPlaybackRate, 16))
      }

      videoElement.value.playbackRate = playbackRate
    }
  }, 1000)
}
// 创建flv.js实例
const createVideo = (url) => {
  if (flvjs.isSupported()) {
    flvPlayer = flvjs.createPlayer(
      {
        type: "flv",
        url, //你的url地址
        isLive: true,
        hasVideo: true,
        hasAudio: true
      },
      {
        enableWorker: false, //不启用分离线程
        enableStashBuffer: true, //关闭IO隐藏缓冲区
        reuseRedirectedURL: true, //重用301/302重定向url,用于随后的请求,如查找、重新连接等。
        autoCleanupSourceBuffer: true, //自动清除缓存
        lazyLoad: false, // 去掉懒加载,新增
        fixAudioTimestampGap: false //false才会音视频同步,新增
      }
    )
    flvPlayer.attachMediaElement(videoElement.value as HTMLMediaElement)
    flvPlayer.load()
    flvPlayer.play()

    flvPlayer.on(flvjs.Events.ERROR, () => {
      flvPlayer && reloadVideo()
    })
  }
  createEl.value = true
}
const antiShake = (Fn) => _.throttle(Fn, 2000, { leading: true })

const onClick = antiShake(() => {
  if (!videoElement.value || !flvPlayer) return
  if (videoElement.value.paused) {
    flvPlayer.play()
  } else {
    flvPlayer.pause()
  }
})

// 这一步其实处理buffer有没有都可以,只不过防止拉流中卡顿的可能性
const play = () => {
  buffered()
}

// 自定义暂停页面的paly事件
const onPlay = antiShake(() => {
  emit("onPlay", true)
})

const onPause = antiShake(() => {
  //初始化静音时页面被遮挡才会由flv.js触发puase
  timer && window.clearInterval(timer)
  if (!props.status && !pauseState.value) {
    destoryVideo()
    emit("onPause", false)
    pauseState.value = true
  }
})

// 重置
const reloadVideo = () => {
  destoryVideo()
  createVideo(props.sources)
}

// 销毁/创建
const watchDestroy = watch(
  () => props.destroy,
  () => {
    if (props.destroy && createEl.value) {
      timer && window.clearInterval(timer)
      destoryVideo()
    } else if (props.destroy === 0) {
      pauseState.value = false
      createVideo(props.sources)
    }
  }
)

// 销毁
const destoryVideo = () => {
  if (!pauseState.value) {
    flvPlayer!.pause() //暂停播放数据流
    pauseState.value = true
  }
  flvPlayer!.unload() //取消数据流加载
  flvPlayer!.detachMediaElement() //将播放实例从节点中取出
  flvPlayer!.destroy() //销毁播放实例
  flvPlayer = null
  createEl.value = false
}

// 组件卸载/清除监听
onBeforeUnmount(() => {
  watchDestroy()
  timer && window.clearInterval(timer)
  props.destroy && createEl.value && destoryVideo()
})
</script>
<style scoped>
html[data-theme="dark"] .wrap,
html[data-theme="dark"] .maskLayer {
  background-color: #000;
}

.maskLayer {
  background-color: var(--bg-dark-color);
}

@keyframes rotate {
  0% {
    transform: rotate(0);
  }

  25% {
    transform: rotate(90deg);
  }

  50% {
    transform: rotate(180deg);
  }

  75% {
    transform: rotate(270deg);
  }

  100% {
    transform: rotate(360deg);
  }
}

.FlvVide {
  object-fit: fill;
}

video::-webkit-media-controls-timeline {
  display: none;
}

video::-webkit-media-controls-current-time-display {
  display: none;
}

.wrap {
  width: 65px;
  height: 65px;
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: 50%;
  background: rgb(154 154 154 / 0%);
  border: 2px solid #fff;
  cursor: pointer;
}

.rotate {
  animation: rotate 3s linear infinite;
}
</style>


我是借鉴这位博主的 https://blog.csdn.net/weixin_45906632/article/details/115031633

这篇分享的也很完整 https://juejin.cn/post/7050739831403446286

2. 使用EasyPlayer.js

上面flv.js已经解决了我一部分的使用需求,但是销毁创建的方法,用户体验很差,只是暂停每次都要等个三秒左右才可以播放(因为要重新创建加拉流这个过程),并且不能支持其他直播协议,不能同时控制多个直播分屏画面(flv.js 创建六个视频共同维护起来很不方便),这些问题EasyPlayer都有做相应的改善

引入EasyPlayer

npm install @easydarwin/easyplayer --save

Vue 集成调用
copy node_modules/@easydarwin/easyplayer/dist/component/EasyPlayer.swf 到 静态文件 根目录
copy node_modules/@easydarwin/easyplayer/dist/component/crossdomain.xml 到 静态文件 根目录
copy node_modules/@easydarwin/easyplayer/dist/component/EasyPlayer-lib.min.js 到 静态文件 根目录

注意: 没有调用会出现无法加载对应插件的报错
在 html 中引用 dist/component/EasyPlayer-lib.min.js
###H.265 copy node_modules/@easydarwin/easyplayer/dist/component/EasyPlayer.wasm 到 静态文件 根目录

详细引入可以查看官网文档 https://github.com/tsingsee/EasyPlayer.js/?tab=readme-ov-file

gitHub打不开的可以看这个https://www.npmjs.com/package/@easydarwin/easyplayer

开发问题和解决方案:

  • 引入时,因为官方针对vue的引入方式是npm ,通过npm安装包import引入组件使用,引入组件后一直报错,我以为是没有将文件copy到根目录的原因(官网有说copy node_modules的文件),我又根据文档copy了文件并且在html中引入,还是会有报错
    解决方法:只要不用npm的引入即可,也就不用import的导入了,也就是npm安装只是为了让你能copy对应的文件,具体的使用还是通过cdn的方式,copy之后就可以移除包,这么看像是文档有误导了
    这篇文章就给了很好的解答 https://juejin.cn/post/7235908012673089573#comment

EasyPlayer组件代码
使用就很简单了,暂时还没有需要多屏实时播放,也不需要处理buffer,只需要暂停播放时触发接口就好

<template>
  <div class="video-box">
    <easy-player
      :video-url="sources"
      ref="videoEl"
      aspect="16:9"
      live
      autoplay
      stretch
      @pause="onPause"
      @play="onPlay"
      :video-title="title || ''"
    />
  </div>
</template>
<script setup lang="ts" name="EasyPlayerFlv">
import { ref, onUnmounted } from "vue"

type VideoProps = {
  sources: string//url
  title?: string//直播名称
}
const emit = defineEmits(["onPlay", "onPause"])
defineProps<VideoProps>()

let videoEl = ref<any>(null)

const onPlay = () => {
  emit("onPlay", true)
}

const onPause = () => {
  emit("onPause", false)
}

onUnmounted(() => {
  console.log("videoEl.value :>> ", videoEl.value)
  // easyPlayer.value!.destroyPlayer()
})
</script>

<style scoped>
.video-box {
  aspect-ratio: 16/9;
  width: 100%;
}

.video-box video {
  width: 100%;
  object-fit: cover;
}
</style>

3. 使用LivePlayer.js

EasyPlayer多屏实时播放 | 时差 | 延迟处理的很完善,EasyPlayer对于fvl.js的延迟问题采用的是追帧和加速播放,随着你暂停时间越久他追帧速度越慢,并且在追到延迟在10s时就停止了,也就是直播画面总是有10s的延迟(直播设备是测试过没问题的,并且在销毁重创画面是实时的)
后来又发现了livePlayer,测试使用发现以上问题都不存在了,延迟也最慢控制在3s,也是我们项目中能接受的范围

引入

	vue2
	
	npm install @liveqing/liveplayer
	
	vue3
	
	npm install @liveqing/liveplayer-v3

第一步 :复制依赖文件(示例 通过 webpack 插件自动复制依赖)

如果正在使用 vue2 + vue-cli, 编辑你的 vue.config.js

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

module.exports = {
    configureWebpack: {
      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: 'js/'},
        ])
      ]
    }
  }

如果正在使用 vue3 + vite, 编辑你的 vite.config.js

import copy from 'rollup-plugin-copy'

export default defineConfig({
  plugins: [vue(), copy({
    targets: [
      {src: 'node_modules/@liveqing/liveplayer-v3/dist/component/liveplayer-lib.min.js', dest: 'public/js'},
    ]
  })]
})

第二步: html模板中引入依赖js

在 html 中引用 www 根目录 liveplayer-lib.min.js

<!DOCTYPE HTML>
<html>
    <head>
        <title>template</title>
        ......
        <script src="js/liveplayer-lib.min.js"></script>
        <!-- 如果正在使用 vue-cli:
            <script src="<%= BASE_URL %>js/liveplayer-lib.min.js"></script>
        -->
    </head>
    <body>
        ......
    </body>
</html>

第三步 编辑你的 Vue 组件

import LivePlayer from '@liveqing/liveplayer' // vue2

// import LivePlayer from '@liveqing/liveplayer-v3' // vue3

  components: {
    LivePlayer
  }//vue3,语法糖就不需要这一步了

<LivePlayer :videoUrl="videoUrl" fluent autoplay live stretch></LivePlayer>

如果你上述有报错或者一些问题可以转变成手动复制文件

copy node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf 到根目录

copy node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml 到根目录

copy node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js 到根目录

我遇到swf文件不能访问的错误,这个问题很简单,只是因为我在html引入LivePlayer文件时没有删除EasyPlayer的引入导致它们互相影响,本来是想做一下比较所以才没有删除EasyPlayer(这时候就要提醒一下引入任何相同插件时确保项目中只有一种此功能插件,可能会因为内部使用相同东西导致冲突,真的会查不到问题在哪,也查不到遇到相关问题的😭)

更多详细了解可以看官方文档https://www.liveqing.com/docs/manuals/LivePlayer.html#%E5%AE%89%E8%A3%85文章来源地址https://www.toymoban.com/news/detail-820673.html

到了这里,关于vue项目中视频实时播放时 播放器遇到的问题和解决过程 flv.js - EasyPlayer - LivePlayer的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【VideoJs】初识videojs && video.js 视频播放器的基本使用 && videojs基础用法 && videojs视频播放器 && vue3中使用videojs

    免费,开源 插件多 可自定义 【推】 虽然,但是Videojs算好了,但我觉得有点杂,特别是文档与插件,且自定义插件有点困难,也可能是我比较菜吧 相比之下,我还是强烈推荐 【Xgplayer ——点我进入】 备用地址 http://t.csdn.cn/H0cAV Xgplayer 优点 优雅、美观 文档清晰明了 大厂出

    2024年02月03日
    浏览(62)
  • 音视频项目—基于FFmpeg和SDL的音视频播放器解析(三)

    介绍 在本系列,我打算花大篇幅讲解我的 gitee 项目音视频播放器,在这个项目,您可以学到音视频解封装,解码,SDL渲染相关的知识。您对源代码感兴趣的话,请查看基于FFmpeg和SDL的音视频播放器 如果您不理解本文,可参考我的前一篇文章音视频项目—基于FFmpeg和SDL的音视

    2024年02月05日
    浏览(71)
  • springboot + vue3实现视频播放Demo(video.js & Vue3-video-play视频播放器)

    ffmpeg官网 长时长视频java存储及vue播放解决方法 【 攻城略地 】vue3 + video.js播放m3u8视频流格式 Vue3-video-play组件官网 Vue3视频播放器组件Vue3-video-play入门教程 vue-video-player播放m3u8格式的视频 Spring boot视频播放(解决MP4大文件无法播放),整合ffmpeg,用m3u8切片播放。 Java获取MP4视频文

    2024年02月07日
    浏览(75)
  • java项目之网络视频播放器(ssm+mysql+jsp)

    风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的网络视频播放器。 技术交流和部署相关看文章末尾!  开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:eclipse/idea Maven包

    2024年02月16日
    浏览(48)
  • 音视频项目—基于FFmpeg和SDL的音视频播放器解析(二十一)

    介绍 在本系列,我打算花大篇幅讲解我的 gitee 项目音视频播放器,在这个项目,您可以学到音视频解封装,解码,SDL渲染相关的知识。您对源代码感兴趣的话,请查看基于FFmpeg和SDL的音视频播放器 如果您不理解本文,可参考我的前一篇文章音视频项目—基于FFmpeg和SDL的音视

    2024年02月02日
    浏览(74)
  • 创维电视机 | 用当贝播放器解决创维电视机不能播放MKV视频的问题

    小故事在下面,感兴趣可以看看,开头我就直接放解决方案 创维电视虽然是基于Android开发的,可以安装apk软件,但是基本不能用,一定要选择适配电视的视频播放器,或者使用本文中提供的创维版当贝播放器。 原软件已与本文绑定,可以通过csdn下载 或者通过我发布的gite

    2024年02月19日
    浏览(54)
  • 【Android入门到项目实战-- 11.4】—— ExoPlayer视频播放器框架的详细使用

    目录 什么是ExoPlayer 一、基本使用  1、添加依赖项  2、布局 3、Activity 二、自定义播放暂停 1、首先如何隐藏默认的开始暂停和快进? 2、自定义 三、控制视频画面旋转和比例调整 四、全屏放大和缩小 1、双击视频放大缩小 2、按钮放大缩小 五、完整的实现代码 XML Activity   

    2024年02月11日
    浏览(56)
  • 【QT项目:视频播放器——Qt opengl编程】通过shader完成显示yuv

    通过Qt opengl不是为了3D绘制,而是为了将视频绘制起来 使用opengl 可以极大降低yuv转rgb的转换开销 1、为什么用QT的opengl 简单,界面可以自动叠加 void paintGL(); // 具体的绘制写在该函数里 void initializeGL(); // 材质初始化 void resizeGL(int width, int height); // 当窗口发生变化(缩放) QO

    2023年04月09日
    浏览(55)
  • 信号:pause、alarm、kill;第二个项目:基于Mplayer的视频播放器

    int pause(void); 功能:让进程睡眠直到接收到捕捉的信号才能继续向下执行 unsigned int alarm(unsigned int seconds); 功能:定时seconds秒后给进程调用发送SIGALRM信号 参数:seconds定时的秒数 返回值:         成功返回之前设定剩余的秒数 int kill(pid_t, int sig) 功能:给PID对应的进程发送

    2024年03月10日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包