Commit 89d62489fc04f3ca7683ea296cf54dbd9f003dc1

Authored by xp.Huang
2 parents 5fc22f51 e13bf6d3

Merge branch 'feat/video-component-support-rtsp-protocol' into 'main_dev'

Merge branch 'ww' into 'main_dev'

See merge request yunteng/thingskit-view!80
... ... @@ -19,6 +19,7 @@
19 19 "dependencies": {
20 20 "@amap/amap-jsapi-loader": "^1.0.1",
21 21 "@amap/amap-jsapi-types": "^0.0.8",
  22 + "@fingerprintjs/fingerprintjs": "^3.4.1",
22 23 "@types/color": "^3.0.3",
23 24 "@types/crypto-js": "^4.1.1",
24 25 "@types/keymaster": "^1.6.30",
... ... @@ -32,6 +33,7 @@
32 33 "echarts-liquidfill": "^3.1.0",
33 34 "echarts-stat": "^1.2.0",
34 35 "echarts-wordcloud": "^2.0.0",
  36 + "flv.js": "^1.6.2",
35 37 "gsap": "^3.11.3",
36 38 "highlight.js": "^11.5.0",
37 39 "html2canvas": "^1.4.1",
... ... @@ -45,6 +47,7 @@
45 47 "screenfull": "^6.0.1",
46 48 "three": "^0.145.0",
47 49 "video.js": "^7.20.3",
  50 + "videojs-flvjs-es6": "^1.0.1",
48 51 "vue": "^3.2.31",
49 52 "vue-3d-loader": "^2.1.7",
50 53 "vue-demi": "^0.13.1",
... ...
  1 +import { defHttp } from "@/utils/external/http/axios";
  2 +import { useGlobSetting } from '@/hooks/external/setting';
  3 +enum Api {
  4 + OPEN_FLV = '/rtsp/openFlv',
  5 + CLOSE_FLV = '/rtsp/closeFlv'
  6 +}
  7 +
  8 +export const getOpenFlvPlayUrl = (url: string, browserId: string) => {
  9 + const { urlPrefix, apiUrl } = useGlobSetting()
  10 + return `${apiUrl || ''}${urlPrefix || ''}${Api.OPEN_FLV}?url=${encodeURIComponent(url)}&browserId=${browserId}`;
  11 +};
  12 +
  13 +export const closeFlvPlay = (url: string, browserId: string) => {
  14 + return defHttp.get({
  15 + url: Api.CLOSE_FLV,
  16 + params: {
  17 + url: encodeURIComponent(url),
  18 + browserId
  19 + }
  20 + });
  21 +};
... ...
1 1 <template>
2 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 5 </video>
8 6 </div>
9 7 </template>
10 8 <script setup lang="ts">
11   -import { onMounted, ref, onUnmounted, watch } from 'vue'
  9 +import { onMounted, ref, onUnmounted, watch, unref } from 'vue'
12 10 import videojs from 'video.js'
  11 +import 'videojs-flvjs-es6'
13 12 import type { VideoJsPlayerOptions } from 'video.js'
14 13 import 'video.js/dist/video-js.min.css'
  14 +import { getJwtToken, getShareJwtToken } from '@/utils/external/auth'
  15 +import { isShareMode } from '@/views/share/hook'
  16 +import { getOpenFlvPlayUrl, closeFlvPlay } from '@/api/external/flvPlay'
  17 +import { useFingerprint } from '@/utils/external/useFingerprint'
  18 +import { GetResult } from '@fingerprintjs/fingerprintjs'
15 19
16 20 const props = defineProps({
17 21 sourceSrc: {
... ... @@ -36,40 +40,117 @@ const props = defineProps({
36 40 }
37 41 })
38 42
  43 +enum VideoPlayerType {
  44 + m3u8 = 'application/x-mpegURL',
  45 + mp4 = 'video/mp4',
  46 + webm = 'video/webm',
  47 + flv = 'video/x-flv',
  48 +}
  49 +
  50 +const isRtspProtocol = (url: string) => {
  51 + const reg = /^rtsp:\/\//g;
  52 + return reg.test(url);
  53 +};
  54 +
  55 +const getVideoTypeByUrl = (url = '') => {
  56 + try {
  57 + const { protocol, pathname } = new URL(url)
  58 +
  59 + if (protocol.startsWith('rtsp:')) return VideoPlayerType.flv
  60 +
  61 + const reg = /[^.]\w*$/
  62 + const mathValue = pathname.match(reg) || []
  63 + const ext = mathValue[0] as keyof typeof VideoPlayerType || 'webm'
  64 + const type = VideoPlayerType[ext]
  65 + return type ? type : VideoPlayerType.webm
  66 + } catch (error) {
  67 + console.error(error)
  68 + return VideoPlayerType.webm
  69 + }
  70 +};
  71 +
  72 +
39 73 // video标签
40 74 const videoRef = ref<HTMLElement | null>(null)
41 75
42 76 // video实例对象
43 77 let videoPlayer: videojs.Player | null = null
44 78
  79 +const fingerprintResult = ref<Nullable<GetResult>>(null)
  80 +
45 81 //options配置
46   -const options: VideoJsPlayerOptions = {
  82 +const options: VideoJsPlayerOptions & Recordable = {
47 83 language: 'zh-CN', // 设置语言
48 84 controls: true, // 是否显示控制条
49 85 preload: 'auto', // 预加载
50 86 autoplay: true, // 是否自动播放
51 87 fluid: false, // 自适应宽高
52 88 poster: props?.avatar || '',
53   - src: props?.sourceSrc || '', // 要嵌入的视频源的源 URL
  89 + // src: getSource() || '', // 要嵌入的视频源的源 URL
  90 + sources: [],
54 91 muted: true,
55 92 userActions: {
56 93 hotkeys: true
  94 + },
  95 + techOrder: ['html5', 'flvjs'],
  96 + flvjs: {
  97 + mediaDataSource: {
  98 + isLive: true,
  99 + cors: true,
  100 + withCredentials: false,
  101 + hasAudio: false,
  102 + autoCleanupSourceBuffer: true,
  103 + autoCleanupMaxBackwardDuration: 60,
  104 + },
  105 + },
  106 +}
  107 +
  108 +
  109 +const { getResult } = useFingerprint()
  110 +async function getSource() {
  111 + fingerprintResult.value = await getResult()
  112 + let src = props.sourceSrc || ''
  113 +
  114 + if (isRtspProtocol(props.sourceSrc!)) {
  115 + src = getOpenFlvPlayUrl(src, unref(fingerprintResult)?.visitorId || '')
57 116 }
  117 +
  118 + return [
  119 + {
  120 + type: getVideoTypeByUrl(props.sourceSrc),
  121 + src
  122 + }
  123 + ]
58 124 }
59 125
60 126 // 初始化videojs
61   -const initVideo = () => {
  127 +const initVideo = async () => {
62 128 if (videoRef.value) {
63   - // 创建 video 实例
64   - videoPlayer = videojs(videoRef.value, options)
  129 + // 创建 video 实例
  130 + options.sources = await getSource()
  131 + if (options.sources && options.sources.length) {
  132 +
  133 + if (isRtspProtocol(props.sourceSrc || '')) {
  134 + options.flvjs = {
  135 + ...(options.flvjs || {}),
  136 + config: {
  137 + headers: {
  138 + 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`,
  139 + }
  140 + }
  141 + }
  142 + }
  143 + videoPlayer = videojs(videoRef.value, options)
  144 + }
65 145 }
66 146 }
67 147
68 148 watch(
69 149 () => props.sourceSrc,
70   - (newData: any) => {
  150 + async (newData: any) => {
  151 + const result = await getSource()
71 152 // props.sourceSrc = newData
72   - videoPlayer?.src(newData) as any
  153 + videoPlayer?.src(result) as any
73 154 videoPlayer?.play()
74 155 },
75 156 {
... ... @@ -82,6 +163,9 @@ onMounted(() => {
82 163 })
83 164
84 165 onUnmounted(() => {
  166 + if (props.sourceSrc) {
  167 + closeFlvPlay(props.sourceSrc, unref(fingerprintResult)!.visitorId!)
  168 + }
85 169 handleVideoDispose()
86 170 })
87 171
... ... @@ -101,6 +185,7 @@ defineExpose({
101 185 display: flex;
102 186 align-items: center;
103 187 justify-content: center;
  188 +
104 189 .my-video {
105 190 width: 100% !important;
106 191 height: 100% !important;
... ...
  1 +import { load } from '@fingerprintjs/fingerprintjs';
  2 +export const useFingerprint = () => {
  3 + const getResult = async () => {
  4 + const fp = await load();
  5 + const result = await fp.get();
  6 + return result;
  7 + };
  8 +
  9 + return { getResult };
  10 +};
... ...