long-screen 0.0.0-stage → 0.1.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 +21 -0
- package/README.md +58 -2
- package/THIRD_PARTY_NOTICES.txt +1044 -0
- package/convert-worker.js +43 -0
- package/core-helper.js +1 -0
- package/core.simd.wasm +0 -0
- package/core.threads.wasm +0 -0
- package/core.wasm +0 -0
- package/index.d.ts +158 -0
- package/index.js +37 -0
- package/package.json +43 -4
- package/worker.js +108 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
var U={format:"RGBA",colorSpace:"srgb"};async function D(a,e,r,t){let s=t&&t.length===e*r*4?t:new Uint8ClampedArray(e*r*4),n=await a.copyTo(s,U);if(n.length!==1||n[0].offset!==0||n[0].stride!==e*4)throw new Error(`unexpected RGBA layout ${JSON.stringify(n)}`);return s}var B=class{free=[];length=0;outstandingCount=0;get outstanding(){return this.outstandingCount}takeBuffer(e){e!==this.length&&(this.free.length=0,this.length=e);let r=this.free.pop()??new ArrayBuffer(e);return this.outstandingCount++,r}releaseBuffer(e){this.outstandingCount--,e.byteLength===this.length&&this.free.push(e)}take(e,r){let t=this.takeBuffer(e*r*4),s=!1;return{width:e,height:r,data:new Uint8ClampedArray(t),release:()=>{s||(s=!0,this.releaseBuffer(t))}}}};function R(a,...e){!a||e.includes(a)||a.release?.()}function F(a,e,r){if(a&&(a.matrix==="rgb"||a.matrix===null&&e==="I444"))return;let t=a?.matrix??(r>=720?"bt709":"smpte170m");if(!(t==="bt2020-ncl"||t==="bt2020-cl"))return(t==="bt709"?0:2)+(a?.fullRange?1:0)}var Y=8,X=48,j=.01;function S(a){return a.meanDiff<=Y&&a.farShare<=j}function _(a,e){let r=0,t=0,s=0;for(let n=0;n<a.length;n+=4)for(let i=0;i<3;i++){let u=Math.abs(a[n+i]-e[n+i]);r+=u,u>X&&t++,s++}return{meanDiff:r/s,farShare:t/s}}var W=1,K=4,I=8,J=64,Q=128,Z=2,ee=4,q=64,te=`
|
|
2
|
+
struct Params { size: vec2<u32>, chroma: vec2<u32>, nv12: u32, luma: vec2<f32>, chromaScale: f32, matrix: vec4<f32> }
|
|
3
|
+
@group(0) @binding(0) var lumaPlane: texture_2d<f32>;
|
|
4
|
+
@group(0) @binding(1) var uPlane: texture_2d<f32>;
|
|
5
|
+
@group(0) @binding(2) var vPlane: texture_2d<f32>;
|
|
6
|
+
@group(0) @binding(3) var linear: sampler;
|
|
7
|
+
@group(0) @binding(4) var<storage, read_write> rgba: array<u32>;
|
|
8
|
+
@group(0) @binding(5) var<uniform> p: Params;
|
|
9
|
+
@compute @workgroup_size(${q})
|
|
10
|
+
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
11
|
+
if (id.x >= p.size.x || id.y >= p.size.y) { return; }
|
|
12
|
+
let y = (textureLoad(lumaPlane, id.xy, 0).r * 255.0 - p.luma.y) * p.luma.x;
|
|
13
|
+
// Chroma interpolated between its samples at this pixel's centre (half resolution in both axes), as Chrome's own
|
|
14
|
+
// copyTo(RGBA) of a hardware-decoded frame does; point sampling is up to 130 levels off it at a sharp colour edge.
|
|
15
|
+
let at = (vec2<f32>(id.xy) + 0.5) * 0.5 / vec2<f32>(p.chroma);
|
|
16
|
+
var c = textureSampleLevel(uPlane, linear, at, 0.0).rg;
|
|
17
|
+
if (p.nv12 == 0u) { c = vec2<f32>(c.r, textureSampleLevel(vPlane, linear, at, 0.0).r); }
|
|
18
|
+
let cb = (c.x * 255.0 - 128.0) * p.chromaScale;
|
|
19
|
+
let cr = (c.y * 255.0 - 128.0) * p.chromaScale;
|
|
20
|
+
let rgb = vec3<f32>(y + p.matrix.x * cr, y - p.matrix.y * cb - p.matrix.z * cr, y + p.matrix.w * cb);
|
|
21
|
+
let v = vec3<u32>(round(clamp(rgb, vec3<f32>(0.0), vec3<f32>(255.0))));
|
|
22
|
+
rgba[id.y * p.size.x + id.x] = v.r | (v.g << 8u) | (v.b << 16u) | 0xff000000u;
|
|
23
|
+
}`;function re(a,e,r,t){let[s,n]=t<2?[.2126,.0722]:[.299,.114],i=1-s-n,u=t%2===1,m=new ArrayBuffer(64),f=new Uint32Array(m),d=new Float32Array(m);return f.set([a,e,Math.ceil(a/2),Math.ceil(e/2),r?1:0]),d.set(u?[1,0]:[255/219,16],6),d[8]=u?1:255/224,d.set([2*(1-s),2*n*(1-n)/i,2*s*(1-s)/i,2*(1-n)],12),m}function C(a,e){let r;return Promise.race([a,new Promise((t,s)=>{r=setTimeout(()=>s(new Error("WebGPU operation timed out")),e)})]).finally(()=>clearTimeout(r))}var O=class{device;pipeline;sampler;adapter;key;storage;planes;group;uniforms;idle;scratch;lost;constructor(e,r,t,s){this.device=e,this.pipeline=r,this.sampler=t,this.adapter=s,this.key="",this.planes=[],this.idle=[],this.scratch=new B,this.uniforms=e.createBuffer({size:64,usage:J|I}),e.lost.then(n=>{this.lost=n.message||"device lost"})}prepare(e,r,t,s){let n=`${e}x${r} ${t} ${s}`,i=this.device;if(n===this.key)return;let u=e*r*4;if(u>i.limits.maxStorageBufferBindingSize||u>i.limits.maxBufferSize)throw new Error(`a ${e}\xD7${r} frame exceeds this device's buffer limits`);this.release();let m=Math.ceil(e/2),f=Math.ceil(r/2),d=ee|Z;this.planes=t==="NV12"?[i.createTexture({size:[e,r],format:"r8unorm",usage:d}),i.createTexture({size:[m,f],format:"rg8unorm",usage:d})]:[i.createTexture({size:[e,r],format:"r8unorm",usage:d}),i.createTexture({size:[m,f],format:"r8unorm",usage:d}),i.createTexture({size:[m,f],format:"r8unorm",usage:d})],this.storage=i.createBuffer({size:u,usage:Q|K}),i.queue.writeBuffer(this.uniforms,0,new Uint8Array(re(e,r,t==="NV12",s)));let[y,b,x=b]=this.planes;this.group=i.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:y.createView()},{binding:1,resource:b.createView()},{binding:2,resource:x.createView()},{binding:3,resource:this.sampler},{binding:4,resource:{buffer:this.storage}},{binding:5,resource:{buffer:this.uniforms}}]}),this.key=n}async convert(e,r,t,s,n,i){if(this.lost)throw new Error(`GPU_CONVERSION_LOST: ${this.lost}`);let u=e.allocationSize(),m=this.scratch.takeBuffer(u),f;try{let y=await e.copyTo(new Uint8Array(m,0,u));if(this.lost)throw new Error(`GPU_CONVERSION_LOST: ${this.lost}`);this.prepare(r,t,s,n);let b=this.device,x=r*t*4,A=new Uint8Array(m,0,u),w=Math.ceil(r/2),l=Math.ceil(t/2);f=this.idle.pop()??b.createBuffer({size:x,usage:W|I}),this.planes.forEach((p,g)=>{let h=y[g];b.queue.writeTexture({texture:p},A,{offset:h.offset,bytesPerRow:h.stride},g?[w,l]:[r,t])});let o=b.createCommandEncoder(),c=o.beginComputePass();c.setPipeline(this.pipeline),c.setBindGroup(0,this.group),c.dispatchWorkgroups(Math.ceil(r/q),t),c.end(),o.copyBufferToBuffer(this.storage,0,f,0,x),b.queue.submit([o.finish()])}finally{this.scratch.releaseBuffer(m)}let d=this.key;try{await f.mapAsync(W)}catch(y){throw f.destroy(),new Error(`GPU_CONVERSION_LOST: ${this.lost??(y instanceof Error?y.message:String(y))}`)}i.set(new Uint8Array(f.getMappedRange())),f.unmap(),d===this.key?this.idle.push(f):f.destroy()}release(){this.storage?.destroy();for(let e of this.planes)e.destroy();for(let e of this.idle)e.destroy();this.storage=void 0,this.planes=[],this.idle=[],this.key=""}destroy(){this.release(),this.uniforms.destroy(),this.device.destroy()}};async function ne(a){if(!a)throw new Error("WebGPU unavailable");let e=await C(a.requestAdapter({powerPreference:"high-performance"}),4e3);if(!e)throw new Error("no WebGPU adapter");let r=e.limits,t=await C(e.requestDevice(r?{requiredLimits:{maxStorageBufferBindingSize:r.maxStorageBufferBindingSize,maxBufferSize:r.maxBufferSize}}:{}),4e3);try{let s=await C(t.createComputePipelineAsync({layout:"auto",compute:{module:t.createShaderModule({code:te}),entryPoint:"main"}}),4e3),n=t.createSampler({magFilter:"linear",minFilter:"linear"}),i=[e.info?.vendor,e.info?.architecture].filter(Boolean).join(" ")||"WebGPU";return new O(t,s,n,i)}catch(s){throw t.destroy(),s}}function ae(a,e){let r=e.codedWidth,t=e.codedHeight,s=a.visibleRect;if(e.rotation!==0)return"rotated container";if(a.displayWidth!==r||a.displayHeight!==t)return"display size differs from the coded size";if(s&&(s.width!==r||s.height!==t||s.x%2||s.y%2))return"visible rectangle off the chroma grid";if(a.format!=="NV12"&&a.format!=="I420")return`${a.format} planes`;let n=F(a.colorSpace??void 0,a.format,t);return n===void 0?`${a.colorSpace?.matrix} matrix`:{format:a.format,code:n}}var se=3;function H(a,e="auto",r,t=new B){let s,n,i,u,m,f=l=>{s=!1,u??=l,n?.destroy(),n=void 0},d=async(l,o,c)=>{let p=o.codedWidth,g=o.codedHeight,h=t.take(p,g);try{return await n.convert(l,p,g,c.format,c.code,h.data),h}catch(v){throw h.release(),v}},y=async l=>{R(await l());let o,c=1/0;try{for(let p=0;p<se;p++){o&&R(o),o=void 0;let g=performance.now();o=await l(),c=Math.min(c,performance.now()-g)}}catch(p){throw o&&R(o),p}return{image:o,ms:c}},b=async(l,o,c)=>{let p,g;try{n=await ne(r===void 0?globalThis.navigator?.gpu:r),{image:p,ms:g}=await y(()=>d(l,o,c))}catch(P){return f(P instanceof Error?P.message:String(P)),a(l,o)}let h,v;return e==="auto"?{image:h,ms:v}=await y(async()=>await a(l,o)):h=await a(l,o),i={..._(h.data,p.data),gpuMS:g,fallbackMS:v,adapter:n.adapter},S(i)?v!==void 0&&g>=v*.9?(p.release(),f(`${g.toFixed(1)} ms against ${v.toFixed(1)} ms`),h):(s=!0,R(h),a.dispose?.(),p):(p.release(),f(`first frame ${i.meanDiff.toFixed(1)} from ${a.describe?.()??"the fallback"}`),h)};return Object.defineProperties(Object.assign(async(l,o)=>{for(;m;)await m.catch(()=>{});if(s===!1)return a(l,o);let c=ae(l,o);if(s){if(typeof c=="string")throw new Error(`GPU conversion cannot take a frame (${c}) after taking earlier ones`);return d(l,o,c)}if(e==="cpu"||typeof c=="string")return f(e==="cpu"?"CPU only":c),a(l,o);let p=b(l,o,c);m=p;try{return await p}finally{m=void 0}},{prefetch:!0,describe:()=>s?`WebGPU planes \u2192 RGBA (${i.adapter}; first frame ${i.meanDiff.toFixed(2)} from the fallback${i.fallbackMS===void 0?"":`, ${i.gpuMS.toFixed(1)} ms against ${i.fallbackMS.toFixed(1)} ms`})`:`${a.describe?.()??"fallback"}${u?` (WebGPU declined: ${u})`:""}`,dispose:()=>{n?.destroy(),n=void 0,a.dispose?.()}}),{gpu:{get:()=>s},check:{get:()=>i}})}var M=class a{gl;texture;framebuffer;constructor(e){this.gl=e,this.texture=e.createTexture(),this.framebuffer=e.createFramebuffer(),e.bindTexture(e.TEXTURE_2D,this.texture),e.pixelStorei(e.UNPACK_COLORSPACE_CONVERSION_WEBGL,e.NONE),e.pixelStorei(e.UNPACK_FLIP_Y_WEBGL,!1),e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1)}static create(){try{let e=typeof OffscreenCanvas>"u"?null:new OffscreenCanvas(1,1).getContext("webgl2");return e?new a(e):void 0}catch{return}}read(e,r,t,s=new Uint8Array(r*t*4)){let n=this.gl;n.bindTexture(n.TEXTURE_2D,this.texture),n.texImage2D(n.TEXTURE_2D,0,n.RGBA8,n.RGBA,n.UNSIGNED_BYTE,e),n.bindFramebuffer(n.FRAMEBUFFER,this.framebuffer),n.framebufferTexture2D(n.FRAMEBUFFER,n.COLOR_ATTACHMENT0,n.TEXTURE_2D,this.texture,0),n.readPixels(0,0,r,t,n.RGBA,n.UNSIGNED_BYTE,s);let i=n.getError();if(i)throw new Error(`WebGL readback failed (${i})`);return s}dispose(){this.gl.deleteTexture(this.texture),this.gl.deleteFramebuffer(this.framebuffer),this.gl.getExtension("WEBGL_lose_context")?.loseContext()}},G={identity:0,srgb:1,"srgb-gamut":2},ie=`@group(0) @binding(0) var src: texture_external;
|
|
24
|
+
struct Params { decode: u32 }
|
|
25
|
+
@group(0) @binding(1) var<uniform> params: Params;
|
|
26
|
+
@vertex fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {
|
|
27
|
+
let p = array(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));
|
|
28
|
+
return vec4f(p[i], 0.0, 1.0);
|
|
29
|
+
}
|
|
30
|
+
// BT.709 \u2192 BT.2020 primaries (ITU-R BT.2087), by columns.
|
|
31
|
+
const TO_2020 = mat3x3f(vec3f(0.627404, 0.069097, 0.016392), vec3f(0.329282, 0.919540, 0.088013), vec3f(0.043314, 0.011361, 0.895595));
|
|
32
|
+
@fragment fn fs(@builtin(position) at: vec4f) -> @location(0) vec4f {
|
|
33
|
+
var c = textureLoad(src, vec2u(at.xy)).rgb;
|
|
34
|
+
if (params.decode >= 1u) {
|
|
35
|
+
// Sign-preserving, so a colour outside BT.709 (negative after the browser's conversion) comes back whole.
|
|
36
|
+
let a = abs(c);
|
|
37
|
+
c = sign(c) * select(pow((a + 0.055) / 1.055, vec3f(2.4)), a / 12.92, a <= vec3f(0.04045));
|
|
38
|
+
}
|
|
39
|
+
if (params.decode == 2u) {
|
|
40
|
+
c = TO_2020 * c;
|
|
41
|
+
}
|
|
42
|
+
return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);
|
|
43
|
+
}`,N=class a{device;pipelines;uniforms;staging;queue;constructor(e){this.device=e,this.pipelines=new Map,this.queue=Promise.resolve();let r=t=>{let s=e.createBuffer({size:16,usage:72});return e.queue.writeBuffer(s,0,new Uint32Array([t,0,0,0])),s};this.uniforms={identity:r(G.identity),srgb:r(G.srgb),"srgb-gamut":r(G["srgb-gamut"])}}static async create(e=globalThis.navigator?.gpu){try{let r=await(await e?.requestAdapter())?.requestDevice();return r?new a(r):void 0}catch{return}}read(e,r,t,s,n,i){let u=this.queue.then(()=>this.readNow(e,r,t,s,n,i));return this.queue=u.catch(()=>{}),u}async readNow(e,r,t,s,n,i){let u=this.device,m=n==="rgba8"?"rgba8unorm":"rgb10a2unorm",f=this.pipelines.get(m);if(!f){let c=u.createShaderModule({code:ie});f=u.createRenderPipeline({layout:"auto",vertex:{module:c,entryPoint:"vs"},fragment:{module:c,entryPoint:"fs",targets:[{format:m}]}}),this.pipelines.set(m,f)}let d=Math.ceil(r*4/256)*256,y=`${m} ${r}x${t}`;this.staging?.key!==y&&(this.staging?.buffer.destroy(),this.staging?.texture.destroy(),this.staging={key:y,texture:u.createTexture({size:[r,t],format:m,usage:17}),buffer:u.createBuffer({size:d*t,usage:9})});let{texture:b,buffer:x}=this.staging,A=u.createCommandEncoder(),w=A.beginRenderPass({colorAttachments:[{view:b.createView(),loadOp:"clear",storeOp:"store"}]});w.setPipeline(f),w.setBindGroup(0,u.createBindGroup({layout:f.getBindGroupLayout(0),entries:[{binding:0,resource:u.importExternalTexture({source:e})},{binding:1,resource:{buffer:this.uniforms[s]}}]})),w.draw(3),w.end(),A.copyTextureToBuffer({texture:b},{buffer:x,bytesPerRow:d},[r,t]),u.queue.submit([A.finish()]),await x.mapAsync(1);let l=new Uint8Array(x.getMappedRange()),o=i??new Uint8Array(r*t*4);if(d===r*4)o.set(l.subarray(0,r*t*4));else for(let c=0;c<t;c++)o.set(l.subarray(c*d,c*d+r*4),c*r*4);return x.unmap(),o}dispose(){this.staging?.buffer.destroy(),this.staging?.texture.destroy(),this.device.destroy()}};function oe(a){let e=new Uint8ClampedArray(a.length),r=new Uint32Array(a.buffer,a.byteOffset,a.length/4);for(let t=0;t<r.length;t++){let s=r[t];e[4*t]=(s&1023)>>2,e[4*t+1]=(s>>10&1023)>>2,e[4*t+2]=(s>>20&1023)>>2,e[4*t+3]=255}return e}var ce=.25,k=async a=>{let e=performance.now();return[await a(),performance.now()-e]};function V(a,e="auto",r){let t,s,n,i,u,m=async(f,d,y)=>{if(i=M.create(),!i){t={name:"2D canvas (unverified)",packing:"rgba8",read:(c,p,g)=>ue(c,p,g)};let[,o]=await k(()=>t.read(f,d,y));s={route:t.name,ms:o,verified:!1};return}let b=i,[x,A]=await k(()=>b.read(f,d,y)),w=[];if(f.format&&typeof f.copyTo=="function"&&w.push({name:"copyTo(RGBA)",packing:"rgba8",read:async(o,c,p,g)=>{let h=g??new Uint8Array(c*p*4);return await o.copyTo(h,U),h}}),e!=="cpu"){u=await N.create(r);let o=a?"rgb10a2":"rgba8";for(let c of["identity","srgb-gamut","srgb"])if(u){let p=u;w.push({name:`WebGPU external texture (${c})`,packing:o,read:(g,h,v,P)=>p.read(g,h,v,c,o,P)})}}let l;for(let o of w){try{await o.read(f,d,y);let[c,p]=await k(()=>o.read(f,d,y)),g=_(o.packing==="rgb10a2"?oe(c):new Uint8ClampedArray(c.buffer,c.byteOffset,c.length),new Uint8ClampedArray(x.buffer));S(g)&&(!l||g.meanDiff<l.check.meanDiff-ce)&&(l={route:o,check:g,ms:p})}catch{}if(l?.route.name==="copyTo(RGBA)")break}l?(t=l.route,s={route:t.name,ms:l.ms,check:l.check,verified:!0}):(t={name:"WebGL readback",packing:"rgba8",read:(o,c,p,g)=>b.read(o,c,p,g)},s={route:t.name,ms:A,verified:!0}),t.name.startsWith("WebGPU")||(u?.dispose(),u=void 0),t.name!=="WebGL readback"&&(i.dispose(),i=void 0)};return{async read(f,d,y,b){t||await(n??=m(f,d,y));let x=await t.read(f,d,y,b);return b&&x!==b&&(b.set(x),x=b),{data:x,packing:t.packing}},choice:()=>s,dispose(){u?.dispose(),i?.dispose()}}}function ue(a,e,r){let t=new OffscreenCanvas(e,r),s=t.getContext("2d",{willReadFrequently:!0});if(!s)throw new Error("A 2D offscreen canvas could not be allocated for frame conversion.");s.drawImage(a,0,0,e,r);let n=s.getImageData(0,0,e,r).data;return new Uint8Array(n.buffer,n.byteOffset,n.length)}var E=new B,$=new B,fe=Object.assign(async(a,e)=>{let r=e.codedWidth,t=e.codedHeight,s=E.takeBuffer(r*t*4);try{return Object.assign({width:r,height:t,data:await D(a,r,t,new Uint8ClampedArray(s))},{release:()=>E.releaseBuffer(s)})}catch(n){throw E.releaseBuffer(s),n}},{describe:()=>"copyTo(RGBA)"}),L,z,T=globalThis;T.onmessage=async({data:a})=>{if("release"in a){(a.planes?$:E).releaseBuffer(a.release);return}let{id:e,frame:r,width:t,height:s}=a;if(a.planes){let n;try{n=$.takeBuffer(r.allocationSize());let i=await r.copyTo(new Uint8Array(n));T.postMessage({id:e,buffer:n,layout:i},[n])}catch(i){n&&$.releaseBuffer(n),T.postMessage({id:e,error:i instanceof Error?i.message:String(i)})}finally{r.close()}return}if(a.raw){let n=E.takeBuffer(t*s*4);try{z??=V(a.raw.hdr,a.compute);let{packing:i}=await z.read(r,t,s,new Uint8Array(n));T.postMessage({id:e,buffer:n,packing:i,choice:z.choice()},[n])}catch(i){E.releaseBuffer(n),T.postMessage({id:e,error:i instanceof Error?i.message:String(i)})}finally{r.close()}return}L??=H(fe,a.compute,void 0,E);try{let n=await L(r,{codedWidth:t,codedHeight:s,width:t,height:s,rotation:0});T.postMessage({id:e,buffer:n.data.buffer,describe:L.describe?.()},[n.data.buffer])}catch(n){T.postMessage({id:e,error:n instanceof Error?n.message:String(n)})}finally{r.close()}};
|
package/core-helper.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var t=globalThis;t.onmessage=async o=>{let{module:r,memory:n,stackTop:c,tls:a}=o.data;try{let e=(await WebAssembly.instantiate(r,{env:{memory:n}})).exports;e.__stack_pointer.value=c,a&&e.__wasm_init_tls?.(a),t.postMessage({ready:!0}),e.ls_pool_worker()}catch(s){t.postMessage({ready:!1,error:s instanceof Error?s.message:String(s)})}};
|
package/core.simd.wasm
ADDED
|
Binary file
|
|
Binary file
|
package/core.wasm
ADDED
|
Binary file
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/** The public types of the npm package. Declarations only, with no imports and no runtime code, so
|
|
2
|
+
* scripts/build-package.ts publishes this file as is as the package's `index.d.ts` (plus the `reconstruct`
|
|
3
|
+
* declaration). src/package/index.ts checks at compile time that these match what the worker sends. */
|
|
4
|
+
/** The language of diagnostic and progress messages and of print labels. */
|
|
5
|
+
export type Locale = 'en' | 'zh';
|
|
6
|
+
|
|
7
|
+
/** How serious a diagnostic is. */
|
|
8
|
+
export type Severity = 'info' | 'warning' | 'error';
|
|
9
|
+
|
|
10
|
+
/** A rectangle in pixels. */
|
|
11
|
+
export interface Rect {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Something the run noticed, kept in the order it happened. */
|
|
19
|
+
export interface Diagnostic {
|
|
20
|
+
/** Stable machine-readable code, e.g. `LOW_CONFIDENCE_PLACEMENT`. */
|
|
21
|
+
code: string;
|
|
22
|
+
severity: Severity;
|
|
23
|
+
/** What happened, in the call's `locale`. */
|
|
24
|
+
message: string;
|
|
25
|
+
/** What to do about it, when there is something to do. */
|
|
26
|
+
action?: string;
|
|
27
|
+
/** Source time in seconds. */
|
|
28
|
+
time?: number;
|
|
29
|
+
/** Source frame index. */
|
|
30
|
+
frame?: number;
|
|
31
|
+
/** The print (`Print.id`) it concerns. */
|
|
32
|
+
canvasId?: string;
|
|
33
|
+
/** Where on the print, in print pixels. */
|
|
34
|
+
region?: Rect;
|
|
35
|
+
/** Heuristic confidence in 0..1; not a probability of being correct. */
|
|
36
|
+
confidence?: number;
|
|
37
|
+
/** Structured values behind the message, for the codes that record them. */
|
|
38
|
+
detail?: unknown;
|
|
39
|
+
/** This event's own count, when the event stands for several. */
|
|
40
|
+
count?: number;
|
|
41
|
+
/** Live events only: how many times this code has fired so far in the run. */
|
|
42
|
+
occurrences?: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Where a run is. Sent at most every few hundred milliseconds. */
|
|
46
|
+
export interface Progress {
|
|
47
|
+
/** `scanning`, `solving`, `rendering`, `pyramid`, `complete`, `partial`, `export`, … */
|
|
48
|
+
phase: string;
|
|
49
|
+
/** Overall share done, 0..1. */
|
|
50
|
+
fraction: number;
|
|
51
|
+
/** Frames handled so far in this phase. */
|
|
52
|
+
frames: number;
|
|
53
|
+
/** Frames in the recording, once known. */
|
|
54
|
+
total?: number;
|
|
55
|
+
/** Source time reached, in seconds. */
|
|
56
|
+
time: number;
|
|
57
|
+
/** A human-readable line in the call's `locale`. */
|
|
58
|
+
message: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Knobs of the reconstruction. Every field is optional; the defaults are the app's. */
|
|
62
|
+
export interface ReconstructSettings {
|
|
63
|
+
/** Long side of the frames motion is analysed at, in pixels. Default 640. */
|
|
64
|
+
analysisSize?: 320 | 480 | 640 | 960 | 1280;
|
|
65
|
+
/** Tile cache budget in MB. Default 128. */
|
|
66
|
+
memoryMB?: 64 | 128 | 256 | 512;
|
|
67
|
+
/** Side of a stored tile in pixels. Default 512. */
|
|
68
|
+
tileSize?: 256 | 512 | 1024;
|
|
69
|
+
/** When the same spot shows different content over time: keep what stayed (`stable`, default) or the latest. */
|
|
70
|
+
temporalPolicy?: 'stable' | 'latest';
|
|
71
|
+
/** `context` (default) also composes a print that keeps the fixed screen chrome around the moving content;
|
|
72
|
+
* `region` gives the moving content alone. */
|
|
73
|
+
framing?: 'context' | 'region';
|
|
74
|
+
/** Where frame analysis runs: `auto` (default), `cpu` or `webgpu`. */
|
|
75
|
+
compute?: 'auto' | 'cpu' | 'webgpu';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Options for {@link reconstruct}. */
|
|
79
|
+
export interface ReconstructOptions {
|
|
80
|
+
/** Language of messages and labels. Default `en`. */
|
|
81
|
+
locale?: Locale;
|
|
82
|
+
settings?: ReconstructSettings;
|
|
83
|
+
/** Called as the run moves on, and while the prints are encoded (`phase: 'export'`). */
|
|
84
|
+
onProgress?: (progress: Progress) => void;
|
|
85
|
+
/** Called for diagnostics as they happen, at most one per code every 750 ms; the full list is in the result. */
|
|
86
|
+
onDiagnostic?: (diagnostic: Diagnostic) => void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** One composed picture: a moving pane, a fixed part of the screen, a separate fragment, or a framed view. */
|
|
90
|
+
export interface Print {
|
|
91
|
+
id: string;
|
|
92
|
+
/** The name the engine gave it (a fixed Chinese vocabulary, the same in every locale). */
|
|
93
|
+
name: string;
|
|
94
|
+
/** `name` in the call's `locale`. */
|
|
95
|
+
label: string;
|
|
96
|
+
/** `moving` (content that travelled), `fixed` (screen parts that stayed), `presentation` (a moving print framed
|
|
97
|
+
* with the fixed parts around it), or another kind the engine adds later. */
|
|
98
|
+
kind: string;
|
|
99
|
+
/** Unplaced-fragment number: 0 for a main print, 1+ for a piece the run could not place against it. */
|
|
100
|
+
fragment: number;
|
|
101
|
+
width: number;
|
|
102
|
+
height: number;
|
|
103
|
+
/** Where the print sits in its own world coordinates. */
|
|
104
|
+
bounds: Rect;
|
|
105
|
+
/** Pixels some frame showed. Transparent pixels in `png` were never observed. */
|
|
106
|
+
observedPixels: number;
|
|
107
|
+
/** The print at native resolution, as one PNG. */
|
|
108
|
+
png: Blob;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** What a run produced. */
|
|
112
|
+
export interface ReconstructResult {
|
|
113
|
+
/** `complete`; `partial` when stopped early or when a later step failed after pixels were committed (see
|
|
114
|
+
* `error`); `error` when nothing could be composed. */
|
|
115
|
+
status: 'complete' | 'partial' | 'error';
|
|
116
|
+
error?: string;
|
|
117
|
+
/** Frames composed into the prints. */
|
|
118
|
+
frames: number;
|
|
119
|
+
/** Main prints first, then fixed parts; within a kind, the most observed first. */
|
|
120
|
+
prints: Print[];
|
|
121
|
+
/** Every diagnostic the run recorded, in order. */
|
|
122
|
+
diagnostics: Diagnostic[];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** A running reconstruction. */
|
|
126
|
+
export interface Reconstruction {
|
|
127
|
+
/** Settles once the prints are encoded and the call's storage is deleted. Rejects when the worker cannot run
|
|
128
|
+
* (no WebCodecs, an unreadable file, a failed core load), and with an `AbortError` after `cancel()`. */
|
|
129
|
+
readonly result: Promise<ReconstructResult>;
|
|
130
|
+
/** Holds the run between two frames. Does nothing once the run has finished. */
|
|
131
|
+
pause(): Promise<void>;
|
|
132
|
+
/** Continues a paused run. */
|
|
133
|
+
resume(): Promise<void>;
|
|
134
|
+
/** Finishes early: `result` still resolves, with what was composed so far (`status: 'partial'`). */
|
|
135
|
+
stop(): Promise<void>;
|
|
136
|
+
/** Abandons the run: the worker is terminated, its storage deleted, and `result` rejects with an `AbortError`. */
|
|
137
|
+
cancel(): void;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Reconstructs one big screenshot from a screen recording (MP4, MOV or WebM that the browser's WebCodecs can
|
|
142
|
+
* decode). Runs in a Worker; the page stays responsive.
|
|
143
|
+
*
|
|
144
|
+
* @param source The recording.
|
|
145
|
+
* @param options Language, settings and progress callbacks.
|
|
146
|
+
* @returns The running reconstruction; await its `result`.
|
|
147
|
+
*/
|
|
148
|
+
export type Reconstruct = (source: Blob, options?: ReconstructOptions) => Reconstruction;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Reconstructs one big screenshot from a screen recording (MP4, MOV or WebM that the browser's WebCodecs can
|
|
152
|
+
* decode). Runs in a Worker; the page stays responsive.
|
|
153
|
+
*
|
|
154
|
+
* @param source The recording.
|
|
155
|
+
* @param options Language, settings and progress callbacks.
|
|
156
|
+
* @returns The running reconstruction; await its `result`.
|
|
157
|
+
*/
|
|
158
|
+
export declare const reconstruct: Reconstruct;
|