p5.record.js
Version:
p5.js addon providing functions to record HTML canvas based sketches
1 lines • 5.39 kB
JavaScript
function e(){let e,t=[];for(let n=0;n<256;n++){e=n;for(let t=0;t<8;t++)e=e&1?3988292384^e>>>1:e>>>1;t[n]=e}return t}const t=e();function n(e,n,r,i){let a=t,o=i+r;e^=-1;for(let t=i;t<o;t++)e=e>>>8^a[(e^n[t])&255];return e^-1}const r=new TextEncoder;var i=class e{#e=[];#t=[];#n=0;constructor(){}static generateCentralDirectoryHeader(e,t){let n=new ArrayBuffer(46+e.filename.length),i=new Uint8Array(n),a=new DataView(n);a.setUint32(0,1347092738),a.setUint16(4,20,!0),a.setUint16(6,20,!0),a.setUint16(12,e.modifyTime,!0),a.setUint16(14,e.modifyDate,!0),a.setUint32(16,e.checksum,!0),a.setUint32(20,e.compressedSize,!0),a.setUint32(24,e.uncompressedSize,!0),a.setUint16(28,e.filename.length,!0),a.setUint32(42,t,!0);let o=r.encode(e.filename);return i.set(o,46),a.buffer}addFile(t){let n=e.generateCentralDirectoryHeader(t,this.#n);this.#e.push(t),this.#t.push(n),this.#n+=t.localFileHeader.byteLength,this.#n+=t.content.length}pack(){let e=this.#t.reduce((e,t)=>(e+=t.byteLength,e),0);this.#n+=e;let t=new ArrayBuffer(22),n=new DataView(t);n.setUint32(0,1347093766),n.setUint16(8,this.#e.length,!0),n.setUint16(10,this.#e.length,!0),n.setUint32(12,e,!0),n.setUint32(16,this.#n-e,!0);let r=[];for(let e of this.#e)r.push(e.localFileHeader),r.push(e.content);return r.push(...this.#t),r.push(n.buffer),new Blob(r)}},a=class{modifyTime;modifyDate;checksum;compressedSize;uncompressedSize;filename;content;constructor(e,t){this.filename=e,this.content=t;let{date:r,time:i}=o();this.modifyTime=i,this.modifyDate=r,this.checksum=n(0,t,t.length,0),this.compressedSize=t.length,this.uncompressedSize=t.length}get localFileHeader(){let e=new ArrayBuffer(30+this.filename.length),t=new Uint8Array(e),n=new DataView(e);n.setUint32(0,1347093252),n.setUint16(4,20,!0),n.setUint16(10,this.modifyTime,!0),n.setUint16(12,this.modifyDate,!0),n.setUint32(14,this.checksum,!0),n.setUint32(18,this.compressedSize,!0),n.setUint32(22,this.uncompressedSize,!0),n.setUint16(26,this.filename.length,!0);let i=r.encode(this.filename);return t.set(i,30),n.buffer}};function o(){let e=new Date,t=e.getUTCHours();t<<=6,t|=e.getUTCMinutes(),t<<=5,t|=e.getUTCSeconds()/2;let n=e.getUTCFullYear()-1980;return n<<=4,n|=e.getUTCMonth()+1,n<<=5,n|=e.getUTCDate(),{date:n,time:t}}const s={"image/png":`png`,"image/jpeg":`jpg`,"image/webp":`webp`};var c=class extends EventTarget{#e=0;#t;#n=[];#r;#i;#a;#o;#s=0;#c;state=`inactive`;constructor(e,t,n=60){super(),this.#t=e,this.#i=1e3/n,this.#c=t}start(){this.state=`recording`,this.dispatchEvent(new CustomEvent(`start`)),this.frame()}stop(){this.state=`inactive`,this.#r&&cancelAnimationFrame(this.#r);let e=Promise.all(this.#n.map(e=>e.arrayBuffer())),t=new i;e.then(e=>{for(let n=0;n<e.length;n++){let r=new a(`capture-${String(n+1).padStart(5,`0`)}.${s[this.#c]}`,new Uint8Array(e[n]));t.addFile(r)}this.dispatchEvent(new CustomEvent(`stop`,{detail:{blob:t.pack()}}))})}pause(){this.state=`paused`,this.#r&&cancelAnimationFrame(this.#r),this.dispatchEvent(new CustomEvent(`pause`))}resume(){this.state=`recording`,this.dispatchEvent(new CustomEvent(`resume`)),this.frame()}frame(e){if(this.state===`recording`){if(this.#a=e-this.#o,this.#o=e,this.#s+=this.#a||0,this.#s>=this.#i||this.#i===1/0){this.#s=0;let e=this.#e;this.#t.toBlob(t=>{this.#n[e]=t},this.#c),this.#e++}this.#i<1/0&&(this.#r=requestAnimationFrame(this.frame.bind(this)))}}},l=class{#e;#t;constructor(e){let t=e.frameRate===`manual`?0:e.frameRate,n=[],r=e.source instanceof HTMLCanvasElement?e.source:e.source.canvas,i=r.captureStream(t),a;a=Object.keys(s).includes(e.mimeType)?new c(r,e.mimeType,t):new MediaRecorder(i,{mimeType:e.mimeType??`video/webm;codecs=vp8`}),a.addEventListener(`start`,()=>{console.log(`recording started`)}),a.addEventListener(`stop`,t=>{let r,i;if(this.#t instanceof c?(r=t.detail.blob,i=`recording.zip`):(r=new Blob(n),i=`recording.webm`),typeof e?.stopCallback!=`function`||e?.stopCallback(r)){let e=URL.createObjectURL(r),t=document.createElement(`a`);t.href=e,t.download=i,t.click()}}),a.addEventListener(`dataavailable`,e=>{n.push(e.data)}),a.addEventListener(`pause`,()=>{console.log(`recording paused`)}),a.addEventListener(`resume`,()=>{console.log(`recording resumed`)}),this.#t=a,this.#e=i}get state(){return this.#t.state}start(){this.#t.start()}stop(){this.#t.stop()}pause(){this.#t.pause()}resume(){this.#t.resume()}frame(){this.#t instanceof c?this.#t.frame():this.#e.getVideoTracks()[0].requestFrame()}};function u(e,t,n){let r,i,a=e.VERSION.split(`.`).map(e=>parseInt(e));if(!(a[0]>2||a[0]===2&&a[1]>0||a[0]===2&&a[1]===0&&a[2]>=3)){console.error(`p5.record.js requires p5.js >= 2.0.3`);return}e.Recorder=l,n.postdraw=function(){r&&r.state===`recording`&&i?.frameRate===`manual`&&r.frame()},t.setRecording=function(e){if(e.frameRate===`manual`&&!(`CanvasCaptureMediaStreamTrack`in window)&&!Object.keys(s).includes(e.mimeType)){console.error(`Your browser does not support directly specifying frame capture timing with { frameRate: 'manual' }.`);return}i=e},t.startRecording=function(){i=Object.assign({source:this.canvas,frameRate:this.getTargetFrameRate(),stopCallback:this._customActions?.recordingStopped},i),r=new l(i),r.start()},t.stopRecording=function(){r.stop()},t.pauseRecording=function(){r.pause()},t.resumeRecording=function(){r.resume()},t.createRecording=function(e){return new l(e)}}typeof p5<`u`&&p5.registerAddon(u);export{l as Recorder,u as p5Record};