I've confirmed any and all issues past this point is C# related, and client is done being rewritten.
225 lines
5.4 KiB
JavaScript
225 lines
5.4 KiB
JavaScript
const peerConnections = {};
|
|
|
|
async function joinChannelCall() {
|
|
LogMessage("Current username: " + currentUsername);
|
|
LogMessage("Current channel: " + currentChannelId);
|
|
|
|
if (!currentUsername || !currentChannelId) {
|
|
LogMessage("Cannot join RTC: missing username or channel.");
|
|
return;
|
|
}
|
|
|
|
await RelaySocket.joinRtcChannel();
|
|
await Media.ensureLocalMedia();
|
|
|
|
const participants = await RelaySocket.getRtcParticipants();
|
|
|
|
LogMessage("Participants: " + JSON.stringify(participants));
|
|
|
|
const existingUsers = participants.filter(x => x !== currentUsername);
|
|
|
|
if (existingUsers.length === 0) {
|
|
LogMessage("Joined call as first participant. Waiting for others...");
|
|
return;
|
|
}
|
|
|
|
for (const username of existingUsers) {
|
|
await sendOffer(username);
|
|
}
|
|
}
|
|
|
|
async function sendOffer(username) {
|
|
const pc = await ensurePeerConnectionForUser(username);
|
|
|
|
await Media.applyLocalStreamToPeerConnection(pc, username);
|
|
|
|
const offer = await pc.createOffer();
|
|
await pc.setLocalDescription(offer);
|
|
|
|
await RelaySocket.sendRtcSignal({
|
|
type: "rtc_offer",
|
|
channelId: currentChannelId,
|
|
from: currentUsername,
|
|
to: username,
|
|
sdp: offer.sdp
|
|
});
|
|
|
|
LogMessage(`Sent offer to ${username}`);
|
|
}
|
|
|
|
async function handleRtcSignal(rawJson) {
|
|
try {
|
|
const msg = typeof rawJson === "string" ? JSON.parse(rawJson) : rawJson;
|
|
|
|
if (!msg || !msg.type) return;
|
|
if (msg.from === currentUsername) return;
|
|
|
|
if (msg.to && msg.to !== currentUsername) {
|
|
LogMessage(`Ignoring RTC signal meant for ${msg.to}`);
|
|
return;
|
|
}
|
|
|
|
LogMessage(`Received signal: ${msg.type} from ${msg.from}`);
|
|
|
|
if (msg.type === "rtc_offer") {
|
|
await handleOffer(msg);
|
|
return;
|
|
}
|
|
|
|
if (msg.type === "rtc_answer") {
|
|
await handleAnswer(msg);
|
|
return;
|
|
}
|
|
|
|
if (msg.type === "rtc_ice") {
|
|
await handleIce(msg);
|
|
return;
|
|
}
|
|
|
|
if (msg.type === "rtc_leave") {
|
|
closePeerConnection(msg.from);
|
|
return;
|
|
}
|
|
|
|
LogMessage("Unhandled RTC signal type: " + msg.type);
|
|
} catch (err) {
|
|
LogMessage("handleRtcSignal failed: " + err);
|
|
}
|
|
}
|
|
|
|
async function handleOffer(msg) {
|
|
const pc = await ensurePeerConnectionForUser(msg.from);
|
|
|
|
await Media.ensureLocalMedia();
|
|
await Media.applyLocalStreamToPeerConnection(pc, msg.from);
|
|
|
|
await pc.setRemoteDescription({
|
|
type: "offer",
|
|
sdp: msg.sdp
|
|
});
|
|
|
|
const answer = await pc.createAnswer();
|
|
await pc.setLocalDescription(answer);
|
|
|
|
await RelaySocket.sendRtcSignal({
|
|
type: "rtc_answer",
|
|
channelId: currentChannelId,
|
|
from: currentUsername,
|
|
to: msg.from,
|
|
sdp: answer.sdp
|
|
});
|
|
|
|
LogMessage(`Sent answer to ${msg.from}`);
|
|
}
|
|
|
|
async function handleAnswer(msg) {
|
|
const pc = peerConnections[msg.from];
|
|
|
|
if (!pc) {
|
|
LogMessage(`No peer connection found for answer from ${msg.from}`);
|
|
return;
|
|
}
|
|
|
|
await pc.setRemoteDescription({
|
|
type: "answer",
|
|
sdp: msg.sdp
|
|
});
|
|
|
|
LogMessage(`Applied answer from ${msg.from}`);
|
|
}
|
|
|
|
async function handleIce(msg) {
|
|
const pc = peerConnections[msg.from];
|
|
|
|
if (!pc) {
|
|
LogMessage(`No peer connection found for ICE from ${msg.from}`);
|
|
return;
|
|
}
|
|
|
|
if (!msg.candidate) return;
|
|
|
|
await pc.addIceCandidate(msg.candidate);
|
|
|
|
LogMessage(`Applied ICE from ${msg.from}`);
|
|
}
|
|
|
|
async function ensurePeerConnectionForUser(username) {
|
|
if (peerConnections[username]) {
|
|
return peerConnections[username];
|
|
}
|
|
|
|
const pc = new RTCPeerConnection(configuration);
|
|
peerConnections[username] = pc;
|
|
|
|
pc.onicecandidate = async event => {
|
|
if (!event.candidate) return;
|
|
|
|
await RelaySocket.sendRtcSignal({
|
|
type: "rtc_ice",
|
|
channelId: currentChannelId,
|
|
from: currentUsername,
|
|
to: username,
|
|
candidate: JSON.stringify(event.candidate)
|
|
});
|
|
};
|
|
|
|
pc.ontrack = event => {
|
|
LogMessage(`Remote track received from ${username}`);
|
|
|
|
const stream = event.streams[0];
|
|
if (!stream) return;
|
|
|
|
Media.attachRemoteStream(username, stream);
|
|
};
|
|
|
|
pc.onconnectionstatechange = () => {
|
|
LogMessage(`Connection ${username}: ${pc.connectionState}`);
|
|
|
|
if (
|
|
pc.connectionState === "failed" ||
|
|
pc.connectionState === "closed" ||
|
|
pc.connectionState === "disconnected"
|
|
) {
|
|
closePeerConnection(username);
|
|
}
|
|
};
|
|
|
|
return pc;
|
|
}
|
|
|
|
async function leaveChannelCall() {
|
|
await RelaySocket.sendRtcSignal({
|
|
type: "rtc_leave",
|
|
channelId: currentChannelId,
|
|
from: currentUsername
|
|
});
|
|
|
|
for (const username of Object.keys(peerConnections)) {
|
|
closePeerConnection(username);
|
|
}
|
|
|
|
await RelaySocket.leaveRtcChannel();
|
|
|
|
LogMessage("Left RTC channel");
|
|
}
|
|
|
|
function closePeerConnection(username) {
|
|
const pc = peerConnections[username];
|
|
if (!pc) return;
|
|
|
|
pc.close();
|
|
delete peerConnections[username];
|
|
|
|
Media.removeRemoteStream(username);
|
|
|
|
LogMessage(`Closed RTC connection with ${username}`);
|
|
}
|
|
|
|
window.RelayRtc = {
|
|
joinChannelCall,
|
|
leaveChannelCall,
|
|
handleRtcSignal,
|
|
peerConnections
|
|
};
|
|
|
|
window.handleRtcSignal = handleRtcSignal; |