Commit 5e6df27d254f5e9402ba7649f2f4e51fb621dc4c
1 parent
b6587d76
feat(src/packages): 修改信息下的单个摄像头,如果是rtsp,则调用接口进行播放
Showing
2 changed files
with
171 additions
and
81 deletions
1 | 1 | <template> |
2 | - <div class="videoPlay"> | |
2 | + <div class="go-content-box" :style="{ width: w + 'px', height: h + 'px' }"> | |
3 | 3 | <video |
4 | - style="object-fit: cover" | |
5 | - :poster="poster" | |
6 | 4 | crossOrigin="anonymous" |
5 | + :id="`my-player`" | |
7 | 6 | ref="videoRef" |
8 | - class="video-js vjs-default-skin vjs-big-play-centered" | |
9 | - controls | |
10 | - > | |
11 | - <source :src="path" /> | |
12 | - </video> | |
7 | + class="video-js my-video vjs-theme-city vjs-big-play-centered" | |
8 | + ></video> | |
13 | 9 | </div> |
14 | 10 | </template> |
15 | -<script lang="ts" setup> | |
16 | -import { nextTick, onBeforeUnmount, onMounted, ref, watch, toRefs } from 'vue' | |
17 | -import videojs, { VideoJsPlayer } from 'video.js' | |
11 | +<script setup lang="ts"> | |
12 | +import { onMounted, ref, onUnmounted, watch, unref } from 'vue' | |
13 | +import videojs from 'video.js' | |
14 | +import 'videojs-flvjs-es6' | |
18 | 15 | import type { VideoJsPlayerOptions } from 'video.js' |
19 | -import 'video.js/dist/video-js.css' | |
20 | -import zh from 'video.js/dist/lang/zh-CN.json' | |
21 | - | |
22 | -const props = withDefaults( | |
23 | - defineProps<{ | |
24 | - path: string | |
25 | - autoPlay?: boolean | |
26 | - h?: number | |
27 | - poster?: string | |
28 | - }>(), | |
29 | - { autoPlay: false } | |
30 | -) | |
16 | +import 'video.js/dist/video-js.min.css' | |
17 | +import { getJwtToken, getShareJwtToken } from '@/utils/external/auth' | |
18 | +import { isShareMode } from '@/views/share/hook' | |
19 | +import { getOpenFlvPlayUrl, closeFlvPlay } from '@/api/external/flvPlay' | |
20 | +import { useFingerprint } from '@/utils/external/useFingerprint' | |
21 | +import { GetResult } from '@fingerprintjs/fingerprintjs' | |
31 | 22 | |
32 | -const videoRef = ref() | |
33 | - | |
34 | -let player: VideoJsPlayer | |
35 | - | |
36 | -const initPlay = async () => { | |
37 | - videojs.addLanguage('zh-CN', zh) | |
38 | - await nextTick() | |
39 | - const options: VideoJsPlayerOptions = { | |
40 | - muted: true, | |
41 | - controls: true, | |
42 | - autoplay: props.autoPlay, | |
43 | - src: props?.path, | |
44 | - poster: props?.poster, | |
45 | - language: 'zh-CN', | |
46 | - techOrder: ['html5'], | |
47 | - preload: 'none' | |
23 | +const props = defineProps({ | |
24 | + sourceSrc: { | |
25 | + type: String | |
26 | + }, | |
27 | + autoplay: { | |
28 | + type: Boolean | |
29 | + }, | |
30 | + name: { | |
31 | + type: String | |
32 | + }, | |
33 | + avatar: { | |
34 | + type: String | |
35 | + }, | |
36 | + w: { | |
37 | + type: Number, | |
38 | + default: 300 | |
39 | + }, | |
40 | + h: { | |
41 | + type: Number, | |
42 | + default: 300 | |
48 | 43 | } |
49 | - player = videojs(videoRef.value, options, () => { | |
50 | - videojs.log('Video is reading') | |
51 | - if (props.autoPlay) { | |
52 | - player.play() | |
44 | +}) | |
45 | + | |
46 | +enum VideoPlayerType { | |
47 | + m3u8 = 'application/x-mpegURL', | |
48 | + mp4 = 'video/mp4', | |
49 | + webm = 'video/webm', | |
50 | + flv = 'video/x-flv' | |
51 | +} | |
52 | + | |
53 | +const isRtspProtocol = (url: string) => { | |
54 | + const reg = /^rtsp:\/\//g | |
55 | + return reg.test(url) | |
56 | +} | |
57 | + | |
58 | +const getVideoTypeByUrl = (url = '') => { | |
59 | + try { | |
60 | + const { protocol, pathname } = new URL(url) | |
61 | + | |
62 | + if (protocol.startsWith('rtsp:')) return VideoPlayerType.flv | |
63 | + | |
64 | + const reg = /[^.]\w*$/ | |
65 | + const mathValue = pathname.match(reg) || [] | |
66 | + const ext = (mathValue[0] as keyof typeof VideoPlayerType) || 'webm' | |
67 | + const type = VideoPlayerType[ext] | |
68 | + return type ? type : VideoPlayerType.webm | |
69 | + } catch (error) { | |
70 | + console.error(error) | |
71 | + return VideoPlayerType.webm | |
72 | + } | |
73 | +} | |
74 | + | |
75 | +// video标签 | |
76 | +const videoRef = ref<HTMLElement | null>(null) | |
77 | + | |
78 | +// video实例对象 | |
79 | +let videoPlayer: videojs.Player | null = null | |
80 | + | |
81 | +const fingerprintResult = ref<Nullable<GetResult>>(null) | |
82 | + | |
83 | +//options配置 | |
84 | +const options: VideoJsPlayerOptions & Recordable = { | |
85 | + language: 'zh-CN', // 设置语言 | |
86 | + controls: true, // 是否显示控制条 | |
87 | + preload: 'auto', // 预加载 | |
88 | + autoplay: true, // 是否自动播放 | |
89 | + fluid: false, // 自适应宽高 | |
90 | + poster: props?.avatar || '', | |
91 | + // src: getSource() || '', // 要嵌入的视频源的源 URL | |
92 | + sources: [], | |
93 | + muted: true, | |
94 | + userActions: { | |
95 | + hotkeys: true | |
96 | + }, | |
97 | + techOrder: ['html5', 'flvjs'], | |
98 | + flvjs: { | |
99 | + mediaDataSource: { | |
100 | + isLive: true, | |
101 | + cors: true, | |
102 | + withCredentials: false, | |
103 | + hasAudio: false | |
104 | + }, | |
105 | + config: { | |
106 | + autoCleanupSourceBuffer: true | |
53 | 107 | } |
54 | - player.on('ended', () => { | |
55 | - videojs.log('Play end') | |
56 | - }) | |
57 | - player.on('error', () => { | |
58 | - player.errorDisplay.close() | |
59 | - videojs.log('Play parse error') | |
60 | - }) | |
61 | - }) | |
108 | + } | |
62 | 109 | } |
63 | 110 | |
64 | -onMounted(() => { | |
65 | - initPlay() | |
66 | -}) | |
111 | +const { getResult } = useFingerprint() | |
112 | +async function getSource() { | |
113 | + fingerprintResult.value = await getResult() | |
114 | + let src = props.sourceSrc || '' | |
67 | 115 | |
68 | -//直接改变路径测试 | |
69 | -watch( | |
70 | - () => props.path, | |
71 | - () => { | |
72 | - if (props.path && props.autoPlay) { | |
73 | - try { | |
74 | - player?.pause() | |
75 | - player?.load() | |
76 | - player?.src(props.path) | |
77 | - player?.play() | |
78 | - } catch (e) { | |
79 | - console.log(e) | |
116 | + if (isRtspProtocol(props.sourceSrc!)) { | |
117 | + src = getOpenFlvPlayUrl(src, unref(fingerprintResult)?.visitorId || '') | |
118 | + } | |
119 | + | |
120 | + return [ | |
121 | + { | |
122 | + type: getVideoTypeByUrl(props.sourceSrc), | |
123 | + src | |
124 | + } | |
125 | + ] | |
126 | +} | |
127 | + | |
128 | +// 初始化videojs | |
129 | +const initVideo = async () => { | |
130 | + if (videoRef.value) { | |
131 | + // 创建 video 实例 | |
132 | + options.sources = await getSource() | |
133 | + if (options.sources && options.sources.length) { | |
134 | + if (isRtspProtocol(props.sourceSrc || '')) { | |
135 | + options.flvjs = { | |
136 | + ...(options.flvjs || {}), | |
137 | + config: { | |
138 | + headers: { | |
139 | + 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}` | |
140 | + } | |
141 | + } | |
142 | + } | |
80 | 143 | } |
144 | + videoPlayer = videojs(videoRef.value, options) | |
81 | 145 | } |
146 | + } | |
147 | +} | |
148 | + | |
149 | +watch( | |
150 | + () => props.sourceSrc, | |
151 | + async (newData: any) => { | |
152 | + const result = await getSource() | |
153 | + // props.sourceSrc = newData | |
154 | + videoPlayer?.src(result) as any | |
155 | + videoPlayer?.play() | |
82 | 156 | }, |
83 | 157 | { |
84 | 158 | immediate: true |
85 | 159 | } |
86 | 160 | ) |
87 | 161 | |
88 | -onBeforeUnmount(() => { | |
89 | - player?.dispose() | |
162 | +onMounted(() => { | |
163 | + initVideo() | |
164 | +}) | |
165 | + | |
166 | +onUnmounted(() => { | |
167 | + if (props.sourceSrc) { | |
168 | + closeFlvPlay(props.sourceSrc, unref(fingerprintResult)!.visitorId!) | |
169 | + } | |
170 | + handleVideoDispose() | |
171 | +}) | |
172 | + | |
173 | +//播放 | |
174 | +const handleVideoPlay = () => videoPlayer?.play() | |
175 | + | |
176 | +const handleVideoDispose = () => videoPlayer?.dispose() && videoPlayer?.pause() | |
177 | +//暂停 | |
178 | +defineExpose({ | |
179 | + handleVideoPlay, | |
180 | + handleVideoDispose | |
90 | 181 | }) |
91 | 182 | </script> |
92 | -<style> | |
93 | -.vjs-poster { | |
94 | - background-size: cover !important; | |
95 | -} | |
96 | -</style> | |
183 | + | |
97 | 184 | <style lang="scss" scoped> |
98 | -.videoPlay { | |
99 | - flex: 1; | |
100 | - height: v-bind('`${h}px`'); | |
101 | - .video-js { | |
102 | - height: 100%; | |
103 | - width: 100%; | |
185 | +.go-content-box { | |
186 | + display: flex; | |
187 | + align-items: center; | |
188 | + justify-content: center; | |
189 | + | |
190 | + .my-video { | |
191 | + width: 100% !important; | |
192 | + height: 100% !important; | |
193 | + position: relative; | |
104 | 194 | } |
105 | 195 | } |
106 | 196 | </style> | ... | ... |
1 | 1 | <template> |
2 | 2 | <div> |
3 | - <VideoPlay :h="h" :path="option.dataset" :autoPlay="autoplay" :poster="option.poster" /> | |
3 | + <VideoPlay :w="w" :h="h" :sourceSrc="option.dataset" :autoPlay="autoplay" :avatar="option.poster" /> | |
4 | 4 | </div> |
5 | 5 | </template> |
6 | 6 | <script setup lang="ts"> |
... | ... | @@ -16,7 +16,7 @@ const props = defineProps({ |
16 | 16 | } |
17 | 17 | }) |
18 | 18 | |
19 | -const { h } = toRefs(props.chartConfig.attr) | |
19 | +const { w, h } = toRefs(props.chartConfig.attr) | |
20 | 20 | |
21 | 21 | const { autoplay, dataset, poster, url } = toRefs(props.chartConfig.option) |
22 | 22 | ... | ... |