UNPKG

@nuralogix.ai/anura-web-core-sdk

Version:

Anura Web Core SDK

2 lines (1 loc) 9.71 kB
var E=(s=>(s.CAMERA_STATUS="cameraStatus",s.SELECTED_DEVICE_CHANGED="selectedDeviceChanged",s.DEVICE_PIXEL_RATIO_CHANGED="devicePixelRatioChanged",s))(E||{});const h=s=>{let e=s.toFixed(2);return e==="0"&&(e=s.toPrecision(1)),Number(e)},w=(s,e)=>{let t="";return e.length>0&&((!e.find(a=>a.device.deviceId===s)||s==="")&&(t=e[0].device.deviceId),e.find(a=>a.device.deviceId===s)&&(t=s)),t};let v=null;class C extends EventTarget{mediaDevices=[];selectedDeviceId="";cameraStream=null;cameraWidth=0;cameraHeight=0;cameraFrameRate=0;settingsInputIds=["iso","exposureTime","focusDistance","colorTemperature","zoom"];standardSettingsIds=["contrast","saturation","sharpness","brightness","exposureCompensation"];videoTrackCapabilities={};#t=!1;constructor(){super(),navigator.mediaDevices.ondevicechange=async()=>{this.#t||(this.#t=!0,await this.list(),this.#t=!1)},this.updatePixelRatio=this.updatePixelRatio.bind(this),this.updatePixelRatio()}static init(){return new this}updatePixelRatio(){v?.();const e=`(resolution: ${window.devicePixelRatio}dppx)`,t=matchMedia(e);t.addEventListener("change",this.updatePixelRatio),v=()=>{t.removeEventListener("change",this.updatePixelRatio)},setTimeout(()=>{this.#e(this.#r())},1e3),this.#e(this.#r())}async enumerate(){try{return(await navigator.mediaDevices.enumerateDevices()).filter(e=>e.kind==="videoinput").map(e=>{const t=e.getCapabilities();return{device:{label:e.label,deviceId:e.deviceId,kind:"videoinput",groupId:e.groupId},capabilities:t}})}catch(e){return console.error("Error enumerating devices:",e),[]}}async requestPermission(){try{return this.cameraStream=await navigator.mediaDevices.getUserMedia({video:!0}),!0}catch(e){return e instanceof DOMException&&e.name==="NotAllowedError"?console.error("Camera permission denied."):e instanceof DOMException&&e.name==="NotFoundError"?console.error("No camera devices found."):console.error("Error requesting camera permission:",e),!1}finally{this.stop()}}async list(){this.mediaDevices=await this.enumerate(),this.selectedDeviceId=w(this.selectedDeviceId,this.mediaDevices),this.#e(this.#s())}setDeviceId(e){this.mediaDevices.length&&this.mediaDevices.find(t=>t.device.deviceId===e)&&(this.selectedDeviceId=e,this.#e(this.#s()))}#a(){if(this.cameraStream){const[e]=this.cameraStream.getVideoTracks(),t=e.getSettings(),{width:a,height:o,frameRate:i}=t,r=e.getCapabilities();this.cameraWidth=a||0,this.cameraHeight=o||0,this.cameraFrameRate=i||0,this.videoTrackCapabilities=Object.fromEntries([...this.settingsInputIds,...this.standardSettingsIds].map(d=>{const n=r[d];return[d,{isSupported:!!n,min:n&&n.min?h(n.min):0,max:n&&n.max?h(n.max):0,step:n&&n.step?h(n.step):0,value:n&&t[d]?h(t[d]):0}]}))}}async#n(){const e={video:{width:640,height:360,deviceId:{exact:this.selectedDeviceId}}};this.cameraStream=await navigator.mediaDevices.getUserMedia(e),this.#a()}async start(e,t,a){this.stop();const o=navigator.mediaDevices.getSupportedConstraints();for(const r of["facingMode","aspectRatio"])if(!(r in o))throw new OverconstrainedError(r,"not supported");if(e/t!==16/9)throw new Error("Resolution must have a 16:9 aspect ratio.");const i={video:{...a?{facingMode:a}:{},deviceId:this.selectedDeviceId,aspectRatio:{exact:16/9},frameRate:{ideal:30,min:15,max:30},width:{ideal:e},height:{ideal:t}}};try{return this.cameraStream=await navigator.mediaDevices.getUserMedia(i),this.#a(),this.#e(this.#i(!0)),!0}catch(r){r.name!="OverconstrainedError"?console.log("Error opening camera",r):console.log(`This camera cannot produce the requested ${r.constraint}.`);try{return console.log("Attempt to open the camera with fallback resolution: 640 x 360 pixels"),await this.#n(),this.#e(this.#i(!0)),!0}catch(d){return console.error("Fallback resolution also failed.",d),!1}}}getVideoTrackCapabilities(){return this.videoTrackCapabilities}stop(){this.cameraStream&&(this.cameraStream.getTracks().forEach(e=>e.stop()),this.cameraStream=null,this.#e(this.#i(!1)),this.cameraWidth=0,this.cameraHeight=0,this.cameraFrameRate=0)}#i(e){return new CustomEvent("cameraStatus",{detail:{isOpen:e,capabilities:this.videoTrackCapabilities}})}#s(){return new CustomEvent("selectedDeviceChanged",{detail:{deviceId:this.selectedDeviceId}})}#r(){return new CustomEvent("devicePixelRatioChanged",{detail:{devicePixelRatio:window.devicePixelRatio}})}#e(e){this.dispatchEvent(e)}}class b{mimeCodec;videoElement=document.createElement("video");videoSource=document.createElement("source");canvas=document.createElement("canvas");mediaStream=null;bytesDownloaded=0;ctx;static init(e){return new this(e)}constructor(e){this.mimeCodec=e.mimeCodec,this.videoLoadedCallback=e.videoLoadedCallback,this.videoEndedCallback=e.videoEndedCallback,this.videoFrameCallback=this.videoFrameCallback.bind(this),this.ctx=this.canvas.getContext("2d")}async videoLoadedCallback(){}videoEndedCallback(){}videoFrameCallback(e,t){this.ctx&&this.ctx.drawImage(this.videoElement,0,0,this.canvas.width,this.canvas.height),this.videoElement.requestVideoFrameCallback(this.videoFrameCallback)}captureFromCanvas(){const{videoWidth:e,videoHeight:t}=this.videoElement;this.canvas.width=e,this.canvas.height=t,this.videoElement.addEventListener("play",()=>{this.videoElement.requestVideoFrameCallback(this.videoFrameCallback)}),this.videoElement.addEventListener("playing",()=>{this.mediaStream=this.canvas.captureStream()}),this.videoElement.addEventListener("ended",()=>{this.mediaStream&&(this.mediaStream.getTracks().forEach(a=>a.stop()),this.videoElement.pause(),this.videoEndedCallback())})}captureFromVideoElement(){"captureStream"in HTMLVideoElement.prototype&&(this.mediaStream=this.videoElement.captureStream())}setMediaStream(){"captureStream"in HTMLVideoElement.prototype?(this.captureFromVideoElement(),this.videoElement.addEventListener("timeupdate",()=>{const{currentTime:e,duration:t}=this.videoElement;t===e&&this.mediaStream&&(this.mediaStream.getTracks().forEach(a=>a.stop()),this.videoElement.pause(),this.videoEndedCallback())})):(console.log("captureFromVideoElement not supported"),this.captureFromCanvas())}async init(e){typeof e>"u"||typeof e!="string"||(this.videoElement=document.createElement("video"),this.videoSource=document.createElement("source"),this.videoSource.type=this.mimeCodec,this.videoSource.src=e,this.videoElement.appendChild(this.videoSource),this.videoElement.playsInline=!0,this.videoElement.onloadedmetadata=()=>{this.setMediaStream(),this.videoLoadedCallback()})}}var D=(s=>(s.MEDIASTREAM_READY="imageSequenceMediaStreamReady",s))(D||{});let u;class y extends EventTarget{canvas=document.createElement("canvas");ctx;imageBitmaps=[];canDrawImage=!0;settings={url:"",imageName:{prefix:"",numberOfDigits:4,extension:""}};static init(e,t){return new this(e,t)}constructor(e,t){super(),this.settings=e,this.videoEndedCallback=t,this.ctx=this.canvas.getContext("2d")}videoEndedCallback(){}getImageName(e){const{url:t,imageName:a}=this.settings,{prefix:o,numberOfDigits:i,extension:r}=a;return`${t}${o}${e.toString().padStart(i,"0")}.${r}`}async getImageBitmapFromUrl(e){const t=await(await fetch(e)).blob();return await createImageBitmap(t)}async getImageBitmapFromArrayOfUrls(e){const t=await Promise.all(e.map(async a=>await this.getImageBitmapFromUrl(a)));this.setImageBitmaps(t)}setImageBitmaps(e){this.imageBitmaps=e}drawImageBitmapOnCanvas(e){if(this.ctx){const{width:t,height:a}=e;this.canvas.width!==t&&(this.canvas.width=t),this.canvas.height!==a&&(this.canvas.height=a),this.ctx.drawImage(e,0,0),e.close()}}getMediaStreamReadyEvent(e){return new CustomEvent("imageSequenceMediaStreamReady",{detail:{isReady:e}})}drawImagesOnCanvas(){const{length:e}=this.imageBitmaps;let t=0;u=setInterval(()=>{this.drawImageBitmapOnCanvas(this.imageBitmaps[t]),t===0&&this.dispatchEvent(this.getMediaStreamReadyEvent(!0)),(t===e-1||!this.canDrawImage)&&(clearInterval(u),this.videoEndedCallback()),t+=1},33)}}var f=(s=>(s.BYTES_DOWNLOADED="bytesDownloaded",s.DOWNLOAD_ERROR="downloadedError",s))(f||{});let p=s=>new Int8Array(s);class I extends EventTarget{canDecompress=!1;static init(){return new this}dispatch(e,t){this.dispatchEvent(new CustomEvent(e,{detail:t}))}#t(e){this.dispatchEvent(e)}getBytesDownloadedEvent(e,t,a){return new CustomEvent("bytesDownloaded",{detail:{bytes:e,url:t,done:a}})}getBytesDownloadErrorEvent(e,t){return new CustomEvent("downloadedError",{detail:{url:e,error:t}})}decompressBrotli(e){const t=atob(e),a=new Uint8Array(t.length);for(let i=0;i<t.length;i++)a[i]=t.charCodeAt(i);const o=new Int8Array(a.length);for(let i=0;i<a.length;i++)o[i]=a[i]>=128?a[i]-256:a[i];return p(o).buffer}async fetchAsset(e,t,a){const o=(i,r,d)=>this.#t(this.getBytesDownloadedEvent(i,r,d));try{const i=(await fetch(e)).body?.getReader();if(!i)throw new Error("Failed to get reader from response body.");let r=0;const d=new ReadableStream({async start(m){await c();async function c(){const{done:g,value:l}=await i.read();if(g){m.close(),o(r,e,!0);return}l&&(m.enqueue(l),r+=l.length,o(r,e,!1)),await c()}}}),n=new Response(d);if(t){const m=await n.json(),{base64EncodedValue:c}=m;if(a==="gzip"){await this.#a(c);return}return this.decompressBrotli(c)}else return await n.arrayBuffer()}catch(i){this.#t(this.getBytesDownloadErrorEvent(e,i))}}async#a(e){const t=Uint8Array.from(atob(e),n=>n.charCodeAt(0)),a=new Blob([t]).stream().pipeThrough(new DecompressionStream("gzip")),o=await new Response(a).arrayBuffer(),i=new TextDecoder().decode(new Uint8Array(o)),r=new Blob([i],{type:"application/javascript"}),d=URL.createObjectURL(r);p=(await import(d)).BrotliDecode,this.canDecompress=!0,URL.revokeObjectURL(d)}}const S={CameraController:C,VideoController:b,ImageSequenceController:y,AssetDownloader:I};export{S as default};