Commit 5e6df27d254f5e9402ba7649f2f4e51fb621dc4c

Authored by fengwotao
1 parent b6587d76

feat(src/packages): 修改信息下的单个摄像头,如果是rtsp,则调用接口进行播放

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
... ...