import express from "express"; import http from "http"; import cors from "cors"; import { Server } from "socket.io"; import * as mediasoup from "mediasoup"; const app = express(); app.use(cors()); const server = http.createServer(app); const io = new Server(server, { path: '/chad/ws', cors: { origin: '*' } }); let worker; let router; const transports = new Map(); // socketId -> [transports] const producers = new Map(); // socketId -> [producers] const consumers = new Map(); // socketId -> [consumers] async function createWorker() { worker = await mediasoup.createWorker(); worker.on("died", () => { console.error("mediasoup worker died, exiting..."); process.exit(1); }); router = await worker.createRouter({ mediaCodecs: [ { kind: "audio", mimeType: "audio/opus", clockRate: 48000, channels: 2, } ], }); console.log("Mediasoup worker & router created"); } createWorker(); io.on("connection", (socket) => { console.log("Client connected:", socket.id); socket.on("getRtpCapabilities", (cb) => { cb(router.rtpCapabilities); }); socket.on("createTransport", async (cb) => { try { const transport = await router.createWebRtcTransport({ listenInfos: [ { protocol: 'udp', ip: "0.0.0.0", announcedIp: "91.144.171.182", portRange: { min: 40000, max: 40100 } } ], enableUdp: true, preferUdp: true, }); transports.set(socket.id, [...(transports.get(socket.id) || []), transport]); cb({ id: transport.id, iceParameters: transport.iceParameters, iceCandidates: transport.iceCandidates, dtlsParameters: transport.dtlsParameters, }); transport.observer.on("close", () => { console.log("transport closed", transport.id); }); } catch (err) { console.error("createTransport error:", err); cb({ error: err.message }); } }); socket.on("connectTransport", async ({ transportId, dtlsParameters }, cb) => { const transport = transports.get(socket.id)?.find((t) => t.id === transportId); if (!transport) return cb({ error: "transport not found" }); await transport.connect({ dtlsParameters }); cb({ connected: true }); }); socket.on("produce", async ({ transportId, kind, rtpParameters }, cb) => { const transport = transports.get(socket.id)?.find((t) => t.id === transportId); if (!transport) return cb({ error: "transport not found" }); const producer = await transport.produce({ kind, rtpParameters }); producers.set(socket.id, [...(producers.get(socket.id) || []), producer]); cb({ id: producer.id }); socket.broadcast.emit("newProducer", { producerId: producer.id }); producer.observer.on("close", () => { console.log("producer closed", producer.id); }); }); socket.on("consume", async ({ producerId, transportId, rtpCapabilities }, cb) => { try { if (!router.canConsume({ producerId, rtpCapabilities })) { return cb({ error: "cannot consume" }); } const transport = transports.get(socket.id)?.find((t) => t.id === transportId); if (!transport) return cb({ error: "transport not found" }); const consumer = await transport.consume({ producerId, rtpCapabilities, paused: false, }); consumers.set(socket.id, [...(consumers.get(socket.id) || []), consumer]); cb({ id: consumer.id, producerId, kind: consumer.kind, rtpParameters: consumer.rtpParameters, }); } catch (err) { console.error("consume error:", err); cb({ error: err.message }); } }); socket.on("disconnect", () => { console.log("Client disconnected:", socket.id); transports.get(socket.id)?.forEach((t) => t.close()); producers.get(socket.id)?.forEach((p) => p.close()); consumers.get(socket.id)?.forEach((c) => c.close()); transports.delete(socket.id); producers.delete(socket.id); consumers.delete(socket.id); }); }); server.listen(process.env.PORT || 3000);