video-player-dd 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/base.css ADDED
@@ -0,0 +1,13 @@
1
+ .wcs-wwav-base-video {
2
+ height: 100%;
3
+ width: 100%;
4
+ background: #333333;
5
+ position: relative;
6
+ }
7
+
8
+ .wcs-wwav-base-video__content {
9
+ width: 100%;
10
+ height: 100%;
11
+ cursor: pointer;
12
+ object-fit: fill;
13
+ }
package/index.ts ADDED
@@ -0,0 +1,274 @@
1
+ import elementResizeDetectorMaker, { type Erd } from 'element-resize-detector'
2
+ import type { VideoMedia, CanvasMedia, Video, PlayerOptions } from './type';
3
+ import WWAVPlayer from '@vanwei-wcs/wwav-player'
4
+ import { VideoStatus } from './utils/index'
5
+ import useVideo from './video.ts'
6
+ import { emitter } from './utils/mitter.ts'
7
+ import useStream from './stream.ts'
8
+ import './base.css'
9
+
10
+ class Base {
11
+ #options: any;
12
+ #erd: Erd = elementResizeDetectorMaker();
13
+ #videoMedia: VideoMedia = null;
14
+ #canvasMedia: CanvasMedia = null;
15
+ #domForceType: string = 'both';
16
+ #baseDiv: HTMLElement;
17
+ #baseDivHeight: number = 0;
18
+ #baseDivWidth: number = 0;
19
+ #domCreated = false;
20
+ #streamType = 'H264';
21
+ #isClosedVideo = true;
22
+ #playerCreated = false;
23
+ #isLive = true;
24
+ #videoMediaDelay: number = 0.8
25
+ #videoMediaPlaySpeed: number = 1
26
+ player: WWAVPlayer | null = null
27
+ attachedElement: boolean = false
28
+
29
+
30
+ constructor({ target, options, isLive = true }: { target: string | HTMLElement; options?: any; isLive?: boolean }) {
31
+ this.#baseDiv = document.createElement('div');
32
+ this.#baseDiv.classList.add('wcs-wwav-base-video')
33
+ this.#options = options;
34
+ this.#isLive = isLive
35
+
36
+ const parent = typeof target === 'string'
37
+ ? document.querySelector(target)
38
+ : target;
39
+
40
+ if (!parent) throw new Error('挂载目标未找到');
41
+ parent.appendChild(this.#baseDiv);
42
+ this.#erd.listenTo(this.#baseDiv, (el) => this.#mediaResize(el));
43
+
44
+ !this.#domCreated && this.#createMediaDom()
45
+ }
46
+
47
+ #createMediaDom = () => {
48
+ if (!this.#canvasMedia) {
49
+ if (this.#domForceType !== 'canvas') {
50
+ this.#videoMedia = document.createElement('video')
51
+ this.#videoMedia.autoplay = true
52
+ this.#videoMedia.classList.add('wcs-wwav-base-video__content')
53
+ // TODO 视频监听
54
+ this.#addVideoListener()
55
+ this.#baseDiv.appendChild(this.#videoMedia)
56
+ }
57
+ if (this.#domForceType !== 'video') {
58
+ this.#canvasMedia = document.createElement('canvas')
59
+ this.#canvasMedia.width = this.#baseDivWidth
60
+ this.#canvasMedia.height = this.#baseDivHeight
61
+ if (this.#domForceType === 'canvas') {
62
+ this.#canvasMedia.setAttribute('style', 'display: block ')
63
+ }
64
+ this.#baseDiv.appendChild(this.#canvasMedia)
65
+ }
66
+ this.#domCreated = true
67
+ } else {
68
+ this.#domCreated = false
69
+ }
70
+ }
71
+
72
+ #clearMediaDom = () => {
73
+ if (!this.player) return
74
+ if (this.#baseDiv) {
75
+ if (this.#videoMedia) {
76
+ this.#removeVideoListener()
77
+ this.#videoMedia.remove()
78
+ }
79
+ if (this.#canvasMedia) {
80
+ this.#canvasMedia.remove()
81
+ }
82
+
83
+ this.#videoMedia = null
84
+ this.#canvasMedia = null
85
+ // 不能使用 this.mediaBox.innerHTML = '', 这会导致 this.Erd.uninstall(this.mediaBox) 报错
86
+ }
87
+ this.#domCreated = false
88
+ }
89
+
90
+ #mediaResize(el: HTMLElement) {
91
+ const { offsetWidth, offsetHeight } = el
92
+ this.#baseDivWidth = offsetWidth
93
+ this.#baseDivHeight = offsetHeight
94
+ if (this.#canvasMedia) {
95
+ this.#canvasMedia.width = offsetWidth
96
+ this.#canvasMedia.height = offsetHeight
97
+ }
98
+ }
99
+
100
+ #changeDomDisplay() {
101
+ if (this.#streamType) {
102
+ if (this.#streamType === 'H265' || this.#streamType === 'h265') {
103
+ this.#videoMedia && (this.#videoMedia.style.display = 'none')
104
+ this.#canvasMedia && (this.#canvasMedia.style.display = 'block')
105
+ } else {
106
+ this.#videoMedia && (this.#videoMedia.style.display = 'block')
107
+ this.#canvasMedia && (this.#canvasMedia.style.display = 'none')
108
+ }
109
+ }
110
+ }
111
+
112
+ // TODO 待做
113
+ #addVideoListener() {
114
+ this.#videoMedia?.addEventListener('progress', this.#videoProgressEvent)
115
+ }
116
+ #removeVideoListener() {
117
+ this.#videoMedia?.removeEventListener('progress', this.#videoProgressEvent)
118
+ }
119
+
120
+ #videoProgressEvent(e) {
121
+ if (this.#videoMedia) {
122
+ // 动态调整播放速率
123
+ if (this.#isLive) {
124
+ const buffered = this.#videoMedia.buffered
125
+ // 动态调整播放速率
126
+ if (buffered.length > 0) {
127
+ const end = buffered.end(0)
128
+ const delay = parseFloat(this.#videoMediaDelay * this.#videoMediaPlaySpeed)
129
+ const diff = +(end - this.#videoMedia.currentTime).toFixed(3)
130
+
131
+ let newPlaySpeedRate = 1
132
+ if (diff < (delay / 2)) {
133
+ newPlaySpeedRate = 0.8 // 可根据情况修改 建议 0.4 - 0.8 之间
134
+ } else if (diff < delay) {
135
+ newPlaySpeedRate = 1
136
+ } else if (diff < delay * 2) {
137
+ newPlaySpeedRate = 1.2
138
+ } else if (diff < delay * 3) {
139
+ newPlaySpeedRate = 1.4
140
+ } else {
141
+ this.#videoMedia.currentTime = end - delay * 0.8
142
+ newPlaySpeedRate = 1
143
+ // this._log('set current time', end - delay * 0.8)
144
+ }
145
+ const playbackRate = (this.#videoMediaPlaySpeed * newPlaySpeedRate > 16) ? 16 : (this.#videoMediaPlaySpeed * newPlaySpeedRate)
146
+ this.#videoMedia.playbackRate = playbackRate
147
+ // this._log(`progress: new playbackrate:${playbackRate}`)
148
+ // this._log(`progress: currentTime:${this.videoMedia.currentTime}, cachedTime:${end}, diff:${diff}`)
149
+ }
150
+ }
151
+ }
152
+ }
153
+
154
+ #createPlayer(videoMedia: VideoMedia, canvasMedia: CanvasMedia, options: PlayerOptions) {
155
+ // 这里debug 为 true 会打印出播放器的日志 目前所有日志都没打印
156
+ const _player = this.player = new WWAVPlayer({ debug: !!options?.debug })
157
+ _player.on('error', this.onPlayerError)
158
+ _player.on('media_info', this.onPlayerMediaInfo)
159
+ _player.on('play', this.onPlayerPlay)
160
+ _player.on('pause', this.onPlayerPause)
161
+ _player.onStream = (info: any) => useStream.onStream(info, this.#streamType)
162
+
163
+ this.#playerCreated = true
164
+
165
+ !this.attachedElement && this.attachElement(videoMedia, canvasMedia, options)
166
+ }
167
+
168
+ closeVideo() {
169
+ this.#destroyPlayer()
170
+ this.#clearMediaDom()
171
+ }
172
+
173
+ async openVideo(video: Video) {
174
+ if (!this.#isClosedVideo) {
175
+ await this.closeVideo()
176
+ }
177
+ if (!this.#domCreated) {
178
+ await this.#createMediaDom()
179
+ }
180
+ if (!this.#playerCreated) {
181
+ await this.#createPlayer(this.#videoMedia, this.#canvasMedia, this.#options)
182
+ }
183
+
184
+ this.#isClosedVideo = false
185
+ useVideo.emitVideoStatus(VideoStatus.vPause)
186
+ if (this.player) {
187
+ this.player?.open(video.url, video.token)
188
+ useStream.startStaticsInfoTimer()
189
+ }
190
+ }
191
+
192
+ controlSound(muted: boolean) {
193
+ this.player && this.player.muted(muted)
194
+ }
195
+
196
+ openTalk(talk: Video) {
197
+ this.player?.openTalk(talk.url, talk.token)
198
+ }
199
+
200
+ closeTalk() {
201
+ this.player && this.player.closeTalk()
202
+ }
203
+
204
+ attachElement(videoMedia: VideoMedia, canvasMedia: CanvasMedia, options: PlayerOptions) {
205
+ if (this.player && !this.attachedElement) {
206
+ if (videoMedia) {
207
+ this.player.attachVideoElement(videoMedia, options)
208
+ }
209
+ if (canvasMedia) {
210
+ this.player.attachCanvasElement(canvasMedia, options)
211
+ }
212
+
213
+ this.attachedElement = true
214
+ }
215
+ this.attachedElement = false
216
+ }
217
+
218
+ onPlayerError(e: any, detail: any, info: any) {
219
+
220
+ const data = {
221
+ reply: info.code,
222
+ message: info.message
223
+ }
224
+ emitter.emit('playerError', data)
225
+ }
226
+
227
+ async onPlayerMediaInfo(info: any) {
228
+ emitter.emit('media-info', info)
229
+ this.#streamType = info.format
230
+ setTimeout(() => {
231
+ this.#changeDomDisplay()
232
+ }, 0)
233
+ }
234
+
235
+ // 2 播放 3 暂停
236
+ changePlayStatus(status: number) {
237
+ if (!this.player) return
238
+
239
+ if (status === VideoStatus.vPlay) {
240
+ this.player.play()
241
+ } else if (status === VideoStatus.vPause) {
242
+ this.player.pause()
243
+ }
244
+ }
245
+
246
+ async #destroyPlayer() {
247
+ useStream.clearStaticsInfoTimer()
248
+ if (this.player) {
249
+ await this.player.destroy()
250
+ }
251
+ this.player = null
252
+ this.#playerCreated = false
253
+ this.attachedElement = false
254
+ }
255
+
256
+ onPlayerPlay() {
257
+ useVideo.emitVideoStatus(VideoStatus.vPlay)
258
+ }
259
+
260
+ onPlayerPause() {
261
+ useVideo.emitVideoStatus(VideoStatus.vPause)
262
+ }
263
+
264
+ async capture() {
265
+ const videoMedia = this.#videoMedia
266
+ const canvasMedia = this.#canvasMedia
267
+ const info = await useVideo.capture(videoMedia, canvasMedia, this.#streamType)
268
+ emitter.emit('playerError', info)
269
+ }
270
+ }
271
+
272
+ export const baseEmit = emitter
273
+
274
+ export default Base