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