jassub 1.1.12 → 1.2.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/LICENSE +1 -1
- package/README.md +3 -3
- package/dist/COPYRIGHT +951 -951
- package/dist/jassub-worker-legacy.js +20 -20
- package/dist/jassub-worker.js +1 -1
- package/dist/jassub.es.js +495 -491
- package/dist/jassub.js +1607 -1607
- package/dist/jassub.umd.js +1 -1
- package/package.json +2 -2
- package/src/jassub.js +58 -38
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 p=Object.defineProperty;var g=Object.getOwnPropertySymbols;var y=Object.prototype.hasOwnProperty,b=Object.prototype.propertyIsEnumerable;var u=(h,n,l)=>n in h?p(h,n,{enumerable:!0,configurable:!0,writable:!0,value:l}):h[n]=l,v=(h,n)=>{for(var l in n||(n={}))y.call(n,l)&&u(h,l,n[l]);if(g)for(var l of g(n))b.call(n,l)&&u(h,l,n[l]);return h};var _=(h,n,l)=>(u(h,typeof n!="symbol"?n+"":n,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||e.totalVideoFrames-e.droppedVideoFrames,i=(r,o)=>{const d=this.getVideoPlaybackQuality(),f=this.mozPresentedFrames||d.totalVideoFrames-d.droppedVideoFrames;if(f>t){const c=this.mozFrameDelay||d.totalFrameDelay-e.totalFrameDelay||0,m=o-r;l(o,{presentationTime:o+c*1e3,expectedDisplayTime:o+m,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+m/1e3,presentedFrames:f,processingDuration:c}),delete this._rvfcpolyfillmap[a]}else this._rvfcpolyfillmap[a]=requestAnimationFrame(c=>i(o,c))},a=Date.now(),s=performance.now();return this._rvfcpolyfillmap[a]=requestAnimationFrame(r=>i(s,r)),a},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(l){cancelAnimationFrame(this._rvfcpolyfillmap[l]),delete this._rvfcpolyfillmap[l]});const n=class extends EventTarget{constructor(e={}){var s,r,o;super(),globalThis.Worker||this.destroy("Worker not supported"),n._test();const t=e.blendMode||"js",i=typeof createImageBitmap!="undefined"&&((s=e.asyncRender)!=null?s:!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._canvasParent=null,this._video?(this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",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._canvas=e.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._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d"),this._canvasctrl=a?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!a&&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._worker=new Worker(n._supportsWebAssembly?e.workerUrl||"jassub-worker.js":e.legacyWorkerUrl||"jassub-worker-legacy.js"),this._worker.onmessage=d=>this._onmessage(d),this._worker.onerror=d=>this._error(d),this._worker.postMessage({target:"init",asyncRender:i,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&&!!e.useLocalFonts}),a===!0&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this.setVideo(e.video),this._onDemandRender&&(this.busy=!1,this._video.requestVideoFrameCallback(this._demandRender.bind(this)))}static _test(){if(n._supportsWebAssembly!==null)return null;const e=document.createElement("canvas"),t=e.getContext("2d");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"),window.ImageData=function(d,f,c){const m=t.createImageData(f,c);return d&&m.data.set(d),m}}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 i=document.createElement("canvas"),a=i.getContext("2d");e.width=i.width=1,e.height=i.height=1,t.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const s=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=s[1]!==r[1],n._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),i.remove()}resize(e=0,t=0,i=0,a=0){let s=null;if((!e||!t)&&this._video){s=this._getVideoPosition();const r=this._computeCanvasSize((s.width||0)*(window.devicePixelRatio||1),(s.height||0)*(window.devicePixelRatio||1));e=r.width,t=r.height,i=s.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),a=s.x}s!=null&&(this._canvas.style.top=i+"px",this._canvas.style.left=a+"px",this._canvas.style.width=s.width+"px",this._canvas.style.height=s.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:i}=this._video,a=t/i;let s=t,r=i;a>e?s=Math.floor(i*e):r=Math.floor(t/e);const o=(t-s)/2,d=(i-r)/2;return{width:s,height:r,x:o,y:d}}_computeCanvasSize(e=0,t=0){const i=this.prescaleFactor<=0?1:this.prescaleFactor;if(t<=0||e<=0)e=0,t=0;else{const a=i<1?-1:1;let s=t;a*s*i<=a*this.prescaleHeightLimit?s*=i:a*s<a*this.prescaleHeightLimit&&(s=this.prescaleHeightLimit),this.maxRenderHeight>0&&s>this.maxRenderHeight&&(s=this.maxRenderHeight),e*=s/t,t=s}return{width:e,height:t}}_timeupdate({type:e}){const i={seeking:!0,waiting:!0,playing:!1}[e];i!=null&&(this._playstate=i),this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(e){e instanceof HTMLVideoElement?(this._removeListeners(),this._video=e,this._onDemandRender!==!0&&(this._playstate=e.paused,e.addEventListener("timeupdate",this._timeupdate.bind(this),!1),e.addEventListener("progress",this._timeupdate.bind(this),!1),e.addEventListener("waiting",this._timeupdate.bind(this),!1),e.addEventListener("seeking",this._timeupdate.bind(this),!1),e.addEventListener("playing",this._timeupdate.bind(this),!1),e.addEventListener("ratechange",this.setRate.bind(this),!1)),e.videoWidth>0&&this.resize(),e.addEventListener("resize",this.resize.bind(this)),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,i){this.sendMessage("video",{isPaused:e,currentTime:t,rate:i})}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:i})=>{e(t,i)})}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:i})=>{e(t,i)})}addFont(e){this.sendMessage("addFont",{font:e})}_sendLocalFont(e){try{queryLocalFonts().then(t=>{const i=t&&t.filter(a=>a.fullName.toLowerCase()===e);i&&i.length&&i[0].blob().then(a=>{a.arrayBuffer().then(s=>{this.addFont(new Uint8Array(s))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){var t,i;try{const a=((t=navigator==null?void 0:navigator.permissions)==null?void 0:t.request)||((i=navigator==null?void 0:navigator.permissions)==null?void 0:i.query);a?a({name:"local-fonts"}).then(s=>{s.state==="granted"&&this._sendLocalFont(e)}):"queryLocalFonts"in self&&this._sendLocalFont(e)}catch(a){console.warn("Local fonts API:",a)}}_unbusy(){this.busy=!1}_demandRender(e,t){if(this._destroyed)return null;this.busy||(this.busy=!0,this.sendMessage("demand",{time:t.mediaTime+this.timeOffset})),this._video.requestVideoFrameCallback(this._demandRender.bind(this))}_render({images:e,async:t,times:i}){const a=Date.now();this._ctx.clearRect(0,0,this._canvasctrl.width,this._canvasctrl.height);for(const s of e)s.image&&(t?(this._ctx.drawImage(s.image,s.x,s.y),s.image.close()):(this._bufferCanvas.width=s.w,this._bufferCanvas.height=s.h,this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(s.image)),s.w,s.h),0,0),this._ctx.drawImage(this._bufferCanvas,s.x,s.y)));if(this.debug){i.drawTime=Date.now()-a;let s=0;for(const r in i)s+=i[r];console.log("Bitmaps: "+e.length+" Total: "+Math.round(s)+"ms",i)}}_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={},i){i?this._worker.postMessage(v({target:e,transferable:i},t),[...i]):this._worker.postMessage(v({target:e},t))}_fetchFromWorker(e,t){try{const i=e.target,a=setTimeout(()=>{r(new Error("Error: Timeout while try to fetch "+i))},5e3),s=({data:o})=>{o.target===i&&(t(null,o),this._worker.removeEventListener("message",s),this._worker.removeEventListener("error",r),clearTimeout(a))},r=o=>{t(o),this._worker.removeEventListener("message",s),this._worker.removeEventListener("error",r),clearTimeout(a)};this._worker.addEventListener("message",s),this._worker.addEventListener("error",r),this._worker.postMessage(e)}catch(i){this._error(i)}}_console({content:e,command:t}){console[t].apply(console,JSON.parse(e))}_onmessage({data:e}){this["_"+e.target]&&this["_"+e.target](e)}_error(e){throw e instanceof ErrorEvent||this.dispatchEvent(new ErrorEvent("error",{message:e instanceof Error?e.cause:e})),e instanceof Error?e:new Error(e instanceof ErrorEvent?e.message:"error",{cause:e})}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),this._video.removeEventListener("timeupdate",this._timeupdate),this._video.removeEventListener("progress",this._timeupdate),this._video.removeEventListener("waiting",this._timeupdate),this._video.removeEventListener("seeking",this._timeupdate),this._video.removeEventListener("playing",this._timeupdate),this._video.removeEventListener("ratechange",this.setRate),this._video.removeEventListener("resize",this.resize))}destroy(e){e&&this._error(e),this._video&&this._video.parentNode.removeChild(this._canvasParent),this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),this._worker.terminate()}};let h=n;return _(h,"_supportsWebAssembly",null),_(h,"_hasAlphaBug",null),h});
|
|
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._canvasParent=null,this._video?(this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",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._canvas=e.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._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d"),this._canvasctrl=a?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!a&&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._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.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"),window.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"),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)*(window.devicePixelRatio||1),(i.height||0)*(window.devicePixelRatio||1));e=r.width,t=r.height,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&&t.filter(a=>a.fullName.toLowerCase()===e);s&&s.length&&s[0].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){throw e instanceof ErrorEvent||this.dispatchEvent(new ErrorEvent("error",{message:e instanceof Error?e.cause:e})),e instanceof Error?e:new Error(e instanceof ErrorEvent?e.message:"error",{cause: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._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});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jassub",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "libass Subtitle Renderer and Parser library for browsers",
|
|
5
5
|
"main": "src/jassub.js",
|
|
6
6
|
"files": [
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/ThaUnknown/jassub",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"rvfc-polyfill": "^1.0.
|
|
31
|
+
"rvfc-polyfill": "^1.0.4"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"vite": "^2.9.12"
|
package/src/jassub.js
CHANGED
|
@@ -37,9 +37,9 @@ export default class JASSUB extends EventTarget {
|
|
|
37
37
|
this.destroy('Worker not supported')
|
|
38
38
|
}
|
|
39
39
|
JASSUB._test()
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
40
|
+
const blendMode = options.blendMode || 'js'
|
|
41
|
+
const asyncRender = typeof createImageBitmap !== 'undefined' && (options.asyncRender ?? true)
|
|
42
|
+
const offscreenRender = typeof OffscreenCanvas !== 'undefined' && (options.offscreenRender ?? true)
|
|
43
43
|
this._onDemandRender = 'requestVideoFrameCallback' in HTMLVideoElement.prototype && (options.onDemandRender ?? true)
|
|
44
44
|
|
|
45
45
|
this.timeOffset = options.timeOffset || 0
|
|
@@ -68,8 +68,8 @@ export default class JASSUB extends EventTarget {
|
|
|
68
68
|
this._bufferCanvas = document.createElement('canvas')
|
|
69
69
|
this._bufferCtx = this._bufferCanvas.getContext('2d')
|
|
70
70
|
|
|
71
|
-
this._canvasctrl =
|
|
72
|
-
this._ctx = !
|
|
71
|
+
this._canvasctrl = offscreenRender ? this._canvas.transferControlToOffscreen() : this._canvas
|
|
72
|
+
this._ctx = !offscreenRender && this._canvasctrl.getContext('2d')
|
|
73
73
|
|
|
74
74
|
this._lastRenderTime = 0
|
|
75
75
|
this.debug = !!options.debug
|
|
@@ -84,11 +84,12 @@ export default class JASSUB extends EventTarget {
|
|
|
84
84
|
|
|
85
85
|
this._worker.postMessage({
|
|
86
86
|
target: 'init',
|
|
87
|
-
asyncRender
|
|
87
|
+
asyncRender,
|
|
88
|
+
onDemandRender: this._onDemandRender,
|
|
88
89
|
width: this._canvas.width,
|
|
89
90
|
height: this._canvas.height,
|
|
90
91
|
preMain: true,
|
|
91
|
-
blendMode
|
|
92
|
+
blendMode,
|
|
92
93
|
subUrl: options.subUrl,
|
|
93
94
|
subContent: options.subContent || null,
|
|
94
95
|
fonts: options.fonts || [],
|
|
@@ -100,13 +101,19 @@ export default class JASSUB extends EventTarget {
|
|
|
100
101
|
libassMemoryLimit: options.libassMemoryLimit || 0,
|
|
101
102
|
libassGlyphLimit: options.libassGlyphLimit || 0,
|
|
102
103
|
hasAlphaBug: JASSUB._hasAlphaBug,
|
|
103
|
-
useLocalFonts: ('queryLocalFonts' in self) &&
|
|
104
|
+
useLocalFonts: ('queryLocalFonts' in self) && (options.useLocalFonts ?? true)
|
|
104
105
|
})
|
|
105
|
-
if (
|
|
106
|
+
if (offscreenRender === true) this.sendMessage('offscreenCanvas', null, [this._canvasctrl])
|
|
107
|
+
|
|
108
|
+
this._boundResize = this.resize.bind(this)
|
|
109
|
+
this._boundTimeUpdate = this._timeupdate.bind(this)
|
|
110
|
+
this._boundSetRate = this.setRate.bind(this)
|
|
106
111
|
this.setVideo(options.video)
|
|
112
|
+
|
|
107
113
|
if (this._onDemandRender) {
|
|
108
114
|
this.busy = false
|
|
109
|
-
this.
|
|
115
|
+
this._lastDemandTime = null
|
|
116
|
+
this._video?.requestVideoFrameCallback(this._handleRVFC.bind(this))
|
|
110
117
|
}
|
|
111
118
|
}
|
|
112
119
|
|
|
@@ -119,7 +126,7 @@ export default class JASSUB extends EventTarget {
|
|
|
119
126
|
if (JASSUB._supportsWebAssembly !== null) return null
|
|
120
127
|
|
|
121
128
|
const canvas1 = document.createElement('canvas')
|
|
122
|
-
const ctx1 = canvas1.getContext('2d')
|
|
129
|
+
const ctx1 = canvas1.getContext('2d', { willReadFrequently: true })
|
|
123
130
|
// test ImageData constructor
|
|
124
131
|
if (typeof ImageData.prototype.constructor === 'function') {
|
|
125
132
|
try {
|
|
@@ -150,7 +157,7 @@ export default class JASSUB extends EventTarget {
|
|
|
150
157
|
// Test for alpha bug, where e.g. WebKit can render a transparent pixel
|
|
151
158
|
// (with alpha == 0) as non-black which then leads to visual artifacts.
|
|
152
159
|
const canvas2 = document.createElement('canvas')
|
|
153
|
-
const ctx2 = canvas2.getContext('2d')
|
|
160
|
+
const ctx2 = canvas2.getContext('2d', { willReadFrequently: true })
|
|
154
161
|
|
|
155
162
|
canvas1.width = canvas2.width = 1
|
|
156
163
|
canvas1.height = canvas2.height = 1
|
|
@@ -272,18 +279,20 @@ export default class JASSUB extends EventTarget {
|
|
|
272
279
|
if (video instanceof HTMLVideoElement) {
|
|
273
280
|
this._removeListeners()
|
|
274
281
|
this._video = video
|
|
275
|
-
if (this._onDemandRender
|
|
282
|
+
if (this._onDemandRender) {
|
|
283
|
+
this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))
|
|
284
|
+
} else {
|
|
276
285
|
this._playstate = video.paused
|
|
277
286
|
|
|
278
|
-
video.addEventListener('timeupdate', this.
|
|
279
|
-
video.addEventListener('progress', this.
|
|
280
|
-
video.addEventListener('waiting', this.
|
|
281
|
-
video.addEventListener('seeking', this.
|
|
282
|
-
video.addEventListener('playing', this.
|
|
283
|
-
video.addEventListener('ratechange', this.
|
|
287
|
+
video.addEventListener('timeupdate', this._boundTimeUpdate, false)
|
|
288
|
+
video.addEventListener('progress', this._boundTimeUpdate, false)
|
|
289
|
+
video.addEventListener('waiting', this._boundTimeUpdate, false)
|
|
290
|
+
video.addEventListener('seeking', this._boundTimeUpdate, false)
|
|
291
|
+
video.addEventListener('playing', this._boundTimeUpdate, false)
|
|
292
|
+
video.addEventListener('ratechange', this._boundSetRate, false)
|
|
284
293
|
}
|
|
285
294
|
if (video.videoWidth > 0) this.resize()
|
|
286
|
-
video.addEventListener('resize', this.
|
|
295
|
+
video.addEventListener('resize', this._boundResize)
|
|
287
296
|
// Support Element Resize Observer
|
|
288
297
|
if (typeof ResizeObserver !== 'undefined') {
|
|
289
298
|
if (!this._ro) this._ro = new ResizeObserver(() => this.resize())
|
|
@@ -494,16 +503,15 @@ export default class JASSUB extends EventTarget {
|
|
|
494
503
|
|
|
495
504
|
_getLocalFont ({ font }) {
|
|
496
505
|
try {
|
|
497
|
-
// electron by default has all permissions enabled, and it doesn't have
|
|
498
|
-
// if this happens,
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
query({ name: 'local-fonts' }).then(permission => {
|
|
506
|
+
// electron by default has all permissions enabled, and it doesn't have perm query
|
|
507
|
+
// if this happens, just send it
|
|
508
|
+
if (navigator?.permissions?.query) {
|
|
509
|
+
navigator.permissions.query({ name: 'local-fonts' }).then(permission => {
|
|
502
510
|
if (permission.state === 'granted') {
|
|
503
511
|
this._sendLocalFont(font)
|
|
504
512
|
}
|
|
505
513
|
})
|
|
506
|
-
} else
|
|
514
|
+
} else {
|
|
507
515
|
this._sendLocalFont(font)
|
|
508
516
|
}
|
|
509
517
|
} catch (e) {
|
|
@@ -512,16 +520,28 @@ export default class JASSUB extends EventTarget {
|
|
|
512
520
|
}
|
|
513
521
|
|
|
514
522
|
_unbusy () {
|
|
515
|
-
|
|
523
|
+
// play catchup, leads to more frames being painted, but also more jitter
|
|
524
|
+
if (this._lastDemandTime) {
|
|
525
|
+
this._demandRender(this._lastDemandTime)
|
|
526
|
+
} else {
|
|
527
|
+
this.busy = false
|
|
528
|
+
}
|
|
516
529
|
}
|
|
517
530
|
|
|
518
|
-
|
|
531
|
+
_handleRVFC (now, { mediaTime }) {
|
|
519
532
|
if (this._destroyed) return null
|
|
520
|
-
if (
|
|
533
|
+
if (this.busy) {
|
|
534
|
+
this._lastDemandTime = mediaTime
|
|
535
|
+
} else {
|
|
521
536
|
this.busy = true
|
|
522
|
-
this.
|
|
537
|
+
this._demandRender(mediaTime)
|
|
523
538
|
}
|
|
524
|
-
this._video.requestVideoFrameCallback(this.
|
|
539
|
+
this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
_demandRender (time) {
|
|
543
|
+
this._lastDemandTime = null
|
|
544
|
+
this.sendMessage('demand', { time: time + this.timeOffset })
|
|
525
545
|
}
|
|
526
546
|
|
|
527
547
|
_render ({ images, async, times }) {
|
|
@@ -631,13 +651,13 @@ export default class JASSUB extends EventTarget {
|
|
|
631
651
|
_removeListeners () {
|
|
632
652
|
if (this._video) {
|
|
633
653
|
if (this._ro) this._ro.unobserve(this._video)
|
|
634
|
-
this._video.removeEventListener('timeupdate', this.
|
|
635
|
-
this._video.removeEventListener('progress', this.
|
|
636
|
-
this._video.removeEventListener('waiting', this.
|
|
637
|
-
this._video.removeEventListener('seeking', this.
|
|
638
|
-
this._video.removeEventListener('playing', this.
|
|
639
|
-
this._video.removeEventListener('ratechange', this.
|
|
640
|
-
this._video.removeEventListener('resize', this.
|
|
654
|
+
this._video.removeEventListener('timeupdate', this._boundTimeUpdate)
|
|
655
|
+
this._video.removeEventListener('progress', this._boundTimeUpdate)
|
|
656
|
+
this._video.removeEventListener('waiting', this._boundTimeUpdate)
|
|
657
|
+
this._video.removeEventListener('seeking', this._boundTimeUpdate)
|
|
658
|
+
this._video.removeEventListener('playing', this._boundTimeUpdate)
|
|
659
|
+
this._video.removeEventListener('ratechange', this._boundSetRate)
|
|
660
|
+
this._video.removeEventListener('resize', this._boundResize)
|
|
641
661
|
}
|
|
642
662
|
}
|
|
643
663
|
|