Commit 2210106373270d42bf854c4f9a2944d82090c798

Authored by fengtao
1 parent 1b907049

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

1 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 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 35 function parsePlayUrl(url: string) {
32 36 try {
33   - return new URL(url).pathname;
  37 + return new URL(url).pathname
34 38 } catch {
35   - return url;
  39 + return url
36 40 }
37 41 }
38 42
39 43 function getStreamTypeByUrl(url = ''): StreamType | undefined {
40 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 47 else if (url.endsWith('.flv')) {
44   - return 'flv';
45   - } else return;
  48 + return 'flv'
  49 + } else return
46 50 }
47 51
48 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 57 const liveConfig = {
54 58 targetLatency: 10,
... ... @@ -56,38 +60,38 @@ const getPluginByStreamType = (): IPlayerOptions => {
56 60 disconnectTime: 0,
57 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 69 const config: IPlayerOptions = {
66 70 flv: liveConfig,
67   - hls: liveConfig,
68   - };
  71 + hls: liveConfig
  72 + }
69 73 switch (streamType) {
70 74 case 'hls':
71   - config.plugins = [HlsPlugin];
72   - break;
  75 + config.plugins = [HlsPlugin]
  76 + break
73 77 case 'mp4':
74   - config.plugins = [Mp4Plugin];
75   - break;
  78 + config.plugins = [Mp4Plugin]
  79 + break
76 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 93 const getPlayerConfig = computed<IPlayerOptions>(() => {
90   - const { url, autoPlay, config } = props;
  94 + const { url, autoPlay, config } = props
91 95
92 96 const basicConfig: IPlayerOptions = {
93 97 ...config,
... ... @@ -97,79 +101,83 @@ const getPlayerConfig = computed<IPlayerOptions>(() => {
97 101 isLive: true,
98 102 autoplay: autoPlay,
99 103 autoplayMuted: autoPlay,
100   - ...getPluginByStreamType(),
101   - };
102   - return basicConfig;
103   -});
  104 + ...getPluginByStreamType()
  105 + }
  106 + return basicConfig
  107 +})
104 108
105 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 114 function initializePlayer() {
111 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 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 134 player.setEventsMiddleware({
127 135 error: (event, callback) => {
128 136 const code = (
129 137 event as unknown as {
130   - error: MediaError;
  138 + error: MediaError
131 139 }
132   - ).error.code;
  140 + ).error.code
133 141 if (code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED) {
134 142 if (!props.url) {
135   - return;
  143 + return
136 144 }
137   - callback();
138   - return;
  145 + callback()
  146 + return
139 147 }
140 148
141 149 if (code === MediaError.MEDIA_ERR_DECODE) {
142 150 // 视频流可以播放 中途解码失败重载
143 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 162 onMounted(() => {
155   - initializePlayer();
156   -});
  163 + initializePlayer()
  164 +})
157 165
158 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 171 watch(
164 172 () => props.url,
165 173 () => {
166   - initializePlayer();
  174 + initializePlayer()
167 175 }
168   -);
  176 +)
169 177
170 178 defineExpose({
171   - getPlayerInstance: () => unref(playerRef),
172   -});
  179 + getPlayerInstance: () => unref(playerRef)
  180 +})
173 181 </script>
174 182
175 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 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 1 <template>
2 2 <div>
3 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 12 </n-spin>
8 13 </div>
9 14 </template>
10 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 24 const props = defineProps({
20 25 chartConfig: {
... ... @@ -25,9 +30,11 @@ const props = defineProps({
25 30
26 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 39 const option = shallowReactive({
33 40 dataset: configOption.dataset,
... ... @@ -36,36 +43,55 @@ const option = shallowReactive({
36 43 })
37 44
38 45 const sourceUrl = ref('')
39   -const playType = ref<StreamType>()
40 46
  47 +const playType = ref<StreamType>()
41 48
42 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 59 id: id,
47 60 accessMode: accessMode,
48 61 playProtocol: playProtocol,
49 62 videoUrl: customUrl,
50 63 params: {
51 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 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 95 </script>
70 96
71 97 <style lang="scss" scoped>
... ...