UNPKG

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

Version:

Anura Web Core SDK

2 lines (1 loc) 9.96 kB
const l={CAMERA_STATUS:"cameraStatus",SELECTED_DEVICE_CHANGED:"selectedDeviceChanged",DEVICE_PIXEL_RATIO_CHANGED:"devicePixelRatioChanged",MEDIA_DEVICE_LIST_CHANGED:"mediaDeviceListChanged"},p=s=>{let e=s.toFixed(2);return e==="0"&&(e=s.toPrecision(1)),Number(e)},b=(s,e)=>{let t="";return e.length>0&&((!e.find(i=>i.device.deviceId===s)||s==="")&&(t=e[0].device.deviceId),e.find(i=>i.device.deviceId===s)&&(t=s)),t};let w=null;class S 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(){w?.();const e=`(resolution: ${window.devicePixelRatio}dppx)`,t=matchMedia(e);t.addEventListener("change",this.updatePixelRatio),w=()=>{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=b(this.selectedDeviceId,this.mediaDevices),this.#e(this.#o()),this.#e(this.#s())}setDeviceId(e){this.mediaDevices.length&&this.mediaDevices.find(t=>t.device.deviceId===e)&&(this.selectedDeviceId=e,this.#e(this.#s()))}#i(){if(this.cameraStream){const[e]=this.cameraStream.getVideoTracks(),t=e.getSettings(),{width:i,height:o,frameRate:a}=t,r=e.getCapabilities();this.cameraWidth=i||0,this.cameraHeight=o||0,this.cameraFrameRate=a||0,this.videoTrackCapabilities=Object.fromEntries([...this.settingsInputIds,...this.standardSettingsIds].map(d=>{const n=r[d];return[d,{isSupported:!!n,min:n&&n.min?p(n.min):0,max:n&&n.max?p(n.max):0,step:n&&n.step?p(n.step):0,value:n&&t[d]?p(t[d]):0}]}))}}async#n(){const e={video:{width:640,height:360,deviceId:{exact:this.selectedDeviceId}}};this.cameraStream=await navigator.mediaDevices.getUserMedia(e),this.#i()}async start(e,t,i){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 a={video:{...i?{facingMode:i}:{},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(a),this.#i(),this.#e(this.#a(!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.#a(!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.#a(!1)),this.cameraWidth=0,this.cameraHeight=0,this.cameraFrameRate=0)}#a(e){return new CustomEvent(l.CAMERA_STATUS,{detail:{isOpen:e,capabilities:this.videoTrackCapabilities}})}#s(){return new CustomEvent(l.SELECTED_DEVICE_CHANGED,{detail:{deviceId:this.selectedDeviceId}})}#o(){return new CustomEvent(l.MEDIA_DEVICE_LIST_CHANGED,{detail:{mediaDevices:this.mediaDevices}})}#r(){return new CustomEvent(l.DEVICE_PIXEL_RATIO_CHANGED,{detail:{devicePixelRatio:window.devicePixelRatio}})}#e(e){this.dispatchEvent(e)}}class f{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(i=>i.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(i=>i.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 y=(s=>(s.MEDIASTREAM_READY="imageSequenceMediaStreamReady",s))(y||{});let C;class x 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:i}=this.settings,{prefix:o,numberOfDigits:a,extension:r}=i;return`${t}${o}${e.toString().padStart(a,"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 i=>await this.getImageBitmapFromUrl(i)));this.setImageBitmaps(t)}setImageBitmaps(e){this.imageBitmaps=e}drawImageBitmapOnCanvas(e){if(this.ctx){const{width:t,height:i}=e;this.canvas.width!==t&&(this.canvas.width=t),this.canvas.height!==i&&(this.canvas.height=i),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;C=setInterval(()=>{this.drawImageBitmapOnCanvas(this.imageBitmaps[t]),t===0&&this.dispatchEvent(this.getMediaStreamReadyEvent(!0)),(t===e-1||!this.canDrawImage)&&(clearInterval(C),this.videoEndedCallback()),t+=1},33)}}var R=(s=>(s.BYTES_DOWNLOADED="bytesDownloaded",s.DOWNLOAD_ERROR="downloadedError",s))(R||{});let D=s=>new Int8Array(s);class k 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,i,o){return new CustomEvent("bytesDownloaded",{detail:{bytes:e,uncompressedSize:t,url:i,done:o}})}getBytesDownloadErrorEvent(e,t){return new CustomEvent("downloadedError",{detail:{url:e,error:t}})}decompressBrotli(e){const t=atob(e),i=new Uint8Array(t.length);for(let a=0;a<t.length;a++)i[a]=t.charCodeAt(a);const o=new Int8Array(i.length);for(let a=0;a<i.length;a++)o[a]=i[a]>=128?i[a]-256:i[a];return D(o).buffer}async fetchAsset(e,t,i,o,a){const r=t+i,d=e.find(c=>c.file===i).uncompressedSize,n=(c,m,E,v)=>this.#t(this.getBytesDownloadedEvent(c,m,E,v));try{const c=(await fetch(r)).body?.getReader();if(!c)throw new Error("Failed to get reader from response body.");let m=0;const E=new ReadableStream({async start(u){await h();async function h(){const{done:I,value:g}=await c.read();if(I){u.close(),n(m,d,r,!0);return}g&&(u.enqueue(g),m+=g.length,n(m,d,r,!1)),await h()}}}),v=new Response(E);if(o){const u=await v.json(),{base64EncodedValue:h}=u;if(a==="gzip"){await this.#i(h);return}return this.decompressBrotli(h)}else return await v.arrayBuffer()}catch(c){this.#t(this.getBytesDownloadErrorEvent(r,c))}}async#i(e){const t=Uint8Array.from(atob(e),n=>n.charCodeAt(0)),i=new Blob([t]).stream().pipeThrough(new DecompressionStream("gzip")),o=await new Response(i).arrayBuffer(),a=new TextDecoder().decode(new Uint8Array(o)),r=new Blob([a],{type:"application/javascript"}),d=URL.createObjectURL(r);D=(await import(d)).BrotliDecode,this.canDecompress=!0,URL.revokeObjectURL(d)}}const A={CameraController:S,VideoController:f,ImageSequenceController:x,AssetDownloader:k};export{l as CameraControllerEvents,A as default};