vicowa-web-components
Version:
211 lines (173 loc) • 6.19 kB
JavaScript
/* eslint-disable */
// disable eslint while this code is not done
import observerFactory from './observerFactory.js';
const privateData = Symbol('privateData');
const MAX_CHUNK_SIZE = 262144;
function sendIceCandidate(p_WebRTC, p_Client, p_Candidate) {
const classData = p_WebRTC[privateData];
classData.comChannel.send(p_Candidate);
}
function handleSignalingMessage(p_WebRTC, p_Event) {
switch (p_Event.command) {
case 'candidate': break;
case 'offer': break;
case 'answer': break;
}
}
function sendOffer(p_WebRTC, p_Offer) {
const classData = p_WebRTC[privateData];
classData.comChannel.send(p_Offer);
}
function addChannelHandlers(p_WebRTC, p_Channel) {
p_Channel.onmessage = () => {
};
p_Channel.onclose = () => {
};
}
function handleReceiveDataChannel(p_WebRTC, p_Channel) {
if (p_Channel) {
const classData = p_WebRTC[privateData];
if (p_Channel.label === 'mainDataChannel') {
classData.mainDataChannel = p_Channel;
} else if (p_Channel.label === 'controlChannel') {
classData.controlChannel = p_Channel;
}
addChannelHandlers(p_WebRTC, p_Channel);
}
}
/**
* Enum for signaling events
* @readonly
* @enum {string}
*/
export const SIGNALING_EVENTS = Object.freeze({
MESSAGE: 'MESSAGE',
CLOSE: 'CLOSE',
ERROR: 'ERROR',
PEER_LIST: 'PEER_LIST',
PEER_UPDATE: 'PEER_UPDATE',
});
export const COMMANDS = Object.freeze({
peerList: 'peerList',
peerUpdate: 'peerUpdate',
inviteChannel: 'inviteChannel',
requestChannelAccess: 'requestChannelAccess',
});
export class SignalingBase {
constructor(p_ComObject) {
this[privateData] = {
observer: observerFactory(),
comObject: p_ComObject,
};
p_ComObject.onmessage = (p_Message) => { this.onMessage(p_Message); };
p_ComObject.onerror = (p_Error) => { this.onError(p_Error); };
p_ComObject.onclose = (p_Info) => { this.onClose(p_Info); };
}
connectChannel(p_ChannelName) {
this.send({
command: 'channelConnect',
id: p_ChannelName,
});
}
getPeerList() { this.send({ command: 'peerList' }); }
send(p_Message) {
this[privateData].comObject.send(p_Message);
}
onMessage(p_Message) { this[privateData].observer.notify(SIGNALING_EVENTS.MESSAGE, p_Message); }
onClose(p_Info) { this[privateData].observer.notify(SIGNALING_EVENTS.CLOSE, p_Info); }
onError(p_Error) { this[privateData].observer.notify(SIGNALING_EVENTS.ERROR, p_Error); }
/**
* Add an observer to handle signaling events
* @param {SIGNALING_EVENTS} p_Event The event this is observing
* @param {Function} p_Callback The callback function
*/
addObserver(p_Event, p_Callback) {
this[privateData].observer.addObserver(p_Event, p_Callback);
if (p_Event === SIGNALING_EVENTS.PEER_LIST) {
this.addObserver(SIGNALING_EVENTS.MESSAGE, (p_Message) => {
switch (p_Message.command) {
case COMMANDS.peerList:
this[privateData].observer.notify(SIGNALING_EVENTS.PEER_LIST, p_Message.data);
break;
case COMMANDS.peerUpdate:
this[privateData].observer.notify(SIGNALING_EVENTS.PEER_UPDATE, p_Message);
break;
}
});
}
}
/**
* Remove an observer to handle signaling events
* @param {SIGNALING_EVENTS} p_Event The event this is observing
* @param {Function} p_Callback The callback function
*/
removeObserver(p_Event, p_Callback) {
this[privateData].observer.removeObserver(p_Event, p_Callback);
}
}
export class WebRTCHandler {
constructor(p_SignalingHandler) {
this[privateData] = {
signaling: p_SignalingHandler,
comChannel: null,
localStream: null,
peerConfig: null,
localPeer: null,
mainDataChannel: null,
controlChannel: null,
};
}
async connectToChannel(p_ChannelID, p_Options) {
const classData = this[privateData];
classData.comChannel = await classData.signaling.connectChannel(p_ChannelID);
classData.comChannel.onMessage = (p_Message) => { handleSignalingMessage.bind(this, p_Message); };
classData.localPeer = new RTCPeerConnection(classData.peerConfig);
classData.localPeer.onicecandidate = (p_CandidateInfo) => sendIceCandidate(this, classData.localPeer, p_CandidateInfo);
classData.localPeer.ondatachannel = (p_Event) => handleReceiveDataChannel(this, p_Event.channel);
const offer = await classData.localPeer.createOffer({ offerToReceiveAudio: p_Options.audio, offerToReceiveVideo: p_Options.video });
await classData.localPeer.setLocalDescription(offer);
sendOffer(this, offer);
}
async startAudioCall(p_Options) {
const classData = this[privateData];
classData.localStream = await window.navigator.mediaDevices.getUserMedia({
audio: true,
video: false,
});
}
async startVideoCall(p_LocalVideoCanvas, p_RemoteVideoCanvas, p_Options) {
const classData = this[privateData];
const options = Object.assign({ audio: true }, p_Options);
classData.localStream = await window.navigator.mediaDevices.getUserMedia({
audio: options.audio,
video: true,
});
p_LocalVideoCanvas.srcObject = privateData.localStream;
}
openDataChannel(p_ChannelID, p_Options) {
const classData = this[privateData];
const options = Object.assign({ ordered: true }, p_Options);
classData.mainDataChannel = classData.localPeer.createDataChannel('mainChannel', options);
classData.controlChannel = classData.localPeer.createDataChannel('controlChannel', { ordered: true });
const handleChannelOpen = (p_Channel) => {
};
const handleChannelClose = (p_Channel) => {
};
classData.mainDataChannel.addEventListener('open', () => { handleChannelOpen(classData.mainDataChannel); });
classData.mainDataChannel.addEventListener('close', () => { handleChannelClose(classData.mainDataChannel); });
classData.controlChannel.addEventListener('open', () => { handleChannelOpen(classData.mainDataChannel); });
classData.controlChannel.addEventListener('close', () => { handleChannelClose(classData.mainDataChannel); });
}
sendData(p_Channel, p_Data) {
let bufferedAmount = p_Channel.bufferedAmount;
let currentLocation = 0;
while (currentLocation < p_Data.length) {
p_Channel.send(p_Data);
}
}
closeDataChannel() {
const classData = this[privateData];
classData.mainDataChannel.close();
classData.controlChannel.close();
}
}