// server.js const http = require('http'); const WebSocket = require('ws'); const mediasoup = require('mediasoup'); const PORT = 7860; let worker, router; const producerTransports = new Map(); // ws → { transport, producer } const consumerTransports = new Map(); // ws → [ { transport, consumer } ] (async () => { // 1) Start HTTP + WebSocket server const server = http.createServer(); const wss = new WebSocket.Server({ server }); server.listen(PORT, () => console.log(`Server running on ${PORT}`)); // 2) Create a single Mediasoup Worker + Router worker = await mediasoup.createWorker(); router = await worker.createRouter({ mediaCodecs: [ { kind: 'audio', mimeType: 'audio/opus', clockRate: 48000, channels: 2 }, { kind: 'video', mimeType: 'video/VP8', clockRate: 90000 }, ]}); wss.on('connection', ws => { ws.on('message', async msg => { const { action, data } = JSON.parse(msg); switch (action) { case 'createProducerTransport': { // Create transport for broadcaster const transport = await router.createWebRtcTransport({ listenIps: ['127.0.0.1'], enableUdp: true, enableTcp: true }); producerTransports.set(ws, { transport, producer: null }); ws.send(JSON.stringify({ action: 'producerTransportCreated', data: { id: transport.id, iceParameters: transport.iceParameters, iceCandidates: transport.iceCandidates, dtlsParameters: transport.dtlsParameters } })); break; } case 'connectProducerTransport': { const { dtlsParameters } = data; const { transport } = producerTransports.get(ws); await transport.connect({ dtlsParameters }); ws.send(JSON.stringify({ action: 'producerTransportConnected' })); break; } case 'produce': { const { kind, rtpParameters } = data; const { transport } = producerTransports.get(ws); const producer = await transport.produce({ kind, rtpParameters }); producerTransports.get(ws).producer = producer; ws.send(JSON.stringify({ action: 'produced', data: { producerId: producer.id } })); break; } case 'createConsumerTransport': { // Create transport for a viewer const transport = await router.createWebRtcTransport({ listenIps: ['127.0.0.1'], enableUdp: true, enableTcp: true }); const arr = consumerTransports.get(ws) || []; arr.push({ transport, consumer: null }); consumerTransports.set(ws, arr); ws.send(JSON.stringify({ action: 'consumerTransportCreated', data: { id: transport.id, iceParameters: transport.iceParameters, iceCandidates: transport.iceCandidates, dtlsParameters: transport.dtlsParameters } })); break; } case 'connectConsumerTransport': { const { dtlsParameters } = data; // assume latest transport const arr = consumerTransports.get(ws); await arr[arr.length - 1].transport.connect({ dtlsParameters }); ws.send(JSON.stringify({ action: 'consumerTransportConnected' })); break; } case 'consume': { // find the broadcaster’s producer const producerEntry = [...producerTransports.values()].find(e => e.producer); if (!producerEntry) { ws.send(JSON.stringify({ action: 'consumeError', data: 'No producer available yet' })); return; } const producer = producerEntry.producer; const arr = consumerTransports.get(ws); const { transport } = arr[arr.length - 1]; const consumer = await transport.consume({ producerId: producer.id, rtpCapabilities: router.rtpCapabilities, paused: false }); arr[arr.length - 1].consumer = consumer; ws.send(JSON.stringify({ action: 'consumed', data: { producerId: producer.id, id: consumer.id, kind: consumer.kind, rtpParameters: consumer.rtpParameters } })); break; } } }); }); })();