libpag 4.5.42 → 4.5.47
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/LICENSE.txt +1 -1
- package/lib/libpag.cjs.js +322 -601
- package/lib/libpag.cjs.js.map +1 -1
- package/lib/libpag.esm.js +322 -601
- package/lib/libpag.esm.js.map +1 -1
- package/lib/libpag.min.js +1 -1
- package/lib/libpag.min.js.map +1 -1
- package/lib/libpag.umd.js +322 -601
- package/lib/libpag.umd.js.map +1 -1
- package/lib/libpag.wasm +0 -0
- package/package.json +1 -1
- package/src/.pag.wasm-mt.md5 +1 -1
- package/src/.pag.wasm.md5 +1 -1
- package/src/core/global-canvas.ts +2 -2
- package/src/core/render-canvas.ts +3 -3
- package/src/core/video-reader.ts +62 -26
- package/src/pag-player.ts +2 -2
- package/src/pag-view.ts +1 -1
- package/src/types.ts +1 -1
- package/src/video-reader-manager.ts +2 -1
- package/src/wasm/libpag.js +1 -1
- package/src/wasm/libpag.wasm +0 -0
- package/src/wasm-mt/libpag.js +1 -1
- package/src/wasm-mt/libpag.wasm +0 -0
- package/src/wechat/pag-view.ts +4 -1
- package/src/wechat/video-reader.ts +19 -6
- package/types/third_party/tgfx/web/src/core/scaler-context.d.ts +6 -0
- package/types/third_party/tgfx/web/src/tgfx.d.ts +3 -2
- package/types/third_party/tgfx/web/src/types.d.ts +6 -0
- package/types/web/src/core/video-reader.d.ts +2 -0
package/lib/libpag.wasm
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/.pag.wasm-mt.md5
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
94f0d9f82511857dbdad5cdd092e89ae
|
package/src/.pag.wasm.md5
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
39a2addcc9a57c9119f9f44de6d33114
|
|
@@ -18,8 +18,8 @@ export class GlobalCanvas {
|
|
|
18
18
|
this._canvas.width = this.width;
|
|
19
19
|
this._canvas.height = this.height;
|
|
20
20
|
|
|
21
|
-
const gl = this._canvas.getContext('
|
|
22
|
-
if (!gl) throw new Error('Canvas context is not
|
|
21
|
+
const gl = this._canvas.getContext('webgl2', WEBGL_CONTEXT_ATTRIBUTES) as WebGL2RenderingContext;
|
|
22
|
+
if (!gl) throw new Error('Canvas context is not WebGL2!');
|
|
23
23
|
this._glContext = BackendContext.from(gl);
|
|
24
24
|
}
|
|
25
25
|
this.retainCount += 1;
|
|
@@ -18,11 +18,11 @@ export class RenderCanvas {
|
|
|
18
18
|
|
|
19
19
|
public constructor(canvas: HTMLCanvasElement | OffscreenCanvas, contextAttributes?: WebGLContextAttributes) {
|
|
20
20
|
this._canvas = canvas;
|
|
21
|
-
const gl = canvas.getContext('
|
|
21
|
+
const gl = canvas.getContext('webgl2', {
|
|
22
22
|
...WEBGL_CONTEXT_ATTRIBUTES,
|
|
23
23
|
...contextAttributes,
|
|
24
|
-
}) as
|
|
25
|
-
if (!gl) throw new Error('Canvas context is not
|
|
24
|
+
}) as WebGL2RenderingContext;
|
|
25
|
+
if (!gl) throw new Error('Canvas context is not WebGL2!');
|
|
26
26
|
this._glContext = BackendContext.from(gl);
|
|
27
27
|
}
|
|
28
28
|
|
package/src/core/video-reader.ts
CHANGED
|
@@ -77,6 +77,7 @@ export class VideoReader {
|
|
|
77
77
|
private bitmapCtx: OffscreenCanvasRenderingContext2D | null = null;
|
|
78
78
|
private currentFrame = -1;
|
|
79
79
|
private targetFrame = -1;
|
|
80
|
+
private visibilityHandle: (() => void) | null = null;
|
|
80
81
|
|
|
81
82
|
public constructor(
|
|
82
83
|
source: Uint8Array | HTMLVideoElement,
|
|
@@ -118,10 +119,10 @@ export class VideoReader {
|
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
public async prepare(targetFrame: number, playbackRate: number): Promise<void> {
|
|
121
|
-
if (targetFrame === this.currentFrame) {
|
|
122
|
+
if (this.isDestroyed || targetFrame === this.currentFrame) {
|
|
122
123
|
return;
|
|
123
124
|
}
|
|
124
|
-
const promise = new Promise<void>(async (resolve
|
|
125
|
+
const promise = new Promise<void>(async (resolve) => {
|
|
125
126
|
this.setError(null); // reset error
|
|
126
127
|
this.isSought = false; // reset seek status
|
|
127
128
|
const {currentTime} = this.videoEl!;
|
|
@@ -137,12 +138,14 @@ export class VideoReader {
|
|
|
137
138
|
} catch (e) {
|
|
138
139
|
this.setError(e);
|
|
139
140
|
this.currentFrame = targetFrame;
|
|
140
|
-
|
|
141
|
+
resolve();
|
|
141
142
|
return;
|
|
142
143
|
}
|
|
143
144
|
await new Promise<void>((resolveInner) => {
|
|
144
145
|
requestAnimationFrame(() => {
|
|
145
|
-
this.
|
|
146
|
+
if (!this.isDestroyed) {
|
|
147
|
+
this.pause();
|
|
148
|
+
}
|
|
146
149
|
resolveInner();
|
|
147
150
|
});
|
|
148
151
|
});
|
|
@@ -154,7 +157,7 @@ export class VideoReader {
|
|
|
154
157
|
// Static frame
|
|
155
158
|
await this.seek(targetTime, false);
|
|
156
159
|
this.currentFrame = targetFrame;
|
|
157
|
-
resolve();
|
|
160
|
+
resolve();
|
|
158
161
|
return;
|
|
159
162
|
} else if ((Math.abs(currentTime - targetTime) < (1 / this.frameRate) * VIDEO_DECODE_WAIT_FRAME) && !this.videoEl!.paused) {
|
|
160
163
|
// Within tolerable frame rate deviation
|
|
@@ -168,18 +171,22 @@ export class VideoReader {
|
|
|
168
171
|
}
|
|
169
172
|
}
|
|
170
173
|
|
|
174
|
+
if (this.isDestroyed || !this.videoEl) {
|
|
175
|
+
resolve();
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
171
178
|
const targetPlaybackRate = Math.min(Math.max(playbackRate, VIDEO_PLAYBACK_RATE_MIN), VIDEO_PLAYBACK_RATE_MAX);
|
|
172
|
-
if (!this.disablePlaybackRate && this.videoEl
|
|
173
|
-
this.videoEl
|
|
179
|
+
if (!this.disablePlaybackRate && this.videoEl.playbackRate !== targetPlaybackRate) {
|
|
180
|
+
this.videoEl.playbackRate = targetPlaybackRate;
|
|
174
181
|
}
|
|
175
182
|
|
|
176
|
-
if (this.isPlaying && this.videoEl
|
|
183
|
+
if (this.isPlaying && this.videoEl.paused) {
|
|
177
184
|
try {
|
|
178
185
|
await this.play();
|
|
179
186
|
} catch (e) {
|
|
180
187
|
this.setError(e);
|
|
181
188
|
this.currentFrame = targetFrame;
|
|
182
|
-
|
|
189
|
+
resolve();
|
|
183
190
|
return;
|
|
184
191
|
}
|
|
185
192
|
}
|
|
@@ -203,13 +210,19 @@ export class VideoReader {
|
|
|
203
210
|
await getWechatNetwork();
|
|
204
211
|
}
|
|
205
212
|
if (document.visibilityState !== 'visible') {
|
|
206
|
-
|
|
213
|
+
// Page is hidden, defer video play until visible
|
|
214
|
+
this.clearVisibilityListener();
|
|
215
|
+
this.visibilityHandle = () => {
|
|
216
|
+
if (this.isDestroyed) {
|
|
217
|
+
this.clearVisibilityListener();
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
207
220
|
if (document.visibilityState === 'visible') {
|
|
208
|
-
if (this.videoEl) this.videoEl.play();
|
|
209
|
-
|
|
221
|
+
if (this.videoEl) this.videoEl.play().catch((e) => { this.setError(e); });
|
|
222
|
+
this.clearVisibilityListener();
|
|
210
223
|
}
|
|
211
224
|
};
|
|
212
|
-
window.addEventListener('visibilitychange', visibilityHandle);
|
|
225
|
+
window.addEventListener('visibilitychange', this.visibilityHandle);
|
|
213
226
|
throw new Error('The play() request was interrupted because the document was hidden!');
|
|
214
227
|
}
|
|
215
228
|
await this.videoEl?.play();
|
|
@@ -233,16 +246,19 @@ export class VideoReader {
|
|
|
233
246
|
}
|
|
234
247
|
|
|
235
248
|
public onDestroy() {
|
|
249
|
+
this.isDestroyed = true;
|
|
236
250
|
if (this.player) {
|
|
237
251
|
this.player.unlinkVideoReader(this);
|
|
238
252
|
this.player = null;
|
|
239
253
|
}
|
|
254
|
+
this.clearVisibilityListener();
|
|
240
255
|
removeAllListeners(this.videoEl!, 'playing');
|
|
241
256
|
removeAllListeners(this.videoEl!, 'timeupdate');
|
|
257
|
+
removeAllListeners(this.videoEl!, 'seeked');
|
|
258
|
+
removeAllListeners(this.videoEl!, 'canplay');
|
|
242
259
|
this.videoEl = null;
|
|
243
260
|
this.bitmapCanvas = null;
|
|
244
261
|
this.bitmapCtx = null;
|
|
245
|
-
this.isDestroyed = true;
|
|
246
262
|
}
|
|
247
263
|
|
|
248
264
|
private seek(targetTime: number, play = true) {
|
|
@@ -253,34 +269,47 @@ export class VideoReader {
|
|
|
253
269
|
}
|
|
254
270
|
|
|
255
271
|
const onSeeked = () => {
|
|
256
|
-
|
|
272
|
+
if (!this.videoEl || this.isDestroyed) {
|
|
273
|
+
clearTimeout(seekTimeout);
|
|
274
|
+
resolve();
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
removeListener(this.videoEl, 'seeked', onSeeked);
|
|
257
278
|
clearTimeout(seekTimeout);
|
|
258
279
|
if (play) {
|
|
259
280
|
// After seeking, the video might still be in 'ended' state
|
|
260
281
|
// Reset it by setting currentTime to itself to clear the ended flag
|
|
261
|
-
if (this.videoEl
|
|
262
|
-
this.videoEl
|
|
282
|
+
if (this.videoEl.ended) {
|
|
283
|
+
this.videoEl.currentTime = this.videoEl.currentTime;
|
|
263
284
|
}
|
|
264
|
-
this.videoEl
|
|
285
|
+
this.videoEl.play().catch((e) => {
|
|
265
286
|
this.setError(e);
|
|
266
287
|
});
|
|
267
|
-
} else if (!play && !this.videoEl
|
|
268
|
-
this.videoEl
|
|
288
|
+
} else if (!play && !this.videoEl.paused) {
|
|
289
|
+
this.videoEl.pause();
|
|
269
290
|
}
|
|
270
291
|
resolve();
|
|
271
292
|
};
|
|
272
293
|
|
|
273
294
|
const onCanPlay = () => {
|
|
274
|
-
|
|
295
|
+
if (!this.videoEl || this.isDestroyed) {
|
|
296
|
+
clearTimeout(seekTimeout);
|
|
297
|
+
resolve();
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
removeListener(this.videoEl, 'canplay', onCanPlay);
|
|
275
301
|
// Now that we have enough data, perform the seek.
|
|
276
|
-
this.videoEl
|
|
277
|
-
addListener(this.videoEl
|
|
302
|
+
this.videoEl.currentTime = targetTime;
|
|
303
|
+
addListener(this.videoEl, 'seeked', onSeeked);
|
|
278
304
|
};
|
|
279
305
|
|
|
280
306
|
const seekTimeout = setTimeout(() => {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
307
|
+
if (this.videoEl) {
|
|
308
|
+
removeListener(this.videoEl, 'canplay', onCanPlay);
|
|
309
|
+
removeListener(this.videoEl, 'seeked', onSeeked);
|
|
310
|
+
}
|
|
311
|
+
this.setError('Seek operation timed out.');
|
|
312
|
+
resolve();
|
|
284
313
|
}, (1000 / this.frameRate) * VIDEO_DECODE_SEEK_TIMEOUT_FRAME);
|
|
285
314
|
|
|
286
315
|
// Check if we need to wait for 'canplay' event before seeking.
|
|
@@ -298,6 +327,13 @@ export class VideoReader {
|
|
|
298
327
|
this.error = e;
|
|
299
328
|
}
|
|
300
329
|
|
|
330
|
+
private clearVisibilityListener() {
|
|
331
|
+
if (this.visibilityHandle) {
|
|
332
|
+
window.removeEventListener('visibilitychange', this.visibilityHandle);
|
|
333
|
+
this.visibilityHandle = null;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
301
337
|
private linkPlayer(player: PAGPlayer | null) {
|
|
302
338
|
this.player = player;
|
|
303
339
|
if (player) {
|
package/src/pag-player.ts
CHANGED
|
@@ -64,11 +64,11 @@ export class PAGPlayer {
|
|
|
64
64
|
callback(res);
|
|
65
65
|
return res;
|
|
66
66
|
}, this.wasmIns);
|
|
67
|
-
//
|
|
67
|
+
// Log video reader errors but don't throw - video decoding failures shouldn't stop playback
|
|
68
68
|
for (const videoReader of this.videoReaders) {
|
|
69
69
|
const error = await videoReader.getError();
|
|
70
70
|
if (error !== null) {
|
|
71
|
-
|
|
71
|
+
console.warn('[PAGPlayer] VideoReader error:', error);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
return res;
|
package/src/pag-view.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -104,6 +104,7 @@ export class VideoReaderManager {
|
|
|
104
104
|
*/
|
|
105
105
|
public async prepareTargetFrame() {
|
|
106
106
|
for (const id of this.videoIDs) {
|
|
107
|
+
if (this.isDestroyed) return;
|
|
107
108
|
// Get the target frame index from WASM
|
|
108
109
|
const targetFrame = this.wasmIns._getTargetFrameByID(id) as number;
|
|
109
110
|
if (targetFrame < 0) {
|
|
@@ -137,4 +138,4 @@ export class VideoReaderManager {
|
|
|
137
138
|
this.isDestroyed = true;
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
}
|
|
141
|
+
}
|