Spaces:
Sleeping
Sleeping
| // server.js | |
| const path = require('path'); | |
| const express = require('express'); | |
| const http = require('http'); | |
| const WebSocket = require('ws'); | |
| const mediasoup = require('mediasoup'); | |
| const app = express(); | |
| const PORT = 7860; | |
| // Serve static files from ./public | |
| app.use(express.static(path.join(__dirname, 'public'))); | |
| let worker, router; | |
| const producerTransports = new Map(); // ws → { transport, producer } | |
| const consumerTransports = new Map(); // ws → [ { transport, consumer } ] | |
| ;(async () => { | |
| // 1) Create the 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 } | |
| ] | |
| }); | |
| // 2) Expose RTP capabilities via HTTP for the clients | |
| app.get('/rtpCapabilities', (req, res) => { | |
| res.json({ rtpCapabilities: router.rtpCapabilities }); | |
| }); | |
| // 3) Create HTTP + WebSocket server | |
| const server = http.createServer(app); | |
| const wss = new WebSocket.Server({ server }); | |
| wss.on('connection', ws => { | |
| ws.on('message', async msg => { | |
| const { action, data } = JSON.parse(msg); | |
| switch (action) { | |
| case 'createProducerTransport': { | |
| const transport = await router.createWebRtcTransport({ | |
| listenIps: ['0.0.0.0'], | |
| // 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': { | |
| const transport = await router.createWebRtcTransport({ | |
| listenIps: ['0.0.0.0'], | |
| // 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; | |
| const arr = consumerTransports.get(ws); | |
| await arr[arr.length - 1].transport.connect({ dtlsParameters }); | |
| ws.send(JSON.stringify({ action: 'consumerTransportConnected' })); | |
| break; | |
| } | |
| case 'consume': { | |
| 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; | |
| } | |
| } | |
| }); | |
| ws.on('close', () => { | |
| // Clean up transports & producers/consumers for this ws | |
| const pEntry = producerTransports.get(ws); | |
| if (pEntry) { | |
| pEntry.producer?.close(); | |
| pEntry.transport.close(); | |
| producerTransports.delete(ws); | |
| } | |
| const cEntries = consumerTransports.get(ws) || []; | |
| cEntries.forEach(({ consumer, transport }) => { | |
| consumer?.close(); | |
| transport.close(); | |
| }); | |
| consumerTransports.delete(ws); | |
| }); | |
| }); | |
| // 4) Start the server | |
| server.listen(PORT, () => console.log(`SFU demo listening on http://localhost:${PORT}`)); | |
| })(); |