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