jassub 1.2.4 → 1.4.0
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/README.md +1 -0
- package/dist/COPYRIGHT +951 -951
- package/dist/jassub-worker-legacy.js +12 -12
- package/dist/{jassub-worker-legacy.js.mem → jassub-worker-legacy.mem} +0 -0
- package/dist/jassub-worker.js +1 -1
- package/dist/jassub-worker.wasm +0 -0
- package/dist/jassub.es.js +83 -76
- package/dist/jassub.umd.js +1 -1
- package/package.json +1 -1
- package/src/jassub.js +80 -75
- package/dist/jassub.js +0 -688
package/dist/jassub.es.js
CHANGED
|
@@ -41,7 +41,7 @@ if (!("requestVideoFrameCallback" in HTMLVideoElement.prototype) && "getVideoPla
|
|
|
41
41
|
}
|
|
42
42
|
const _JASSUB = class extends EventTarget {
|
|
43
43
|
constructor(options = {}) {
|
|
44
|
-
var _a, _b, _c
|
|
44
|
+
var _a, _b, _c;
|
|
45
45
|
super();
|
|
46
46
|
if (!globalThis.Worker) {
|
|
47
47
|
this.destroy("Worker not supported");
|
|
@@ -53,6 +53,8 @@ const _JASSUB = class extends EventTarget {
|
|
|
53
53
|
this._onDemandRender = "requestVideoFrameCallback" in HTMLVideoElement.prototype && ((_c = options.onDemandRender) != null ? _c : true);
|
|
54
54
|
this.timeOffset = options.timeOffset || 0;
|
|
55
55
|
this._video = options.video;
|
|
56
|
+
this._videoHeight = 0;
|
|
57
|
+
this._videoWidth = 0;
|
|
56
58
|
this._canvas = options.canvas;
|
|
57
59
|
if (this._video && !this._canvas) {
|
|
58
60
|
this._canvasParent = document.createElement("div");
|
|
@@ -83,39 +85,44 @@ const _JASSUB = class extends EventTarget {
|
|
|
83
85
|
this._worker = new Worker(_JASSUB._supportsWebAssembly ? options.workerUrl || "jassub-worker.js" : options.legacyWorkerUrl || "jassub-worker-legacy.js");
|
|
84
86
|
this._worker.onmessage = (e) => this._onmessage(e);
|
|
85
87
|
this._worker.onerror = (e) => this._error(e);
|
|
86
|
-
this.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
this.
|
|
114
|
-
|
|
115
|
-
this.
|
|
116
|
-
this.
|
|
117
|
-
|
|
118
|
-
|
|
88
|
+
this._init = () => {
|
|
89
|
+
var _a2, _b2;
|
|
90
|
+
if (this._destroyed)
|
|
91
|
+
return;
|
|
92
|
+
this._worker.postMessage({
|
|
93
|
+
target: "init",
|
|
94
|
+
asyncRender,
|
|
95
|
+
onDemandRender: this._onDemandRender,
|
|
96
|
+
width: this._canvasctrl.width,
|
|
97
|
+
height: this._canvasctrl.height,
|
|
98
|
+
preMain: true,
|
|
99
|
+
blendMode,
|
|
100
|
+
subUrl: options.subUrl,
|
|
101
|
+
subContent: options.subContent || null,
|
|
102
|
+
fonts: options.fonts || [],
|
|
103
|
+
availableFonts: options.availableFonts || { "liberation sans": "./default.woff2" },
|
|
104
|
+
fallbackFont: options.fallbackFont || "liberation sans",
|
|
105
|
+
debug: this.debug,
|
|
106
|
+
targetFps: options.targetFps || 24,
|
|
107
|
+
dropAllAnimations: options.dropAllAnimations,
|
|
108
|
+
libassMemoryLimit: options.libassMemoryLimit || 0,
|
|
109
|
+
libassGlyphLimit: options.libassGlyphLimit || 0,
|
|
110
|
+
hasAlphaBug: _JASSUB._hasAlphaBug,
|
|
111
|
+
useLocalFonts: "queryLocalFonts" in self && ((_a2 = options.useLocalFonts) != null ? _a2 : true)
|
|
112
|
+
});
|
|
113
|
+
if (offscreenRender === true)
|
|
114
|
+
this.sendMessage("offscreenCanvas", null, [this._canvasctrl]);
|
|
115
|
+
this._boundResize = this.resize.bind(this);
|
|
116
|
+
this._boundTimeUpdate = this._timeupdate.bind(this);
|
|
117
|
+
this._boundSetRate = this.setRate.bind(this);
|
|
118
|
+
if (this._video)
|
|
119
|
+
this.setVideo(options.video);
|
|
120
|
+
if (this._onDemandRender) {
|
|
121
|
+
this.busy = false;
|
|
122
|
+
this._lastDemandTime = null;
|
|
123
|
+
(_b2 = this._video) == null ? void 0 : _b2.requestVideoFrameCallback(this._handleRVFC.bind(this));
|
|
124
|
+
}
|
|
125
|
+
};
|
|
119
126
|
}
|
|
120
127
|
static _test() {
|
|
121
128
|
if (_JASSUB._supportsWebAssembly !== null)
|
|
@@ -160,49 +167,36 @@ const _JASSUB = class extends EventTarget {
|
|
|
160
167
|
canvas1.remove();
|
|
161
168
|
canvas2.remove();
|
|
162
169
|
}
|
|
163
|
-
resize(width = 0, height = 0, top = 0, left = 0) {
|
|
164
|
-
let videoSize = null;
|
|
170
|
+
resize(width = 0, height = 0, top = 0, left = 0, force = ((_a) => (_a = this._video) == null ? void 0 : _a.paused)()) {
|
|
165
171
|
if ((!width || !height) && this._video) {
|
|
166
|
-
videoSize = this._getVideoPosition();
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
172
|
+
const videoSize = this._getVideoPosition();
|
|
173
|
+
let renderSize = null;
|
|
174
|
+
if (this._videoWidth) {
|
|
175
|
+
const widthRatio = this._video.videoWidth / this._videoWidth;
|
|
176
|
+
const heightRatio = this._video.videoHeight / this._videoHeight;
|
|
177
|
+
renderSize = this._computeCanvasSize((videoSize.width || 0) / widthRatio, (videoSize.height || 0) / heightRatio);
|
|
178
|
+
} else {
|
|
179
|
+
renderSize = this._computeCanvasSize(videoSize.width || 0, videoSize.height || 0);
|
|
180
|
+
}
|
|
181
|
+
width = renderSize.width;
|
|
182
|
+
height = renderSize.height;
|
|
170
183
|
if (this._canvasParent) {
|
|
171
184
|
top = videoSize.y - (this._canvasParent.getBoundingClientRect().top - this._video.getBoundingClientRect().top);
|
|
172
185
|
left = videoSize.x;
|
|
173
186
|
}
|
|
174
|
-
}
|
|
175
|
-
if (videoSize != null) {
|
|
176
|
-
this._canvas.style.top = top + "px";
|
|
177
|
-
this._canvas.style.left = left + "px";
|
|
178
187
|
this._canvas.style.width = videoSize.width + "px";
|
|
179
188
|
this._canvas.style.height = videoSize.height + "px";
|
|
180
189
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
this._resizeTimeoutBuffer = setTimeout(() => {
|
|
185
|
-
this._resizeTimeoutBuffer = void 0;
|
|
186
|
-
this._canvasctrl.width = width;
|
|
187
|
-
this._canvasctrl.height = height;
|
|
188
|
-
this.sendMessage("canvas", { width, height });
|
|
189
|
-
}, 100);
|
|
190
|
-
} else {
|
|
191
|
-
this._canvasctrl.width = width;
|
|
192
|
-
this._canvasctrl.height = height;
|
|
193
|
-
this.sendMessage("canvas", { width, height });
|
|
194
|
-
this._resizeTimeoutBuffer = setTimeout(() => {
|
|
195
|
-
this._resizeTimeoutBuffer = void 0;
|
|
196
|
-
}, 100);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
190
|
+
this._canvas.style.top = top + "px";
|
|
191
|
+
this._canvas.style.left = left + "px";
|
|
192
|
+
this.sendMessage("canvas", { width, height, force: force && this.busy === false });
|
|
199
193
|
}
|
|
200
|
-
_getVideoPosition() {
|
|
201
|
-
const videoRatio =
|
|
194
|
+
_getVideoPosition(width = this._video.videoWidth, height = this._video.videoHeight) {
|
|
195
|
+
const videoRatio = width / height;
|
|
202
196
|
const { offsetWidth, offsetHeight } = this._video;
|
|
203
197
|
const elementRatio = offsetWidth / offsetHeight;
|
|
204
|
-
|
|
205
|
-
|
|
198
|
+
width = offsetWidth;
|
|
199
|
+
height = offsetHeight;
|
|
206
200
|
if (elementRatio > videoRatio) {
|
|
207
201
|
width = Math.floor(offsetHeight * videoRatio);
|
|
208
202
|
} else {
|
|
@@ -214,12 +208,13 @@ const _JASSUB = class extends EventTarget {
|
|
|
214
208
|
}
|
|
215
209
|
_computeCanvasSize(width = 0, height = 0) {
|
|
216
210
|
const scalefactor = this.prescaleFactor <= 0 ? 1 : this.prescaleFactor;
|
|
211
|
+
const ratio = self.devicePixelRatio || 1;
|
|
217
212
|
if (height <= 0 || width <= 0) {
|
|
218
213
|
width = 0;
|
|
219
214
|
height = 0;
|
|
220
215
|
} else {
|
|
221
216
|
const sgn = scalefactor < 1 ? -1 : 1;
|
|
222
|
-
let newH = height;
|
|
217
|
+
let newH = height * ratio;
|
|
223
218
|
if (sgn * newH * scalefactor <= sgn * this.prescaleHeightLimit) {
|
|
224
219
|
newH *= scalefactor;
|
|
225
220
|
} else if (sgn * newH < sgn * this.prescaleHeightLimit) {
|
|
@@ -227,7 +222,7 @@ const _JASSUB = class extends EventTarget {
|
|
|
227
222
|
}
|
|
228
223
|
if (this.maxRenderHeight > 0 && newH > this.maxRenderHeight)
|
|
229
224
|
newH = this.maxRenderHeight;
|
|
230
|
-
width *= newH / height;
|
|
225
|
+
width *= ratio * newH / height;
|
|
231
226
|
height = newH;
|
|
232
227
|
}
|
|
233
228
|
return { width, height };
|
|
@@ -257,10 +252,10 @@ const _JASSUB = class extends EventTarget {
|
|
|
257
252
|
video.addEventListener("seeking", this._boundTimeUpdate, false);
|
|
258
253
|
video.addEventListener("playing", this._boundTimeUpdate, false);
|
|
259
254
|
video.addEventListener("ratechange", this._boundSetRate, false);
|
|
255
|
+
video.addEventListener("resize", this._boundResize);
|
|
260
256
|
}
|
|
261
257
|
if (video.videoWidth > 0)
|
|
262
258
|
this.resize();
|
|
263
|
-
video.addEventListener("resize", this._boundResize);
|
|
264
259
|
if (typeof ResizeObserver !== "undefined") {
|
|
265
260
|
if (!this._ro)
|
|
266
261
|
this._ro = new ResizeObserver(() => this.resize());
|
|
@@ -365,24 +360,35 @@ const _JASSUB = class extends EventTarget {
|
|
|
365
360
|
this.busy = false;
|
|
366
361
|
}
|
|
367
362
|
}
|
|
368
|
-
_handleRVFC(now, { mediaTime }) {
|
|
363
|
+
_handleRVFC(now, { mediaTime, width, height }) {
|
|
369
364
|
if (this._destroyed)
|
|
370
365
|
return null;
|
|
371
366
|
if (this.busy) {
|
|
372
|
-
this._lastDemandTime = mediaTime;
|
|
367
|
+
this._lastDemandTime = { mediaTime, width, height };
|
|
373
368
|
} else {
|
|
374
369
|
this.busy = true;
|
|
375
|
-
this._demandRender(mediaTime);
|
|
370
|
+
this._demandRender({ mediaTime, width, height });
|
|
376
371
|
}
|
|
377
372
|
this._video.requestVideoFrameCallback(this._handleRVFC.bind(this));
|
|
378
373
|
}
|
|
379
|
-
_demandRender(
|
|
374
|
+
_demandRender({ mediaTime, width, height }) {
|
|
380
375
|
this._lastDemandTime = null;
|
|
381
|
-
this.
|
|
376
|
+
if (width !== this._videoWidth || height !== this._videoHeight) {
|
|
377
|
+
this._videoWidth = width;
|
|
378
|
+
this._videoHeight = height;
|
|
379
|
+
this.resize();
|
|
380
|
+
}
|
|
381
|
+
this.sendMessage("demand", { time: mediaTime + this.timeOffset });
|
|
382
382
|
}
|
|
383
|
-
_render({ images, async, times }) {
|
|
383
|
+
_render({ images, async, times, width, height }) {
|
|
384
|
+
this._unbusy();
|
|
384
385
|
const drawStartTime = Date.now();
|
|
385
|
-
|
|
386
|
+
if (this._canvasctrl.width !== width || this._canvasctrl.height !== height) {
|
|
387
|
+
this._canvasctrl.width = width;
|
|
388
|
+
this._canvasctrl.height = height;
|
|
389
|
+
} else {
|
|
390
|
+
this._ctx.clearRect(0, 0, this._canvasctrl.width, this._canvasctrl.height);
|
|
391
|
+
}
|
|
386
392
|
for (const image of images) {
|
|
387
393
|
if (image.image) {
|
|
388
394
|
if (async) {
|
|
@@ -413,6 +419,7 @@ const _JASSUB = class extends EventTarget {
|
|
|
413
419
|
return uint8;
|
|
414
420
|
}
|
|
415
421
|
_ready() {
|
|
422
|
+
this._init();
|
|
416
423
|
this.dispatchEvent(new CustomEvent("ready"));
|
|
417
424
|
}
|
|
418
425
|
sendMessage(target, data = {}, transferable) {
|
package/dist/jassub.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(h,n){typeof exports=="object"&&typeof module!="undefined"?module.exports=n():typeof define=="function"&&define.amd?define(n):(h=typeof globalThis!="undefined"?globalThis:h||self,h.JASSUB=n())})(this,function(){"use strict";var _=Object.defineProperty;var v=(h,n,d)=>n in h?_(h,n,{enumerable:!0,configurable:!0,writable:!0,value:d}):h[n]=d;var f=(h,n,d)=>(v(h,typeof n!="symbol"?n+"":n,d),d);!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(d){const e=this.getVideoPlaybackQuality(),t=this.mozPresentedFrames||this.mozPaintedFrames||e.totalVideoFrames-e.droppedVideoFrames,s=(r,o)=>{const l=this.getVideoPlaybackQuality(),m=this.mozPresentedFrames||this.mozPaintedFrames||l.totalVideoFrames-l.droppedVideoFrames;if(m>t){const c=this.mozFrameDelay||l.totalFrameDelay-e.totalFrameDelay||0,u=o-r;d(o,{presentationTime:o+c*1e3,expectedDisplayTime:o+u,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+u/1e3,presentedFrames:m,processingDuration:c}),delete this._rvfcpolyfillmap[a]}else this._rvfcpolyfillmap[a]=requestAnimationFrame(c=>s(o,c))},a=Date.now(),i=performance.now();return this._rvfcpolyfillmap[a]=requestAnimationFrame(r=>s(i,r)),a},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(d){cancelAnimationFrame(this._rvfcpolyfillmap[d]),delete this._rvfcpolyfillmap[d]});const n=class extends EventTarget{constructor(e={}){var i,r,o,l,m;super(),globalThis.Worker||this.destroy("Worker not supported"),n._test();const t=e.blendMode||"js",s=typeof createImageBitmap!="undefined"&&((i=e.asyncRender)!=null?i:!0),a=typeof OffscreenCanvas!="undefined"&&((r=e.offscreenRender)!=null?r:!0);this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&((o=e.onDemandRender)!=null?o:!0),this.timeOffset=e.timeOffset||0,this._video=e.video,this._canvas=e.canvas,this._video&&!this._canvas?(this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",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._video.nextSibling?this._video.parentNode.insertBefore(this._canvasParent,this._video.nextSibling):this._video.parentNode.appendChild(this._canvasParent)):this._canvas||this.destroy("Don't know where to render: you should give video or canvas in options."),this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d",{desynchronized:!0,willReadFrequently:!0}),this._canvasctrl=a?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!a&&this._canvasctrl.getContext("2d",{desynchronized:!0}),this._lastRenderTime=0,this.debug=!!e.debug,this.prescaleFactor=e.prescaleFactor||1,this.prescaleHeightLimit=e.prescaleHeightLimit||1080,this.maxRenderHeight=e.maxRenderHeight||0,this._worker=new Worker(n._supportsWebAssembly?e.workerUrl||"jassub-worker.js":e.legacyWorkerUrl||"jassub-worker-legacy.js"),this._worker.onmessage=c=>this._onmessage(c),this._worker.onerror=c=>this._error(c),this._worker.postMessage({target:"init",asyncRender:s,onDemandRender:this._onDemandRender,width:this._canvas.width,height:this._canvas.height,preMain:!0,blendMode:t,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,libassMemoryLimit:e.libassMemoryLimit||0,libassGlyphLimit:e.libassGlyphLimit||0,hasAlphaBug:n._hasAlphaBug,useLocalFonts:"queryLocalFonts"in self&&((l=e.useLocalFonts)!=null?l:!0)}),a===!0&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=this.setRate.bind(this),this._video&&this.setVideo(e.video),this._onDemandRender&&(this.busy=!1,this._lastDemandTime=null,(m=this._video)==null||m.requestVideoFrameCallback(this._handleRVFC.bind(this)))}static _test(){if(n._supportsWebAssembly!==null)return null;const e=document.createElement("canvas"),t=e.getContext("2d",{willReadFrequently:!0});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(l,m,c){const u=t.createImageData(m,c);return l&&u.data.set(l),u}}try{if(typeof WebAssembly=="object"&&typeof WebAssembly.instantiate=="function"){const o=new WebAssembly.Module(Uint8Array.of(0,97,115,109,1,0,0,0));o instanceof WebAssembly.Module&&(n._supportsWebAssembly=new WebAssembly.Instance(o)instanceof WebAssembly.Instance)}}catch{n._supportsWebAssembly=!1}const s=document.createElement("canvas"),a=s.getContext("2d",{willReadFrequently:!0});e.width=s.width=1,e.height=s.height=1,t.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const i=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;n._hasAlphaBug=i[1]!==r[1],n._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),e.remove(),s.remove()}resize(e=0,t=0,s=0,a=0){let i=null;if((!e||!t)&&this._video){i=this._getVideoPosition();const r=this._computeCanvasSize((i.width||0)*(self.devicePixelRatio||1),(i.height||0)*(self.devicePixelRatio||1));e=r.width,t=r.height,this._canvasParent&&(s=i.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),a=i.x)}i!=null&&(this._canvas.style.top=s+"px",this._canvas.style.left=a+"px",this._canvas.style.width=i.width+"px",this._canvas.style.height=i.height+"px"),this._canvasctrl.width===e&&this._canvasctrl.height===t||(this._resizeTimeoutBuffer?(clearTimeout(this._resizeTimeoutBuffer),this._resizeTimeoutBuffer=setTimeout(()=>{this._resizeTimeoutBuffer=void 0,this._canvasctrl.width=e,this._canvasctrl.height=t,this.sendMessage("canvas",{width:e,height:t})},100)):(this._canvasctrl.width=e,this._canvasctrl.height=t,this.sendMessage("canvas",{width:e,height:t}),this._resizeTimeoutBuffer=setTimeout(()=>{this._resizeTimeoutBuffer=void 0},100)))}_getVideoPosition(){const e=this._video.videoWidth/this._video.videoHeight,{offsetWidth:t,offsetHeight:s}=this._video,a=t/s;let i=t,r=s;a>e?i=Math.floor(s*e):r=Math.floor(t/e);const o=(t-i)/2,l=(s-r)/2;return{width:i,height:r,x:o,y:l}}_computeCanvasSize(e=0,t=0){const s=this.prescaleFactor<=0?1:this.prescaleFactor;if(t<=0||e<=0)e=0,t=0;else{const a=s<1?-1:1;let i=t;a*i*s<=a*this.prescaleHeightLimit?i*=s:a*i<a*this.prescaleHeightLimit&&(i=this.prescaleHeightLimit),this.maxRenderHeight>0&&i>this.maxRenderHeight&&(i=this.maxRenderHeight),e*=i/t,t=i}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.videoWidth>0&&this.resize(),e.addEventListener("resize",this._boundResize),typeof ResizeObserver!="undefined"&&(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})}setTrack(e){this.sendMessage("setTrack",{content:e})}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})}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",{event: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==null?void 0:t.find(a=>a.fullName.toLowerCase()===e);s&&s.blob().then(a=>{a.arrayBuffer().then(i=>{this.addFont(new Uint8Array(i))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){var t;try{(t=navigator==null?void 0:navigator.permissions)!=null&&t.query?navigator.permissions.query({name:"local-fonts"}).then(s=>{s.state==="granted"&&this._sendLocalFont(e)}):this._sendLocalFont(e)}catch(s){console.warn("Local fonts API:",s)}}_unbusy(){this._lastDemandTime?this._demandRender(this._lastDemandTime):this.busy=!1}_handleRVFC(e,{mediaTime:t}){if(this._destroyed)return null;this.busy?this._lastDemandTime=t:(this.busy=!0,this._demandRender(t)),this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))}_demandRender(e){this._lastDemandTime=null,this.sendMessage("demand",{time:e+this.timeOffset})}_render({images:e,async:t,times:s}){const a=Date.now();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.drawTime=Date.now()-a;let i=0;for(const r in s)i+=s[r];console.log("Bitmaps: "+e.length+" Total: "+Math.round(i)+"ms",s)}}_fixAlpha(e){if(n._hasAlphaBug)for(let t=3;t<e.length;t+=4)e[t]=e[t]>1?e[t]:1;return e}_ready(){this.dispatchEvent(new CustomEvent("ready"))}sendMessage(e,t={},s){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),i=({data:o})=>{o.target===s&&(t(null,o),this._worker.removeEventListener("message",i),this._worker.removeEventListener("error",r),clearTimeout(a))},r=o=>{t(o),this._worker.removeEventListener("message",i),this._worker.removeEventListener("error",r),clearTimeout(a)};this._worker.addEventListener("message",i),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){this.dispatchEvent(e instanceof ErrorEvent?e:new ErrorEvent("error",{cause:e instanceof Error?e.cause:e})),e instanceof Error||(e instanceof ErrorEvent?e=e.error:e=new Error("error",{cause:e})),console.error(e)}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),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))}destroy(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()}};let h=n;return f(h,"_supportsWebAssembly",null),f(h,"_hasAlphaBug",null),h});
|
|
1
|
+
(function(d,o){typeof exports=="object"&&typeof module!="undefined"?module.exports=o():typeof define=="function"&&define.amd?define(o):(d=typeof globalThis!="undefined"?globalThis:d||self,d.JASSUB=o())})(this,function(){"use strict";var v=Object.defineProperty;var f=(d,o,l)=>o in d?v(d,o,{enumerable:!0,configurable:!0,writable:!0,value:l}):d[o]=l;var u=(d,o,l)=>(f(d,typeof o!="symbol"?o+"":o,l),l);!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(l){const e=this.getVideoPlaybackQuality(),t=this.mozPresentedFrames||this.mozPaintedFrames||e.totalVideoFrames-e.droppedVideoFrames,s=(n,i)=>{const h=this.getVideoPlaybackQuality(),c=this.mozPresentedFrames||this.mozPaintedFrames||h.totalVideoFrames-h.droppedVideoFrames;if(c>t){const m=this.mozFrameDelay||h.totalFrameDelay-e.totalFrameDelay||0,_=i-n;l(i,{presentationTime:i+m*1e3,expectedDisplayTime:i+_,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+_/1e3,presentedFrames:c,processingDuration:m}),delete this._rvfcpolyfillmap[a]}else this._rvfcpolyfillmap[a]=requestAnimationFrame(m=>s(i,m))},a=Date.now(),r=performance.now();return this._rvfcpolyfillmap[a]=requestAnimationFrame(n=>s(r,n)),a},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(l){cancelAnimationFrame(this._rvfcpolyfillmap[l]),delete this._rvfcpolyfillmap[l]});const o=class extends EventTarget{constructor(e={}){var r,n,i;super(),globalThis.Worker||this.destroy("Worker not supported"),o._test();const t=e.blendMode||"js",s=typeof createImageBitmap!="undefined"&&((r=e.asyncRender)!=null?r:!0),a=typeof OffscreenCanvas!="undefined"&&((n=e.offscreenRender)!=null?n:!0);this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&((i=e.onDemandRender)!=null?i:!0),this.timeOffset=e.timeOffset||0,this._video=e.video,this._videoHeight=0,this._videoWidth=0,this._canvas=e.canvas,this._video&&!this._canvas?(this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",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._video.nextSibling?this._video.parentNode.insertBefore(this._canvasParent,this._video.nextSibling):this._video.parentNode.appendChild(this._canvasParent)):this._canvas||this.destroy("Don't know where to render: you should give video or canvas in options."),this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d",{desynchronized:!0,willReadFrequently:!0}),this._canvasctrl=a?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!a&&this._canvasctrl.getContext("2d",{desynchronized:!0}),this._lastRenderTime=0,this.debug=!!e.debug,this.prescaleFactor=e.prescaleFactor||1,this.prescaleHeightLimit=e.prescaleHeightLimit||1080,this.maxRenderHeight=e.maxRenderHeight||0,this._worker=new Worker(o._supportsWebAssembly?e.workerUrl||"jassub-worker.js":e.legacyWorkerUrl||"jassub-worker-legacy.js"),this._worker.onmessage=h=>this._onmessage(h),this._worker.onerror=h=>this._error(h),this._init=()=>{var h,c;this._destroyed||(this._worker.postMessage({target:"init",asyncRender:s,onDemandRender:this._onDemandRender,width:this._canvasctrl.width,height:this._canvasctrl.height,preMain:!0,blendMode:t,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,libassMemoryLimit:e.libassMemoryLimit||0,libassGlyphLimit:e.libassGlyphLimit||0,hasAlphaBug:o._hasAlphaBug,useLocalFonts:"queryLocalFonts"in self&&((h=e.useLocalFonts)!=null?h:!0)}),a===!0&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=this.setRate.bind(this),this._video&&this.setVideo(e.video),this._onDemandRender&&(this.busy=!1,this._lastDemandTime=null,(c=this._video)==null||c.requestVideoFrameCallback(this._handleRVFC.bind(this))))}}static _test(){if(o._supportsWebAssembly!==null)return null;const e=document.createElement("canvas"),t=e.getContext("2d",{willReadFrequently:!0});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,c,m){const _=t.createImageData(c,m);return h&&_.data.set(h),_}}try{if(typeof WebAssembly=="object"&&typeof WebAssembly.instantiate=="function"){const i=new WebAssembly.Module(Uint8Array.of(0,97,115,109,1,0,0,0));i instanceof WebAssembly.Module&&(o._supportsWebAssembly=new WebAssembly.Instance(i)instanceof WebAssembly.Instance)}}catch{o._supportsWebAssembly=!1}const s=document.createElement("canvas"),a=s.getContext("2d",{willReadFrequently:!0});e.width=s.width=1,e.height=s.height=1,t.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const r=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 n=a.getImageData(0,0,1,1).data;o._hasAlphaBug=r[1]!==n[1],o._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),e.remove(),s.remove()}resize(e=0,t=0,s=0,a=0,r=(n=>(n=this._video)==null?void 0:n.paused)()){if((!e||!t)&&this._video){const i=this._getVideoPosition();let h=null;if(this._videoWidth){const c=this._video.videoWidth/this._videoWidth,m=this._video.videoHeight/this._videoHeight;h=this._computeCanvasSize((i.width||0)/c,(i.height||0)/m)}else h=this._computeCanvasSize(i.width||0,i.height||0);e=h.width,t=h.height,this._canvasParent&&(s=i.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),a=i.x),this._canvas.style.width=i.width+"px",this._canvas.style.height=i.height+"px"}this._canvas.style.top=s+"px",this._canvas.style.left=a+"px",this.sendMessage("canvas",{width:e,height:t,force:r&&this.busy===!1})}_getVideoPosition(e=this._video.videoWidth,t=this._video.videoHeight){const s=e/t,{offsetWidth:a,offsetHeight:r}=this._video,n=a/r;e=a,t=r,n>s?e=Math.floor(r*s):t=Math.floor(a/s);const i=(a-e)/2,h=(r-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(t<=0||e<=0)e=0,t=0;else{const r=s<1?-1:1;let n=t*a;r*n*s<=r*this.prescaleHeightLimit?n*=s:r*n<r*this.prescaleHeightLimit&&(n=this.prescaleHeightLimit),this.maxRenderHeight>0&&n>this.maxRenderHeight&&(n=this.maxRenderHeight),e*=a*n/t,t=n}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)),e.videoWidth>0&&this.resize(),typeof ResizeObserver!="undefined"&&(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})}setTrack(e){this.sendMessage("setTrack",{content:e})}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})}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",{event: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==null?void 0:t.find(a=>a.fullName.toLowerCase()===e);s&&s.blob().then(a=>{a.arrayBuffer().then(r=>{this.addFont(new Uint8Array(r))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){var t;try{(t=navigator==null?void 0:navigator.permissions)!=null&&t.query?navigator.permissions.query({name:"local-fonts"}).then(s=>{s.state==="granted"&&this._sendLocalFont(e)}):this._sendLocalFont(e)}catch(s){console.warn("Local fonts API:",s)}}_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})}_render({images:e,async:t,times:s,width:a,height:r}){this._unbusy();const n=Date.now();this._canvasctrl.width!==a||this._canvasctrl.height!==r?(this._canvasctrl.width=a,this._canvasctrl.height=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.drawTime=Date.now()-n;let i=0;for(const h in s)i+=s[h];console.log("Bitmaps: "+e.length+" Total: "+Math.round(i)+"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"))}sendMessage(e,t={},s){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(()=>{n(new Error("Error: Timeout while try to fetch "+s))},5e3),r=({data:i})=>{i.target===s&&(t(null,i),this._worker.removeEventListener("message",r),this._worker.removeEventListener("error",n),clearTimeout(a))},n=i=>{t(i),this._worker.removeEventListener("message",r),this._worker.removeEventListener("error",n),clearTimeout(a)};this._worker.addEventListener("message",r),this._worker.addEventListener("error",n),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){this.dispatchEvent(e instanceof ErrorEvent?e:new ErrorEvent("error",{cause:e instanceof Error?e.cause:e})),e instanceof Error||(e instanceof ErrorEvent?e=e.error:e=new Error("error",{cause:e})),console.error(e)}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),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))}destroy(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()}};let d=o;return u(d,"_supportsWebAssembly",null),u(d,"_hasAlphaBug",null),d});
|
package/package.json
CHANGED
package/src/jassub.js
CHANGED
|
@@ -44,6 +44,8 @@ export default class JASSUB extends EventTarget {
|
|
|
44
44
|
|
|
45
45
|
this.timeOffset = options.timeOffset || 0
|
|
46
46
|
this._video = options.video
|
|
47
|
+
this._videoHeight = 0
|
|
48
|
+
this._videoWidth = 0
|
|
47
49
|
this._canvas = options.canvas
|
|
48
50
|
if (this._video && !this._canvas) {
|
|
49
51
|
this._canvasParent = document.createElement('div')
|
|
@@ -82,38 +84,41 @@ export default class JASSUB extends EventTarget {
|
|
|
82
84
|
this._worker.onmessage = e => this._onmessage(e)
|
|
83
85
|
this._worker.onerror = e => this._error(e)
|
|
84
86
|
|
|
85
|
-
this.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
87
|
+
this._init = () => {
|
|
88
|
+
if (this._destroyed) return
|
|
89
|
+
this._worker.postMessage({
|
|
90
|
+
target: 'init',
|
|
91
|
+
asyncRender,
|
|
92
|
+
onDemandRender: this._onDemandRender,
|
|
93
|
+
width: this._canvasctrl.width,
|
|
94
|
+
height: this._canvasctrl.height,
|
|
95
|
+
preMain: true,
|
|
96
|
+
blendMode,
|
|
97
|
+
subUrl: options.subUrl,
|
|
98
|
+
subContent: options.subContent || null,
|
|
99
|
+
fonts: options.fonts || [],
|
|
100
|
+
availableFonts: options.availableFonts || { 'liberation sans': './default.woff2' },
|
|
101
|
+
fallbackFont: options.fallbackFont || 'liberation sans',
|
|
102
|
+
debug: this.debug,
|
|
103
|
+
targetFps: options.targetFps || 24,
|
|
104
|
+
dropAllAnimations: options.dropAllAnimations,
|
|
105
|
+
libassMemoryLimit: options.libassMemoryLimit || 0,
|
|
106
|
+
libassGlyphLimit: options.libassGlyphLimit || 0,
|
|
107
|
+
hasAlphaBug: JASSUB._hasAlphaBug,
|
|
108
|
+
useLocalFonts: ('queryLocalFonts' in self) && (options.useLocalFonts ?? true)
|
|
109
|
+
})
|
|
110
|
+
if (offscreenRender === true) this.sendMessage('offscreenCanvas', null, [this._canvasctrl])
|
|
107
111
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
this._boundResize = this.resize.bind(this)
|
|
113
|
+
this._boundTimeUpdate = this._timeupdate.bind(this)
|
|
114
|
+
this._boundSetRate = this.setRate.bind(this)
|
|
115
|
+
if (this._video) this.setVideo(options.video)
|
|
112
116
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
if (this._onDemandRender) {
|
|
118
|
+
this.busy = false
|
|
119
|
+
this._lastDemandTime = null
|
|
120
|
+
this._video?.requestVideoFrameCallback(this._handleRVFC.bind(this))
|
|
121
|
+
}
|
|
117
122
|
}
|
|
118
123
|
}
|
|
119
124
|
|
|
@@ -179,54 +184,41 @@ export default class JASSUB extends EventTarget {
|
|
|
179
184
|
* @param {Number} [height=0]
|
|
180
185
|
* @param {Number} [top=0]
|
|
181
186
|
* @param {Number} [left=0]
|
|
187
|
+
* @param {Boolean} [force=false]
|
|
182
188
|
*/
|
|
183
|
-
resize (width = 0, height = 0, top = 0, left = 0) {
|
|
184
|
-
let videoSize = null
|
|
189
|
+
resize (width = 0, height = 0, top = 0, left = 0, force = this._video?.paused) {
|
|
185
190
|
if ((!width || !height) && this._video) {
|
|
186
|
-
videoSize = this._getVideoPosition()
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
191
|
+
const videoSize = this._getVideoPosition()
|
|
192
|
+
let renderSize = null
|
|
193
|
+
// support anamorphic video
|
|
194
|
+
if (this._videoWidth) {
|
|
195
|
+
const widthRatio = this._video.videoWidth / this._videoWidth
|
|
196
|
+
const heightRatio = this._video.videoHeight / this._videoHeight
|
|
197
|
+
renderSize = this._computeCanvasSize((videoSize.width || 0) / widthRatio, (videoSize.height || 0) / heightRatio)
|
|
198
|
+
} else {
|
|
199
|
+
renderSize = this._computeCanvasSize(videoSize.width || 0, videoSize.height || 0)
|
|
200
|
+
}
|
|
201
|
+
width = renderSize.width
|
|
202
|
+
height = renderSize.height
|
|
190
203
|
if (this._canvasParent) {
|
|
191
204
|
top = videoSize.y - (this._canvasParent.getBoundingClientRect().top - this._video.getBoundingClientRect().top)
|
|
192
205
|
left = videoSize.x
|
|
193
206
|
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (videoSize != null) {
|
|
197
|
-
this._canvas.style.top = top + 'px'
|
|
198
|
-
this._canvas.style.left = left + 'px'
|
|
199
207
|
this._canvas.style.width = videoSize.width + 'px'
|
|
200
208
|
this._canvas.style.height = videoSize.height + 'px'
|
|
201
209
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
clearTimeout(this._resizeTimeoutBuffer)
|
|
207
|
-
this._resizeTimeoutBuffer = setTimeout(() => {
|
|
208
|
-
this._resizeTimeoutBuffer = undefined
|
|
209
|
-
this._canvasctrl.width = width
|
|
210
|
-
this._canvasctrl.height = height
|
|
211
|
-
this.sendMessage('canvas', { width, height })
|
|
212
|
-
}, 100)
|
|
213
|
-
} else {
|
|
214
|
-
this._canvasctrl.width = width
|
|
215
|
-
this._canvasctrl.height = height
|
|
216
|
-
this.sendMessage('canvas', { width, height })
|
|
217
|
-
this._resizeTimeoutBuffer = setTimeout(() => {
|
|
218
|
-
this._resizeTimeoutBuffer = undefined
|
|
219
|
-
}, 100)
|
|
220
|
-
}
|
|
221
|
-
}
|
|
210
|
+
|
|
211
|
+
this._canvas.style.top = top + 'px'
|
|
212
|
+
this._canvas.style.left = left + 'px'
|
|
213
|
+
this.sendMessage('canvas', { width, height, force: force && this.busy === false })
|
|
222
214
|
}
|
|
223
215
|
|
|
224
|
-
_getVideoPosition () {
|
|
225
|
-
const videoRatio =
|
|
216
|
+
_getVideoPosition (width = this._video.videoWidth, height = this._video.videoHeight) {
|
|
217
|
+
const videoRatio = width / height
|
|
226
218
|
const { offsetWidth, offsetHeight } = this._video
|
|
227
219
|
const elementRatio = offsetWidth / offsetHeight
|
|
228
|
-
|
|
229
|
-
|
|
220
|
+
width = offsetWidth
|
|
221
|
+
height = offsetHeight
|
|
230
222
|
if (elementRatio > videoRatio) {
|
|
231
223
|
width = Math.floor(offsetHeight * videoRatio)
|
|
232
224
|
} else {
|
|
@@ -241,13 +233,14 @@ export default class JASSUB extends EventTarget {
|
|
|
241
233
|
|
|
242
234
|
_computeCanvasSize (width = 0, height = 0) {
|
|
243
235
|
const scalefactor = this.prescaleFactor <= 0 ? 1.0 : this.prescaleFactor
|
|
236
|
+
const ratio = self.devicePixelRatio || 1
|
|
244
237
|
|
|
245
238
|
if (height <= 0 || width <= 0) {
|
|
246
239
|
width = 0
|
|
247
240
|
height = 0
|
|
248
241
|
} else {
|
|
249
242
|
const sgn = scalefactor < 1 ? -1 : 1
|
|
250
|
-
let newH = height
|
|
243
|
+
let newH = height * ratio
|
|
251
244
|
if (sgn * newH * scalefactor <= sgn * this.prescaleHeightLimit) {
|
|
252
245
|
newH *= scalefactor
|
|
253
246
|
} else if (sgn * newH < sgn * this.prescaleHeightLimit) {
|
|
@@ -256,7 +249,7 @@ export default class JASSUB extends EventTarget {
|
|
|
256
249
|
|
|
257
250
|
if (this.maxRenderHeight > 0 && newH > this.maxRenderHeight) newH = this.maxRenderHeight
|
|
258
251
|
|
|
259
|
-
width *= newH / height
|
|
252
|
+
width *= ratio * newH / height
|
|
260
253
|
height = newH
|
|
261
254
|
}
|
|
262
255
|
|
|
@@ -293,9 +286,9 @@ export default class JASSUB extends EventTarget {
|
|
|
293
286
|
video.addEventListener('seeking', this._boundTimeUpdate, false)
|
|
294
287
|
video.addEventListener('playing', this._boundTimeUpdate, false)
|
|
295
288
|
video.addEventListener('ratechange', this._boundSetRate, false)
|
|
289
|
+
video.addEventListener('resize', this._boundResize)
|
|
296
290
|
}
|
|
297
291
|
if (video.videoWidth > 0) this.resize()
|
|
298
|
-
video.addEventListener('resize', this._boundResize)
|
|
299
292
|
// Support Element Resize Observer
|
|
300
293
|
if (typeof ResizeObserver !== 'undefined') {
|
|
301
294
|
if (!this._ro) this._ro = new ResizeObserver(() => this.resize())
|
|
@@ -531,25 +524,36 @@ export default class JASSUB extends EventTarget {
|
|
|
531
524
|
}
|
|
532
525
|
}
|
|
533
526
|
|
|
534
|
-
_handleRVFC (now, { mediaTime }) {
|
|
527
|
+
_handleRVFC (now, { mediaTime, width, height }) {
|
|
535
528
|
if (this._destroyed) return null
|
|
536
529
|
if (this.busy) {
|
|
537
|
-
this._lastDemandTime = mediaTime
|
|
530
|
+
this._lastDemandTime = { mediaTime, width, height }
|
|
538
531
|
} else {
|
|
539
532
|
this.busy = true
|
|
540
|
-
this._demandRender(mediaTime)
|
|
533
|
+
this._demandRender({ mediaTime, width, height })
|
|
541
534
|
}
|
|
542
535
|
this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))
|
|
543
536
|
}
|
|
544
537
|
|
|
545
|
-
_demandRender (
|
|
538
|
+
_demandRender ({ mediaTime, width, height }) {
|
|
546
539
|
this._lastDemandTime = null
|
|
547
|
-
this.
|
|
540
|
+
if (width !== this._videoWidth || height !== this._videoHeight) {
|
|
541
|
+
this._videoWidth = width
|
|
542
|
+
this._videoHeight = height
|
|
543
|
+
this.resize()
|
|
544
|
+
}
|
|
545
|
+
this.sendMessage('demand', { time: mediaTime + this.timeOffset })
|
|
548
546
|
}
|
|
549
547
|
|
|
550
|
-
_render ({ images, async, times }) {
|
|
548
|
+
_render ({ images, async, times, width, height }) {
|
|
549
|
+
this._unbusy()
|
|
551
550
|
const drawStartTime = Date.now()
|
|
552
|
-
|
|
551
|
+
if (this._canvasctrl.width !== width || this._canvasctrl.height !== height) {
|
|
552
|
+
this._canvasctrl.width = width
|
|
553
|
+
this._canvasctrl.height = height
|
|
554
|
+
} else {
|
|
555
|
+
this._ctx.clearRect(0, 0, this._canvasctrl.width, this._canvasctrl.height)
|
|
556
|
+
}
|
|
553
557
|
for (const image of images) {
|
|
554
558
|
if (image.image) {
|
|
555
559
|
if (async) {
|
|
@@ -581,6 +585,7 @@ export default class JASSUB extends EventTarget {
|
|
|
581
585
|
}
|
|
582
586
|
|
|
583
587
|
_ready () {
|
|
588
|
+
this._init()
|
|
584
589
|
this.dispatchEvent(new CustomEvent('ready'))
|
|
585
590
|
}
|
|
586
591
|
|