Files
Relay/RelayClient/Resources/Raw/wwwroot/rtc.js

227 lines
5.6 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); //Creates an offer to each person in call for MESH RTC
}
}
async function sendOffer(username) {
const pc = await ensurePeerConnectionForUser(username);
await Media.applyLocalStreamToPeerConnection(pc, username);
const offer = await pc.createOffer();
// LogMessage(`Offer created: ${JSON.stringify(offer)}`);
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) {
LogMessage(`Offer handler: ${msg}`);
const pc = await ensurePeerConnectionForUser(msg.from);
await Media.ensureLocalMedia();
await Media.applyLocalStreamToPeerConnection(pc, msg.from);
// const offer = JSON.parse(msg.offer);
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;