video-auth-js-sdk
Version:
A SDK to authenticate users with camera through a realtime stream
455 lines (409 loc) • 22 kB
JavaScript
'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;