UNPKG

video-auth-js-sdk

Version:

A SDK to authenticate users with camera through a realtime stream

455 lines (409 loc) 22 kB
'use strict'; import App from "./App"; import {CallTopicManager} from "./call/callTopicManager"; import buildConfig from "./buildConfig.json" import {errorList} from "./errorHandler"; import Utility from "./utility/utility" class VideoAuthStream { constructor({ token, async: { appId, deviceId, socketAddress, messageTtl = 0, serverName }, // faceApi: faceApiConfig = {}, enableFaceBox = true, // onSuccess = null, onError = null, onDebug = null, onReady = null, videoContainerId }) { // const { // modelPath = 'assets/model/' // } = faceApiConfig; this._app = new App({ socketAddress, appId, serverName, deviceId, token, // onSuccess, onError, onDebug, onReady, messageTtl: messageTtl || 0, videoContainerId, // modelPath, enableFaceBox }); this._isDestroyed = false; this._faceImage = null; // this._app.events.on(this._app.events.eventsList.AI_MESSAGE, this._processAiMessage.bind(this)); this._app.events.on(this._app.events.eventsList.CALL_MESSAGE, this._processCallMessage.bind(this)); this._app.events.on(this._app.events.eventsList.ASYNC_READY, this._requestAuthSession.bind(this)); // this.errorList = errorList; } _processAiMessage(message){ this._app.publicCallbacks.onDebug({ type: 'PROGRESS', message: `Received message from ai-simulator`, data: message }); switch(message.actionId) { case 1: this._app.authSessionInfo.callId = message.callId; this._app.authSessionInfo.clientId = message.clientId; this._app.authSessionInfo.brokerAddress = message.brokerAddress; this._app.authSessionInfo.brokerAddressWeb = message.brokerAddressWeb; this._app.authSessionInfo.turnAddress = message.turnAddress; this._app.authSessionInfo.generateTurnsList(message.turnAddress); this._app.authSessionInfo.topicSend = message.topicSend; this._app.authSessionInfo.kurentoAddress = message.kurentoAddress; this._createSessionInCallServer(); break; case 2: if (message.callId == this._app.authSessionInfo.callId) { if(message.done == true) { this._app.publicCallbacks.onSuccess({ sessionId: this._app.authSessionInfo.callId, done: true }); } else { this._app.publicCallbacks.onError({ sessionId: this._app.authSessionInfo.callId, done: false, message: message.reason, code: errorList.SERVER_PROGRESS_ERROR.code }); } this.destroy(); } break; } } _requestAuthSession(){ this._app.publicCallbacks.onDebug({ type: 'PROGRESS', message: `Async is ready.`, }); this._app.publicCallbacks.onReady(); let parentNode = document.getElementById(this._app.params.videoContainerId); if(parentNode) { let internalParentNode = document.createElement('div'); internalParentNode.classList.add('auth-video-container'); internalParentNode.style.position = 'relative'; internalParentNode.style.width = '100%'; internalParentNode.style.height = '100%'; this._app.store.videoParentTag = internalParentNode; // this._app.store.videoParentTag = parentNode; this._app.store.videoTag = document.createElement('video'); this._app.store.videoTag.classList.add('auth'); this._app.store.videoTag.classList.add('video'); this._app.store.videoTag.id = "video-auth-video-tag"; this._app.store.videoTag.style.width = '100%'; this._app.store.videoTag.style.height = '100%'; internalParentNode.appendChild(this._app.store.videoTag) // this._app.store.canvasTag = document.createElement('canvas'); // this._app.store.canvasTag.classList.add('auth'); // this._app.store.canvasTag.classList.add('canvas'); // this._app.store.canvasTag.id = "video-auth-canvas-tag"; // this._app.store.canvasTag.style.position = "absolute"; // this._app.store.canvasTag.style.top = "0"; // this._app.store.canvasTag.style.left = "0"; if(this._app.params.enableFaceBox) { this._faceImage = document.createElement('img'); this._faceImage.src = 'data:image/png;base64,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'; this._faceImage.classList.add('face-box'); this._faceImage.id = "video-auth-img-tag"; this._faceImage.style.position = "absolute"; this._faceImage.style.top = "0"; this._faceImage.style.left = "0"; internalParentNode.appendChild(this._faceImage); } // internalParentNode.appendChild(this._app.store.canvasTag); parentNode.appendChild(internalParentNode); this._app.store.externalParentTag = parentNode; // setTimeout(() => { // this._app.store.videoTag.srcObject = this._app.store.localCameraStream; // this._app.store.videoTag.play(); this._setupCamera(); // }, 1000); } else { console.error("[SDK] Error: can not find the video parent tag.") } } async _setupCamera() { const video = this._app.store.videoTag; if (!video ) return null; // setup webcam. note that navigator.mediaDevices requires that page is accessed via https if (!navigator.mediaDevices) { console.error('Camera Error: access not supported'); this._app.publicCallbacks.onError(errorList.MEDIA_DEVICES_NOT_SUPPORTED); return null; } const constraints = { audio: false, video: { facingMode: 'user', exact: "environment", resizeMode: 'crop-and-scale' } }; if (window.innerWidth > window.innerHeight) constraints.video.width = {ideal: window.innerWidth }; //width: 1920, height: 1280 }//ideal: 1280}//window.innerWidth }; else constraints.video.height = { ideal: window.innerHeight }; try { this._app.store.localCameraStream = await navigator.mediaDevices.getUserMedia(constraints); } catch (err) { if (err.name === 'PermissionDeniedError' || err.name === 'NotAllowedError'){ console.error(`Camera Error: camera permission denied: ${err.message || err}`); this._app.publicCallbacks.onError(errorList.VIDEO_PERMISSION_ERROR); } if (err.name === 'SourceUnavailableError') { console.error(`Camera Error: camera not available: ${err.message || err}`); this._app.publicCallbacks.onError(this._app.errorHandler.getFilledErrorObject({ ...errorList.CAMERA_NOT_AVAILABLE, replacements: [(err.message || err)] })); } return null; } if (this._app.store.localCameraStream) { video.srcObject = this._app.store.localCameraStream; } else { console.error('Camera Error: stream empty'); return null; } const track = this._app.store.localCameraStream.getVideoTracks()[0]; const settings = track.getSettings(); if (settings.deviceId) delete settings.deviceId; if (settings.groupId) delete settings.groupId; // if (settings.aspectRatio) settings.aspectRatio = Math.trunc(100 * settings.aspectRatio) / 100; console.log(`Camera active: ${track.label}`); console.log(`Camera settings: `, {settings}); return new Promise((resolve) => { video.onloadeddata = async () => { if(this._app.params.enableFaceBox) { this._calculateImageSize(); } setTimeout(()=>{ video.play(); resolve(true); }); }; }); } _calculateImageSize(){ let base = "videoWidth"; const video = this._app.store.videoTag; if(video.videoWidth < video.videoHeight) { base = "videoHeight"; } // let aspect = video.videoWidth / video.videoHeight; if(base == "videoWidth") { // this._faceImage.width = video.offsetWidth; // this._faceImage.height = parseInt(video.offsetWidth / aspect); this._faceImage.style.left = parseInt((video.offsetWidth - this._faceImage.width) / 2) + 'px'; this._faceImage.style.top = parseInt((video.offsetHeight - this._faceImage.height) / 2) + 'px'; } else { // this._faceImage.height = video.offsetHeight; // this._faceImage.width = parseInt(video.offsetHeight / aspect); this._faceImage.style.top = parseInt((video.offsetHeight - this._faceImage.height) / 2) + 'px'; this._faceImage.style.left = parseInt((video.offsetWidth - this._faceImage.width) / 2) + 'px'; } } _createSessionInCallServer() { let that = this, message = { id: 'CREATE_SESSION', brokerAddress: this._app.authSessionInfo.brokerAddressWeb, turnAddress: this._app.authSessionInfo.turnAddress, chatId: this._app.authSessionInfo.callId, }; this._app.messenger.sendCallMessage(message); } _processCallMessage(message) { let uniqueId = message.uniqueId; if (message.done !== 'FALSE' || (message.done === 'FALSE' && message.desc === 'duplicated')) { // this._app.store.asyncRequestTimeouts[uniqueId] && clearTimeout(this._app.store.asyncRequestTimeouts[uniqueId]); } else if (message.done === 'FALSE') { console.error({ type: 'CALL_ERROR', code: 7000, message: "Call Server error: " + (message.desc ? message.desc : message.message), }); } switch (message.id) { case 'PROCESS_SDP_ANSWER': this.handleProcessSdpAnswer(message); break; case 'ADD_ICE_CANDIDATE': this.handleAddIceCandidate(message); break; case 'GET_KEY_FRAME': // user.videoTopicManager().restartMediaOnKeyFrame(app.store.user.get().id, [2000, 4000, 8000, 12000]); break; case 'STOP': if (this._app.store.messagesCallbacks[uniqueId]) { this._app.store.messagesCallbacks[uniqueId](message); } break; case 'CLOSE': if (this._app.store.messagesCallbacks[uniqueId]) { this._app.store.messagesCallbacks[uniqueId](message); } break; case 'SESSION_NEW_CREATED': case 'SESSION_REFRESH': this._handleCreateSessionResult(message); break; case 'RECEIVEMETADATA': // handleReceivedMetaData(message, uniqueId); break; case 'ERROR': // publicized.raiseCallError(app.errorHandler.getFilledErrorObject({ // ...errorList.CALL_SERVER_ERROR, // replacements: [JSON.stringify(message)] // }), null, true); break; case 'SEND_SDP_OFFER': case 'RECIVE_SDP_OFFER': case 'SDP_ANSWER_RECEIVED': break; default: console.warn("[SDK][onmessage] Invalid message, id: " + message.id, message); // if (jsonMessage.match(/NOT CREATE SESSION/g)) { // if (currentCallParams && Object.keys(currentCallParams)) { // //handleCallSocketOpen(currentCallParams); // callStateController.createSessionInChat(currentCallParams); // } // } break; } } _handleCreateSessionResult(res){ if (res.done === 'TRUE') { this._topicManager = new CallTopicManager({ app: this._app, callId: this._app.authSessionInfo.callId, topic: 'Vi-' + this._app.authSessionInfo.topicSend, mediaType: 'video', direction: 'send', onPeerConnect(){} }); this._topicManager.createTopic(); } } handleProcessSdpAnswer(jsonMessage) { let peer = this._topicManager.getPeer(); if (peer == null) { console.error({ type: 'CALL_ERROR', code: 7000, message: "[handleProcessSdpAnswer] Skip, no WebRTC Peer", }); return; } peer.processAnswer(jsonMessage.sdpAnswer, (err) => { if (err) { console.error({ type: 'CALL_ERROR', message: "[handleProcessSdpAnswer] Error: " + err, }); this._app.publicCallbacks.onError({ code: errorList.FAILED_TO_OPEN_PEER.code, message: "[handleProcessSdpAnswer] Error: " + err }); return; } }); } handleAddIceCandidate(jsonMessage) { let peer = this._topicManager.getPeer(); if (!peer) { console.error({ type: 'CALL_ERROR', message: "[handleAddIceCandidate] Skip, no WebRTC Peer", error: JSON.stringify(peer) }); return; } peer.addIceCandidate(jsonMessage.candidate, (err) => { if (err) { console.error("[handleAddIceCandidate] " + err); console.error({ type: 'CALL_ERROR', message: "[handleAddIceCandidate] " + err, error: JSON.stringify(jsonMessage.candidate), }); return; } }); } isFaceDetected() { return this._app.store.faceDetected; } startServerAuthentication(callConfig) { this._app.authSessionInfo.callId = callConfig.callId; this._app.authSessionInfo.clientId = callConfig.clientId; this._app.authSessionInfo.brokerAddress = callConfig.brokerAddress; this._app.authSessionInfo.brokerAddressWeb = callConfig.brokerAddressWeb; this._app.authSessionInfo.turnAddress = callConfig.turnAddress; this._app.authSessionInfo.generateTurnsList(callConfig.turnAddress); this._app.authSessionInfo.topicSend = callConfig.topicSend; this._app.authSessionInfo.kurentoAddress = callConfig.kurentoAddress; this._createSessionInCallServer(); // setTimeout(()=> { // if(!this._isDestroyed) { // this._app.publicCallbacks.onSuccess({ // sessionId: this._app.authSessionInfo.callId, // done: true // }); // this.destroy(); // } // }, this._app.authSessionInfo.verificationTime); // this._app.messenger.sendAiMessage({ // actionId: 1 // }); } async destroy() { this._isDestroyed = true; return new Promise(resolve => { console.log('innn?') this._app.messenger.sendCallMessage({ id: 'STOP', topic: 'Vi-' + this._app.authSessionInfo.topicSend, clientId: this._app.authSessionInfo.clientId, }); // this._app.faceApi.destroy(); if(this._app.store.videoParentTag) { this._app.store.videoParentTag.remove(); this._app.store.videoParentTag = null; } if(this._app.store.localCameraStream) { this._app.store.localCameraStream.getTracks().forEach(item => item.stop()); this._app.store.localCameraStream = null; } setTimeout(async function () { this._topicManager && this._topicManager.destroy(); if (!this._app) return; await this._app.async.destroy(); this._app.events.removeAllListeners(); this._app.messenger.clearRequests(); this._app = null; resolve(); }.bind(this), 1500); }); } generateUUID(){ return Utility.generateUUID(); } version() { console.log("%c[SDK] Version: video-auth-js-sdk@" + buildConfig.version, "color:green; font-size:13px"); console.log("%c[SDK] Build date:" + buildConfig.date, "color:green;font-size:13px"); console.log("%c[SDK] Additional info: " + buildConfig.VersionInfo, "color:green;font-size:13px"); return buildConfig; }; } window.VideoAuthStream = VideoAuthStream; // export default VideoAuthStream; module.exports = VideoAuthStream;