Commit 46ab5b88ffe1591822d10e3276cbdc0d1b787ac4

Authored by ww
1 parent 8d4d46db

feat: 监控视频组件实现flv协议播放

@@ -32,6 +32,7 @@ @@ -32,6 +32,7 @@
32 "echarts-liquidfill": "^3.1.0", 32 "echarts-liquidfill": "^3.1.0",
33 "echarts-stat": "^1.2.0", 33 "echarts-stat": "^1.2.0",
34 "echarts-wordcloud": "^2.0.0", 34 "echarts-wordcloud": "^2.0.0",
  35 + "flv.js": "^1.6.2",
35 "gsap": "^3.11.3", 36 "gsap": "^3.11.3",
36 "highlight.js": "^11.5.0", 37 "highlight.js": "^11.5.0",
37 "html2canvas": "^1.4.1", 38 "html2canvas": "^1.4.1",
@@ -44,6 +45,7 @@ @@ -44,6 +45,7 @@
44 "screenfull": "^6.0.1", 45 "screenfull": "^6.0.1",
45 "three": "^0.145.0", 46 "three": "^0.145.0",
46 "video.js": "^7.20.3", 47 "video.js": "^7.20.3",
  48 + "videojs-flvjs-es6": "^1.0.1",
47 "vue": "^3.2.31", 49 "vue": "^3.2.31",
48 "vue-demi": "^0.13.1", 50 "vue-demi": "^0.13.1",
49 "vue-i18n": "^9.2.2", 51 "vue-i18n": "^9.2.2",
1 <template> 1 <template>
2 <div class="go-content-box" :style="{ width: w + 'px', height: h + 'px' }"> 2 <div class="go-content-box" :style="{ width: w + 'px', height: h + 'px' }">
3 - <video  
4 - crossOrigin="anonymous"  
5 - :id="`my-player${index}`" ref="videoRef" class="video-js my-video vjs-theme-city vjs-big-play-centered">  
6 - <source :src="sourceSrc" /> 3 + <video crossOrigin="anonymous" :id="`my-player${index}`" ref="videoRef"
  4 + class="video-js my-video vjs-theme-city vjs-big-play-centered">
7 </video> 5 </video>
8 </div> 6 </div>
9 </template> 7 </template>
10 <script setup lang="ts"> 8 <script setup lang="ts">
11 import { onMounted, ref, onUnmounted, watch } from 'vue' 9 import { onMounted, ref, onUnmounted, watch } from 'vue'
12 import videojs from 'video.js' 10 import videojs from 'video.js'
  11 +import 'videojs-flvjs-es6'
13 import type { VideoJsPlayerOptions } from 'video.js' 12 import type { VideoJsPlayerOptions } from 'video.js'
14 import 'video.js/dist/video-js.min.css' 13 import 'video.js/dist/video-js.min.css'
15 14
@@ -36,6 +35,37 @@ const props = defineProps({ @@ -36,6 +35,37 @@ const props = defineProps({
36 } 35 }
37 }) 36 })
38 37
  38 +enum VideoPlayerType {
  39 + m3u8 = 'application/x-mpegURL',
  40 + mp4 = 'video/mp4',
  41 + webm = 'video/webm',
  42 + flv = 'video/x-flv',
  43 +}
  44 +
  45 +const isRtspProtocol = (url: string) => {
  46 + const reg = /^rtsp:\/\//g;
  47 + return reg.test(url);
  48 +};
  49 +
  50 +const getVideoTypeByUrl = (url = '') => {
  51 +
  52 + try {
  53 + const { protocol, pathname } = new URL(url)
  54 +
  55 + if (isRtspProtocol(protocol)) return VideoPlayerType.flv
  56 +
  57 + const reg = /[^.]\w*$/
  58 + const mathValue = pathname.match(reg) || []
  59 + const ext = mathValue[0] as keyof typeof VideoPlayerType || 'webm'
  60 + const type = VideoPlayerType[ext]
  61 + return type ? type : VideoPlayerType.webm
  62 + } catch (error) {
  63 + console.error(error)
  64 + return VideoPlayerType.webm
  65 + }
  66 +};
  67 +
  68 +
39 // video标签 69 // video标签
40 const videoRef = ref<HTMLElement | null>(null) 70 const videoRef = ref<HTMLElement | null>(null)
41 71
@@ -43,33 +73,56 @@ const videoRef = ref<HTMLElement | null>(null) @@ -43,33 +73,56 @@ const videoRef = ref<HTMLElement | null>(null)
43 let videoPlayer: videojs.Player | null = null 73 let videoPlayer: videojs.Player | null = null
44 74
45 //options配置 75 //options配置
46 -const options: VideoJsPlayerOptions = { 76 +const options: VideoJsPlayerOptions & Recordable = {
47 language: 'zh-CN', // 设置语言 77 language: 'zh-CN', // 设置语言
48 controls: true, // 是否显示控制条 78 controls: true, // 是否显示控制条
49 preload: 'auto', // 预加载 79 preload: 'auto', // 预加载
50 autoplay: true, // 是否自动播放 80 autoplay: true, // 是否自动播放
51 fluid: false, // 自适应宽高 81 fluid: false, // 自适应宽高
52 poster: props?.avatar || '', 82 poster: props?.avatar || '',
53 - src: props?.sourceSrc || '', // 要嵌入的视频源的源 URL 83 + // src: getSource() || '', // 要嵌入的视频源的源 URL
  84 + sources: [],
54 muted: true, 85 muted: true,
55 userActions: { 86 userActions: {
56 hotkeys: true 87 hotkeys: true
57 - } 88 + },
  89 + techOrder: ['html5', 'flvjs'],
  90 + flvjs: {
  91 + mediaDataSource: {
  92 + isLive: true,
  93 + cors: true,
  94 + withCredentials: false,
  95 + }
  96 + },
  97 +}
  98 +
  99 +
  100 +async function getSource() {
  101 + return [
  102 + {
  103 + type: getVideoTypeByUrl(props.sourceSrc),
  104 + src: props.sourceSrc || ''
  105 + }
  106 + ]
58 } 107 }
59 108
60 // 初始化videojs 109 // 初始化videojs
61 -const initVideo = () => { 110 +const initVideo = async () => {
62 if (videoRef.value) { 111 if (videoRef.value) {
63 - // 创建 video 实例  
64 - videoPlayer = videojs(videoRef.value, options) 112 + // 创建 video 实例
  113 + options.sources = await getSource()
  114 + if (options.sources && options.sources.length) {
  115 + videoPlayer = videojs(videoRef.value, options)
  116 + }
65 } 117 }
66 } 118 }
67 119
68 watch( 120 watch(
69 () => props.sourceSrc, 121 () => props.sourceSrc,
70 - (newData: any) => { 122 + async (newData: any) => {
  123 + const result = await getSource()
71 // props.sourceSrc = newData 124 // props.sourceSrc = newData
72 - videoPlayer?.src(newData) as any 125 + videoPlayer?.src(result) as any
73 videoPlayer?.play() 126 videoPlayer?.play()
74 }, 127 },
75 { 128 {