@b8safe/react-native-safe
Version:
Package for native integration of B8Safe SDK
213 lines (211 loc) • 8.38 kB
JavaScript
"use strict";
import { mediaDevices, RTCPeerConnection } from 'react-native-webrtc';
export class RTCConnectionInterface {
constructor(options) {
this.pc = null;
this.hashChecker = options.hashChecker;
this.options = options;
this.connected = false;
this.functions = {};
this.datachannel = null;
}
initRTC() {
this.pc = new RTCPeerConnection({
iceServers: [{
urls: 'turn:54.94.8.152:3478',
username: 'yJr4LWEp82m6gVCh',
credential: 'xrp73KVqagYMG2te'
}]
});
this.pc.addEventListener('iceconnectionstatechange', () => {
if (this.pc == null) return;
switch (this.pc.iceConnectionState) {
case 'connected':
case 'completed':
this.connected = true;
if (this.options.onConnected != null) this.options.onConnected();
break;
default:
if (this.options.onDisconnected != null) this.options.onDisconnected();
this.connected = false;
}
});
this.datachannel = this.pc.createDataChannel('b8safe', {
ordered: true,
maxRetransmits: 30
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
addDataChannelFunction(key, func) {
this.functions[key] = func;
}
removeDataChannelFunction(key) {
if (this.functions[key] != null) delete this.functions[key];
}
handleChannelData(data) {
if (typeof data === 'object' && 'function' in data) {
const func = this.functions[data.function];
if (typeof func === 'function') func(data.data ?? null);
}
}
sdpFilterCodec(kind, codec, realSdp) {
const allowed = [];
const rtxRegex = new RegExp('a=fmtp:(\\d+) apt=(\\d+)\\r$');
const codecRegex = new RegExp('a=rtpmap:([0-9]+) ' + codec.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
const videoRegex = new RegExp('(m=' + kind + ' .*?)( ([0-9]+))*\\s*$');
const lines = realSdp.split('\n');
let isKind = false;
for (let i = 0; i < lines.length; i++) {
if (lines[i]?.startsWith('m=' + kind + ' ')) isKind = true;else if (lines[i]?.startsWith('m=')) isKind = false;
if (isKind) {
let match = lines[i]?.match(codecRegex);
if (match == null) continue;
if (match[1] != null) allowed.push(parseInt(match[1], 10));
match = lines[i]?.match(rtxRegex);
if (match == null) continue;
if (match[1] != null && match[2] != null && allowed.includes(parseInt(match[2], 10))) allowed.push(parseInt(match[1], 10));
}
}
const skipRegex = 'a=(fmtp|rtcp-fb|rtpmap):([0-9]+)';
let sdp = '';
isKind = false;
for (let i = 0; i < lines.length; i++) {
if (lines[i]?.startsWith('m=' + kind + ' ')) isKind = true;else if (lines[i]?.startsWith('m=')) isKind = false;
if (isKind) {
const skipMatch = lines[i]?.match(skipRegex);
if (skipMatch != null && skipMatch[2] != null && !allowed.includes(parseInt(skipMatch[2], 10))) {
continue;
} else if (lines[i]?.match(videoRegex) != null) {
sdp += lines[i]?.replace(videoRegex, '$1 ' + allowed.join(' ')) + '\n';
} else {
sdp += lines[i] + '\n';
}
} else {
sdp += lines[i] + '\n';
}
}
return sdp;
}
negotiate(hash, videoEnabled = false) {
if (this.pc == null) throw new Error('PeerConnection not initialized on negotiate');
this.pc.createOffer({
OfferToReceiveVideo: true,
OfferToReceiveAudio: false
}).then(offer => {
offer._sdp = offer.sdp.replace('a=max-message-size:262144', 'a=max-message-size:65536');
if (this.pc == null) throw new Error('PeerConnection not initialized on negotiate');
this.pc.setLocalDescription(offer);
}).then(() => {
return new Promise(resolve => {
if (this.pc == null) throw new Error('PeerConnection not initialized on negotiate');
if (this.pc.iceGatheringState === 'complete') {
resolve(true);
} else {
const checkState = () => {
if (this.pc == null) {
throw new Error('PeerConnection not initialized on negotiate > checkState');
}
if (this.pc.iceGatheringState === 'complete') {
this.pc.removeEventListener('icegatheringstatechange', checkState);
resolve(true);
}
};
this.pc.addEventListener('icegatheringstatechange', checkState);
}
});
}).then(() => {
if (this.pc == null) throw new Error('PeerConnection not initialized on negotiate');
const offer = this.pc.localDescription;
if (offer != null) {
offer._sdp = this.sdpFilterCodec('video', 'VP8/90000', offer._sdp);
fetch(`https://kyc.b8.com.br/${this.hashChecker}/${hash}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
streamingType: videoEnabled ? 'video' : 'image',
rtcMessage: offer
})
}).then(res => {
console.info('Teste de resultado: ', res);
// if (res && res.success && res.rtcMessage.type === 'answer') {
// pc.current?.setRemoteDescription(new RTCSessionDescription(res.rtcMessage))
// .then(() => console.log('pc.current?.signalingState', pc.current?.signalingState))
// .catch(e => console.log(e, pc.current?.connectionState, pc.current?.signalingState, pc.current?.iceConnectionState));
// }
// else if (res?.message) showMessage(res.message);
});
}
});
}
changeStreamResolution(_params) {
if (this.pc == null) throw new Error('PeerConnection not initialized');
const maxBitrateInBitsPerSecond = 75000;
const senders = this.pc.getSenders();
senders.forEach(sender => {
if (sender.track?.kind === 'video') {
const params = sender.getParameters();
if (params.encodings[0] != null) params.encodings[0].maxBitrate = maxBitrateInBitsPerSecond;
sender.setParameters(params).then(() => console.info('parameter defined')).catch(e => console.error('error:', e));
}
});
}
start(hash, type, videoEnabled = true) {
if (this.pc == null) this.initRTC();
if (this.datachannel == null) throw new Error('Datachannel not initialized');
this.datachannel.addEventListener('open', () => console.info('Datachannel oppened'));
this.datachannel.addEventListener('close', () => console.info('Datachannel closed'));
this.datachannel.addEventListener('message', message => {
this.handleChannelData(JSON.parse(message.data.toString()));
});
if (videoEnabled) {
const constraints = {
audio: false,
video: {
minWidth: 960,
minHeight: 540,
width: 1280,
height: 720,
maxWidth: 3840,
maxHeight: 2160,
frameRate: 24,
facingMode: type === 'front' ? 'user' : 'environment',
deviceId: ''
}
};
mediaDevices.enumerateDevices().then(deviceList => {
const dList = deviceList;
let deviceId = '';
dList.forEach(device => {
if (device.facing === (type === 'front' ? 'front' : 'environment')) deviceId = device.deviceId;
// devices.push({ sourceId: device.deviceId });
});
constraints.video.deviceId = deviceId;
mediaDevices.getUserMedia(constraints).then(stream => {
stream.getTracks().forEach(track => {
if (this.pc == null) throw new Error('PeerConnection not initialized');
this.pc.addTrack(track, stream);
});
this.options.onConnectionStart != null && this.options.onConnectionStart(stream);
if (type === 'back') {
this.changeStreamResolution({
maxFramerate: 30,
scaleResolutionDownBy: 2,
maxBitrate: 3000
});
}
return this.negotiate(hash, videoEnabled);
}).catch(e => console.error(e));
});
} else {
this.negotiate(hash, false);
}
}
stop() {
if (this.pc == null) throw new Error('PeerConnection not initialized');
this.pc.close();
if (this.options.onConnectionStop != null) this.options.onConnectionStop();
}
}
//# sourceMappingURL=RTCConnectionInterface.js.map