Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

libpgs

Package Overview
Dependencies
Maintainers
0
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

libpgs - npm Package Compare versions

Comparing version 0.4.0 to 0.4.1

dist/pgsRendererHelper.d.ts

2

dist/libpgs.js

@@ -1,1 +0,1 @@

var e=function(){function e(e){var t,s,i=this;if(this.$timeOffset=0,this.onTimeUpdate=function(){i.renderAtVideoTimestamp()},this.updateTimestamps=[],this.previousTimestampIndex=0,this.onWorkerMessage=function(e){if("loaded"===e.data.op)i.updateTimestamps=e.data.updateTimestamps,i.renderAtVideoTimestamp()},e.video&&(this.video=e.video),e.canvas)this.canvas=e.canvas,this.canvasOwner=!1;else{if(!this.video)throw new Error("No canvas or video element was provided!");this.canvas=this.createCanvasElement(),this.canvasOwner=!0,this.video.parentElement.appendChild(this.canvas)}var o=this.canvas.transferControlToOffscreen(),r=null!==(t=e.workerUrl)&&void 0!==t?t:"libpgs.worker.js";this.worker=new Worker(r),this.worker.onmessage=this.onWorkerMessage,this.worker.postMessage({op:"init",canvas:o},[o]),this.$timeOffset=null!==(s=e.timeOffset)&&void 0!==s?s:0,e.subUrl&&this.loadFromUrl(e.subUrl),this.registerVideoEvents()}return Object.defineProperty(e.prototype,"timeOffset",{get:function(){return this.$timeOffset},set:function(e){this.$timeOffset!==e&&(this.$timeOffset=e,this.renderAtVideoTimestamp())},enumerable:!1,configurable:!0}),e.prototype.registerVideoEvents=function(){this.video&&this.video.addEventListener("timeupdate",this.onTimeUpdate)},e.prototype.unregisterVideoEvents=function(){this.video&&this.video.removeEventListener("timeupdate",this.onTimeUpdate)},e.prototype.renderAtVideoTimestamp=function(){this.video&&this.renderAtTimestamp(this.video.currentTime+this.$timeOffset)},e.prototype.createCanvasElement=function(){var e=document.createElement("canvas");return e.style.position="absolute",e.style.top="0",e.style.left="0",e.style.right="0",e.style.bottom="0",e.style.pointerEvents="none",e.style.objectFit="contain",e.style.width="100%",e.style.height="100%",e},e.prototype.destroyCanvasElement=function(){this.canvas.remove()},e.prototype.renderAtTimestamp=function(e){e=1e3*e*90;for(var t=-1,s=0,i=this.updateTimestamps;s<i.length;s++){if(i[s]>e)break;t++}this.previousTimestampIndex!==t&&(this.previousTimestampIndex=t,this.worker.postMessage({op:"render",index:t}))},e.prototype.loadFromUrl=function(e){this.worker.postMessage({op:"loadFromUrl",url:e})},e.prototype.loadFromBuffer=function(e){this.worker.postMessage({op:"loadFromBuffer",buffer:e})},e.prototype.dispose=function(){this.worker.terminate(),this.unregisterVideoEvents(),this.canvasOwner&&this.destroyCanvasElement()},e}();export{e as PgsRenderer};
var e=function(){function e(){}return e.getIndexFromTimestamps=function(e,t){var s=1e3*e*90,i=-1;if(t.length>0&&s<t[t.length-1])for(var o=0,r=t;o<r.length;o++){if(r[o]>s)break;i++}return i},e}(),t=function(){function t(e){var t,s,i=this;if(this.$timeOffset=0,this.onTimeUpdate=function(){i.renderAtVideoTimestamp()},this.updateTimestamps=[],this.previousTimestampIndex=0,this.onWorkerMessage=function(e){if("updateTimestamps"===e.data.op)i.updateTimestamps=e.data.updateTimestamps,i.renderAtVideoTimestamp()},e.video&&(this.video=e.video),e.canvas)this.canvas=e.canvas,this.canvasOwner=!1;else{if(!this.video)throw new Error("No canvas or video element was provided!");this.canvas=this.createCanvasElement(),this.canvasOwner=!0,this.video.parentElement.appendChild(this.canvas)}var o=this.canvas.transferControlToOffscreen(),r=null!==(t=e.workerUrl)&&void 0!==t?t:"libpgs.worker.js";this.worker=new Worker(r),this.worker.onmessage=this.onWorkerMessage,this.worker.postMessage({op:"init",canvas:o},[o]),this.$timeOffset=null!==(s=e.timeOffset)&&void 0!==s?s:0,e.subUrl&&this.loadFromUrl(e.subUrl),this.registerVideoEvents()}return Object.defineProperty(t.prototype,"timeOffset",{get:function(){return this.$timeOffset},set:function(e){this.$timeOffset!==e&&(this.$timeOffset=e,this.renderAtVideoTimestamp())},enumerable:!1,configurable:!0}),t.prototype.registerVideoEvents=function(){this.video&&this.video.addEventListener("timeupdate",this.onTimeUpdate)},t.prototype.unregisterVideoEvents=function(){this.video&&this.video.removeEventListener("timeupdate",this.onTimeUpdate)},t.prototype.renderAtVideoTimestamp=function(){this.video&&this.renderAtTimestamp(this.video.currentTime+this.$timeOffset)},t.prototype.createCanvasElement=function(){var e=document.createElement("canvas");return e.style.position="absolute",e.style.top="0",e.style.left="0",e.style.right="0",e.style.bottom="0",e.style.pointerEvents="none",e.style.objectFit="contain",e.style.width="100%",e.style.height="100%",e},t.prototype.destroyCanvasElement=function(){this.canvas.remove()},t.prototype.renderAtTimestamp=function(t){var s=e.getIndexFromTimestamps(t,this.updateTimestamps);this.previousTimestampIndex!==s&&(this.previousTimestampIndex=s,this.worker.postMessage({op:"render",index:s}))},t.prototype.loadFromUrl=function(e){this.worker.postMessage({op:"loadFromUrl",url:e})},t.prototype.loadFromBuffer=function(e){this.worker.postMessage({op:"loadFromBuffer",buffer:e})},t.prototype.dispose=function(){this.worker.terminate(),this.unregisterVideoEvents(),this.canvasOwner&&this.destroyCanvasElement()},t}();export{t as PgsRenderer};

@@ -1,1 +0,1 @@

function t(t,e,n,i){return new(n||(n=Promise))((function(r,o){function a(t){try{d(i.next(t))}catch(t){o(t)}}function s(t){try{d(i.throw(t))}catch(t){o(t)}}function d(t){var e;t.done?r(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(a,s)}d((i=i.apply(t,e||[])).next())}))}function e(t,e){var n,i,r,o,a={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(s){return function(d){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;o&&(o=0,s[0]&&(a=0)),a;)try{if(n=1,i&&(r=2&s[0]?i.return:s[0]?i.throw||((r=i.return)&&r.call(i),0):i.next)&&!(r=r.call(i,s[1])).done)return r;switch(i=0,r&&(s=[2&s[0],r.value]),s[0]){case 0:case 1:r=s;break;case 4:return a.label++,{value:s[1],done:!1};case 5:a.label++,i=s[1],s=[0];continue;case 7:s=a.ops.pop(),a.trys.pop();continue;default:if(!(r=a.trys,(r=r.length>0&&r[r.length-1])||6!==s[0]&&2!==s[0])){a=0;continue}if(3===s[0]&&(!r||s[1]>r[0]&&s[1]<r[3])){a.label=s[1];break}if(6===s[0]&&a.label<r[1]){a.label=r[1],r=s;break}if(r&&a.label<r[2]){a.label=r[2],a.ops.push(s);break}r[2]&&a.ops.pop(),a.trys.pop();continue}s=e.call(t,a)}catch(t){s=[6,t],i=0}finally{n=r=0}if(5&s[0])throw s[1];return{value:s[0]?s[1]:void 0,done:!0}}([s,d])}}}var n;"function"==typeof SuppressedError&&SuppressedError,function(t){t[t.paletteDefinition=20]="paletteDefinition",t[t.objectDefinition=21]="objectDefinition",t[t.presentationComposition=22]="presentationComposition",t[t.windowDefinition=23]="windowDefinition",t[t.end=128]="end"}(n||(n={}));var i=function(){function t(){this.id=0,this.windowId=0,this.croppedFlag=0,this.horizontalPosition=0,this.verticalPosition=0,this.croppingHorizontalPosition=0,this.croppingVerticalPosition=0,this.croppingWidth=0,this.croppingHeightPosition=0}return Object.defineProperty(t.prototype,"hasCropping",{get:function(){return!!(128&this.croppedFlag)},enumerable:!1,configurable:!0}),t}(),r=function(){function t(){this.width=0,this.height=0,this.frameRate=0,this.compositionNumber=0,this.compositionState=0,this.paletteUpdateFlag=0,this.paletteId=0,this.compositionObjects=[]}return Object.defineProperty(t.prototype,"segmentType",{get:function(){return n.presentationComposition},enumerable:!1,configurable:!0}),t.prototype.read=function(t,e){this.width=t.readUInt16(),this.height=t.readUInt16(),this.frameRate=t.readUInt8(),this.compositionNumber=t.readUInt16(),this.compositionState=t.readUInt8(),this.paletteUpdateFlag=t.readUInt8(),this.paletteId=t.readUInt8();var n=t.readUInt8();this.compositionObjects=[];for(var r=0;r<n;r++){var o=new i;o.id=t.readUInt16(),o.windowId=t.readUInt8(),o.croppedFlag=t.readUInt8(),o.horizontalPosition=t.readUInt16(),o.verticalPosition=t.readUInt16(),o.hasCropping&&(o.croppingHorizontalPosition=t.readUInt16(),o.croppingVerticalPosition=t.readUInt16(),o.croppingWidth=t.readUInt16(),o.croppingHeightPosition=t.readUInt16()),this.compositionObjects.push(o)}},t}(),o=function(){this.y=0,this.cr=0,this.cb=0,this.r=0,this.g=0,this.b=0,this.a=0},a=function(){function t(){this.id=0,this.versionNumber=0,this.entries={}}return Object.defineProperty(t.prototype,"segmentType",{get:function(){return n.paletteDefinition},enumerable:!1,configurable:!0}),t.prototype.read=function(e,n){this.id=e.readUInt8(),this.versionNumber=e.readUInt8();var i=(n-2)/5;this.entries={};for(var r=0;r<i;r++){var a=e.readUInt8(),s=new o;s.y=e.readUInt8(),s.cr=e.readUInt8(),s.cb=e.readUInt8(),s.a=e.readUInt8();var d=s.y,p=s.cb-128,h=s.cr-128;s.r=t.clamp(Math.round(d+1.402*h),0,255),s.g=t.clamp(Math.round(d-.34414*p-.71414*h),0,255),s.b=t.clamp(Math.round(d+1.772*p),0,255),this.entries[a]=s}},t.clamp=function(t,e,n){return Math.max(e,Math.min(t,n))},t}(),s=function(){function t(){this.id=0,this.versionNumber=0,this.lastInSequenceFlag=0,this.width=0,this.height=0,this.dataLength=0}return Object.defineProperty(t.prototype,"isFirstInSequence",{get:function(){return!!(128&this.lastInSequenceFlag)},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"isLastInSequence",{get:function(){return!!(64&this.lastInSequenceFlag)},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"segmentType",{get:function(){return n.objectDefinition},enumerable:!1,configurable:!0}),t.prototype.read=function(t,e){this.id=t.readUInt16(),this.versionNumber=t.readUInt8(),this.lastInSequenceFlag=t.readUInt8(),this.isFirstInSequence?(this.dataLength=t.readUInt24(),this.width=t.readUInt16(),this.height=t.readUInt16(),this.data=t.readBytes(e-11)):this.data=t.readBytes(e-4)},t}(),d=function(){this.id=0,this.horizontalPosition=0,this.verticalPosition=0,this.width=0,this.height=0},p=function(){function t(){this.windows=[]}return Object.defineProperty(t.prototype,"segmentType",{get:function(){return n.windowDefinition},enumerable:!1,configurable:!0}),t.prototype.read=function(t,e){var n=t.readUInt8();this.windows=[];for(var i=0;i<n;i++){var r=new d;r.id=t.readUInt8(),r.horizontalPosition=t.readUInt16(),r.verticalPosition=t.readUInt16(),r.width=t.readUInt16(),r.height=t.readUInt16(),this.windows.push(r)}},t}(),h=function(){function t(){this.presentationTimestamp=0,this.decodingTimestamp=0,this.paletteDefinitions=[],this.objectDefinitions=[],this.windowDefinitions=[]}return t.prototype.read=function(t,e){for(this.presentationTimestamp=0,this.decodingTimestamp=0,this.presentationComposition=void 0,this.paletteDefinitions=[],this.objectDefinitions=[],this.windowDefinitions=[];;){var i=0,o=0;if(e){if(20551!=t.readUInt16())throw new Error("Invalid magic number!");i=t.readUInt32(),o=t.readUInt32()}var d=t.readUInt8(),h=t.readUInt16();switch(d){case n.paletteDefinition:var u=new a;u.read(t,h),this.paletteDefinitions.push(u);break;case n.objectDefinition:var c=new s;c.read(t,h),this.objectDefinitions.push(c);break;case n.presentationComposition:var f=new r;f.read(t,h),this.presentationComposition=f,this.presentationTimestamp=i,this.decodingTimestamp=o;break;case n.windowDefinition:var l=new p;l.read(t,h),this.windowDefinitions.push(l);break;case n.end:return;default:throw new Error("Unsupported segment type ".concat(d))}}},t}(),u=function(){function t(t){this.$position=0,this.array=t}return Object.defineProperty(t.prototype,"position",{get:function(){return this.$position},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"length",{get:function(){return this.array.length},enumerable:!1,configurable:!0}),t.prototype.readByte=function(){return this.array[this.$position++]},t.prototype.readBytes=function(t){var e=this.array.slice(this.$position,this.$position+t);return this.$position+=t,e},t}(),c=function(){function t(t){t instanceof Uint8Array?this.reader=new u(t):this.reader=t}return Object.defineProperty(t.prototype,"position",{get:function(){return this.reader.position},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"length",{get:function(){return this.reader.length},enumerable:!1,configurable:!0}),t.prototype.readUInt8=function(){return this.reader.readByte()},t.prototype.readUInt16=function(){return(this.reader.readByte()<<8)+this.reader.readByte()},t.prototype.readUInt24=function(){return(this.reader.readByte()<<16)+(this.reader.readByte()<<8)+this.reader.readByte()},t.prototype.readUInt32=function(){return(this.reader.readByte()<<24)+(this.reader.readByte()<<16)+(this.reader.readByte()<<8)+this.reader.readByte()},t.prototype.readBytes=function(t){return this.reader.readBytes(t)},t}(),f=function(){function t(){}return t.decode=function(t,e){t instanceof Uint8Array&&(t=new u(t));for(var n=0,i=0,r=0;t.position<t.length;){var o=t.readByte();if(0==o){var a=t.readByte();if(0!=a){var s=!!(128&a),d=63&a;!!(64&a)&&(d=(d<<8)+t.readByte());for(var p=s?t.readByte():0,h=0;h<d;h++)e(r++,n++,i,p)}else n=0,i++}else e(r++,n++,i,o)}},t}(),l=function(){function t(t){this.$position=0,this.subReaderIndex=0,this.subReaders=t.map((function(t){return t instanceof Uint8Array?new u(t):t}));for(var e=0,n=0,i=t;n<i.length;n++){e+=i[n].length}this.$length=e}return Object.defineProperty(t.prototype,"position",{get:function(){return this.$position},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"length",{get:function(){return this.$length},enumerable:!1,configurable:!0}),t.prototype.readByte=function(){for(;this.subReaders[this.subReaderIndex].position>=this.subReaders[this.subReaderIndex].length;)this.subReaderIndex++;return this.$position++,this.subReaders[this.subReaderIndex].readByte()},t.prototype.readBytes=function(t){for(var e=new Uint8Array(t),n=0;n<t;n++)e[n]=this.readByte();return e},t}(),y=new(function(){function n(){this.displaySets=[],this.updateTimestamps=[]}return n.prototype.loadFromUrlAsync=function(n){return t(this,void 0,void 0,(function(){var t;return e(this,(function(e){switch(e.label){case 0:return[4,fetch(n)];case 1:return[4,e.sent().arrayBuffer()];case 2:return t=e.sent(),this.loadFromBuffer(t),[2]}}))}))},n.prototype.loadFromBuffer=function(t){this.displaySets=[],this.updateTimestamps=[];for(var e=new c(new Uint8Array(t));e.position<e.length;){var n=new h;n.read(e,!0),this.displaySets.push(n),this.updateTimestamps.push(n.presentationTimestamp)}},n.prototype.setCanvas=function(t){this.canvas=t,this.context=t.getContext("2d")},n.prototype.renderAtTimestamp=function(t){t=1e3*t*90;for(var e=-1,n=0,i=this.updateTimestamps;n<i.length;n++){if(i[n]>t)break;e++}this.renderAtIndex(e)},n.prototype.renderAtIndex=function(t){var e;if(this.canvas&&this.context&&(this.context.clearRect(0,0,this.canvas.width,this.canvas.height),!(t<0||t>=this.displaySets.length))){var n=this.displaySets[t];if(n.presentationComposition){this.canvas.width==n.presentationComposition.width&&this.canvas.height==n.presentationComposition.height||(this.canvas.width=n.presentationComposition.width,this.canvas.height=n.presentationComposition.height);for(var i=[],r=[],o=[],a=t;a>=0&&(i.unshift.apply(i,this.displaySets[a].objectDefinitions),r.unshift.apply(r,this.displaySets[a].paletteDefinitions),o.unshift.apply(o,this.displaySets[a].windowDefinitions.flatMap((function(t){return t.windows}))),0===(null===(e=this.displaySets[a].presentationComposition)||void 0===e?void 0:e.compositionState));)a--;var s=r.find((function(t){var e;return t.id===(null===(e=n.presentationComposition)||void 0===e?void 0:e.paletteId)}));if(s)for(var d=function(t){var e=o.find((function(e){return e.id===t.windowId}));if(!e)return"continue";var n=p.getPixelDataFromComposition(t,s,i);n&&p.context.drawImage(n,e.horizontalPosition,e.verticalPosition)},p=this,h=0,u=n.presentationComposition.compositionObjects;h<u.length;h++){d(u[h])}}}},n.prototype.createCanvas=function(t,e){if("undefined"!=typeof document){var n=document.createElement("canvas");return n.width=t,n.height=e,n}return new OffscreenCanvas(t,e)},n.prototype.getPixelDataFromComposition=function(t,e,n){for(var i=0,r=0,o=[],a=0,s=n;a<s.length;a++){var d=s[a];d.id==t.id&&(d.isFirstInSequence&&(i=d.width,r=d.height),d.data&&o.push(d.data))}if(0!=o.length){var p=new l(o),h=this.createCanvas(i,r),u=h.getContext("2d"),c=u.createImageData(i,r),y=c.data;return f.decode(p,(function(t,n,i,r){var o=null==e?void 0:e.entries[r];o&&(y[4*t]=o.r,y[4*t+1]=o.g,y[4*t+2]=o.b,y[4*t+3]=o.a)})),u.putImageData(c,0,0),h}},n}()),m=function(){postMessage({op:"loaded",updateTimestamps:y.updateTimestamps})};onmessage=function(t){switch(t.data.op){case"init":var e=t.data.canvas;y.setCanvas(e);break;case"loadFromUrl":var n=t.data.url;y.loadFromUrlAsync(n).then((function(){m()}));break;case"loadFromBuffer":var i=t.data.buffer;y.loadFromBuffer(i),m();break;case"render":var r=t.data.index;y.renderAtIndex(r)}};
function t(t,e,i,n){return new(i||(i=Promise))((function(r,o){function a(t){try{h(n.next(t))}catch(t){o(t)}}function s(t){try{h(n.throw(t))}catch(t){o(t)}}function h(t){var e;t.done?r(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(a,s)}h((n=n.apply(t,e||[])).next())}))}function e(t,e){var i,n,r,o,a={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(s){return function(h){return function(s){if(i)throw new TypeError("Generator is already executing.");for(;o&&(o=0,s[0]&&(a=0)),a;)try{if(i=1,n&&(r=2&s[0]?n.return:s[0]?n.throw||((r=n.return)&&r.call(n),0):n.next)&&!(r=r.call(n,s[1])).done)return r;switch(n=0,r&&(s=[2&s[0],r.value]),s[0]){case 0:case 1:r=s;break;case 4:return a.label++,{value:s[1],done:!1};case 5:a.label++,n=s[1],s=[0];continue;case 7:s=a.ops.pop(),a.trys.pop();continue;default:if(!(r=a.trys,(r=r.length>0&&r[r.length-1])||6!==s[0]&&2!==s[0])){a=0;continue}if(3===s[0]&&(!r||s[1]>r[0]&&s[1]<r[3])){a.label=s[1];break}if(6===s[0]&&a.label<r[1]){a.label=r[1],r=s;break}if(r&&a.label<r[2]){a.label=r[2],a.ops.push(s);break}r[2]&&a.ops.pop(),a.trys.pop();continue}s=e.call(t,a)}catch(t){s=[6,t],n=0}finally{i=r=0}if(5&s[0])throw s[1];return{value:s[0]?s[1]:void 0,done:!0}}([s,h])}}}var i;"function"==typeof SuppressedError&&SuppressedError,function(t){t[t.paletteDefinition=20]="paletteDefinition",t[t.objectDefinition=21]="objectDefinition",t[t.presentationComposition=22]="presentationComposition",t[t.windowDefinition=23]="windowDefinition",t[t.end=128]="end"}(i||(i={}));var n=function(){function t(){this.id=0,this.windowId=0,this.croppedFlag=0,this.horizontalPosition=0,this.verticalPosition=0,this.croppingHorizontalPosition=0,this.croppingVerticalPosition=0,this.croppingWidth=0,this.croppingHeightPosition=0}return Object.defineProperty(t.prototype,"hasCropping",{get:function(){return!!(128&this.croppedFlag)},enumerable:!1,configurable:!0}),t}(),r=function(){function t(){this.width=0,this.height=0,this.frameRate=0,this.compositionNumber=0,this.compositionState=0,this.paletteUpdateFlag=0,this.paletteId=0,this.compositionObjects=[]}return Object.defineProperty(t.prototype,"segmentType",{get:function(){return i.presentationComposition},enumerable:!1,configurable:!0}),t.prototype.read=function(t,e){this.width=t.readUInt16(),this.height=t.readUInt16(),this.frameRate=t.readUInt8(),this.compositionNumber=t.readUInt16(),this.compositionState=t.readUInt8(),this.paletteUpdateFlag=t.readUInt8(),this.paletteId=t.readUInt8();var i=t.readUInt8();this.compositionObjects=[];for(var r=0;r<i;r++){var o=new n;o.id=t.readUInt16(),o.windowId=t.readUInt8(),o.croppedFlag=t.readUInt8(),o.horizontalPosition=t.readUInt16(),o.verticalPosition=t.readUInt16(),o.hasCropping&&(o.croppingHorizontalPosition=t.readUInt16(),o.croppingVerticalPosition=t.readUInt16(),o.croppingWidth=t.readUInt16(),o.croppingHeightPosition=t.readUInt16()),this.compositionObjects.push(o)}},t}(),o=function(){function t(){this.id=0,this.versionNumber=0,this.rgba=[]}return Object.defineProperty(t.prototype,"segmentType",{get:function(){return i.paletteDefinition},enumerable:!1,configurable:!0}),t.prototype.read=function(e,i){this.id=e.readUInt8(),this.versionNumber=e.readUInt8();var n=(i-2)/5,r=new Uint32Array(1),o=new Uint8Array(r.buffer);this.rgba=[];for(var a=0;a<n;a++){var s=e.readUInt8(),h=e.readUInt8(),d=e.readUInt8()-128,u=e.readUInt8()-128,p=e.readUInt8(),c=t.clamp(Math.round(h+1.402*d),0,255),f=t.clamp(Math.round(h-.34414*u-.71414*d),0,255),l=t.clamp(Math.round(h+1.772*u),0,255);o[0]=c,o[1]=f,o[2]=l,o[3]=p,this.rgba[s]=r[0]}},t.clamp=function(t,e,i){return t<e?e:t>i?i:t},t}(),a=function(){function t(){this.id=0,this.versionNumber=0,this.lastInSequenceFlag=0,this.width=0,this.height=0,this.dataLength=0}return Object.defineProperty(t.prototype,"isFirstInSequence",{get:function(){return!!(128&this.lastInSequenceFlag)},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"isLastInSequence",{get:function(){return!!(64&this.lastInSequenceFlag)},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"segmentType",{get:function(){return i.objectDefinition},enumerable:!1,configurable:!0}),t.prototype.read=function(t,e){this.id=t.readUInt16(),this.versionNumber=t.readUInt8(),this.lastInSequenceFlag=t.readUInt8(),this.isFirstInSequence?(this.dataLength=t.readUInt24(),this.width=t.readUInt16(),this.height=t.readUInt16(),this.data=t.readBytes(e-11)):this.data=t.readBytes(e-4)},t}(),s=function(){this.id=0,this.horizontalPosition=0,this.verticalPosition=0,this.width=0,this.height=0},h=function(){function t(){this.windows=[]}return Object.defineProperty(t.prototype,"segmentType",{get:function(){return i.windowDefinition},enumerable:!1,configurable:!0}),t.prototype.read=function(t,e){var i=t.readUInt8();this.windows=[];for(var n=0;n<i;n++){var r=new s;r.id=t.readUInt8(),r.horizontalPosition=t.readUInt16(),r.verticalPosition=t.readUInt16(),r.width=t.readUInt16(),r.height=t.readUInt16(),this.windows.push(r)}},t}(),d=function(){function n(){this.presentationTimestamp=0,this.decodingTimestamp=0,this.paletteDefinitions=[],this.objectDefinitions=[],this.windowDefinitions=[]}return n.prototype.read=function(n,s){return t(this,void 0,void 0,(function(){var t,d,u,p,c,f,l,y,b;return e(this,(function(e){switch(e.label){case 0:this.presentationTimestamp=0,this.decodingTimestamp=0,this.presentationComposition=void 0,this.paletteDefinitions=[],this.objectDefinitions=[],this.windowDefinitions=[],t=void 0,"requestData"in n.baseReader&&(t=n.baseReader),e.label=1;case 1:return d=0,u=0,s?[4,null==t?void 0:t.requestData(10)]:[3,3];case 2:if(e.sent(),20551!=n.readUInt16())throw new Error("Invalid magic number!");d=n.readUInt32(),u=n.readUInt32(),e.label=3;case 3:return[4,null==t?void 0:t.requestData(3)];case 4:return e.sent(),p=n.readUInt8(),c=n.readUInt16(),[4,null==t?void 0:t.requestData(c)];case 5:switch(e.sent(),p){case i.paletteDefinition:(f=new o).read(n,c),this.paletteDefinitions.push(f);break;case i.objectDefinition:(l=new a).read(n,c),this.objectDefinitions.push(l);break;case i.presentationComposition:(y=new r).read(n,c),this.presentationComposition=y,this.presentationTimestamp=d,this.decodingTimestamp=u;break;case i.windowDefinition:(b=new h).read(n,c),this.windowDefinitions.push(b);break;case i.end:return[2];default:throw new Error("Unsupported segment type ".concat(p))}return[3,1];case 6:return[2]}}))}))},n}(),u=function(){function t(t){this.$position=0,this.array=t}return Object.defineProperty(t.prototype,"position",{get:function(){return this.$position},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"length",{get:function(){return this.array.length},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"eof",{get:function(){return this.position>=this.length},enumerable:!1,configurable:!0}),t.prototype.readByte=function(){return this.array[this.$position++]},t.prototype.readBytes=function(t){var e=this.array.slice(this.$position,this.$position+t);return this.$position+=t,e},t}(),p=function(){function t(t){t instanceof Uint8Array?this.baseReader=new u(t):this.baseReader=t}return Object.defineProperty(t.prototype,"position",{get:function(){return this.baseReader.position},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"length",{get:function(){return this.baseReader.length},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"eof",{get:function(){return this.baseReader.eof},enumerable:!1,configurable:!0}),t.prototype.readUInt8=function(){return this.baseReader.readByte()},t.prototype.readUInt16=function(){return(this.baseReader.readByte()<<8)+this.baseReader.readByte()},t.prototype.readUInt24=function(){return(this.baseReader.readByte()<<16)+(this.baseReader.readByte()<<8)+this.baseReader.readByte()},t.prototype.readUInt32=function(){return(this.baseReader.readByte()<<24)+(this.baseReader.readByte()<<16)+(this.baseReader.readByte()<<8)+this.baseReader.readByte()},t.prototype.readBytes=function(t){return this.baseReader.readBytes(t)},t}(),c=function(){function t(){}return t.decode=function(t,e,i){t instanceof Uint8Array&&(t=new u(t));for(var n=0;t.position<t.length;){var r=t.readByte();if(0==r){var o=t.readByte();if(0!=o){var a=!!(128&o),s=63&o;!!(64&o)&&(s=(s<<8)+t.readByte());for(var h=a?t.readByte():0,d=0;d<s;d++)i[n++]=e[h]}}else i[n++]=e[r]}return n},t}(),f=function(){function t(t){this.$position=0,this.subReaderIndex=0,this.subReaders=t.map((function(t){return t instanceof Uint8Array?new u(t):t}));for(var e=0,i=0,n=t;i<n.length;i++){e+=n[i].length}this.$length=e}return t.prototype.push=function(t){t instanceof Uint8Array?this.subReaders.push(new u(t)):this.subReaders.push(t),this.$length+=t.length},Object.defineProperty(t.prototype,"position",{get:function(){return this.$position},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"length",{get:function(){return this.$length},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"eof",{get:function(){return this.position>=this.length},enumerable:!1,configurable:!0}),t.prototype.readByte=function(){for(;this.subReaders[this.subReaderIndex].position>=this.subReaders[this.subReaderIndex].length;)this.subReaderIndex++;return this.$position++,this.subReaders[this.subReaderIndex].readByte()},t.prototype.readBytes=function(t){for(var e=new Uint8Array(t),i=0;i<t;i++)e[i]=this.readByte();return e},t}(),l=function(){function t(){this.empty=!0,this.x=0,this.y=0,this.width=0,this.height=0}return t.prototype.reset=function(){this.empty=!0,this.x=0,this.y=0,this.width=0,this.height=0},t.prototype.set=function(t,e,i,n){void 0===i&&(i=0),void 0===n&&(n=0),this.empty=!1,this.x=t,this.y=e,this.width=i,this.height=n},t.prototype.union=function(t,e,i,n){void 0===i&&(i=0),void 0===n&&(n=0),this.empty?(this.empty=!1,this.x=t,this.y=e,this.width=i,this.height=n):(t<this.x&&(this.width+=this.x-t,this.x=t),e<this.y&&(this.height+=this.y-e,this.y=e),t+i>this.x+this.width&&(this.width=t+i-this.x),e+n>this.y+this.height&&(this.height=e+n-this.y))},t}(),y=function(){function i(t){this.$eof=!1,this.stream=t,this.reader=new f([])}return Object.defineProperty(i.prototype,"position",{get:function(){return this.reader.position},enumerable:!1,configurable:!0}),Object.defineProperty(i.prototype,"length",{get:function(){return this.reader.length},enumerable:!1,configurable:!0}),Object.defineProperty(i.prototype,"eof",{get:function(){return this.$eof},enumerable:!1,configurable:!0}),i.prototype.readByte=function(){return this.reader.readByte()},i.prototype.readBytes=function(t){return this.reader.readBytes(t)},i.prototype.requestData=function(){return t(this,arguments,void 0,(function(t){var i,n,r;return void 0===t&&(t=0),e(this,(function(e){switch(e.label){case 0:return this.reader.position+t+1>this.reader.length&&!this.$eof?[4,this.stream.read()]:[3,2];case 1:return i=e.sent(),n=i.value,r=i.done,n&&this.reader.push(n),r&&(this.$eof=!0),[3,0];case 2:return[2,this.reader.position+t<=this.reader.length]}}))}))},i}(),b=function(){function t(t,e,i){this.width=t,this.height=e,this.compositionData=i}return t.prototype.draw=function(t,e){for(var i=0,n=this.compositionData;i<n.length;i++){n[i].draw(t,e)}},t}(),g=function(){function t(t,e){this.window=t,this.pixelData=e}return t.prototype.draw=function(t,e){t.drawImage(this.pixelData,this.window.horizontalPosition,this.window.verticalPosition),null==e||e.union(this.window.horizontalPosition,this.window.verticalPosition,this.pixelData.width,this.pixelData.height)},t}(),m=function(){function t(){}return t.getIndexFromTimestamps=function(t,e){var i=1e3*t*90,n=-1;if(e.length>0&&i<e[e.length-1])for(var r=0,o=e;r<o.length;r++){if(o[r]>i)break;n++}return n},t}(),w=new(function(){function i(){this.displaySets=[],this.updateTimestamps=[],this.dirtyArea=new l}return i.prototype.loadFromUrl=function(i,n){return t(this,void 0,void 0,(function(){var t,r,o,a;return e(this,(function(e){switch(e.label){case 0:return[4,fetch(i)];case 1:if(!(t=e.sent()).ok)throw new Error("HTTP error: ".concat(t.status));return r=null===(a=t.body)||void 0===a?void 0:a.getReader(),o=new y(r),[4,this.loadFromReader(o,n)];case 2:return e.sent(),[2]}}))}))},i.prototype.loadFromBuffer=function(i,n){return t(this,void 0,void 0,(function(){return e(this,(function(t){switch(t.label){case 0:return[4,this.loadFromReader(new u(new Uint8Array(i)),n)];case 1:return t.sent(),[2]}}))}))},i.prototype.loadFromReader=function(i,n){return t(this,void 0,void 0,(function(){var t,r,o,a;return e(this,(function(e){switch(e.label){case 0:this.displaySets=[],this.updateTimestamps=[],this.cachedRenderData=void 0,t=performance.now(),r=new p(i),e.label=1;case 1:return i.eof?[3,3]:[4,(o=new d).read(r,!0)];case 2:return e.sent(),this.displaySets.push(o),this.updateTimestamps.push(o.presentationTimestamp),(null==n?void 0:n.onProgress)&&(a=performance.now())>t+1e3&&(t=a,n.onProgress()),[3,1];case 3:return(null==n?void 0:n.onProgress)&&n.onProgress(),[2]}}))}))},i.prototype.setCanvas=function(t){this.canvas=t,this.context=t.getContext("2d")},i.prototype.renderAtTimestamp=function(t){var e=m.getIndexFromTimestamps(t,this.updateTimestamps);this.renderAtIndex(e)},i.prototype.renderAtIndex=function(t){if(this.canvas&&this.context){this.dirtyArea.empty||(this.context.clearRect(this.dirtyArea.x,this.dirtyArea.y,this.dirtyArea.width,this.dirtyArea.height),this.dirtyArea.reset());var e=this.buildRenderDataAtIndex(t);e&&(this.canvas.width==e.width&&this.canvas.height==e.height||(this.canvas.width=e.width,this.canvas.height=e.height),e.draw(this.context,this.dirtyArea));var i=t+1,n=this.buildRenderDataAtIndex(t+1);this.cachedRenderData={index:i,data:n}}},i.prototype.buildRenderDataAtIndex=function(t){var e;if(this.cachedRenderData&&this.cachedRenderData.index===t)return this.cachedRenderData.data;if(!(t<0||t>=this.displaySets.length)){var i=this.displaySets[t];if(i.presentationComposition){for(var n=[],r=[],o=[],a=t;a>=0&&(n.unshift.apply(n,this.displaySets[a].objectDefinitions),r.unshift.apply(r,this.displaySets[a].paletteDefinitions),o.unshift.apply(o,this.displaySets[a].windowDefinitions.flatMap((function(t){return t.windows}))),0===(null===(e=this.displaySets[a].presentationComposition)||void 0===e?void 0:e.compositionState));)a--;var s=r.find((function(t){var e;return t.id===(null===(e=i.presentationComposition)||void 0===e?void 0:e.paletteId)}));if(s){for(var h=[],d=function(t){var e=o.find((function(e){return e.id===t.windowId}));if(!e)return"continue";var i=u.getPixelDataFromComposition(t,s,n);i&&h.push(new g(e,i))},u=this,p=0,c=i.presentationComposition.compositionObjects;p<c.length;p++){d(c[p])}if(0!==h.length)return new b(i.presentationComposition.width,i.presentationComposition.height,h)}}}},i.prototype.createCanvas=function(t,e){if("undefined"!=typeof document){var i=document.createElement("canvas");return i.width=t,i.height=e,i}return new OffscreenCanvas(t,e)},i.prototype.getPixelDataFromComposition=function(t,e,i){for(var n=0,r=0,o=[],a=0,s=i;a<s.length;a++){var h=s[a];h.id==t.id&&(h.isFirstInSequence&&(n=h.width,r=h.height),h.data&&o.push(h.data))}if(0!=o.length){var d=new f(o),u=this.createCanvas(n,r),p=u.getContext("2d"),l=p.createImageData(n,r),y=new Uint32Array(l.data.buffer);return c.decode(d,e.rgba,y),p.putImageData(l,0,0),u}},i}()),v=function(){postMessage({op:"updateTimestamps",updateTimestamps:w.updateTimestamps})};onmessage=function(t){switch(t.data.op){case"init":var e=t.data.canvas;w.setCanvas(e);break;case"loadFromUrl":var i=t.data.url;w.loadFromUrl(i,{onProgress:function(){v()}}).then((function(){v()}));break;case"loadFromBuffer":var n=t.data.buffer;w.loadFromBuffer(n).then((function(){v()}));break;case"render":var r=t.data.index;requestAnimationFrame((function(){w.renderAtIndex(r)}))}};

@@ -22,3 +22,3 @@ import { BigEndianBinaryReader } from "../utils/bigEndianBinaryReader";

*/
read(reader: BigEndianBinaryReader, includeHeader: boolean): void;
read(reader: BigEndianBinaryReader, includeHeader: boolean): Promise<void>;
}
import { Segment } from "./segment";
import { BigEndianBinaryReader } from "../utils/bigEndianBinaryReader";
export declare class PaletteEntry {
y: number;
cr: number;
cb: number;
r: number;
g: number;
b: number;
a: number;
}
export declare class PaletteDefinitionSegment implements Segment {
id: number;
versionNumber: number;
entries: {
[key: number]: PaletteEntry;
};
rgba: number[];
get segmentType(): number;

@@ -19,0 +8,0 @@ read(reader: BigEndianBinaryReader, length: number): void;

import { DisplaySet } from "./pgs/displaySet";
import { BinaryReader } from "./utils/binaryReader";
import { RenderData } from "./renderData";
export interface PgsLoadOptions {
/**
* Async pgs streams can return partial updates. When invoked, the `displaySets` and `updateTimestamps` are updated
* to the last available subtitle. There is a minimum threshold of one-second to prevent to many updates.
*/
onProgress?: () => void;
}
/**

@@ -18,11 +27,21 @@ * This handles the low-level PGS loading and rendering. This renderer can operate inside the web worker without being

* @param url The url to the PGS file.
* @param options Optional loading options. Use `onProgress` as callback for partial update while loading.
*/
loadFromUrlAsync(url: string): Promise<void>;
loadFromUrl(url: string, options?: PgsLoadOptions): Promise<void>;
/**
* Loads the subtitle file from the given buffer.
* @param buffer The PGS data.
* @param options Optional loading options. Use `onProgress` as callback for partial update while loading.
*/
loadFromBuffer(buffer: ArrayBuffer): void;
loadFromBuffer(buffer: ArrayBuffer, options?: PgsLoadOptions): Promise<void>;
/**
* Loads the subtitle file from the given buffer.
* @param reader The PGS data reader.
* @param options Optional loading options. Use `onProgress` as callback for partial update while loading.
*/
loadFromReader(reader: BinaryReader, options?: PgsLoadOptions): Promise<void>;
private canvas?;
private context?;
private cachedRenderData?;
private readonly dirtyArea;
/**

@@ -43,4 +62,9 @@ * Sets the canvas to render to.

renderAtIndex(index: number): void;
/**
* Pre-compiles the required render data (windows and pixel data) for the frame at the given index.
* @param index The index of the display set to render.
*/
buildRenderDataAtIndex(index: number): RenderData | undefined;
private createCanvas;
private getPixelDataFromComposition;
}

@@ -11,4 +11,5 @@ import { BinaryReader } from "./binaryReader";

get length(): number;
get eof(): boolean;
readByte(): number;
readBytes(count: number): Uint8Array;
}

@@ -6,6 +6,7 @@ import { BinaryReader } from "./binaryReader";

*/
private readonly reader;
readonly baseReader: BinaryReader;
constructor(buffer: BinaryReader | Uint8Array);
get position(): number;
get length(): number;
get eof(): boolean;
readUInt8(): number;

@@ -12,0 +13,0 @@ readUInt16(): number;

@@ -11,2 +11,6 @@ export interface BinaryReader {

/**
* Gets if the binary reader has reached the end of the data.
*/
get eof(): boolean;
/**
* Reads a single byte from this buffer.

@@ -13,0 +17,0 @@ */

@@ -7,10 +7,16 @@ import { BinaryReader } from "./binaryReader";

private readonly subReaders;
private readonly $length;
private $length;
private $position;
private subReaderIndex;
constructor(subReaders: BinaryReader[] | Uint8Array[]);
/**
* Adding another sub-reader to the collection.
* @param subReader The new sub-reader to add.
*/
push(subReader: BinaryReader | Uint8Array): void;
get position(): number;
get length(): number;
get eof(): boolean;
readByte(): number;
readBytes(count: number): Uint8Array;
}

@@ -9,5 +9,7 @@ import { BinaryReader } from "./binaryReader";

* @param reader The run length encoded binary data reader or buffer.
* @param setter This callback is invoked for every pixel and provides the index, coordinate and value.
* @param source The source maps the index value to the raw output pixel data.
* @param target The pixel data is written to the output.
* @return Returns the number of decoded pixels.
*/
static decode(reader: BinaryReader | Uint8Array, setter: (idx: number, x: number, y: number, value: number) => void): void;
static decode(reader: BinaryReader | Uint8Array, source: number[] | Uint8Array | Uint16Array | Uint32Array, target: number[] | Uint8Array | Uint16Array | Uint32Array): number;
}
{
"name": "libpgs",
"version": "0.4.0",
"version": "0.4.1",
"author": "David Schulte",

@@ -5,0 +5,0 @@ "license": "MIT",

@@ -7,3 +7,3 @@ # libpgs-js

This project is still in progress. It should be able to play 99% of Blue ray subtitles _(Yes, I made that number up)_.
This project is still in progress. It should be able to play 99% of Blu-ray subtitles _(Yes, I made that number up)_.
But some rare used PGS features - like cropping - aren't implemented yet.

@@ -10,0 +10,0 @@

@@ -7,2 +7,3 @@ import {BigEndianBinaryReader} from "../utils/bigEndianBinaryReader";

import {SegmentType} from "./segmentType";
import {AsyncBinaryReader} from "../utils/asyncBinaryReader";

@@ -26,3 +27,3 @@ /**

*/
public read(reader: BigEndianBinaryReader, includeHeader: boolean) {
public async read(reader: BigEndianBinaryReader, includeHeader: boolean) {

@@ -37,2 +38,8 @@ // Clear

// Handles async readers
let asyncReader: AsyncBinaryReader | undefined = undefined;
if ('requestData' in reader.baseReader) {
asyncReader = reader.baseReader as AsyncBinaryReader;
}
while (true)

@@ -46,2 +53,3 @@ {

{
await asyncReader?.requestData(10);
const magicNumber = reader.readUInt16();

@@ -56,4 +64,7 @@ if (magicNumber != 0x5047) {

await asyncReader?.requestData(3);
const type = reader.readUInt8();
const size = reader.readUInt16()
await asyncReader?.requestData(size);
switch (type) {

@@ -60,0 +71,0 @@ case SegmentType.paletteDefinition:

@@ -5,18 +5,6 @@ import {Segment} from "./segment";

export class PaletteEntry {
public y: number = 0;
public cr: number = 0;
public cb: number = 0;
public r: number = 0;
public g: number = 0;
public b: number = 0;
public a: number = 0;
}
export class PaletteDefinitionSegment implements Segment {
public id: number = 0;
public versionNumber: number = 0;
public entries: { [key: number]: PaletteEntry } = {};
public rgba: number[] = [];

@@ -32,22 +20,30 @@ public get segmentType(): number {

const count = (length - 2) / 5;
this.entries = {};
// Creates a buffer to store the mapping as the 4 byte color data.
const data32 = new Uint32Array(1);
const data8 = new Uint8Array(data32.buffer);
this.rgba = [];
for (let i = 0; i < count; i++) {
const id = reader.readUInt8();
const entry = new PaletteEntry();
// Load the YCrCbA value
entry.y = reader.readUInt8();
entry.cr = reader.readUInt8();
entry.cb = reader.readUInt8();
entry.a = reader.readUInt8();
const y = reader.readUInt8();
const cr = reader.readUInt8() - 128;
const cb = reader.readUInt8() - 128;
const a = reader.readUInt8();
// Also store the RGBA value
const y = entry.y;
const cb = entry.cb - 128;
const cr = entry.cr - 128;
entry.r = PaletteDefinitionSegment.clamp(Math.round(y + 1.40200 * cr), 0, 255);
entry.g = PaletteDefinitionSegment.clamp(Math.round(y - 0.34414 * cb - 0.71414 * cr), 0, 255);
entry.b = PaletteDefinitionSegment.clamp(Math.round(y + 1.77200 * cb), 0, 255);
// Convert to rgba
const r = PaletteDefinitionSegment.clamp(Math.round(y + 1.40200 * cr), 0, 255);
const g = PaletteDefinitionSegment.clamp(Math.round(y - 0.34414 * cb - 0.71414 * cr), 0, 255);
const b = PaletteDefinitionSegment.clamp(Math.round(y + 1.77200 * cb), 0, 255);
this.entries[id] = entry;
// Convert to 32bit number for faster copy in the image decode.
// We cannot use the bit-shifting here. The buffers will keep the systems endianness. The same endianness
// must be used to write the rgba values to the pixel buffer to even out.
data8[0] = r;
data8[1] = g;
data8[2] = b;
data8[3] = a;
this.rgba[id] = data32[0];
}

@@ -57,4 +53,4 @@ }

private static clamp(value: number, min: number, max: number): number {
return Math.max(min, Math.min(value, max));
return value < min ? min : value > max ? max : value;
}
}
import {PgsRendererOptions} from "./pgsRendererOptions";
import {PgsRendererHelper} from "./pgsRendererHelper";

@@ -135,13 +136,4 @@ /**

public renderAtTimestamp(time: number): void {
time = time * 1000 * 90; // Convert to PGS time
const index = PgsRendererHelper.getIndexFromTimestamps(time, this.updateTimestamps);
// Find the last subtitle index for the given time stamp
let index = -1;
for (const updateTimestamp of this.updateTimestamps) {
if (updateTimestamp > time) {
break;
}
index++;
}
// Only tell the worker, if the subtitle index was changed!

@@ -167,3 +159,3 @@ if (this.previousTimestampIndex === index) return;

// Is called once a subtitle file was loaded.
case 'loaded':
case 'updateTimestamps':
// Stores the update timestamps, so we don't need to push the timestamp to the worker on every tick.

@@ -170,0 +162,0 @@ // Instead, we push the timestamp index if it was changed.

@@ -9,3 +9,17 @@ import {DisplaySet} from "./pgs/displaySet";

import {WindowDefinition} from "./pgs/windowDefinitionSegment";
import {Rect} from "./utils/rect";
import {StreamBinaryReader} from "./utils/streamBinaryReader";
import {BinaryReader} from "./utils/binaryReader";
import {ArrayBinaryReader} from "./utils/arrayBinaryReader";
import {CompositionRenderData, RenderData} from "./renderData";
import {PgsRendererHelper} from "./pgsRendererHelper";
export interface PgsLoadOptions {
/**
* Async pgs streams can return partial updates. When invoked, the `displaySets` and `updateTimestamps` are updated
* to the last available subtitle. There is a minimum threshold of one-second to prevent to many updates.
*/
onProgress?: () => void;
}
/**

@@ -32,7 +46,13 @@ * This handles the low-level PGS loading and rendering. This renderer can operate inside the web worker without being

* @param url The url to the PGS file.
* @param options Optional loading options. Use `onProgress` as callback for partial update while loading.
*/
public async loadFromUrlAsync(url: string): Promise<void> {
const result = await fetch(url);
const buffer = await result.arrayBuffer();
this.loadFromBuffer(buffer);
public async loadFromUrl(url: string, options?: PgsLoadOptions): Promise<void> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const stream = response.body?.getReader()!;
const reader = new StreamBinaryReader(stream)
await this.loadFromReader(reader, options);
}

@@ -43,13 +63,42 @@

* @param buffer The PGS data.
* @param options Optional loading options. Use `onProgress` as callback for partial update while loading.
*/
public loadFromBuffer(buffer: ArrayBuffer): void {
public async loadFromBuffer(buffer: ArrayBuffer, options?: PgsLoadOptions): Promise<void> {
await this.loadFromReader(new ArrayBinaryReader(new Uint8Array(buffer)), options);
}
/**
* Loads the subtitle file from the given buffer.
* @param reader The PGS data reader.
* @param options Optional loading options. Use `onProgress` as callback for partial update while loading.
*/
public async loadFromReader(reader: BinaryReader, options?: PgsLoadOptions): Promise<void> {
this.displaySets = [];
this.updateTimestamps = [];
const reader = new BigEndianBinaryReader(new Uint8Array(buffer));
while (reader.position < reader.length) {
this.cachedRenderData = undefined;
let lastUpdateTime = performance.now();
const bigEndianReader = new BigEndianBinaryReader(reader);
while (!reader.eof) {
const displaySet = new DisplaySet();
displaySet.read(reader, true);
await displaySet.read(bigEndianReader, true);
this.displaySets.push(displaySet);
this.updateTimestamps.push(displaySet.presentationTimestamp);
// For async loading, we support frequent progress updates. Sending one update for every new display set
// would be too much. Instead, we use a one-second threshold.
if (options?.onProgress) {
let now = performance.now();
if (now > lastUpdateTime + 1000) {
lastUpdateTime = now;
options.onProgress();
}
}
}
// Call final update.
if (options?.onProgress) {
options.onProgress();
}
}

@@ -64,2 +113,9 @@

// Information about the next compiled render data. This is calculated after the current subtitle is renderd.
// So by the time the next subtitle change is requested, this should already be completed.
private cachedRenderData?: { index: number, data: RenderData | undefined };
// We keep track of the dirty area on the canvas. Clearing the whole canvas is slow when only a small area was used.
private readonly dirtyArea = new Rect();
/**

@@ -79,14 +135,3 @@ * Sets the canvas to render to.

public renderAtTimestamp(time: number): void {
time = time * 1000 * 90; // Convert to PGS time
// Find the last subtitle index for the given time stamp
let index = -1;
for (const updateTimestamp of this.updateTimestamps) {
if (updateTimestamp > time) {
break;
}
index++;
}
const index = PgsRendererHelper.getIndexFromTimestamps(time, this.updateTimestamps);
this.renderAtIndex(index);

@@ -102,5 +147,37 @@ }

// Clear the canvas on invalid indices. It is possible to seek to a position before the first subtitle while
// a later subtile is on screen. This subtitle must be clear, even there is no valid new subtitle data.
// a later subtitle is on screen. This subtitle must be clear, even there is no valid new subtitle data.
// Ignoring the render would keep the previous subtitle on screen.
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
if (!this.dirtyArea.empty) {
this.context.clearRect(this.dirtyArea.x, this.dirtyArea.y, this.dirtyArea.width, this.dirtyArea.height);
this.dirtyArea.reset();
}
const renderData = this.buildRenderDataAtIndex(index);
if (renderData) {
// Resize the canvas if needed.
if (this.canvas.width != renderData.width ||
this.canvas.height != renderData.height) {
this.canvas.width = renderData.width;
this.canvas.height = renderData.height;
}
renderData.draw(this.context, this.dirtyArea);
}
// Pre-calculating the next subtitle, so it is ready whenever the next subtitle change is requested.
const nextIndex = index + 1;
const nextRenderData = this.buildRenderDataAtIndex(index + 1);
this.cachedRenderData = { index: nextIndex, data: nextRenderData };
}
/**
* Pre-compiles the required render data (windows and pixel data) for the frame at the given index.
* @param index The index of the display set to render.
*/
public buildRenderDataAtIndex(index: number): RenderData | undefined {
// Check if this index was already cached.
if (this.cachedRenderData && this.cachedRenderData.index === index) {
return this.cachedRenderData.data;
}
if (index < 0 || index >= this.displaySets.length) {

@@ -113,9 +190,2 @@ return;

// Resize the canvas if needed.
if (this.canvas.width != displaySet.presentationComposition.width ||
this.canvas.height != displaySet.presentationComposition.height) {
this.canvas.width = displaySet.presentationComposition.width;
this.canvas.height = displaySet.presentationComposition.height;
}
// We need to collect all valid objects and palettes up to this point. PGS can update and reuse elements from

@@ -150,2 +220,3 @@ // previous display sets. The `compositionState` defines if the previous elements should be cleared.

const compositionData: CompositionRenderData[] = [];
for (const compositionObject of displaySet.presentationComposition.compositionObjects) {

@@ -159,5 +230,10 @@ // Find the window to draw on.

if (pixelData) {
this.context.drawImage(pixelData, window.horizontalPosition, window.verticalPosition);
compositionData.push(new CompositionRenderData(window, pixelData));
}
}
if (compositionData.length === 0) return;
return new RenderData(displaySet.presentationComposition.width, displaySet.presentationComposition.height,
compositionData);
}

@@ -208,16 +284,7 @@

const imageData = context.createImageData(width, height);
const buffer = imageData.data;
const imageBuffer = new Uint32Array(imageData.data.buffer);
// The pixel data is run-length encoded. The decoded value is the palette entry index.
RunLengthEncoding.decode(data, (idx, x, y, value) => {
const col = palette?.entries[value];
if (!col) return;
RunLengthEncoding.decode(data, palette.rgba, imageBuffer);
// Writing the four byte pixel data as RGBA.
buffer[idx * 4] = col.r;
buffer[idx * 4 + 1] = col.g;
buffer[idx * 4 + 2] = col.b;
buffer[idx * 4 + 3] = col.a;
});
context.putImageData(imageData, 0, 0);

@@ -224,0 +291,0 @@ return canvas;

@@ -23,2 +23,6 @@ import {BinaryReader} from "./binaryReader";

public get eof(): boolean {
return this.position >= this.length;
}
public readByte(): number {

@@ -25,0 +29,0 @@ return this.array[this.$position++];

@@ -8,10 +8,10 @@ import {BinaryReader} from "./binaryReader";

*/
private readonly reader: BinaryReader;
public readonly baseReader: BinaryReader;
public constructor(buffer: BinaryReader | Uint8Array) {
if (buffer instanceof Uint8Array) {
this.reader = new ArrayBinaryReader(buffer);
this.baseReader = new ArrayBinaryReader(buffer);
}
else {
this.reader = buffer;
this.baseReader = buffer;
}

@@ -21,16 +21,20 @@ }

public get position(): number {
return this.reader.position;
return this.baseReader.position;
}
public get length(): number {
return this.reader.length;
return this.baseReader.length;
}
public get eof(): boolean {
return this.baseReader.eof;
}
public readUInt8(): number {
return this.reader.readByte();
return this.baseReader.readByte();
}
public readUInt16(): number {
const b1 = this.reader.readByte();
const b2 = this.reader.readByte();
const b1 = this.baseReader.readByte();
const b2 = this.baseReader.readByte();
return (b1 << 8) + b2;

@@ -40,5 +44,5 @@ }

public readUInt24(): number {
const b1 = this.reader.readByte();
const b2 = this.reader.readByte();
const b3 = this.reader.readByte();
const b1 = this.baseReader.readByte();
const b2 = this.baseReader.readByte();
const b3 = this.baseReader.readByte();
return (b1 << 16) + (b2 << 8) + b3;

@@ -48,6 +52,6 @@ }

public readUInt32(): number {
const b1 = this.reader.readByte();
const b2 = this.reader.readByte();
const b3 = this.reader.readByte();
const b4 = this.reader.readByte();
const b1 = this.baseReader.readByte();
const b2 = this.baseReader.readByte();
const b3 = this.baseReader.readByte();
const b4 = this.baseReader.readByte();
return (b1 << 24) + (b2 << 16) + (b3 << 8) + b4;

@@ -57,4 +61,4 @@ }

public readBytes(count: number): Uint8Array {
return this.reader.readBytes(count);
return this.baseReader.readBytes(count);
}
}

@@ -13,2 +13,7 @@ export interface BinaryReader {

/**
* Gets if the binary reader has reached the end of the data.
*/
get eof(): boolean;
/**
* Reads a single byte from this buffer.

@@ -15,0 +20,0 @@ */

@@ -9,3 +9,3 @@ import {BinaryReader} from "./binaryReader";

private readonly subReaders: BinaryReader[];
private readonly $length: number;
private $length: number;

@@ -30,2 +30,16 @@ private $position: number = 0;

/**
* Adding another sub-reader to the collection.
* @param subReader The new sub-reader to add.
*/
public push(subReader: BinaryReader | Uint8Array) {
if (subReader instanceof Uint8Array) {
this.subReaders.push(new ArrayBinaryReader(subReader));
} else {
this.subReaders.push(subReader);
}
this.$length += subReader.length;
}
public get position(): number {

@@ -39,2 +53,6 @@ return this.$position;

public get eof(): boolean {
return this.position >= this.length;
}
public readByte(): number {

@@ -41,0 +59,0 @@ while (this.subReaders[this.subReaderIndex].position >= this.subReaders[this.subReaderIndex].length) {

@@ -11,5 +11,9 @@ import {BinaryReader} from "./binaryReader";

* @param reader The run length encoded binary data reader or buffer.
* @param setter This callback is invoked for every pixel and provides the index, coordinate and value.
* @param source The source maps the index value to the raw output pixel data.
* @param target The pixel data is written to the output.
* @return Returns the number of decoded pixels.
*/
static decode(reader: BinaryReader | Uint8Array, setter: (idx: number, x: number, y: number, value: number) => void): void {
static decode(reader: BinaryReader | Uint8Array,
source: number[] | Uint8Array | Uint16Array | Uint32Array,
target: number[] | Uint8Array | Uint16Array | Uint32Array): number {
if (reader instanceof Uint8Array) {

@@ -19,4 +23,2 @@ reader = new ArrayBinaryReader(reader);

let x = 0;
let y = 0;
let idx = 0;

@@ -27,3 +29,3 @@ while (reader.position < reader.length) {

if (byte1 != 0x00) {
setter(idx++, x++, y, byte1);
target[idx++] = source[byte1];
continue;

@@ -35,4 +37,2 @@ }

if (byte2 == 0x00) {
x = 0;
y++;
continue;

@@ -49,6 +49,8 @@ }

for (let i = 0; i < num; i++) {
setter(idx++, x++, y, value);
target[idx++] = source[value];
}
}
return idx;
}
}

@@ -6,5 +6,5 @@ import {PgsRendererInternal} from "./pgsRendererInternal";

// Inform the main process that the subtitle data was loaded and return all update timestamps
const sendLoadedMessage = () => {
const submitTimestamps = () => {
postMessage({
op: 'loaded',
op: 'updateTimestamps',
updateTimestamps: renderer.updateTimestamps

@@ -24,4 +24,8 @@ })

const url: string = e.data.url;
renderer.loadFromUrlAsync(url).then(() => {
sendLoadedMessage();
renderer.loadFromUrl(url, {
onProgress: () => {
submitTimestamps();
}
}).then(() => {
submitTimestamps();
});

@@ -32,5 +36,6 @@ break;

const buffer: ArrayBuffer = e.data.buffer;
renderer.loadFromBuffer(buffer);
renderer.loadFromBuffer(buffer).then(() => {
submitTimestamps();
});
sendLoadedMessage();
break;

@@ -40,3 +45,5 @@

const index: number = e.data.index;
renderer.renderAtIndex(index);
requestAnimationFrame(() => {
renderer.renderAtIndex(index);
});
break;

@@ -43,0 +50,0 @@ }

@@ -8,3 +8,3 @@ /**

test('load and render pgs', () => {
test('load and render pgs', async () => {
const dataSup = fs.readFileSync(`${__dirname}/files/test.sup`);

@@ -16,3 +16,3 @@

renderer.setCanvas(canvas);
renderer.loadFromBuffer(dataSup);
await renderer.loadFromBuffer(dataSup);

@@ -19,0 +19,0 @@ // Helper function to render and compare the image in the test directory.

@@ -8,12 +8,11 @@ import {RunLengthEncoding} from "../src/utils/runLengthEncoding";

]);
const result: {idx: number, x: number, y: number, value: number}[] = [];
RunLengthEncoding.decode(data, (idx, x, y, value) => {
result.push({ idx, x, y, value });
});
const map = [0x00, 0x01, 0x02, 0x03, 0x04];
const output = new Uint8Array(4);
const length = RunLengthEncoding.decode(data, map, output);
expect(result.length).toBe(4);
expect(result[0]).toMatchObject({ idx: 0, x: 0, y: 0, value: 0x01 });
expect(result[1]).toMatchObject({ idx: 1, x: 1, y: 0, value: 0x02 });
expect(result[2]).toMatchObject({ idx: 2, x: 0, y: 1, value: 0x03 });
expect(result[3]).toMatchObject({ idx: 3, x: 1, y: 1, value: 0x04 });
expect(length).toBe(4);
expect(output[0]).toBe(0x01);
expect(output[1]).toBe(0x02);
expect(output[2]).toBe(0x03);
expect(output[3]).toBe(0x04);
});

@@ -25,10 +24,9 @@

]);
const result: {idx: number, x: number, y: number, value: number}[] = [];
RunLengthEncoding.decode(data, (idx, x, y, value) => {
result.push({ idx, x, y, value });
});
const map = [0x00];
const output = new Uint8Array(8);
const length = RunLengthEncoding.decode(data, map, output);
expect(result.length).toBe(8);
expect(length).toBe(8);
for (let i = 0; i < 8; i++) {
expect(result[i]).toMatchObject({ idx: i, x: i, y: 0, value: 0x00 });
expect(output[i]).toBe(0x00);
}

@@ -41,10 +39,9 @@ });

]);
const result: {idx: number, x: number, y: number, value: number}[] = [];
RunLengthEncoding.decode(data, (idx, x, y, value) => {
result.push({ idx, x, y, value });
});
const map = [0x00, 0x01];
const output = new Uint8Array(8);
const length = RunLengthEncoding.decode(data, map, output);
expect(result.length).toBe(8);
expect(length).toBe(8);
for (let i = 0; i < 8; i++) {
expect(result[i]).toMatchObject({ idx: i, x: i, y: 0, value: 0x01 });
expect(output[i]).toBe(0x01);
}

@@ -57,10 +54,9 @@ });

]);
const result: {idx: number, x: number, y: number, value: number}[] = [];
RunLengthEncoding.decode(data, (idx, x, y, value) => {
result.push({ idx, x, y, value });
});
const map = [0x00];
const output = new Uint8Array(522);
const length = RunLengthEncoding.decode(data, map, output);
expect(result.length).toBe(522);
expect(length).toBe(522);
for (let i = 0; i < 522; i++) {
expect(result[i]).toMatchObject({ idx: i, x: i, y: 0, value: 0x00 });
expect(output[i]).toBe(0x00);
}

@@ -74,11 +70,10 @@ });

]);
const result: {idx: number, x: number, y: number, value: number}[] = [];
RunLengthEncoding.decode(data, (idx, x, y, value) => {
result.push({ idx, x, y, value });
});
const map = [0x00, 0x01];
const output = new Uint8Array(522);
const length = RunLengthEncoding.decode(data, map, output);
expect(result.length).toBe(522);
expect(length).toBe(522);
for (let i = 0; i < 522; i++) {
expect(result[i]).toMatchObject({ idx: i, x: i, y: 0, value: 0x01 });
expect(output[i]).toBe(0x01);
}
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc