Commit 2210106373270d42bf854c4f9a2944d82090c798

Authored by fengtao
1 parent 1b907049

perf(src/views): 优化单个摄像头,设置自动播放失效

1 <script setup lang="ts"> 1 <script setup lang="ts">
2 -import Player, { Events, IError } from 'xgplayer';  
3 -import { FlvPlugin } from 'xgplayer-flv';  
4 -import Mp4Plugin from 'xgplayer-mp4';  
5 -import { HlsPlugin } from 'xgplayer-hls';  
6 -import { onMounted, shallowRef, computed, unref, toRaw, onUnmounted, ref, watch } from 'vue';  
7 -import PresetPlayer from 'xgplayer';  
8 -import { IPlayerOptions } from 'xgplayer/es/player';  
9 -import 'xgplayer/dist/index.min.css';  
10 -import { StreamType, XGPlayerProps } from './types';  
11 -import { isShareMode } from "@/views/share/hook";  
12 -import { getJwtToken, getShareJwtToken } from "@/utils/external/auth";  
13 -  
14 -const props = withDefaults(defineProps<{  
15 - streamType?: StreamType;  
16 - autoPlay?: boolean;  
17 - url?: string;  
18 - withToken?: boolean;  
19 - config?: Omit<IPlayerOptions, 'url'>;  
20 -}>(), {  
21 - streamType: 'auto',  
22 - autoPlay: true,  
23 - config: () => ({}),  
24 -}); 2 +import Player, { Events, IError } from 'xgplayer'
  3 +import { FlvPlugin } from 'xgplayer-flv'
  4 +import Mp4Plugin from 'xgplayer-mp4'
  5 +import { HlsPlugin } from 'xgplayer-hls'
  6 +import { onMounted, shallowRef, computed, unref, toRaw, onUnmounted, ref, watch } from 'vue'
  7 +import PresetPlayer from 'xgplayer'
  8 +import { IPlayerOptions } from 'xgplayer/es/player'
  9 +import 'xgplayer/dist/index.min.css'
  10 +import { StreamType, UserActionEventType, XGPlayerProps } from './types'
  11 +import { isShareMode } from '@/views/share/hook'
  12 +import { getJwtToken, getShareJwtToken } from '@/utils/external/auth'
  13 +
  14 +const props = withDefaults(
  15 + defineProps<{
  16 + streamType?: StreamType
  17 + autoPlay?: boolean
  18 + url?: string
  19 + withToken?: boolean
  20 + config?: Omit<IPlayerOptions, 'url'>
  21 + }>(),
  22 + {
  23 + streamType: 'auto',
  24 + autoPlay: true,
  25 + config: () => ({})
  26 + }
  27 +)
25 28
26 const emits = defineEmits<{ 29 const emits = defineEmits<{
27 - (eventName: 'ready', player: PresetPlayer): void;  
28 - (eventName: 'onUnmounted', player: PresetPlayer): void;  
29 -}>(); 30 + (eventName: 'ready', player: PresetPlayer): void
  31 + (eventName: 'userAction', event: UserActionEventType, player: PresetPlayer): void
  32 + (eventName: 'onUnmounted', player: PresetPlayer): void
  33 +}>()
30 34
31 function parsePlayUrl(url: string) { 35 function parsePlayUrl(url: string) {
32 try { 36 try {
33 - return new URL(url).pathname; 37 + return new URL(url).pathname
34 } catch { 38 } catch {
35 - return url; 39 + return url
36 } 40 }
37 } 41 }
38 42
39 function getStreamTypeByUrl(url = ''): StreamType | undefined { 43 function getStreamTypeByUrl(url = ''): StreamType | undefined {
40 url = parsePlayUrl(url) || '' 44 url = parsePlayUrl(url) || ''
41 - if (url.endsWith('.m3u8')) return 'hls';  
42 - else if (url.endsWith('.mp4')) return 'mp4'; 45 + if (url.endsWith('.m3u8')) return 'hls'
  46 + else if (url.endsWith('.mp4')) return 'mp4'
43 else if (url.endsWith('.flv')) { 47 else if (url.endsWith('.flv')) {
44 - return 'flv';  
45 - } else return; 48 + return 'flv'
  49 + } else return
46 } 50 }
47 51
48 const getPluginByStreamType = (): IPlayerOptions => { 52 const getPluginByStreamType = (): IPlayerOptions => {
49 - let { url, withToken } = props;  
50 - let { streamType } = props;  
51 - streamType = streamType === 'auto' ? getStreamTypeByUrl(url)! : streamType; 53 + let { url, withToken } = props
  54 + let { streamType } = props
  55 + streamType = streamType === 'auto' ? getStreamTypeByUrl(url)! : streamType
52 56
53 const liveConfig = { 57 const liveConfig = {
54 targetLatency: 10, 58 targetLatency: 10,
@@ -56,38 +60,38 @@ const getPluginByStreamType = (): IPlayerOptions => { @@ -56,38 +60,38 @@ const getPluginByStreamType = (): IPlayerOptions => {
56 disconnectTime: 0, 60 disconnectTime: 0,
57 fetchOptions: withToken 61 fetchOptions: withToken
58 ? { 62 ? {
59 - headers: {  
60 - 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`,  
61 - },  
62 - }  
63 - : {},  
64 - }; 63 + headers: {
  64 + 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`
  65 + }
  66 + }
  67 + : {}
  68 + }
65 const config: IPlayerOptions = { 69 const config: IPlayerOptions = {
66 flv: liveConfig, 70 flv: liveConfig,
67 - hls: liveConfig,  
68 - }; 71 + hls: liveConfig
  72 + }
69 switch (streamType) { 73 switch (streamType) {
70 case 'hls': 74 case 'hls':
71 - config.plugins = [HlsPlugin];  
72 - break; 75 + config.plugins = [HlsPlugin]
  76 + break
73 case 'mp4': 77 case 'mp4':
74 - config.plugins = [Mp4Plugin];  
75 - break; 78 + config.plugins = [Mp4Plugin]
  79 + break
76 case 'flv': 80 case 'flv':
77 - config.plugins = [FlvPlugin];  
78 - break; 81 + config.plugins = [FlvPlugin]
  82 + break
79 } 83 }
80 - return config;  
81 -}; 84 + return config
  85 +}
82 86
83 -const videoElRef = shallowRef<Nullable<HTMLDivElement>>(); 87 +const videoElRef = shallowRef<Nullable<HTMLDivElement>>()
84 88
85 -const playerRef = shallowRef<Nullable<PresetPlayer>>(); 89 +const playerRef = shallowRef<Nullable<PresetPlayer>>()
86 90
87 -const propsRef = ref<XGPlayerProps>({}); 91 +const propsRef = ref<XGPlayerProps>({})
88 92
89 const getPlayerConfig = computed<IPlayerOptions>(() => { 93 const getPlayerConfig = computed<IPlayerOptions>(() => {
90 - const { url, autoPlay, config } = props; 94 + const { url, autoPlay, config } = props
91 95
92 const basicConfig: IPlayerOptions = { 96 const basicConfig: IPlayerOptions = {
93 ...config, 97 ...config,
@@ -97,79 +101,83 @@ const getPlayerConfig = computed<IPlayerOptions>(() => { @@ -97,79 +101,83 @@ const getPlayerConfig = computed<IPlayerOptions>(() => {
97 isLive: true, 101 isLive: true,
98 autoplay: autoPlay, 102 autoplay: autoPlay,
99 autoplayMuted: autoPlay, 103 autoplayMuted: autoPlay,
100 - ...getPluginByStreamType(),  
101 - };  
102 - return basicConfig;  
103 -}); 104 + ...getPluginByStreamType()
  105 + }
  106 + return basicConfig
  107 +})
104 108
105 function onDecodeError() { 109 function onDecodeError() {
106 - console.warn('player happend decode error');  
107 - playerRef.value?.switchURL(props.url!); 110 + console.warn('player happend decode error')
  111 + playerRef.value?.switchURL(props.url!)
108 } 112 }
109 113
110 function initializePlayer() { 114 function initializePlayer() {
111 if (unref(playerRef)) { 115 if (unref(playerRef)) {
112 - playerRef.value?.destroy?.();  
113 - playerRef.value = null; 116 + playerRef.value?.destroy?.()
  117 + playerRef.value = null
114 } 118 }
115 119
116 - const config = toRaw(unref(getPlayerConfig)); 120 + const config = toRaw(unref(getPlayerConfig))
117 121
118 - if (!unref(videoElRef)) return; 122 + if (!unref(videoElRef)) return
119 123
120 - const player = (playerRef.value = new Player(Object.assign(config, { el: unref(videoElRef) }))); 124 + const player = (playerRef.value = new Player(Object.assign(config, { el: unref(videoElRef) })))
121 125
122 player.on(Events.READY, () => { 126 player.on(Events.READY, () => {
123 - emits('ready', player);  
124 - }); 127 + emits('ready', player)
  128 + })
  129 +
  130 + player.on(Events.USER_ACTION, (event: UserActionEventType) => {
  131 + emits('userAction', event, player)
  132 + })
125 133
126 player.setEventsMiddleware({ 134 player.setEventsMiddleware({
127 error: (event, callback) => { 135 error: (event, callback) => {
128 const code = ( 136 const code = (
129 event as unknown as { 137 event as unknown as {
130 - error: MediaError; 138 + error: MediaError
131 } 139 }
132 - ).error.code; 140 + ).error.code
133 if (code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED) { 141 if (code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED) {
134 if (!props.url) { 142 if (!props.url) {
135 - return; 143 + return
136 } 144 }
137 - callback();  
138 - return; 145 + callback()
  146 + return
139 } 147 }
140 148
141 if (code === MediaError.MEDIA_ERR_DECODE) { 149 if (code === MediaError.MEDIA_ERR_DECODE) {
142 // 视频流可以播放 中途解码失败重载 150 // 视频流可以播放 中途解码失败重载
143 if (playerRef.value?.isPlaying) { 151 if (playerRef.value?.isPlaying) {
144 - onDecodeError(); 152 + onDecodeError()
145 } 153 }
146 - return; 154 + return
147 } 155 }
148 156
149 - callback();  
150 - },  
151 - }); 157 + callback()
  158 + }
  159 + })
152 } 160 }
153 161
154 onMounted(() => { 162 onMounted(() => {
155 - initializePlayer();  
156 -}); 163 + initializePlayer()
  164 +})
157 165
158 onUnmounted(() => { 166 onUnmounted(() => {
159 - emits('onUnmounted', unref(playerRef)!);  
160 - playerRef.value?.destroy?.();  
161 -}); 167 + emits('onUnmounted', unref(playerRef)!)
  168 + playerRef.value?.destroy?.()
  169 +})
162 170
163 watch( 171 watch(
164 () => props.url, 172 () => props.url,
165 () => { 173 () => {
166 - initializePlayer(); 174 + initializePlayer()
167 } 175 }
168 -); 176 +)
169 177
170 defineExpose({ 178 defineExpose({
171 - getPlayerInstance: () => unref(playerRef),  
172 -}); 179 + getPlayerInstance: () => unref(playerRef)
  180 +})
173 </script> 181 </script>
174 182
175 <template> 183 <template>
1 -import { IPlayerOptions } from 'xgplayer/es/player'; 1 +import { IPlayerOptions } from 'xgplayer/es/player'
2 2
3 -export type StreamType = 'flv' | 'mp4' | 'hls' | 'auto'; 3 +export type StreamType = 'flv' | 'mp4' | 'hls' | 'auto'
4 export interface XGPlayerProps { 4 export interface XGPlayerProps {
5 - streamType?: StreamType;  
6 - autoPlay?: boolean;  
7 - url?: string;  
8 - withToken?: boolean;  
9 - config?: Omit<IPlayerOptions, 'url'>; 5 + streamType?: StreamType
  6 + autoPlay?: boolean
  7 + url?: string
  8 + withToken?: boolean
  9 + config?: Omit<IPlayerOptions, 'url'>
  10 +}
  11 +
  12 +export interface UserActionEventType {
  13 + action: string // 用户行为
  14 + pluginName: string // 从哪个插件触发
  15 + props: [
  16 + {
  17 + // 发生变化的属性列表
  18 + props: string // 发生变化的属性
  19 + from: any // 变化前的值
  20 + to: any // 变化后的值
  21 + }
  22 + ]
  23 + event: Event // 事件
  24 + currentTime: number // 当前播放时间
  25 + duration: number // 当前播放器时长
  26 + ended: boolean // 是否播放结束
  27 + paused: boolean // 是否暂停
  28 + from?: boolean
  29 + to?: boolean
10 } 30 }
1 <template> 1 <template>
2 <div> 2 <div>
3 <n-spin size="medium" :show="showLoading" class="player-spin"> 3 <n-spin size="medium" :show="showLoading" class="player-spin">
4 - <XGPlayer :url="sourceUrl" :stream-type="playType"  
5 - :config="{width: '100%', height: '100%', poster: option.poster}"  
6 - :auto-play="option.autoplay"/> 4 + <XGPlayer
  5 + ref="XGPlayerRef"
  6 + @userAction="handleOnUserAction"
  7 + :url="sourceUrl"
  8 + :stream-type="playType"
  9 + :config="{ width: '100%', height: '100%', poster: option.poster }"
  10 + :auto-play="option.autoplay"
  11 + />
7 </n-spin> 12 </n-spin>
8 </div> 13 </div>
9 </template> 14 </template>
10 <script setup lang="ts"> 15 <script setup lang="ts">
11 -import {PropType, toRefs, shallowReactive, watch, ref} from 'vue'  
12 -import {CreateComponentType} from '@/packages/index.d'  
13 -import {Dataset, getPlayUrl, option as configOption} from './config'  
14 -import {XGPlayer} from '@/components/Video'  
15 -import {StreamType} from "@/components/Video/src/types";  
16 -import {CameraRecord} from "@/api/external/common/model";  
17 -import { isNullOrUnDef } from '@/utils/external/is'; 16 +import { PropType, toRefs, shallowReactive, watch, ref, nextTick } from 'vue'
  17 +import { CreateComponentType } from '@/packages/index.d'
  18 +import { Dataset, getPlayUrl, option as configOption } from './config'
  19 +import { XGPlayer } from '@/components/Video'
  20 +import { StreamType, UserActionEventType } from '@/components/Video/src/types'
  21 +import { CameraRecord } from '@/api/external/common/model'
  22 +import { isNullOrUnDef } from '@/utils/external/is'
18 23
19 const props = defineProps({ 24 const props = defineProps({
20 chartConfig: { 25 chartConfig: {
@@ -25,9 +30,11 @@ const props = defineProps({ @@ -25,9 +30,11 @@ const props = defineProps({
25 30
26 const showLoading = ref(false) 31 const showLoading = ref(false)
27 32
28 -const {w, h} = toRefs(props.chartConfig.attr) 33 +const XGPlayerRef = ref<InstanceType<typeof XGPlayer>>()
29 34
30 -const {autoplay, dataset, poster, customVideoUrl} = toRefs(props.chartConfig.option as typeof configOption) 35 +const { w, h } = toRefs(props.chartConfig.attr)
  36 +
  37 +const { autoplay, dataset, poster, customVideoUrl } = toRefs(props.chartConfig.option as typeof configOption)
31 38
32 const option = shallowReactive({ 39 const option = shallowReactive({
33 dataset: configOption.dataset, 40 dataset: configOption.dataset,
@@ -36,36 +43,55 @@ const option = shallowReactive({ @@ -36,36 +43,55 @@ const option = shallowReactive({
36 }) 43 })
37 44
38 const sourceUrl = ref('') 45 const sourceUrl = ref('')
39 -const playType = ref<StreamType>()  
40 46
  47 +const playType = ref<StreamType>()
41 48
42 async function getPlaySource(params: Dataset) { 49 async function getPlaySource(params: Dataset) {
43 - const {id, channelId, deviceId, accessMode, customUrl, playProtocol} = params  
44 - if (isNullOrUnDef(accessMode)) return  
45 - const {type, url} = await getPlayUrl({ 50 + if (!autoplay.value) return
  51 + doPlaySource(params)
  52 +}
  53 +
  54 +async function doPlaySource(params?: Dataset) {
  55 + const { id, channelId, deviceId, accessMode, customUrl, playProtocol } = params as unknown as Dataset
  56 + if (isNullOrUnDef(accessMode)) return
  57 + const { type, url } =
  58 + (await getPlayUrl({
46 id: id, 59 id: id,
47 accessMode: accessMode, 60 accessMode: accessMode,
48 playProtocol: playProtocol, 61 playProtocol: playProtocol,
49 videoUrl: customUrl, 62 videoUrl: customUrl,
50 params: { 63 params: {
51 deviceId: deviceId, 64 deviceId: deviceId,
52 - channelNo: channelId,  
53 - },  
54 - } as unknown as CameraRecord) || {}  
55 - sourceUrl.value = url!  
56 - playType.value = type 65 + channelNo: channelId
  66 + }
  67 + } as unknown as CameraRecord)) || {}
  68 + sourceUrl.value = url!
  69 + playType.value = type
  70 +}
  71 +
  72 +function handleOnUserAction(event: UserActionEventType) {
  73 + const { from, to } = event
  74 + if (from && !to) {
  75 + doPlaySource(dataset?.value)
  76 + nextTick(() => {
  77 + XGPlayerRef.value?.getPlayerInstance()?.play()
  78 + })
  79 + } else if (!from && to) {
  80 + nextTick(() => {
  81 + XGPlayerRef.value?.getPlayerInstance()?.pause()
  82 + })
  83 + }
57 } 84 }
58 85
59 watch( 86 watch(
60 - () => dataset?.value,  
61 - async (newData) => {  
62 - getPlaySource(newData)  
63 - },  
64 - {  
65 - immediate: true  
66 - } 87 + () => dataset?.value,
  88 + async newData => {
  89 + getPlaySource(newData)
  90 + },
  91 + {
  92 + immediate: true
  93 + }
67 ) 94 )
68 -  
69 </script> 95 </script>
70 96
71 <style lang="scss" scoped> 97 <style lang="scss" scoped>