@nuralogix.ai/anura-web-core-sdk
Version:
Anura Web Core SDK
2 lines (1 loc) • 10.7 kB
JavaScript
var w=(a=>(a.PERMISSION_DENIED="permission_denied",a.NO_DEVICES="no_devices",a.UNKNOWN="unknown",a))(w||{});const l={CAMERA_STATUS:"cameraStatus",SELECTED_DEVICE_CHANGED:"selectedDeviceChanged",DEVICE_PIXEL_RATIO_CHANGED:"devicePixelRatioChanged",MEDIA_DEVICE_LIST_CHANGED:"mediaDeviceListChanged"},p=a=>{let e=a.toFixed(2);return e==="0"&&(e=a.toPrecision(1)),Number(e)},b=(a,e)=>{let t="";return e.length>0&&((!e.find(i=>i.device.deviceId===a)||a==="")&&(t=e[0].device.deviceId),e.find(i=>i.device.deviceId===a)&&(t=a)),t};let C=null;class f 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(){C?.();const e=`(resolution: ${window.devicePixelRatio}dppx)`,t=matchMedia(e);t.addEventListener("change",this.updatePixelRatio),C=()=>{t.removeEventListener("change",this.updatePixelRatio)},setTimeout(()=>{this.#e(this.#n())},1e3),this.#e(this.#n())}async enumerate(){try{return(await navigator.mediaDevices.enumerateDevices()).filter(e=>e.kind==="videoinput").map(e=>{const t=typeof e.getCapabilities=="function"?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}),{success:!0}}catch(e){return e instanceof DOMException&&e.name==="NotAllowedError"?(console.error("Camera permission denied."),{success:!1,error:"permission_denied",message:"Camera permission denied by user"}):e instanceof DOMException&&e.name==="NotFoundError"?(console.error("No camera devices found."),{success:!1,error:"no_devices",message:"No camera devices found on this system"}):(console.error("Error requesting camera permission:",e),{success:!1,error:"unknown",message:e instanceof Error?e.message:"Unknown error occurred"})}finally{this.stop()}}async list(){this.mediaDevices=await this.enumerate(),this.selectedDeviceId=b(this.selectedDeviceId,this.mediaDevices),this.#e(this.#d()),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:n,frameRate:s}=t,o=e.getCapabilities();this.cameraWidth=i||0,this.cameraHeight=n||0,this.cameraFrameRate=s||0,this.videoTrackCapabilities=Object.fromEntries([...this.settingsInputIds,...this.standardSettingsIds].map(d=>{const r=o[d];return[d,{isSupported:!!r,min:r&&r.min?p(r.min):0,max:r&&r.max?p(r.max):0,step:r&&r.step?p(r.step):0,value:r&&t[d]?p(t[d]):0}]}))}}async#o(e){const t={video:{...e?{facingMode:{ideal:e}}:{deviceId:{exact:this.selectedDeviceId}},width:640,height:360}};this.cameraStream=await navigator.mediaDevices.getUserMedia(t),this.#r(),this.#i()}#r(){if(!this.cameraStream)return;const[e]=this.cameraStream.getVideoTracks(),{deviceId:t}=e.getSettings();t&&t!==this.selectedDeviceId&&(this.selectedDeviceId=t,this.#e(this.#s()))}async start(e,t,i){this.stop();const n=navigator.mediaDevices.getSupportedConstraints();if(!("facingMode"in n))throw new DOMException("Constraint 'facingMode' is not supported","OverconstrainedError");if(e/t!==16/9)throw new Error("Resolution must have a 16:9 aspect ratio.");const s={video:{...i?{facingMode:{ideal:i}}:{deviceId:{exact:this.selectedDeviceId}},..."aspectRatio"in n?{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(s),this.#r(),this.#i(),this.#e(this.#a(!0)),!0}catch(o){o.name!="OverconstrainedError"?console.log("Error opening camera",o):console.log(`This camera cannot produce the requested ${o.constraint}.`);try{return console.log("Attempt to open the camera with fallback resolution: 640 x 360 pixels"),await this.#o(i),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}})}#d(){return new CustomEvent(l.MEDIA_DEVICE_LIST_CHANGED,{detail:{mediaDevices:this.mediaDevices}})}#n(){return new CustomEvent(l.DEVICE_PIXEL_RATIO_CHANGED,{detail:{devicePixelRatio:window.devicePixelRatio}})}#e(e){this.dispatchEvent(e)}}class y{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 x=(a=>(a.MEDIASTREAM_READY="imageSequenceMediaStreamReady",a))(x||{});let D;class R 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:n,numberOfDigits:s,extension:o}=i;return`${t}${n}${e.toString().padStart(s,"0")}.${o}`}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;D=setInterval(()=>{this.drawImageBitmapOnCanvas(this.imageBitmaps[t]),t===0&&this.dispatchEvent(this.getMediaStreamReadyEvent(!0)),(t===e-1||!this.canDrawImage)&&(clearInterval(D),this.videoEndedCallback()),t+=1},33)}}var k=(a=>(a.BYTES_DOWNLOADED="bytesDownloaded",a.DOWNLOAD_ERROR="downloadedError",a))(k||{});let I=a=>new Int8Array(a);class T 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,n){return new CustomEvent("bytesDownloaded",{detail:{bytes:e,uncompressedSize:t,url:i,done:n}})}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 s=0;s<t.length;s++)i[s]=t.charCodeAt(s);const n=new Int8Array(i.length);for(let s=0;s<i.length;s++)n[s]=i[s]>=128?i[s]-256:i[s];return I(n).buffer}async fetchAsset(e,t,i,n,s){const o=t+i,d=e.find(c=>c.file===i).uncompressedSize,r=(c,m,E,v)=>this.#t(this.getBytesDownloadedEvent(c,m,E,v));try{const c=(await fetch(o)).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:S,value:g}=await c.read();if(S){u.close(),r(m,d,o,!0);return}g&&(u.enqueue(g),m+=g.length,r(m,d,o,!1)),await h()}}}),v=new Response(E);if(n){const u=await v.json(),{base64EncodedValue:h}=u;if(s==="gzip"){await this.#i(h);return}return this.decompressBrotli(h)}else return await v.arrayBuffer()}catch(c){this.#t(this.getBytesDownloadErrorEvent(o,c))}}async#i(e){const t=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),i=new Blob([t]).stream().pipeThrough(new DecompressionStream("gzip")),n=await new Response(i).arrayBuffer(),s=new TextDecoder().decode(new Uint8Array(n)),o=new Blob([s],{type:"application/javascript"}),d=URL.createObjectURL(o);I=(await import(d)).BrotliDecode,this.canDecompress=!0,URL.revokeObjectURL(d)}}const A={CameraController:f,VideoController:y,ImageSequenceController:R,AssetDownloader:T};export{l as CameraControllerEvents,w as CameraPermissionError,A as default};