jassub 1.7.17 → 1.7.18
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/dist/COPYRIGHT +951 -951
- package/dist/jassub-worker.js +15 -15
- package/dist/jassub-worker.wasm.js +1 -1
- package/dist/jassub.es.js +83 -87
- package/dist/jassub.umd.js +1 -1
- package/index.d.ts +5 -2
- package/package.json +1 -1
- package/src/jassub.js +13 -5
package/dist/jassub.es.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
typeof HTMLVideoElement < "u" && !("requestVideoFrameCallback" in HTMLVideoElement.prototype) && "getVideoPlaybackQuality" in HTMLVideoElement.prototype && (HTMLVideoElement.prototype._rvfcpolyfillmap = {}, HTMLVideoElement.prototype.requestVideoFrameCallback = function(c) {
|
|
2
|
-
const e = performance.now(), t = this.getVideoPlaybackQuality(), s = this.mozPresentedFrames || this.mozPaintedFrames || t.totalVideoFrames - t.droppedVideoFrames,
|
|
3
|
-
const
|
|
4
|
-
if (
|
|
5
|
-
const d = this.mozFrameDelay ||
|
|
2
|
+
const e = performance.now(), t = this.getVideoPlaybackQuality(), s = this.mozPresentedFrames || this.mozPaintedFrames || t.totalVideoFrames - t.droppedVideoFrames, i = (n, r) => {
|
|
3
|
+
const a = this.getVideoPlaybackQuality(), h = this.mozPresentedFrames || this.mozPaintedFrames || a.totalVideoFrames - a.droppedVideoFrames;
|
|
4
|
+
if (h > s) {
|
|
5
|
+
const d = this.mozFrameDelay || a.totalFrameDelay - t.totalFrameDelay || 0, l = r - n;
|
|
6
6
|
c(r, {
|
|
7
7
|
presentationTime: r + d * 1e3,
|
|
8
8
|
expectedDisplayTime: r + l,
|
|
9
9
|
width: this.videoWidth,
|
|
10
10
|
height: this.videoHeight,
|
|
11
11
|
mediaTime: Math.max(0, this.currentTime || 0) + l / 1e3,
|
|
12
|
-
presentedFrames:
|
|
12
|
+
presentedFrames: h,
|
|
13
13
|
processingDuration: d
|
|
14
14
|
}), delete this._rvfcpolyfillmap[e];
|
|
15
15
|
} else
|
|
16
|
-
this._rvfcpolyfillmap[e] = requestAnimationFrame((d) =>
|
|
16
|
+
this._rvfcpolyfillmap[e] = requestAnimationFrame((d) => i(r, d));
|
|
17
17
|
};
|
|
18
|
-
return this._rvfcpolyfillmap[e] = requestAnimationFrame((n) =>
|
|
18
|
+
return this._rvfcpolyfillmap[e] = requestAnimationFrame((n) => i(e, n)), e;
|
|
19
19
|
}, HTMLVideoElement.prototype.cancelVideoFrameCallback = function(c) {
|
|
20
20
|
cancelAnimationFrame(this._rvfcpolyfillmap[c]), delete this._rvfcpolyfillmap[c];
|
|
21
21
|
});
|
|
@@ -42,7 +42,7 @@ const _ = {
|
|
|
42
42
|
BT601: "0.913 0.0774 0.0096 0 0 -0.1051 1.1508 -0.0456 0 0 0.0063 0.0207 0.973"
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
|
-
class
|
|
45
|
+
class o extends EventTarget {
|
|
46
46
|
/**
|
|
47
47
|
* @param {Object} options Settings object.
|
|
48
48
|
* @param {HTMLVideoElement} options.video Video to use as target for rendering and event listeners. Optional if canvas is specified instead.
|
|
@@ -73,26 +73,23 @@ class h extends EventTarget {
|
|
|
73
73
|
* @param {Number} [options.libassGlyphLimit] libass glyph cache memory limit in MiB (approximate).
|
|
74
74
|
*/
|
|
75
75
|
constructor(e) {
|
|
76
|
-
if (super(), !globalThis.Worker)
|
|
77
|
-
|
|
78
|
-
if (!e)
|
|
79
|
-
throw this.destroy("No options provided");
|
|
76
|
+
if (super(), !globalThis.Worker) throw this.destroy("Worker not supported");
|
|
77
|
+
if (!e) throw this.destroy("No options provided");
|
|
80
78
|
this._loaded = /** @type {Promise<void>} */
|
|
81
79
|
new Promise((s) => {
|
|
82
80
|
this._init = s;
|
|
83
81
|
});
|
|
84
|
-
const t =
|
|
82
|
+
const t = o._test();
|
|
85
83
|
if (this._onDemandRender = "requestVideoFrameCallback" in HTMLVideoElement.prototype && (e.onDemandRender ?? !0), this._offscreenRender = "transferControlToOffscreen" in HTMLCanvasElement.prototype && !e.canvas && (e.offscreenRender ?? !0), this.timeOffset = e.timeOffset || 0, this._video = e.video, this._videoHeight = 0, this._videoWidth = 0, this._videoColorSpace = null, this._canvas = e.canvas, this._video && !this._canvas)
|
|
86
84
|
this._canvasParent = document.createElement("div"), this._canvasParent.className = "JASSUB", this._canvasParent.style.position = "relative", this._canvas = this._createCanvas(), this._video.insertAdjacentElement("afterend", this._canvasParent);
|
|
87
85
|
else if (!this._canvas)
|
|
88
86
|
throw this.destroy("Don't know where to render: you should give video or canvas in options.");
|
|
89
|
-
if (this._bufferCanvas = document.createElement("canvas"), this._bufferCtx = this._bufferCanvas.getContext("2d"), !this._bufferCtx)
|
|
90
|
-
throw this.destroy("Canvas rendering not supported");
|
|
87
|
+
if (this._bufferCanvas = document.createElement("canvas"), this._bufferCtx = this._bufferCanvas.getContext("2d"), !this._bufferCtx) throw this.destroy("Canvas rendering not supported");
|
|
91
88
|
this._canvasctrl = this._offscreenRender ? this._canvas.transferControlToOffscreen() : this._canvas, this._ctx = !this._offscreenRender && this._canvasctrl.getContext("2d"), this._lastRenderTime = 0, this.debug = !!e.debug, this.prescaleFactor = e.prescaleFactor || 1, this.prescaleHeightLimit = e.prescaleHeightLimit || 1080, this.maxRenderHeight = e.maxRenderHeight || 0, this._boundResize = this.resize.bind(this), this._boundTimeUpdate = this._timeupdate.bind(this), this._boundSetRate = this.setRate.bind(this), this._boundUpdateColorSpace = this._updateColorSpace.bind(this), this._video && this.setVideo(e.video), this._onDemandRender && (this.busy = !1, this._lastDemandTime = null), this._worker = new Worker(e.workerUrl || "jassub-worker.js"), this._worker.onmessage = (s) => this._onmessage(s), this._worker.onerror = (s) => this._error(s), t.then(() => {
|
|
92
89
|
this._worker.postMessage({
|
|
93
90
|
target: "init",
|
|
94
|
-
wasmUrl:
|
|
95
|
-
legacyWasmUrl: e.legacyWasmUrl
|
|
91
|
+
wasmUrl: o._supportsSIMD && e.modernWasmUrl ? e.modernWasmUrl : e.wasmUrl ?? "jassub-worker.wasm",
|
|
92
|
+
legacyWasmUrl: e.legacyWasmUrl ?? "jassub-worker.wasm.js",
|
|
96
93
|
asyncRender: typeof createImageBitmap < "u" && (e.asyncRender ?? !0),
|
|
97
94
|
onDemandRender: this._onDemandRender,
|
|
98
95
|
width: this._canvasctrl.width || 0,
|
|
@@ -111,7 +108,7 @@ class h extends EventTarget {
|
|
|
111
108
|
libassGlyphLimit: e.libassGlyphLimit || 0,
|
|
112
109
|
// @ts-ignore
|
|
113
110
|
useLocalFonts: typeof queryLocalFonts < "u" && (e.useLocalFonts ?? !0),
|
|
114
|
-
hasBitmapBug:
|
|
111
|
+
hasBitmapBug: o._hasBitmapBug
|
|
115
112
|
}), this._offscreenRender === !0 && this.sendMessage("offscreenCanvas", null, [this._canvasctrl]);
|
|
116
113
|
});
|
|
117
114
|
}
|
|
@@ -125,47 +122,50 @@ class h extends EventTarget {
|
|
|
125
122
|
static _hasAlphaBug = null;
|
|
126
123
|
/** @type {boolean|null} */
|
|
127
124
|
static _hasBitmapBug = null;
|
|
128
|
-
static
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
125
|
+
static _testSIMD() {
|
|
126
|
+
if (o._supportsSIMD === null)
|
|
127
|
+
try {
|
|
128
|
+
o._supportsSIMD = WebAssembly.validate(Uint8Array.of(0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123, 3, 2, 1, 0, 10, 10, 1, 8, 0, 65, 0, 253, 15, 253, 98, 11));
|
|
129
|
+
} catch {
|
|
130
|
+
o._supportsSIMD = !1;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
static async _testImageBugs() {
|
|
134
|
+
if (o._hasBitmapBug !== null) return;
|
|
136
135
|
const e = document.createElement("canvas"), t = e.getContext("2d", { willReadFrequently: !0 });
|
|
137
|
-
if (!t)
|
|
138
|
-
throw new Error("Canvas rendering not supported");
|
|
136
|
+
if (!t) throw new Error("Canvas rendering not supported");
|
|
139
137
|
if (typeof ImageData.prototype.constructor == "function")
|
|
140
138
|
try {
|
|
141
139
|
new ImageData(new Uint8ClampedArray([0, 0, 0, 0]), 1, 1);
|
|
142
140
|
} catch {
|
|
143
|
-
console.log("Detected that ImageData is not constructable despite browser saying so"), self.ImageData = function(
|
|
141
|
+
console.log("Detected that ImageData is not constructable despite browser saying so"), self.ImageData = function(h, d, l) {
|
|
144
142
|
const m = t.createImageData(d, l);
|
|
145
|
-
return
|
|
143
|
+
return h && m.data.set(h), m;
|
|
146
144
|
};
|
|
147
145
|
}
|
|
148
|
-
const s = document.createElement("canvas"),
|
|
149
|
-
if (!
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
h.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
h._hasBitmapBug = !0, console.log("Detected a browser having issue with partial bitmaps, applying workaround");
|
|
146
|
+
const s = document.createElement("canvas"), i = s.getContext("2d", { willReadFrequently: !0 });
|
|
147
|
+
if (!i) throw new Error("Canvas rendering not supported");
|
|
148
|
+
e.width = s.width = 1, e.height = s.height = 1, t.clearRect(0, 0, 1, 1), i.clearRect(0, 0, 1, 1);
|
|
149
|
+
const n = i.getImageData(0, 0, 1, 1).data;
|
|
150
|
+
t.putImageData(new ImageData(new Uint8ClampedArray([0, 255, 0, 0]), 1, 1), 0, 0), i.drawImage(e, 0, 0);
|
|
151
|
+
const r = i.getImageData(0, 0, 1, 1).data;
|
|
152
|
+
if (o._hasAlphaBug = n[1] !== r[1], o._hasAlphaBug && console.log("Detected a browser having issue with transparent pixels, applying workaround"), typeof createImageBitmap < "u") {
|
|
153
|
+
const a = new Uint8ClampedArray([255, 0, 255, 0, 255]).subarray(1, 5);
|
|
154
|
+
i.drawImage(await createImageBitmap(new ImageData(a, 1)), 0, 0);
|
|
155
|
+
const { data: h } = i.getImageData(0, 0, 1, 1);
|
|
156
|
+
o._hasBitmapBug = !1;
|
|
157
|
+
for (const [d, l] of h.entries())
|
|
158
|
+
if (Math.abs(a[d] - l) > 15) {
|
|
159
|
+
o._hasBitmapBug = !0, console.log("Detected a browser having issue with partial bitmaps, applying workaround");
|
|
163
160
|
break;
|
|
164
161
|
}
|
|
165
162
|
} else
|
|
166
|
-
|
|
163
|
+
o._hasBitmapBug = !1;
|
|
167
164
|
e.remove(), s.remove();
|
|
168
165
|
}
|
|
166
|
+
static async _test() {
|
|
167
|
+
o._testSIMD(), await o._testImageBugs();
|
|
168
|
+
}
|
|
169
169
|
/**
|
|
170
170
|
* Resize the canvas to given parameters. Auto-generated if values are ommited.
|
|
171
171
|
* @param {Number} [width=0]
|
|
@@ -174,32 +174,32 @@ class h extends EventTarget {
|
|
|
174
174
|
* @param {Number} [left=0]
|
|
175
175
|
* @param {Boolean} [force=false]
|
|
176
176
|
*/
|
|
177
|
-
resize(e = 0, t = 0, s = 0,
|
|
177
|
+
resize(e = 0, t = 0, s = 0, i = 0, n = this._video?.paused) {
|
|
178
178
|
if ((!e || !t) && this._video) {
|
|
179
179
|
const r = this._getVideoPosition();
|
|
180
|
-
let
|
|
180
|
+
let a = null;
|
|
181
181
|
if (this._videoWidth) {
|
|
182
|
-
const
|
|
183
|
-
|
|
182
|
+
const h = this._video.videoWidth / this._videoWidth, d = this._video.videoHeight / this._videoHeight;
|
|
183
|
+
a = this._computeCanvasSize((r.width || 0) / h, (r.height || 0) / d);
|
|
184
184
|
} else
|
|
185
|
-
|
|
186
|
-
e =
|
|
185
|
+
a = this._computeCanvasSize(r.width || 0, r.height || 0);
|
|
186
|
+
e = a.width, t = a.height, this._canvasParent && (s = r.y - (this._canvasParent.getBoundingClientRect().top - this._video.getBoundingClientRect().top), i = r.x), this._canvas.style.width = r.width + "px", this._canvas.style.height = r.height + "px";
|
|
187
187
|
}
|
|
188
|
-
this._canvas.style.top = s + "px", this._canvas.style.left =
|
|
188
|
+
this._canvas.style.top = s + "px", this._canvas.style.left = i + "px", n && this.busy === !1 ? this.busy = !0 : n = !1, this.sendMessage("canvas", { width: e, height: t, force: n });
|
|
189
189
|
}
|
|
190
190
|
_getVideoPosition(e = this._video.videoWidth, t = this._video.videoHeight) {
|
|
191
|
-
const s = e / t, { offsetWidth:
|
|
192
|
-
e =
|
|
193
|
-
const
|
|
194
|
-
return { width: e, height: t, x:
|
|
191
|
+
const s = e / t, { offsetWidth: i, offsetHeight: n } = this._video, r = i / n;
|
|
192
|
+
e = i, t = n, r > s ? e = Math.floor(n * s) : t = Math.floor(i / s);
|
|
193
|
+
const a = (i - e) / 2, h = (n - t) / 2;
|
|
194
|
+
return { width: e, height: t, x: a, y: h };
|
|
195
195
|
}
|
|
196
196
|
_computeCanvasSize(e = 0, t = 0) {
|
|
197
|
-
const s = this.prescaleFactor <= 0 ? 1 : this.prescaleFactor,
|
|
198
|
-
if (e = e *
|
|
197
|
+
const s = this.prescaleFactor <= 0 ? 1 : this.prescaleFactor, i = self.devicePixelRatio || 1;
|
|
198
|
+
if (e = e * i, t = t * i, t <= 0 || e <= 0)
|
|
199
199
|
e = 0, t = 0;
|
|
200
200
|
else {
|
|
201
201
|
const n = s < 1 ? -1 : 1;
|
|
202
|
-
let r = t *
|
|
202
|
+
let r = t * i;
|
|
203
203
|
n * r * s <= n * this.prescaleHeightLimit ? r *= s : n * r < n * this.prescaleHeightLimit && (r = this.prescaleHeightLimit), this.maxRenderHeight > 0 && r > this.maxRenderHeight && (r = this.maxRenderHeight), e *= r / t, t = r;
|
|
204
204
|
}
|
|
205
205
|
return { width: e, height: t };
|
|
@@ -385,9 +385,9 @@ class h extends EventTarget {
|
|
|
385
385
|
_sendLocalFont(e) {
|
|
386
386
|
try {
|
|
387
387
|
queryLocalFonts().then((t) => {
|
|
388
|
-
const s = t?.find((
|
|
389
|
-
s && s.blob().then((
|
|
390
|
-
|
|
388
|
+
const s = t?.find((i) => i.fullName.toLowerCase() === e);
|
|
389
|
+
s && s.blob().then((i) => {
|
|
390
|
+
i.arrayBuffer().then((n) => {
|
|
391
391
|
this.addFont(new Uint8Array(n));
|
|
392
392
|
});
|
|
393
393
|
});
|
|
@@ -408,24 +408,21 @@ class h extends EventTarget {
|
|
|
408
408
|
_unbusy() {
|
|
409
409
|
this._lastDemandTime ? this._demandRender(this._lastDemandTime) : this.busy = !1;
|
|
410
410
|
}
|
|
411
|
-
_handleRVFC(e, { mediaTime: t, width: s, height:
|
|
412
|
-
if (this._destroyed)
|
|
413
|
-
|
|
414
|
-
this.busy ? this._lastDemandTime = { mediaTime: t, width: s, height: a } : (this.busy = !0, this._demandRender({ mediaTime: t, width: s, height: a })), this._video.requestVideoFrameCallback(this._handleRVFC.bind(this));
|
|
411
|
+
_handleRVFC(e, { mediaTime: t, width: s, height: i }) {
|
|
412
|
+
if (this._destroyed) return null;
|
|
413
|
+
this.busy ? this._lastDemandTime = { mediaTime: t, width: s, height: i } : (this.busy = !0, this._demandRender({ mediaTime: t, width: s, height: i })), this._video.requestVideoFrameCallback(this._handleRVFC.bind(this));
|
|
415
414
|
}
|
|
416
415
|
_demandRender({ mediaTime: e, width: t, height: s }) {
|
|
417
416
|
this._lastDemandTime = null, (t !== this._videoWidth || s !== this._videoHeight) && (this._videoWidth = t, this._videoHeight = s, this.resize()), this.sendMessage("demand", { time: e + this.timeOffset });
|
|
418
417
|
}
|
|
419
418
|
// if we're using offscreen render, we can't use ctx filters, so we can't use a transfered canvas
|
|
420
419
|
_detachOffscreen() {
|
|
421
|
-
if (!this._offscreenRender || this._ctx)
|
|
422
|
-
return null;
|
|
420
|
+
if (!this._offscreenRender || this._ctx) return null;
|
|
423
421
|
this._canvas.remove(), this._createCanvas(), this._canvasctrl = this._canvas, this._ctx = this._canvasctrl.getContext("2d"), this.sendMessage("detachOffscreen"), this.busy = !1, this.resize(0, 0, 0, 0, !0);
|
|
424
422
|
}
|
|
425
423
|
// if the video or track changed, we need to re-attach the offscreen canvas
|
|
426
424
|
_reAttachOffscreen() {
|
|
427
|
-
if (!this._offscreenRender || !this._ctx)
|
|
428
|
-
return null;
|
|
425
|
+
if (!this._offscreenRender || !this._ctx) return null;
|
|
429
426
|
this._canvas.remove(), this._createCanvas(), this._canvasctrl = this._canvas.transferControlToOffscreen(), this._ctx = !1, this.sendMessage("offscreenCanvas", null, [this._canvasctrl]), this.resize(0, 0, 0, 0, !0);
|
|
430
427
|
}
|
|
431
428
|
_updateColorSpace() {
|
|
@@ -447,22 +444,21 @@ class h extends EventTarget {
|
|
|
447
444
|
_verifyColorSpace({ subtitleColorSpace: e, videoColorSpace: t = this._videoColorSpace }) {
|
|
448
445
|
!e || !t || e !== t && (this._detachOffscreen(), this._ctx.filter = `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='f'><feColorMatrix type='matrix' values='${f[e][t]} 0 0 0 0 0 1 0'/></filter></svg>#f")`);
|
|
449
446
|
}
|
|
450
|
-
_render({ images: e, asyncRender: t, times: s, width:
|
|
451
|
-
this._unbusy(), this.debug && (s.IPCTime = Date.now() - s.JSRenderTime), (this._canvasctrl.width !==
|
|
452
|
-
for (const
|
|
453
|
-
|
|
447
|
+
_render({ images: e, asyncRender: t, times: s, width: i, height: n, colorSpace: r }) {
|
|
448
|
+
this._unbusy(), this.debug && (s.IPCTime = Date.now() - s.JSRenderTime), (this._canvasctrl.width !== i || this._canvasctrl.height !== n) && (this._canvasctrl.width = i, this._canvasctrl.height = n, this._verifyColorSpace({ subtitleColorSpace: r })), this._ctx.clearRect(0, 0, this._canvasctrl.width, this._canvasctrl.height);
|
|
449
|
+
for (const a of e)
|
|
450
|
+
a.image && (t ? (this._ctx.drawImage(a.image, a.x, a.y), a.image.close()) : (this._bufferCanvas.width = a.w, this._bufferCanvas.height = a.h, this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(a.image)), a.w, a.h), 0, 0), this._ctx.drawImage(this._bufferCanvas, a.x, a.y)));
|
|
454
451
|
if (this.debug) {
|
|
455
452
|
s.JSRenderTime = Date.now() - s.JSRenderTime - s.IPCTime;
|
|
456
|
-
let
|
|
457
|
-
const
|
|
453
|
+
let a = 0;
|
|
454
|
+
const h = s.bitmaps || e.length;
|
|
458
455
|
delete s.bitmaps;
|
|
459
|
-
for (const d in s)
|
|
460
|
-
|
|
461
|
-
console.log("Bitmaps: " + o + " Total: " + (i | 0) + "ms", s);
|
|
456
|
+
for (const d in s) a += s[d];
|
|
457
|
+
console.log("Bitmaps: " + h + " Total: " + (a | 0) + "ms", s);
|
|
462
458
|
}
|
|
463
459
|
}
|
|
464
460
|
_fixAlpha(e) {
|
|
465
|
-
if (
|
|
461
|
+
if (o._hasAlphaBug)
|
|
466
462
|
for (let t = 3; t < e.length; t += 4)
|
|
467
463
|
e[t] = e[t] > 1 ? e[t] : 1;
|
|
468
464
|
return e;
|
|
@@ -488,12 +484,12 @@ class h extends EventTarget {
|
|
|
488
484
|
}
|
|
489
485
|
_fetchFromWorker(e, t) {
|
|
490
486
|
try {
|
|
491
|
-
const s = e.target,
|
|
487
|
+
const s = e.target, i = setTimeout(() => {
|
|
492
488
|
r(new Error("Error: Timeout while try to fetch " + s));
|
|
493
|
-
}, 5e3), n = ({ data:
|
|
494
|
-
|
|
495
|
-
}, r = (
|
|
496
|
-
t(
|
|
489
|
+
}, 5e3), n = ({ data: a }) => {
|
|
490
|
+
a.target === s && (t(null, a), this._worker.removeEventListener("message", n), this._worker.removeEventListener("error", r), clearTimeout(i));
|
|
491
|
+
}, r = (a) => {
|
|
492
|
+
t(a), this._worker.removeEventListener("message", n), this._worker.removeEventListener("error", r), clearTimeout(i);
|
|
497
493
|
};
|
|
498
494
|
this._worker.addEventListener("message", n), this._worker.addEventListener("error", r), this._worker.postMessage(e);
|
|
499
495
|
} catch (s) {
|
|
@@ -522,5 +518,5 @@ class h extends EventTarget {
|
|
|
522
518
|
}
|
|
523
519
|
}
|
|
524
520
|
export {
|
|
525
|
-
|
|
521
|
+
o as default
|
|
526
522
|
};
|
package/dist/jassub.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(c,m){typeof exports=="object"&&typeof module<"u"?module.exports=m():typeof define=="function"&&define.amd?define(m):(c=typeof globalThis<"u"?globalThis:c||self,c.JASSUB=m())})(this,function(){"use strict";typeof HTMLVideoElement<"u"&&!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(_){const e=performance.now(),t=this.getVideoPlaybackQuality(),s=this.mozPresentedFrames||this.mozPaintedFrames||t.totalVideoFrames-t.droppedVideoFrames,a=(n,r)=>{const i=this.getVideoPlaybackQuality(),h=this.mozPresentedFrames||this.mozPaintedFrames||i.totalVideoFrames-i.droppedVideoFrames;if(h>s){const d=this.mozFrameDelay||i.totalFrameDelay-t.totalFrameDelay||0,l=r-n;_(r,{presentationTime:r+d*1e3,expectedDisplayTime:r+l,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+l/1e3,presentedFrames:h,processingDuration:d}),delete this._rvfcpolyfillmap[e]}else this._rvfcpolyfillmap[e]=requestAnimationFrame(d=>a(r,d))};return this._rvfcpolyfillmap[e]=requestAnimationFrame(n=>a(e,n)),e},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(_){cancelAnimationFrame(this._rvfcpolyfillmap[_]),delete this._rvfcpolyfillmap[_]});const c={bt709:"BT709",bt470bg:"BT601",smpte170m:"BT601"},m={BT601:{BT709:"1.0863 -0.0723 -0.014 0 0 0.0965 0.8451 0.0584 0 0 -0.0141 -0.0277 1.0418"},BT709:{BT601:"0.9137 0.0784 0.0079 0 0 -0.1049 1.1722 -0.0671 0 0 0.0096 0.0322 0.9582"},FCC:{BT709:"1.0873 -0.0736 -0.0137 0 0 0.0974 0.8494 0.0531 0 0 -0.0127 -0.0251 1.0378",BT601:"1.001 -0.0008 -0.0002 0 0 0.0009 1.005 -0.006 0 0 0.0013 0.0027 0.996"},SMPTE240M:{BT709:"0.9993 0.0006 0.0001 0 0 -0.0004 0.9812 0.0192 0 0 -0.0034 -0.0114 1.0148",BT601:"0.913 0.0774 0.0096 0 0 -0.1051 1.1508 -0.0456 0 0 0.0063 0.0207 0.973"}};class o extends EventTarget{constructor(e){if(super(),!globalThis.Worker)throw this.destroy("Worker not supported");if(!e)throw this.destroy("No options provided");this._loaded=new Promise(s=>{this._init=s});const t=o._test();if(this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&(e.onDemandRender??!0),this._offscreenRender="transferControlToOffscreen"in HTMLCanvasElement.prototype&&!e.canvas&&(e.offscreenRender??!0),this.timeOffset=e.timeOffset||0,this._video=e.video,this._videoHeight=0,this._videoWidth=0,this._videoColorSpace=null,this._canvas=e.canvas,this._video&&!this._canvas)this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",this._canvas=this._createCanvas(),this._video.insertAdjacentElement("afterend",this._canvasParent);else if(!this._canvas)throw this.destroy("Don't know where to render: you should give video or canvas in options.");if(this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d"),!this._bufferCtx)throw this.destroy("Canvas rendering not supported");this._canvasctrl=this._offscreenRender?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!this._offscreenRender&&this._canvasctrl.getContext("2d"),this._lastRenderTime=0,this.debug=!!e.debug,this.prescaleFactor=e.prescaleFactor||1,this.prescaleHeightLimit=e.prescaleHeightLimit||1080,this.maxRenderHeight=e.maxRenderHeight||0,this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=this.setRate.bind(this),this._boundUpdateColorSpace=this._updateColorSpace.bind(this),this._video&&this.setVideo(e.video),this._onDemandRender&&(this.busy=!1,this._lastDemandTime=null),this._worker=new Worker(e.workerUrl||"jassub-worker.js"),this._worker.onmessage=s=>this._onmessage(s),this._worker.onerror=s=>this._error(s),t.then(()=>{this._worker.postMessage({target:"init",wasmUrl:o._supportsSIMD&&e.modernWasmUrl?e.modernWasmUrl:e.wasmUrl||"jassub-worker.wasm",legacyWasmUrl:e.legacyWasmUrl||"jassub-worker.wasm.js",asyncRender:typeof createImageBitmap<"u"&&(e.asyncRender??!0),onDemandRender:this._onDemandRender,width:this._canvasctrl.width||0,height:this._canvasctrl.height||0,blendMode:e.blendMode||"js",subUrl:e.subUrl,subContent:e.subContent||null,fonts:e.fonts||[],availableFonts:e.availableFonts||{"liberation sans":"./default.woff2"},fallbackFont:e.fallbackFont||"liberation sans",debug:this.debug,targetFps:e.targetFps||24,dropAllAnimations:e.dropAllAnimations,dropAllBlur:e.dropAllBlur,libassMemoryLimit:e.libassMemoryLimit||0,libassGlyphLimit:e.libassGlyphLimit||0,useLocalFonts:typeof queryLocalFonts<"u"&&(e.useLocalFonts??!0),hasBitmapBug:o._hasBitmapBug}),this._offscreenRender===!0&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl])})}_createCanvas(){return this._canvas=document.createElement("canvas"),this._canvas.style.display="block",this._canvas.style.position="absolute",this._canvas.style.pointerEvents="none",this._canvasParent.appendChild(this._canvas),this._canvas}static _supportsSIMD=null;static _hasAlphaBug=null;static _hasBitmapBug=null;static async _test(){if(o._hasBitmapBug!==null)return null;try{o._supportsSIMD=WebAssembly.validate(Uint8Array.of(0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11))}catch{o._supportsSIMD=!1}const e=document.createElement("canvas"),t=e.getContext("2d",{willReadFrequently:!0});if(!t)throw new Error("Canvas rendering not supported");if(typeof ImageData.prototype.constructor=="function")try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch{console.log("Detected that ImageData is not constructable despite browser saying so"),self.ImageData=function(h,d,l){const f=t.createImageData(d,l);return h&&f.data.set(h),f}}const s=document.createElement("canvas"),a=s.getContext("2d",{willReadFrequently:!0});if(!a)throw new Error("Canvas rendering not supported");e.width=s.width=1,e.height=s.height=1,t.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const n=a.getImageData(0,0,1,1).data;t.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),a.drawImage(e,0,0);const r=a.getImageData(0,0,1,1).data;if(o._hasAlphaBug=n[1]!==r[1],o._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),typeof createImageBitmap<"u"){const i=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);a.drawImage(await createImageBitmap(new ImageData(i,1)),0,0);const{data:h}=a.getImageData(0,0,1,1);o._hasBitmapBug=!1;for(const[d,l]of h.entries())if(Math.abs(i[d]-l)>15){o._hasBitmapBug=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else o._hasBitmapBug=!1;e.remove(),s.remove()}resize(e=0,t=0,s=0,a=0,n=this._video?.paused){if((!e||!t)&&this._video){const r=this._getVideoPosition();let i=null;if(this._videoWidth){const h=this._video.videoWidth/this._videoWidth,d=this._video.videoHeight/this._videoHeight;i=this._computeCanvasSize((r.width||0)/h,(r.height||0)/d)}else i=this._computeCanvasSize(r.width||0,r.height||0);e=i.width,t=i.height,this._canvasParent&&(s=r.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),a=r.x),this._canvas.style.width=r.width+"px",this._canvas.style.height=r.height+"px"}this._canvas.style.top=s+"px",this._canvas.style.left=a+"px",n&&this.busy===!1?this.busy=!0:n=!1,this.sendMessage("canvas",{width:e,height:t,force:n})}_getVideoPosition(e=this._video.videoWidth,t=this._video.videoHeight){const s=e/t,{offsetWidth:a,offsetHeight:n}=this._video,r=a/n;e=a,t=n,r>s?e=Math.floor(n*s):t=Math.floor(a/s);const i=(a-e)/2,h=(n-t)/2;return{width:e,height:t,x:i,y:h}}_computeCanvasSize(e=0,t=0){const s=this.prescaleFactor<=0?1:this.prescaleFactor,a=self.devicePixelRatio||1;if(e=e*a,t=t*a,t<=0||e<=0)e=0,t=0;else{const n=s<1?-1:1;let r=t*a;n*r*s<=n*this.prescaleHeightLimit?r*=s:n*r<n*this.prescaleHeightLimit&&(r=this.prescaleHeightLimit),this.maxRenderHeight>0&&r>this.maxRenderHeight&&(r=this.maxRenderHeight),e*=r/t,t=r}return{width:e,height:t}}_timeupdate({type:e}){const s={seeking:!0,waiting:!0,playing:!1}[e];s!=null&&(this._playstate=s),this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(e){e instanceof HTMLVideoElement?(this._removeListeners(),this._video=e,this._onDemandRender?this._video.requestVideoFrameCallback(this._handleRVFC.bind(this)):(this._playstate=e.paused,e.addEventListener("timeupdate",this._boundTimeUpdate,!1),e.addEventListener("progress",this._boundTimeUpdate,!1),e.addEventListener("waiting",this._boundTimeUpdate,!1),e.addEventListener("seeking",this._boundTimeUpdate,!1),e.addEventListener("playing",this._boundTimeUpdate,!1),e.addEventListener("ratechange",this._boundSetRate,!1),e.addEventListener("resize",this._boundResize,!1)),"VideoFrame"in window&&(e.addEventListener("loadedmetadata",this._boundUpdateColorSpace,!1),e.readyState>2&&this._updateColorSpace()),e.videoWidth>0&&this.resize(),typeof ResizeObserver<"u"&&(this._ro||(this._ro=new ResizeObserver(()=>this.resize())),this._ro.observe(e))):this._error("Video element invalid!")}runBenchmark(){this.sendMessage("runBenchmark")}setTrackByUrl(e){this.sendMessage("setTrackByUrl",{url:e}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}setTrack(e){this.sendMessage("setTrack",{content:e}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}freeTrack(){this.sendMessage("freeTrack")}setIsPaused(e){this.sendMessage("video",{isPaused:e})}setRate(e){this.sendMessage("video",{rate:e})}setCurrentTime(e,t,s){this.sendMessage("video",{isPaused:e,currentTime:t,rate:s,colorSpace:this._videoColorSpace})}createEvent(e){this.sendMessage("createEvent",{event:e})}setEvent(e,t){this.sendMessage("setEvent",{event:e,index:t})}removeEvent(e){this.sendMessage("removeEvent",{index:e})}getEvents(e){this._fetchFromWorker({target:"getEvents"},(t,{events:s})=>{e(t,s)})}createStyle(e){this.sendMessage("createStyle",{style:e})}setStyle(e,t){this.sendMessage("setStyle",{style:e,index:t})}removeStyle(e){this.sendMessage("removeStyle",{index:e})}getStyles(e){this._fetchFromWorker({target:"getStyles"},(t,{styles:s})=>{e(t,s)})}addFont(e){this.sendMessage("addFont",{font:e})}_sendLocalFont(e){try{queryLocalFonts().then(t=>{const s=t?.find(a=>a.fullName.toLowerCase()===e);s&&s.blob().then(a=>{a.arrayBuffer().then(n=>{this.addFont(new Uint8Array(n))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){try{navigator?.permissions?.query?navigator.permissions.query({name:"local-fonts"}).then(t=>{t.state==="granted"&&this._sendLocalFont(e)}):this._sendLocalFont(e)}catch(t){console.warn("Local fonts API:",t)}}_unbusy(){this._lastDemandTime?this._demandRender(this._lastDemandTime):this.busy=!1}_handleRVFC(e,{mediaTime:t,width:s,height:a}){if(this._destroyed)return null;this.busy?this._lastDemandTime={mediaTime:t,width:s,height:a}:(this.busy=!0,this._demandRender({mediaTime:t,width:s,height:a})),this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))}_demandRender({mediaTime:e,width:t,height:s}){this._lastDemandTime=null,(t!==this._videoWidth||s!==this._videoHeight)&&(this._videoWidth=t,this._videoHeight=s,this.resize()),this.sendMessage("demand",{time:e+this.timeOffset})}_detachOffscreen(){if(!this._offscreenRender||this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas,this._ctx=this._canvasctrl.getContext("2d"),this.sendMessage("detachOffscreen"),this.busy=!1,this.resize(0,0,0,0,!0)}_reAttachOffscreen(){if(!this._offscreenRender||!this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas.transferControlToOffscreen(),this._ctx=!1,this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this.resize(0,0,0,0,!0)}_updateColorSpace(){this._video.requestVideoFrameCallback(()=>{try{const e=new VideoFrame(this._video);this._videoColorSpace=c[e.colorSpace.matrix],e.close(),this.sendMessage("getColorSpace")}catch(e){console.warn(e)}})}_verifyColorSpace({subtitleColorSpace:e,videoColorSpace:t=this._videoColorSpace}){!e||!t||e!==t&&(this._detachOffscreen(),this._ctx.filter=`url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='f'><feColorMatrix type='matrix' values='${m[e][t]} 0 0 0 0 0 1 0'/></filter></svg>#f")`)}_render({images:e,asyncRender:t,times:s,width:a,height:n,colorSpace:r}){this._unbusy(),this.debug&&(s.IPCTime=Date.now()-s.JSRenderTime),(this._canvasctrl.width!==a||this._canvasctrl.height!==n)&&(this._canvasctrl.width=a,this._canvasctrl.height=n,this._verifyColorSpace({subtitleColorSpace:r})),this._ctx.clearRect(0,0,this._canvasctrl.width,this._canvasctrl.height);for(const i of e)i.image&&(t?(this._ctx.drawImage(i.image,i.x,i.y),i.image.close()):(this._bufferCanvas.width=i.w,this._bufferCanvas.height=i.h,this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(i.image)),i.w,i.h),0,0),this._ctx.drawImage(this._bufferCanvas,i.x,i.y)));if(this.debug){s.JSRenderTime=Date.now()-s.JSRenderTime-s.IPCTime;let i=0;const h=s.bitmaps||e.length;delete s.bitmaps;for(const d in s)i+=s[d];console.log("Bitmaps: "+h+" Total: "+(i|0)+"ms",s)}}_fixAlpha(e){if(o._hasAlphaBug)for(let t=3;t<e.length;t+=4)e[t]=e[t]>1?e[t]:1;return e}_ready(){this._init(),this.dispatchEvent(new CustomEvent("ready"))}async sendMessage(e,t={},s){await this._loaded,s?this._worker.postMessage({target:e,transferable:s,...t},[...s]):this._worker.postMessage({target:e,...t})}_fetchFromWorker(e,t){try{const s=e.target,a=setTimeout(()=>{r(new Error("Error: Timeout while try to fetch "+s))},5e3),n=({data:i})=>{i.target===s&&(t(null,i),this._worker.removeEventListener("message",n),this._worker.removeEventListener("error",r),clearTimeout(a))},r=i=>{t(i),this._worker.removeEventListener("message",n),this._worker.removeEventListener("error",r),clearTimeout(a)};this._worker.addEventListener("message",n),this._worker.addEventListener("error",r),this._worker.postMessage(e)}catch(s){this._error(s)}}_console({content:e,command:t}){console[t].apply(console,JSON.parse(e))}_onmessage({data:e}){this["_"+e.target]&&this["_"+e.target](e)}_error(e){const t=e instanceof Error?e:e instanceof ErrorEvent?e.error:new Error(e),s=e instanceof Event?new ErrorEvent(e.type,e):new ErrorEvent("error",{error:t});return this.dispatchEvent(s),console.error(t),t}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),this._ctx&&(this._ctx.filter="none"),this._video.removeEventListener("timeupdate",this._boundTimeUpdate),this._video.removeEventListener("progress",this._boundTimeUpdate),this._video.removeEventListener("waiting",this._boundTimeUpdate),this._video.removeEventListener("seeking",this._boundTimeUpdate),this._video.removeEventListener("playing",this._boundTimeUpdate),this._video.removeEventListener("ratechange",this._boundSetRate),this._video.removeEventListener("resize",this._boundResize),this._video.removeEventListener("loadedmetadata",this._boundUpdateColorSpace))}destroy(e){return e&&(e=this._error(e)),this._video&&this._canvasParent&&this._video.parentNode?.removeChild(this._canvasParent),this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),this._worker?.terminate(),e}}return o});
|
|
1
|
+
(function(c,m){typeof exports=="object"&&typeof module<"u"?module.exports=m():typeof define=="function"&&define.amd?define(m):(c=typeof globalThis<"u"?globalThis:c||self,c.JASSUB=m())})(this,function(){"use strict";typeof HTMLVideoElement<"u"&&!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(_){const e=performance.now(),t=this.getVideoPlaybackQuality(),s=this.mozPresentedFrames||this.mozPaintedFrames||t.totalVideoFrames-t.droppedVideoFrames,i=(n,r)=>{const a=this.getVideoPlaybackQuality(),h=this.mozPresentedFrames||this.mozPaintedFrames||a.totalVideoFrames-a.droppedVideoFrames;if(h>s){const d=this.mozFrameDelay||a.totalFrameDelay-t.totalFrameDelay||0,l=r-n;_(r,{presentationTime:r+d*1e3,expectedDisplayTime:r+l,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+l/1e3,presentedFrames:h,processingDuration:d}),delete this._rvfcpolyfillmap[e]}else this._rvfcpolyfillmap[e]=requestAnimationFrame(d=>i(r,d))};return this._rvfcpolyfillmap[e]=requestAnimationFrame(n=>i(e,n)),e},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(_){cancelAnimationFrame(this._rvfcpolyfillmap[_]),delete this._rvfcpolyfillmap[_]});const c={bt709:"BT709",bt470bg:"BT601",smpte170m:"BT601"},m={BT601:{BT709:"1.0863 -0.0723 -0.014 0 0 0.0965 0.8451 0.0584 0 0 -0.0141 -0.0277 1.0418"},BT709:{BT601:"0.9137 0.0784 0.0079 0 0 -0.1049 1.1722 -0.0671 0 0 0.0096 0.0322 0.9582"},FCC:{BT709:"1.0873 -0.0736 -0.0137 0 0 0.0974 0.8494 0.0531 0 0 -0.0127 -0.0251 1.0378",BT601:"1.001 -0.0008 -0.0002 0 0 0.0009 1.005 -0.006 0 0 0.0013 0.0027 0.996"},SMPTE240M:{BT709:"0.9993 0.0006 0.0001 0 0 -0.0004 0.9812 0.0192 0 0 -0.0034 -0.0114 1.0148",BT601:"0.913 0.0774 0.0096 0 0 -0.1051 1.1508 -0.0456 0 0 0.0063 0.0207 0.973"}};class o extends EventTarget{constructor(e){if(super(),!globalThis.Worker)throw this.destroy("Worker not supported");if(!e)throw this.destroy("No options provided");this._loaded=new Promise(s=>{this._init=s});const t=o._test();if(this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&(e.onDemandRender??!0),this._offscreenRender="transferControlToOffscreen"in HTMLCanvasElement.prototype&&!e.canvas&&(e.offscreenRender??!0),this.timeOffset=e.timeOffset||0,this._video=e.video,this._videoHeight=0,this._videoWidth=0,this._videoColorSpace=null,this._canvas=e.canvas,this._video&&!this._canvas)this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",this._canvas=this._createCanvas(),this._video.insertAdjacentElement("afterend",this._canvasParent);else if(!this._canvas)throw this.destroy("Don't know where to render: you should give video or canvas in options.");if(this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d"),!this._bufferCtx)throw this.destroy("Canvas rendering not supported");this._canvasctrl=this._offscreenRender?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!this._offscreenRender&&this._canvasctrl.getContext("2d"),this._lastRenderTime=0,this.debug=!!e.debug,this.prescaleFactor=e.prescaleFactor||1,this.prescaleHeightLimit=e.prescaleHeightLimit||1080,this.maxRenderHeight=e.maxRenderHeight||0,this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=this.setRate.bind(this),this._boundUpdateColorSpace=this._updateColorSpace.bind(this),this._video&&this.setVideo(e.video),this._onDemandRender&&(this.busy=!1,this._lastDemandTime=null),this._worker=new Worker(e.workerUrl||"jassub-worker.js"),this._worker.onmessage=s=>this._onmessage(s),this._worker.onerror=s=>this._error(s),t.then(()=>{this._worker.postMessage({target:"init",wasmUrl:o._supportsSIMD&&e.modernWasmUrl?e.modernWasmUrl:e.wasmUrl??"jassub-worker.wasm",legacyWasmUrl:e.legacyWasmUrl??"jassub-worker.wasm.js",asyncRender:typeof createImageBitmap<"u"&&(e.asyncRender??!0),onDemandRender:this._onDemandRender,width:this._canvasctrl.width||0,height:this._canvasctrl.height||0,blendMode:e.blendMode||"js",subUrl:e.subUrl,subContent:e.subContent||null,fonts:e.fonts||[],availableFonts:e.availableFonts||{"liberation sans":"./default.woff2"},fallbackFont:e.fallbackFont||"liberation sans",debug:this.debug,targetFps:e.targetFps||24,dropAllAnimations:e.dropAllAnimations,dropAllBlur:e.dropAllBlur,libassMemoryLimit:e.libassMemoryLimit||0,libassGlyphLimit:e.libassGlyphLimit||0,useLocalFonts:typeof queryLocalFonts<"u"&&(e.useLocalFonts??!0),hasBitmapBug:o._hasBitmapBug}),this._offscreenRender===!0&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl])})}_createCanvas(){return this._canvas=document.createElement("canvas"),this._canvas.style.display="block",this._canvas.style.position="absolute",this._canvas.style.pointerEvents="none",this._canvasParent.appendChild(this._canvas),this._canvas}static _supportsSIMD=null;static _hasAlphaBug=null;static _hasBitmapBug=null;static _testSIMD(){if(o._supportsSIMD===null)try{o._supportsSIMD=WebAssembly.validate(Uint8Array.of(0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11))}catch{o._supportsSIMD=!1}}static async _testImageBugs(){if(o._hasBitmapBug!==null)return;const e=document.createElement("canvas"),t=e.getContext("2d",{willReadFrequently:!0});if(!t)throw new Error("Canvas rendering not supported");if(typeof ImageData.prototype.constructor=="function")try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch{console.log("Detected that ImageData is not constructable despite browser saying so"),self.ImageData=function(h,d,l){const f=t.createImageData(d,l);return h&&f.data.set(h),f}}const s=document.createElement("canvas"),i=s.getContext("2d",{willReadFrequently:!0});if(!i)throw new Error("Canvas rendering not supported");e.width=s.width=1,e.height=s.height=1,t.clearRect(0,0,1,1),i.clearRect(0,0,1,1);const n=i.getImageData(0,0,1,1).data;t.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),i.drawImage(e,0,0);const r=i.getImageData(0,0,1,1).data;if(o._hasAlphaBug=n[1]!==r[1],o._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),typeof createImageBitmap<"u"){const a=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);i.drawImage(await createImageBitmap(new ImageData(a,1)),0,0);const{data:h}=i.getImageData(0,0,1,1);o._hasBitmapBug=!1;for(const[d,l]of h.entries())if(Math.abs(a[d]-l)>15){o._hasBitmapBug=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else o._hasBitmapBug=!1;e.remove(),s.remove()}static async _test(){o._testSIMD(),await o._testImageBugs()}resize(e=0,t=0,s=0,i=0,n=this._video?.paused){if((!e||!t)&&this._video){const r=this._getVideoPosition();let a=null;if(this._videoWidth){const h=this._video.videoWidth/this._videoWidth,d=this._video.videoHeight/this._videoHeight;a=this._computeCanvasSize((r.width||0)/h,(r.height||0)/d)}else a=this._computeCanvasSize(r.width||0,r.height||0);e=a.width,t=a.height,this._canvasParent&&(s=r.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),i=r.x),this._canvas.style.width=r.width+"px",this._canvas.style.height=r.height+"px"}this._canvas.style.top=s+"px",this._canvas.style.left=i+"px",n&&this.busy===!1?this.busy=!0:n=!1,this.sendMessage("canvas",{width:e,height:t,force:n})}_getVideoPosition(e=this._video.videoWidth,t=this._video.videoHeight){const s=e/t,{offsetWidth:i,offsetHeight:n}=this._video,r=i/n;e=i,t=n,r>s?e=Math.floor(n*s):t=Math.floor(i/s);const a=(i-e)/2,h=(n-t)/2;return{width:e,height:t,x:a,y:h}}_computeCanvasSize(e=0,t=0){const s=this.prescaleFactor<=0?1:this.prescaleFactor,i=self.devicePixelRatio||1;if(e=e*i,t=t*i,t<=0||e<=0)e=0,t=0;else{const n=s<1?-1:1;let r=t*i;n*r*s<=n*this.prescaleHeightLimit?r*=s:n*r<n*this.prescaleHeightLimit&&(r=this.prescaleHeightLimit),this.maxRenderHeight>0&&r>this.maxRenderHeight&&(r=this.maxRenderHeight),e*=r/t,t=r}return{width:e,height:t}}_timeupdate({type:e}){const s={seeking:!0,waiting:!0,playing:!1}[e];s!=null&&(this._playstate=s),this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(e){e instanceof HTMLVideoElement?(this._removeListeners(),this._video=e,this._onDemandRender?this._video.requestVideoFrameCallback(this._handleRVFC.bind(this)):(this._playstate=e.paused,e.addEventListener("timeupdate",this._boundTimeUpdate,!1),e.addEventListener("progress",this._boundTimeUpdate,!1),e.addEventListener("waiting",this._boundTimeUpdate,!1),e.addEventListener("seeking",this._boundTimeUpdate,!1),e.addEventListener("playing",this._boundTimeUpdate,!1),e.addEventListener("ratechange",this._boundSetRate,!1),e.addEventListener("resize",this._boundResize,!1)),"VideoFrame"in window&&(e.addEventListener("loadedmetadata",this._boundUpdateColorSpace,!1),e.readyState>2&&this._updateColorSpace()),e.videoWidth>0&&this.resize(),typeof ResizeObserver<"u"&&(this._ro||(this._ro=new ResizeObserver(()=>this.resize())),this._ro.observe(e))):this._error("Video element invalid!")}runBenchmark(){this.sendMessage("runBenchmark")}setTrackByUrl(e){this.sendMessage("setTrackByUrl",{url:e}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}setTrack(e){this.sendMessage("setTrack",{content:e}),this._reAttachOffscreen(),this._ctx&&(this._ctx.filter="none")}freeTrack(){this.sendMessage("freeTrack")}setIsPaused(e){this.sendMessage("video",{isPaused:e})}setRate(e){this.sendMessage("video",{rate:e})}setCurrentTime(e,t,s){this.sendMessage("video",{isPaused:e,currentTime:t,rate:s,colorSpace:this._videoColorSpace})}createEvent(e){this.sendMessage("createEvent",{event:e})}setEvent(e,t){this.sendMessage("setEvent",{event:e,index:t})}removeEvent(e){this.sendMessage("removeEvent",{index:e})}getEvents(e){this._fetchFromWorker({target:"getEvents"},(t,{events:s})=>{e(t,s)})}createStyle(e){this.sendMessage("createStyle",{style:e})}setStyle(e,t){this.sendMessage("setStyle",{style:e,index:t})}removeStyle(e){this.sendMessage("removeStyle",{index:e})}getStyles(e){this._fetchFromWorker({target:"getStyles"},(t,{styles:s})=>{e(t,s)})}addFont(e){this.sendMessage("addFont",{font:e})}_sendLocalFont(e){try{queryLocalFonts().then(t=>{const s=t?.find(i=>i.fullName.toLowerCase()===e);s&&s.blob().then(i=>{i.arrayBuffer().then(n=>{this.addFont(new Uint8Array(n))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){try{navigator?.permissions?.query?navigator.permissions.query({name:"local-fonts"}).then(t=>{t.state==="granted"&&this._sendLocalFont(e)}):this._sendLocalFont(e)}catch(t){console.warn("Local fonts API:",t)}}_unbusy(){this._lastDemandTime?this._demandRender(this._lastDemandTime):this.busy=!1}_handleRVFC(e,{mediaTime:t,width:s,height:i}){if(this._destroyed)return null;this.busy?this._lastDemandTime={mediaTime:t,width:s,height:i}:(this.busy=!0,this._demandRender({mediaTime:t,width:s,height:i})),this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))}_demandRender({mediaTime:e,width:t,height:s}){this._lastDemandTime=null,(t!==this._videoWidth||s!==this._videoHeight)&&(this._videoWidth=t,this._videoHeight=s,this.resize()),this.sendMessage("demand",{time:e+this.timeOffset})}_detachOffscreen(){if(!this._offscreenRender||this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas,this._ctx=this._canvasctrl.getContext("2d"),this.sendMessage("detachOffscreen"),this.busy=!1,this.resize(0,0,0,0,!0)}_reAttachOffscreen(){if(!this._offscreenRender||!this._ctx)return null;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas.transferControlToOffscreen(),this._ctx=!1,this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this.resize(0,0,0,0,!0)}_updateColorSpace(){this._video.requestVideoFrameCallback(()=>{try{const e=new VideoFrame(this._video);this._videoColorSpace=c[e.colorSpace.matrix],e.close(),this.sendMessage("getColorSpace")}catch(e){console.warn(e)}})}_verifyColorSpace({subtitleColorSpace:e,videoColorSpace:t=this._videoColorSpace}){!e||!t||e!==t&&(this._detachOffscreen(),this._ctx.filter=`url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='f'><feColorMatrix type='matrix' values='${m[e][t]} 0 0 0 0 0 1 0'/></filter></svg>#f")`)}_render({images:e,asyncRender:t,times:s,width:i,height:n,colorSpace:r}){this._unbusy(),this.debug&&(s.IPCTime=Date.now()-s.JSRenderTime),(this._canvasctrl.width!==i||this._canvasctrl.height!==n)&&(this._canvasctrl.width=i,this._canvasctrl.height=n,this._verifyColorSpace({subtitleColorSpace:r})),this._ctx.clearRect(0,0,this._canvasctrl.width,this._canvasctrl.height);for(const a of e)a.image&&(t?(this._ctx.drawImage(a.image,a.x,a.y),a.image.close()):(this._bufferCanvas.width=a.w,this._bufferCanvas.height=a.h,this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(a.image)),a.w,a.h),0,0),this._ctx.drawImage(this._bufferCanvas,a.x,a.y)));if(this.debug){s.JSRenderTime=Date.now()-s.JSRenderTime-s.IPCTime;let a=0;const h=s.bitmaps||e.length;delete s.bitmaps;for(const d in s)a+=s[d];console.log("Bitmaps: "+h+" Total: "+(a|0)+"ms",s)}}_fixAlpha(e){if(o._hasAlphaBug)for(let t=3;t<e.length;t+=4)e[t]=e[t]>1?e[t]:1;return e}_ready(){this._init(),this.dispatchEvent(new CustomEvent("ready"))}async sendMessage(e,t={},s){await this._loaded,s?this._worker.postMessage({target:e,transferable:s,...t},[...s]):this._worker.postMessage({target:e,...t})}_fetchFromWorker(e,t){try{const s=e.target,i=setTimeout(()=>{r(new Error("Error: Timeout while try to fetch "+s))},5e3),n=({data:a})=>{a.target===s&&(t(null,a),this._worker.removeEventListener("message",n),this._worker.removeEventListener("error",r),clearTimeout(i))},r=a=>{t(a),this._worker.removeEventListener("message",n),this._worker.removeEventListener("error",r),clearTimeout(i)};this._worker.addEventListener("message",n),this._worker.addEventListener("error",r),this._worker.postMessage(e)}catch(s){this._error(s)}}_console({content:e,command:t}){console[t].apply(console,JSON.parse(e))}_onmessage({data:e}){this["_"+e.target]&&this["_"+e.target](e)}_error(e){const t=e instanceof Error?e:e instanceof ErrorEvent?e.error:new Error(e),s=e instanceof Event?new ErrorEvent(e.type,e):new ErrorEvent("error",{error:t});return this.dispatchEvent(s),console.error(t),t}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),this._ctx&&(this._ctx.filter="none"),this._video.removeEventListener("timeupdate",this._boundTimeUpdate),this._video.removeEventListener("progress",this._boundTimeUpdate),this._video.removeEventListener("waiting",this._boundTimeUpdate),this._video.removeEventListener("seeking",this._boundTimeUpdate),this._video.removeEventListener("playing",this._boundTimeUpdate),this._video.removeEventListener("ratechange",this._boundSetRate),this._video.removeEventListener("resize",this._boundResize),this._video.removeEventListener("loadedmetadata",this._boundUpdateColorSpace))}destroy(e){return e&&(e=this._error(e)),this._video&&this._canvasParent&&this._video.parentNode?.removeChild(this._canvasParent),this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),this._worker?.terminate(),e}}return o});
|
package/index.d.ts
CHANGED
|
@@ -79,8 +79,8 @@ interface JassubOptions {
|
|
|
79
79
|
libassGlyphLimit?: number;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
type ASS_EventCallback = (error: Error | null, event: ASS_Event) => void;
|
|
83
|
-
type ASS_StyleCallback = (error: Error | null, event: ASS_Style) => void;
|
|
82
|
+
type ASS_EventCallback = (error: Error | null, event: ASS_Event[]) => void;
|
|
83
|
+
type ASS_StyleCallback = (error: Error | null, event: ASS_Style[]) => void;
|
|
84
84
|
|
|
85
85
|
export default class JASSUB {
|
|
86
86
|
constructor (options: JassubOptions);
|
|
@@ -111,4 +111,7 @@ export default class JASSUB {
|
|
|
111
111
|
|
|
112
112
|
sendMessage (target: string, data?: Record<string, unknown>, transferable?: Transferable[]): void;
|
|
113
113
|
destroy (err?: string): void;
|
|
114
|
+
|
|
115
|
+
protected _ctx: CanvasRenderingContext2D;
|
|
116
|
+
protected _canvas: HTMLCanvasElement;
|
|
114
117
|
}
|
package/package.json
CHANGED
package/src/jassub.js
CHANGED
|
@@ -123,8 +123,8 @@ export default class JASSUB extends EventTarget {
|
|
|
123
123
|
test.then(() => {
|
|
124
124
|
this._worker.postMessage({
|
|
125
125
|
target: 'init',
|
|
126
|
-
wasmUrl: JASSUB._supportsSIMD && options.modernWasmUrl ? options.modernWasmUrl : options.wasmUrl
|
|
127
|
-
legacyWasmUrl: options.legacyWasmUrl
|
|
126
|
+
wasmUrl: JASSUB._supportsSIMD && options.modernWasmUrl ? options.modernWasmUrl : options.wasmUrl ?? 'jassub-worker.wasm',
|
|
127
|
+
legacyWasmUrl: options.legacyWasmUrl ?? 'jassub-worker.wasm.js',
|
|
128
128
|
asyncRender: typeof createImageBitmap !== 'undefined' && (options.asyncRender ?? true),
|
|
129
129
|
onDemandRender: this._onDemandRender,
|
|
130
130
|
width: this._canvasctrl.width || 0,
|
|
@@ -167,15 +167,18 @@ export default class JASSUB extends EventTarget {
|
|
|
167
167
|
/** @type {boolean|null} */
|
|
168
168
|
static _hasBitmapBug = null
|
|
169
169
|
|
|
170
|
-
static
|
|
171
|
-
|
|
172
|
-
if (JASSUB._hasBitmapBug !== null) return null
|
|
170
|
+
static _testSIMD () {
|
|
171
|
+
if (JASSUB._supportsSIMD !== null) return
|
|
173
172
|
|
|
174
173
|
try {
|
|
175
174
|
JASSUB._supportsSIMD = WebAssembly.validate(Uint8Array.of(0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123, 3, 2, 1, 0, 10, 10, 1, 8, 0, 65, 0, 253, 15, 253, 98, 11))
|
|
176
175
|
} catch (e) {
|
|
177
176
|
JASSUB._supportsSIMD = false
|
|
178
177
|
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
static async _testImageBugs () {
|
|
181
|
+
if (JASSUB._hasBitmapBug !== null) return
|
|
179
182
|
|
|
180
183
|
const canvas1 = document.createElement('canvas')
|
|
181
184
|
const ctx1 = canvas1.getContext('2d', { willReadFrequently: true })
|
|
@@ -237,6 +240,11 @@ export default class JASSUB extends EventTarget {
|
|
|
237
240
|
canvas2.remove()
|
|
238
241
|
}
|
|
239
242
|
|
|
243
|
+
static async _test () {
|
|
244
|
+
JASSUB._testSIMD()
|
|
245
|
+
await JASSUB._testImageBugs()
|
|
246
|
+
}
|
|
247
|
+
|
|
240
248
|
/**
|
|
241
249
|
* Resize the canvas to given parameters. Auto-generated if values are ommited.
|
|
242
250
|
* @param {Number} [width=0]
|