File size: 5,341 Bytes
42e18e6
6d4d78a
 
42e18e6
 
 
 
6d4d78a
42e18e6
6d4d78a
 
 
 
42e18e6
 
 
 
6d4d78a
 
42e18e6
6d4d78a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
314b7d8
42e18e6
 
 
64a031b
42e18e6
 
6d4d78a
c663034
 
6d4d78a
 
 
42e18e6
 
 
6d4d78a
 
 
 
 
 
42e18e6
 
 
64a031b
42e18e6
 
 
 
 
 
 
64a031b
42e18e6
 
 
 
 
6d4d78a
 
 
 
42e18e6
 
 
 
6d4d78a
c663034
 
6d4d78a
 
 
42e18e6
 
 
 
 
6d4d78a
 
 
 
 
 
42e18e6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6d4d78a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42e18e6
6d4d78a
 
 
42e18e6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// 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}`));
})();