139 lines
4.8 KiB
JavaScript
139 lines
4.8 KiB
JavaScript
|
WT_DECLARE_WT_MEMBER(1, JavaScriptConstructor, "WebRTCClient", function (WT, client, offerId, offerBtn, sendMsg, sendBtn, textBrowser, localId, url) {
|
||
|
this.peerConnectionMap = {};
|
||
|
this.dataChannelMap = {};
|
||
|
this.config = {};
|
||
|
this.log = (text) => {
|
||
|
textBrowser.value = `${textBrowser.value}\n${text}`;
|
||
|
};
|
||
|
|
||
|
function sendLocalDescription(ws, id, pc, type) {
|
||
|
(type == 'offer' ? pc.createOffer() : pc.createAnswer())
|
||
|
.then((desc) => pc.setLocalDescription(desc))
|
||
|
.then(() => {
|
||
|
const { sdp, type } = pc.localDescription;
|
||
|
ws.send(JSON.stringify({
|
||
|
id,
|
||
|
type,
|
||
|
description: sdp,
|
||
|
}));
|
||
|
});
|
||
|
};
|
||
|
|
||
|
function sendLocalCandidate(ws, id, cand) {
|
||
|
const { candidate, sdpMid } = cand;
|
||
|
ws.send(JSON.stringify({
|
||
|
id,
|
||
|
type: 'candidate',
|
||
|
candidate,
|
||
|
mid: sdpMid,
|
||
|
}));
|
||
|
};
|
||
|
|
||
|
this.setupDataChannel = (dc, id) => {
|
||
|
dc.onopen = () => {
|
||
|
console.log(`DataChannel from ${id} open`);
|
||
|
sendMsg.disabled = false;
|
||
|
sendBtn.disabled = false;
|
||
|
sendBtn.onclick = () => dc.send(sendMsg.value);
|
||
|
};
|
||
|
dc.onclose = () => { console.log(`DataChannel from ${id} closed`); };
|
||
|
dc.onmessage = (e) => {
|
||
|
if (typeof (e.data) != 'string')
|
||
|
return;
|
||
|
console.log(`Message from ${id} received: ${e.data}`);
|
||
|
this.log(`${id}: ${e.data}`);
|
||
|
};
|
||
|
|
||
|
this.dataChannelMap[id] = dc;
|
||
|
return dc;
|
||
|
};
|
||
|
|
||
|
this.createPeerConnection = (ws, id) => {
|
||
|
const pc = new RTCPeerConnection(this.config);
|
||
|
pc.oniceconnectionstatechange = () => console.log(`Connection state: ${pc.iceConnectionState}`);
|
||
|
pc.onicegatheringstatechange = () => console.log(`Gathering state: ${pc.iceGatheringState}`);
|
||
|
pc.onicecandidate = (e) => {
|
||
|
if (e.candidate && e.candidate.candidate) {
|
||
|
sendLocalCandidate(ws, id, e.candidate);
|
||
|
}
|
||
|
};
|
||
|
pc.ondatachannel = (e) => {
|
||
|
const dc = e.channel;
|
||
|
console.log(`DataChannel from ${id} received with label "${dc.label}"`);
|
||
|
this.setupDataChannel(dc, id);
|
||
|
|
||
|
dc.send(`Hello from ${localId}`);
|
||
|
|
||
|
sendMsg.disabled = false;
|
||
|
sendBtn.disabled = false;
|
||
|
sendBtn.onclick = () => dc.send(sendMsg.value);
|
||
|
};
|
||
|
|
||
|
this.peerConnectionMap[id] = pc;
|
||
|
return pc;
|
||
|
};
|
||
|
|
||
|
this.offerPeerConnection = function (ws, id) {
|
||
|
console.log(`Offering to ${id}`);
|
||
|
pc = this.createPeerConnection(ws, id);
|
||
|
|
||
|
const label = "test";
|
||
|
console.log(`Creating DataChannel with label "${label}"`);
|
||
|
const dc = pc.createDataChannel(label);
|
||
|
this.setupDataChannel(dc, id);
|
||
|
|
||
|
sendLocalDescription(ws, id, pc, 'offer');
|
||
|
};
|
||
|
|
||
|
this.openSignaling = function (url) {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
const ws = new WebSocket(url);
|
||
|
ws.onopen = () => resolve(ws);
|
||
|
ws.onerror = () => reject(new Error('WebSocket error'));
|
||
|
ws.onclose = () => console.error('WebSocket disconnected');
|
||
|
ws.onmessage = (e) => {
|
||
|
if (typeof (e.data) != 'string') return;
|
||
|
const message = JSON.parse(e.data);
|
||
|
console.log(message);
|
||
|
const { id, type } = message;
|
||
|
let pc = this.peerConnectionMap[id];
|
||
|
if (!pc) {
|
||
|
if (type != 'offer')
|
||
|
return;
|
||
|
console.log(`Answering to ${id}`);
|
||
|
pc = this.createPeerConnection(ws, id);
|
||
|
}
|
||
|
switch (type) {
|
||
|
case 'offer':
|
||
|
case 'answer':
|
||
|
pc.setRemoteDescription({
|
||
|
sdp: message.description,
|
||
|
type: message.type,
|
||
|
}).then(() => {
|
||
|
if (type == 'offer') {
|
||
|
sendLocalDescription(ws, id, pc, 'answer');
|
||
|
}
|
||
|
});
|
||
|
break;
|
||
|
|
||
|
case 'candidate':
|
||
|
pc.addIceCandidate({
|
||
|
candidate: message.candidate,
|
||
|
sdpMid: message.mid,
|
||
|
});
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
|
||
|
client.wtWebRTCClient = this;
|
||
|
this.openSignaling(`${url}/${localId}`).then(ws => {
|
||
|
this.log('WebSocket connected, signaling ready');
|
||
|
offerId.disabled = false;
|
||
|
offerBtn.disabled = false;
|
||
|
offerBtn.onclick = () => {
|
||
|
this.offerPeerConnection(ws, offerId.value);
|
||
|
}
|
||
|
});
|
||
|
});
|