Commit 4458cd983103fc0648cd3ce99ea90397da61b875
Committed by
xp.Huang
1 parent
db42c0a2
fix: 单个视频切换视频源,切换后未播放为新的视频源
Showing
4 changed files
with
73 additions
and
71 deletions
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: baseSize?.w + 'px', height: baseSize?.h + 'px' }"> |
3 | <video | 3 | <video |
4 | crossOrigin="anonymous" | 4 | crossOrigin="anonymous" |
5 | :id="`my-player`" | 5 | :id="`my-player`" |
@@ -9,7 +9,7 @@ | @@ -9,7 +9,7 @@ | ||
9 | </div> | 9 | </div> |
10 | </template> | 10 | </template> |
11 | <script setup lang="ts"> | 11 | <script setup lang="ts"> |
12 | -import { onMounted, ref, onUnmounted, watch, unref } from 'vue' | 12 | +import { onMounted, ref, onUnmounted, watch, unref, PropType } from 'vue' |
13 | import videojs from 'video.js' | 13 | import videojs from 'video.js' |
14 | import 'videojs-flvjs-es6' | 14 | import 'videojs-flvjs-es6' |
15 | import type { VideoJsPlayerOptions } from 'video.js' | 15 | import type { VideoJsPlayerOptions } from 'video.js' |
@@ -19,6 +19,7 @@ import { isShareMode } from '@/views/share/hook' | @@ -19,6 +19,7 @@ import { isShareMode } from '@/views/share/hook' | ||
19 | import { getOpenFlvPlayUrl, closeFlvPlay } from '@/api/external/flvPlay' | 19 | import { getOpenFlvPlayUrl, closeFlvPlay } from '@/api/external/flvPlay' |
20 | import { useFingerprint } from '@/utils/external/useFingerprint' | 20 | import { useFingerprint } from '@/utils/external/useFingerprint' |
21 | import { GetResult } from '@fingerprintjs/fingerprintjs' | 21 | import { GetResult } from '@fingerprintjs/fingerprintjs' |
22 | +import { VideoPlayerTypeEnum } from '../config' | ||
22 | 23 | ||
23 | const props = defineProps({ | 24 | const props = defineProps({ |
24 | sourceSrc: { | 25 | sourceSrc: { |
@@ -33,23 +34,11 @@ const props = defineProps({ | @@ -33,23 +34,11 @@ const props = defineProps({ | ||
33 | avatar: { | 34 | avatar: { |
34 | type: String | 35 | type: String |
35 | }, | 36 | }, |
36 | - w: { | ||
37 | - type: Number, | ||
38 | - default: 300 | ||
39 | - }, | ||
40 | - h: { | ||
41 | - type: Number, | ||
42 | - default: 300 | 37 | + baseSize: { |
38 | + type: Object as PropType<{ w: number; h: number }> | ||
43 | } | 39 | } |
44 | }) | 40 | }) |
45 | 41 | ||
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) => { | 42 | const isRtspProtocol = (url: string) => { |
54 | const reg = /^rtsp:\/\//g | 43 | const reg = /^rtsp:\/\//g |
55 | return reg.test(url) | 44 | return reg.test(url) |
@@ -58,15 +47,15 @@ const isRtspProtocol = (url: string) => { | @@ -58,15 +47,15 @@ const isRtspProtocol = (url: string) => { | ||
58 | const getVideoTypeByUrl = (url = '') => { | 47 | const getVideoTypeByUrl = (url = '') => { |
59 | try { | 48 | try { |
60 | const { protocol, pathname } = new URL(url) | 49 | const { protocol, pathname } = new URL(url) |
61 | - if (protocol.startsWith('rtsp:')) return VideoPlayerType.flv | 50 | + if (protocol.startsWith('rtsp:')) return VideoPlayerTypeEnum.flv |
62 | const reg = /[^.]\w*$/ | 51 | const reg = /[^.]\w*$/ |
63 | const mathValue = pathname.match(reg) || [] | 52 | const mathValue = pathname.match(reg) || [] |
64 | - const ext = (mathValue[0] as keyof typeof VideoPlayerType) || 'webm' | ||
65 | - const type = VideoPlayerType[ext] | ||
66 | - return type ? type : VideoPlayerType.webm | 53 | + const ext = (mathValue[0] as keyof typeof VideoPlayerTypeEnum) || 'webm' |
54 | + const type = VideoPlayerTypeEnum[ext] | ||
55 | + return type ? type : VideoPlayerTypeEnum.webm | ||
67 | } catch (error) { | 56 | } catch (error) { |
68 | console.error(error) | 57 | console.error(error) |
69 | - return VideoPlayerType.webm | 58 | + return VideoPlayerTypeEnum.webm |
70 | } | 59 | } |
71 | } | 60 | } |
72 | 61 | ||
@@ -82,11 +71,9 @@ const fingerprintResult = ref<Nullable<GetResult>>(null) | @@ -82,11 +71,9 @@ const fingerprintResult = ref<Nullable<GetResult>>(null) | ||
82 | const options: VideoJsPlayerOptions & Recordable = { | 71 | const options: VideoJsPlayerOptions & Recordable = { |
83 | language: 'zh-CN', // 设置语言 | 72 | language: 'zh-CN', // 设置语言 |
84 | controls: true, // 是否显示控制条 | 73 | controls: true, // 是否显示控制条 |
85 | - // preload: 'auto', // 预加载 | ||
86 | autoplay: props.autoPlay ? true : false, // 是否自动播放 | 74 | autoplay: props.autoPlay ? true : false, // 是否自动播放 |
87 | fluid: false, // 自适应宽高 | 75 | fluid: false, // 自适应宽高 |
88 | poster: props?.avatar || '', | 76 | poster: props?.avatar || '', |
89 | - // src: getSource() || '', // 要嵌入的视频源的源 URL | ||
90 | sources: [], | 77 | sources: [], |
91 | muted: props.autoPlay ? true : false, | 78 | muted: props.autoPlay ? true : false, |
92 | userActions: { | 79 | userActions: { |
@@ -107,6 +94,7 @@ const options: VideoJsPlayerOptions & Recordable = { | @@ -107,6 +94,7 @@ const options: VideoJsPlayerOptions & Recordable = { | ||
107 | } | 94 | } |
108 | 95 | ||
109 | const { getResult } = useFingerprint() | 96 | const { getResult } = useFingerprint() |
97 | + | ||
110 | async function getSource() { | 98 | async function getSource() { |
111 | fingerprintResult.value = await getResult() | 99 | fingerprintResult.value = await getResult() |
112 | let src = props.sourceSrc || '' | 100 | let src = props.sourceSrc || '' |
@@ -144,11 +132,11 @@ const initVideo = async () => { | @@ -144,11 +132,11 @@ const initVideo = async () => { | ||
144 | 132 | ||
145 | watch( | 133 | watch( |
146 | () => props.sourceSrc, | 134 | () => props.sourceSrc, |
147 | - async (newData: any) => { | 135 | + async () => { |
136 | + videoPlayer?.src('') | ||
148 | const result = await getSource() | 137 | const result = await getSource() |
149 | - // props.sourceSrc = newData | ||
150 | - if(props.autoPlay){ | ||
151 | - videoPlayer?.src(result) as any | 138 | + if (props.autoPlay) { |
139 | + videoPlayer?.src(result) | ||
152 | videoPlayer?.play() | 140 | videoPlayer?.play() |
153 | } | 141 | } |
154 | }, | 142 | }, |
@@ -157,6 +145,20 @@ watch( | @@ -157,6 +145,20 @@ watch( | ||
157 | } | 145 | } |
158 | ) | 146 | ) |
159 | 147 | ||
148 | +watch( | ||
149 | + () => props.autoPlay, | ||
150 | + async (newData: boolean) => { | ||
151 | + if (newData) { | ||
152 | + handleVideoPlay() | ||
153 | + } else { | ||
154 | + videoPlayer?.pause() | ||
155 | + } | ||
156 | + }, | ||
157 | + { | ||
158 | + immediate: true | ||
159 | + } | ||
160 | +) | ||
161 | + | ||
160 | onMounted(() => { | 162 | onMounted(() => { |
161 | initVideo() | 163 | initVideo() |
162 | }) | 164 | }) |
@@ -171,8 +173,8 @@ onUnmounted(() => { | @@ -171,8 +173,8 @@ onUnmounted(() => { | ||
171 | //播放 | 173 | //播放 |
172 | const handleVideoPlay = () => videoPlayer?.play() | 174 | const handleVideoPlay = () => videoPlayer?.play() |
173 | 175 | ||
176 | +//暂停和销毁 | ||
174 | const handleVideoDispose = () => videoPlayer?.dispose() && videoPlayer?.pause() | 177 | const handleVideoDispose = () => videoPlayer?.dispose() && videoPlayer?.pause() |
175 | -//暂停 | ||
176 | defineExpose({ | 178 | defineExpose({ |
177 | handleVideoPlay, | 179 | handleVideoPlay, |
178 | handleVideoDispose | 180 | handleVideoDispose |
@@ -8,10 +8,30 @@ export enum sourceTypeEnum { | @@ -8,10 +8,30 @@ export enum sourceTypeEnum { | ||
8 | PLATFORM = 'platform' | 8 | PLATFORM = 'platform' |
9 | } | 9 | } |
10 | 10 | ||
11 | +export enum VideoPlayerTypeEnum { | ||
12 | + m3u8 = 'application/x-mpegURL', | ||
13 | + mp4 = 'video/mp4', | ||
14 | + webm = 'video/webm', | ||
15 | + flv = 'video/x-flv' | ||
16 | +} | ||
17 | + | ||
18 | +export interface videoListInterface { | ||
19 | + name: string | ||
20 | + accessMode: number | ||
21 | + id: string | ||
22 | + videoUrl: string | ||
23 | + label: string | ||
24 | + value: string | ||
25 | +} | ||
26 | + | ||
27 | +export enum AccessMode { | ||
28 | + ManuallyEnter = 0, | ||
29 | + Streaming = 1 | ||
30 | +} | ||
31 | + | ||
11 | export const option = { | 32 | export const option = { |
12 | - url:'', | ||
13 | dataset: '', | 33 | dataset: '', |
14 | - autoplay: false, | 34 | + autoplay: true, |
15 | poster: '', | 35 | poster: '', |
16 | sourceType: 'custom', | 36 | sourceType: 'custom', |
17 | organization: '' | 37 | organization: '' |
@@ -9,7 +9,7 @@ | @@ -9,7 +9,7 @@ | ||
9 | <setting-item> | 9 | <setting-item> |
10 | <n-radio-group @update:value="handleChecked" v-model:value="optionData.sourceType" name="radiogroup"> | 10 | <n-radio-group @update:value="handleChecked" v-model:value="optionData.sourceType" name="radiogroup"> |
11 | <n-space> | 11 | <n-space> |
12 | - <n-radio v-for="(item, index) in sourceTypes" :key="item.value" :value="item.value"> | 12 | + <n-radio v-for="(item, index) in sourceTypes" :key="item.value + index" :value="item.value"> |
13 | {{ item.label }} | 13 | {{ item.label }} |
14 | </n-radio> | 14 | </n-radio> |
15 | </n-space> | 15 | </n-space> |
@@ -40,7 +40,7 @@ | @@ -40,7 +40,7 @@ | ||
40 | <setting-item> | 40 | <setting-item> |
41 | <n-select | 41 | <n-select |
42 | @update:value="handleSelect" | 42 | @update:value="handleSelect" |
43 | - v-model:value="optionData.dataset" | 43 | + v-model:value="url" |
44 | :options="videoOptions" | 44 | :options="videoOptions" |
45 | placeholder="请选择视频地址" | 45 | placeholder="请选择视频地址" |
46 | /> | 46 | /> |
@@ -56,21 +56,12 @@ | @@ -56,21 +56,12 @@ | ||
56 | 56 | ||
57 | <script setup lang="ts"> | 57 | <script setup lang="ts"> |
58 | import { PropType, ref, onMounted } from 'vue' | 58 | import { PropType, ref, onMounted } from 'vue' |
59 | -import { option, sourceTypeEnum } from './config' | 59 | +import { AccessMode, option, sourceTypeEnum, videoListInterface } from './config' |
60 | import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' | 60 | import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' |
61 | import { NTreeSelect } from 'naive-ui' | 61 | import { NTreeSelect } from 'naive-ui' |
62 | import { getOrganizationList, getVideoList, getVideoUrl } from '@/api/external/common/index' | 62 | import { getOrganizationList, getVideoList, getVideoUrl } from '@/api/external/common/index' |
63 | import { TKUpload } from '@/components/external/Common/TKUpload' | 63 | import { TKUpload } from '@/components/external/Common/TKUpload' |
64 | 64 | ||
65 | -interface videoListIF { | ||
66 | - name: string | ||
67 | - accessMode: number | ||
68 | - id: string | ||
69 | - videoUrl: string | ||
70 | - label: string | ||
71 | - value: string | ||
72 | -} | ||
73 | - | ||
74 | const props = defineProps({ | 65 | const props = defineProps({ |
75 | optionData: { | 66 | optionData: { |
76 | type: Object as PropType<typeof option>, | 67 | type: Object as PropType<typeof option>, |
@@ -91,7 +82,9 @@ const sourceTypes = [ | @@ -91,7 +82,9 @@ const sourceTypes = [ | ||
91 | 82 | ||
92 | const originationOption = ref([]) | 83 | const originationOption = ref([]) |
93 | 84 | ||
94 | -const videoOptions = ref<videoListIF[]>([]) | 85 | +const url = ref('') |
86 | + | ||
87 | +const videoOptions = ref<videoListInterface[]>([]) | ||
95 | 88 | ||
96 | const getOriginationList = async () => { | 89 | const getOriginationList = async () => { |
97 | const res = await getOrganizationList() | 90 | const res = await getOrganizationList() |
@@ -106,9 +99,9 @@ const handleUpdateTreeValue = (value: string) => { | @@ -106,9 +99,9 @@ const handleUpdateTreeValue = (value: string) => { | ||
106 | const getVideoLists = async (organizationId: string) => { | 99 | const getVideoLists = async (organizationId: string) => { |
107 | const res = await getVideoList({ organizationId }) | 100 | const res = await getVideoList({ organizationId }) |
108 | if (!res) return | 101 | if (!res) return |
109 | - videoOptions.value = res?.data?.map((item: videoListIF) => ({ | 102 | + videoOptions.value = res?.data?.map((item: videoListInterface) => ({ |
110 | label: item.name, | 103 | label: item.name, |
111 | - value: item.accessMode === 1 ? item.id : item.videoUrl, | 104 | + value: item.accessMode === AccessMode.Streaming ? item.id : item.videoUrl, |
112 | id: item.id, | 105 | id: item.id, |
113 | accessMode: item.accessMode | 106 | accessMode: item.accessMode |
114 | })) | 107 | })) |
@@ -118,7 +111,7 @@ const getVideoUrlById = async (id: string) => { | @@ -118,7 +111,7 @@ const getVideoUrlById = async (id: string) => { | ||
118 | const res = await getVideoUrl(id) | 111 | const res = await getVideoUrl(id) |
119 | if (!res) return | 112 | if (!res) return |
120 | const { url } = res.data | 113 | const { url } = res.data |
121 | - props.optionData.url = url | 114 | + props.optionData.dataset = url |
122 | } | 115 | } |
123 | 116 | ||
124 | const handleChecked = (value: string) => { | 117 | const handleChecked = (value: string) => { |
@@ -128,13 +121,13 @@ const handleChecked = (value: string) => { | @@ -128,13 +121,13 @@ const handleChecked = (value: string) => { | ||
128 | } | 121 | } |
129 | } | 122 | } |
130 | 123 | ||
131 | -const handleSelect = (_: string, e: videoListIF) => { | ||
132 | - const { accessMode, id } = e | 124 | +const handleSelect = (_: string, e: videoListInterface) => { |
125 | + const { accessMode, id, value } = e | ||
133 | //1表示,需要从服务端调取接口换取播放的地址,0则不需要 | 126 | //1表示,需要从服务端调取接口换取播放的地址,0则不需要 |
134 | - if (accessMode === 1) { | 127 | + if (accessMode === AccessMode.Streaming) { |
135 | getVideoUrlById(id) | 128 | getVideoUrlById(id) |
136 | } else { | 129 | } else { |
137 | - props.optionData.url = '' | 130 | + props.optionData.dataset = value as string |
138 | } | 131 | } |
139 | } | 132 | } |
140 | 133 |
1 | <template> | 1 | <template> |
2 | <div> | 2 | <div> |
3 | - <VideoPlay :w="w" :h="h" :sourceSrc="option.dataset" :autoPlay="option.autoplay" :avatar="option.poster" /> | 3 | + <VideoPlay :baseSize="{ w, h }" :sourceSrc="option.dataset" :autoPlay="option.autoplay" :avatar="option.poster" /> |
4 | </div> | 4 | </div> |
5 | </template> | 5 | </template> |
6 | <script setup lang="ts"> | 6 | <script setup lang="ts"> |
@@ -18,22 +18,18 @@ const props = defineProps({ | @@ -18,22 +18,18 @@ const props = defineProps({ | ||
18 | 18 | ||
19 | const { w, h } = toRefs(props.chartConfig.attr) | 19 | const { w, h } = toRefs(props.chartConfig.attr) |
20 | 20 | ||
21 | -const { autoplay, dataset, poster, url } = toRefs(props.chartConfig.option) | 21 | +const { autoplay, dataset, poster } = toRefs(props.chartConfig.option as typeof configOption) |
22 | 22 | ||
23 | const option = shallowReactive({ | 23 | const option = shallowReactive({ |
24 | dataset: configOption.dataset, | 24 | dataset: configOption.dataset, |
25 | poster: configOption.poster, | 25 | poster: configOption.poster, |
26 | - autoplay:configOption.autoplay | 26 | + autoplay: configOption.autoplay |
27 | }) | 27 | }) |
28 | 28 | ||
29 | watch( | 29 | watch( |
30 | () => dataset?.value, | 30 | () => dataset?.value, |
31 | (newData: string) => { | 31 | (newData: string) => { |
32 | - if (url?.value) { | ||
33 | - option.dataset = url?.value | ||
34 | - } else { | ||
35 | - option.dataset = newData | ||
36 | - } | 32 | + option.dataset = newData |
37 | }, | 33 | }, |
38 | { | 34 | { |
39 | immediate: true | 35 | immediate: true |
@@ -41,19 +37,10 @@ watch( | @@ -41,19 +37,10 @@ watch( | ||
41 | ) | 37 | ) |
42 | 38 | ||
43 | watch( | 39 | watch( |
44 | - () => poster?.value, | ||
45 | - (newData: string) => { | ||
46 | - option.poster = newData | ||
47 | - }, | ||
48 | - { | ||
49 | - immediate: true | ||
50 | - } | ||
51 | -) | ||
52 | - | ||
53 | -watch( | ||
54 | - () => autoplay?.value, | ||
55 | - (newData:boolean) => { | ||
56 | - option.autoplay = newData | 40 | + () => [poster.value, autoplay.value], |
41 | + newData => { | ||
42 | + option.poster = newData.at(-2) as string | ||
43 | + option.autoplay = newData.at(-1) as boolean | ||
57 | }, | 44 | }, |
58 | { | 45 | { |
59 | immediate: true | 46 | immediate: true |