vani-meeting-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dump.rdb +0 -0
- package/package.json +41 -0
- package/pm2.config.js +8 -0
- package/src/ServerHandler.ts +32 -0
- package/src/base/BaseSFUWebsocket.ts +20 -0
- package/src/index.ts +3 -0
- package/src/lib/redis/RedisHandler.ts +150 -0
- package/src/models/Event.ts +36 -0
- package/src/models/MessagePayload.ts +35 -0
- package/src/models/Participant.ts +24 -0
- package/src/models/WebSocketServerStartRequest.ts +14 -0
- package/src/sfu/SFUEachRoomHandler.ts +265 -0
- package/src/sfu/SFUEachRoomUserHandler.ts +468 -0
- package/src/sfu/SFUHandler.ts +67 -0
- package/src/utility/Constant.ts +63 -0
- package/src/utility/EventEmitterHandler.ts +10 -0
- package/src/utility/VaniEventListener.ts +7 -0
- package/src/websocket/EachSocketConnectionHandler.ts +451 -0
- package/src/websocket/WebSocketHandler.ts +30 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
import { Router } from "mediasoup/node/lib/Router";
|
|
2
|
+
import { BaseSFUWebsocket } from "../base/BaseSFUWebsocket";
|
|
3
|
+
import { Participant } from "../models/Participant";
|
|
4
|
+
import { SFUMessageType } from "../websocket/EachSocketConnectionHandler";
|
|
5
|
+
import { SFUEachRoomHandlerInterface, SFUEachRoomProducer, SFUMessageBody } from "./SFUEachRoomHandler";
|
|
6
|
+
import { WebRtcTransport } from "mediasoup/node/lib/WebRtcTransport";
|
|
7
|
+
import { Transport } from "mediasoup/node/lib/Transport";
|
|
8
|
+
import { Producer } from "mediasoup/node/lib/Producer";
|
|
9
|
+
import { Consumer } from "mediasoup/node/lib/Consumer";
|
|
10
|
+
import webrtcTransportConfiguration from "../utility/Constant";
|
|
11
|
+
|
|
12
|
+
export class SFUEachRoomUserHandler extends BaseSFUWebsocket {
|
|
13
|
+
|
|
14
|
+
private isUserPresentInRoom = true
|
|
15
|
+
public selfParticipant: Participant
|
|
16
|
+
public recvRouter?: Router;
|
|
17
|
+
public sendRouter?: Router;
|
|
18
|
+
private webrtcSendTransport?: WebRtcTransport
|
|
19
|
+
private webrtcRecieveTransport?: WebRtcTransport
|
|
20
|
+
private roomHandlerDataSource?: SFUEachRoomHandlerInterface
|
|
21
|
+
public producers: Producer[] = []
|
|
22
|
+
public consumers: Consumer[] = []
|
|
23
|
+
|
|
24
|
+
constructor(participant: Participant, sendRouter: Router, recvRouters: Router, roomHandlerDataSource: SFUEachRoomHandlerInterface) {
|
|
25
|
+
super()
|
|
26
|
+
this.selfParticipant = participant;
|
|
27
|
+
this.sendRouter = sendRouter
|
|
28
|
+
this.recvRouter = recvRouters
|
|
29
|
+
this.roomHandlerDataSource = roomHandlerDataSource
|
|
30
|
+
this.checkAllProducerAndConsuer = this.checkAllProducerAndConsuer.bind(this)
|
|
31
|
+
this.onConsumeProductId = this.onConsumeProductId.bind(this)
|
|
32
|
+
this.onReadyToConsume = this.onReadyToConsume.bind(this)
|
|
33
|
+
this.resumeConsumer = this.resumeConsumer.bind(this)
|
|
34
|
+
this.pauseConsumer = this.pauseConsumer.bind(this)
|
|
35
|
+
this.updateSpatialConsumer = this.updateSpatialConsumer.bind(this)
|
|
36
|
+
this.onTransportProduceSyncRequest = this.onTransportProduceSyncRequest.bind(this)
|
|
37
|
+
this.onResumeProducer = this.onResumeProducer.bind(this)
|
|
38
|
+
this.onPauseProducer = this.onPauseProducer.bind(this)
|
|
39
|
+
this.onNewProducer = this.onNewProducer.bind(this)
|
|
40
|
+
this.onGetAllProducers = this.onGetAllProducers.bind(this)
|
|
41
|
+
this.onProducerClosed = this.onProducerClosed.bind(this)
|
|
42
|
+
this.createWebrtcSendTransport = this.createWebrtcSendTransport.bind(this)
|
|
43
|
+
this.createWebrtcRecieveTransport = this.createWebrtcRecieveTransport.bind(this)
|
|
44
|
+
this.connectTransport = this.connectTransport.bind(this)
|
|
45
|
+
this.onRestartIceCandidate = this.onRestartIceCandidate.bind(this)
|
|
46
|
+
this.checkAllProducerAndConsuer = this.checkAllProducerAndConsuer.bind(this)
|
|
47
|
+
this.cleanUp = this.cleanUp.bind(this)
|
|
48
|
+
|
|
49
|
+
setTimeout(this.checkAllProducerAndConsuer, 5000)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public onNewMessage(payload: SFUMessageBody) {
|
|
53
|
+
if (payload.type === SFUMessageType.OnCreateTransports) {
|
|
54
|
+
this.createWebrtcSendTransport(this.selfParticipant)
|
|
55
|
+
this.createWebrtcRecieveTransport(this.selfParticipant)
|
|
56
|
+
}
|
|
57
|
+
else if (payload.type === SFUMessageType.OnTransportConnect) {
|
|
58
|
+
this.connectTransport(payload)
|
|
59
|
+
}
|
|
60
|
+
else if (payload.type === SFUMessageType.OnTransportProduceSyncRequest) {
|
|
61
|
+
this.onTransportProduceSyncRequest(payload)
|
|
62
|
+
}
|
|
63
|
+
else if (payload.type === SFUMessageType.OnReadyToConsume) {
|
|
64
|
+
this.onReadyToConsume(payload)
|
|
65
|
+
}
|
|
66
|
+
else if (payload.type === SFUMessageType.ConsumeProductId) {
|
|
67
|
+
this.onConsumeProductId(payload)
|
|
68
|
+
}
|
|
69
|
+
else if (payload.type === SFUMessageType.ResumeConsumer) {
|
|
70
|
+
this.resumeConsumer(payload)
|
|
71
|
+
}
|
|
72
|
+
else if (payload.type === SFUMessageType.PauseConsumer) {
|
|
73
|
+
this.pauseConsumer(payload)
|
|
74
|
+
}
|
|
75
|
+
else if (payload.type === SFUMessageType.UpdateSpatialConsumer) {
|
|
76
|
+
this.updateSpatialConsumer(payload)
|
|
77
|
+
}
|
|
78
|
+
else if (payload.type === SFUMessageType.OnProducerClosed) {
|
|
79
|
+
this.onProducerClosed(payload)
|
|
80
|
+
}
|
|
81
|
+
else if (payload.type === SFUMessageType.OnResumeProducer) {
|
|
82
|
+
this.onResumeProducer(payload)
|
|
83
|
+
}
|
|
84
|
+
else if (payload.type === SFUMessageType.OnPauseProducer) {
|
|
85
|
+
this.onPauseProducer(payload)
|
|
86
|
+
}
|
|
87
|
+
else if (payload.type === SFUMessageType.OnRestartIceCandidate) {
|
|
88
|
+
this.onRestartIceCandidate(payload)
|
|
89
|
+
}
|
|
90
|
+
else if (payload.type === SFUMessageType.GetAllProducers) {
|
|
91
|
+
this.onGetAllProducers(payload)
|
|
92
|
+
}
|
|
93
|
+
else if (payload.type === SFUMessageType.OnStopProducer) {
|
|
94
|
+
this.onProducerClosed(payload)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
private async onConsumeProductId(payload: SFUMessageBody) {
|
|
102
|
+
try {
|
|
103
|
+
if (this.roomHandlerDataSource) {
|
|
104
|
+
|
|
105
|
+
const producer = this.roomHandlerDataSource.getProducerById(payload.message.producerId)
|
|
106
|
+
if (producer && producer.producer.closed === false) {
|
|
107
|
+
|
|
108
|
+
const consumer = await this.webrtcRecieveTransport?.consume(payload.message)
|
|
109
|
+
if (consumer) {
|
|
110
|
+
consumer.on("producerclose", () => {
|
|
111
|
+
console.log("producerclose");
|
|
112
|
+
consumer.close()
|
|
113
|
+
this.consumers = this.consumers.filter((eachConsumer) => eachConsumer.id !== consumer.id)
|
|
114
|
+
const response = { producerId: consumer.producerId }
|
|
115
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.userId!, this.preapreClientMessageBody(true, this.selfParticipant, this.preapreWebSocketMessageBody(SFUMessageType.OnTrackEnded, response)));
|
|
116
|
+
|
|
117
|
+
})
|
|
118
|
+
consumer.on("producerpause", () => {
|
|
119
|
+
console.log("producerpause");
|
|
120
|
+
|
|
121
|
+
if (consumer.closed === false) {
|
|
122
|
+
consumer.pause()
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
consumer.on("producerresume", () => {
|
|
126
|
+
console.log("producerresume");
|
|
127
|
+
|
|
128
|
+
if (consumer.closed === false) {
|
|
129
|
+
consumer.resume()// To be Done
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
consumer.on("@close", () => {
|
|
133
|
+
console.log("On closed")
|
|
134
|
+
this.consumers = this.consumers.filter((eachConsumer) => eachConsumer.id !== consumer.id)
|
|
135
|
+
})
|
|
136
|
+
this.consumers.push(consumer)
|
|
137
|
+
const consumeData = { consumer: { id: consumer?.id, producerId: consumer?.producerId, kind: consumer?.kind, rtpParameters: consumer?.rtpParameters, appData: consumer?.appData } }
|
|
138
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.userId!, this.preapreClientMessageBody(true, this.selfParticipant, this.preapreWebSocketMessageBody(SFUMessageType.OnServerConsumer, consumeData)));
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
console.error("Not able to consume")
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
console.error(" onConsumeProductId : Producer not found ", this.selfParticipant.userData.name, payload)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
console.error(" Product Consume Error ", payload)
|
|
152
|
+
console.error(err)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private onReadyToConsume(payload: SFUMessageBody) {
|
|
159
|
+
if (this.roomHandlerDataSource) {
|
|
160
|
+
this.roomHandlerDataSource.getAllProducerForRoom().forEach((eachRoomProducer: SFUEachRoomProducer) => {
|
|
161
|
+
if (eachRoomProducer.participant.userId && this.selfParticipant.userId && eachRoomProducer.participant.userId !== this.selfParticipant.userId && eachRoomProducer.producer) {
|
|
162
|
+
const producerData = { producer: { id: eachRoomProducer.producer.id, rtpParameters: eachRoomProducer.producer.rtpParameters, appData: eachRoomProducer.producer.appData } }
|
|
163
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.userId!, this.preapreClientMessageBody(true, this.selfParticipant, this.preapreWebSocketMessageBody(SFUMessageType.OnNewProducer, producerData)));
|
|
164
|
+
console.log("On New Producer")
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private resumeConsumer(payload: SFUMessageBody) {
|
|
171
|
+
const consumerId = payload.message.id;
|
|
172
|
+
if (consumerId) {
|
|
173
|
+
const consumer = this.consumers.find((consumer) => consumer.id === consumerId)
|
|
174
|
+
if (consumer && consumer.closed === false && consumer?.producerPaused === false) {
|
|
175
|
+
consumer.resume()
|
|
176
|
+
console.log("resumeConsumer", this.selfParticipant.userData.name)
|
|
177
|
+
|
|
178
|
+
}
|
|
179
|
+
else if (!consumer) {
|
|
180
|
+
console.error(" resumeConsumer : Consumer not found ")
|
|
181
|
+
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
private pauseConsumer(payload: SFUMessageBody) {
|
|
186
|
+
const consumerId = payload.message.id;
|
|
187
|
+
if (consumerId) {
|
|
188
|
+
const consumer = this.consumers.find((consumer) => consumer.id === consumerId)
|
|
189
|
+
if (consumer && consumer.closed === false) {
|
|
190
|
+
consumer.pause()
|
|
191
|
+
console.log("pauseConsumer", this.selfParticipant.userData.name)
|
|
192
|
+
|
|
193
|
+
}
|
|
194
|
+
else if (!consumer) {
|
|
195
|
+
console.error(" pauseConsumer : Consumer not found ")
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
private updateSpatialConsumer(payload: SFUMessageBody) {
|
|
200
|
+
const consumerId = payload.message.id;
|
|
201
|
+
const spatialLayer = payload.message.spatialLayer
|
|
202
|
+
if (consumerId && spatialLayer !== undefined) {
|
|
203
|
+
const consumer = this.consumers.find((consumer) => consumer.id === consumerId)
|
|
204
|
+
if (consumer && consumer.closed === false) {
|
|
205
|
+
console.log("updateSpatialConsumer consumer", consumer.id)
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
consumer.setPreferredLayers({ spatialLayer: spatialLayer })
|
|
209
|
+
console.log("consumer.setPreferredLayers", spatialLayer)
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
console.error(err)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (!consumer) {
|
|
216
|
+
console.error(" updateSpatialConsumer : Consumer not found ")
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
private async onTransportProduceSyncRequest(payload: SFUMessageBody) {
|
|
224
|
+
if (this.roomHandlerDataSource) {
|
|
225
|
+
|
|
226
|
+
const producer = await this.webrtcSendTransport?.produce(payload.message)
|
|
227
|
+
|
|
228
|
+
if (producer) {
|
|
229
|
+
await this.onNewProducer(producer)
|
|
230
|
+
if (producer.kind === 'audio') {
|
|
231
|
+
this.roomHandlerDataSource.addAudioObserverForProducer(producer)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.userId!, this.preapreClientMessageBody(true, this.selfParticipant, this.preapreWebSocketMessageBody(SFUMessageType.OnProduceSyncDone, { transportId: this.webrtcSendTransport?.id, producerId: producer.id, appData: producer.appData })));
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
console.error(" Error in creating producer ", payload)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private async onResumeProducer(payload: SFUMessageBody) {
|
|
245
|
+
const producerId = payload.message.producerId
|
|
246
|
+
const producer = this.producers.find((eachProducer) => eachProducer.id === producerId)
|
|
247
|
+
if (producer && producer.closed === false) {
|
|
248
|
+
producer.resume()
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
private async onPauseProducer(payload: SFUMessageBody) {
|
|
252
|
+
const producerId = payload.message.producerId
|
|
253
|
+
const producer = this.producers.find((eachProducer) => eachProducer.id === producerId)
|
|
254
|
+
if (producer && producer.closed === false) {
|
|
255
|
+
producer.pause()
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
private async onNewProducer(producer: Producer) {
|
|
259
|
+
if (this.roomHandlerDataSource) {
|
|
260
|
+
|
|
261
|
+
if (producer.closed === false) {
|
|
262
|
+
await this.roomHandlerDataSource.pipeToRoute(producer)
|
|
263
|
+
this.producers.push(producer)
|
|
264
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.roomId!, this.preapreClientMessageBody(false, this.selfParticipant, this.preapreWebSocketMessageBody(SFUMessageType.OnNewProducer, { producer: { id: producer.id, appData: producer.appData } })));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private async onGetAllProducers(payload: SFUMessageBody) {
|
|
270
|
+
const producers: Producer[] = []
|
|
271
|
+
if (this.roomHandlerDataSource) {
|
|
272
|
+
|
|
273
|
+
this.roomHandlerDataSource.getAllProducerForRoom().forEach((eachRoomProducer: SFUEachRoomProducer) => {
|
|
274
|
+
if (eachRoomProducer.participant.userId && this.selfParticipant.userId && eachRoomProducer.participant.userId !== this.selfParticipant.userId && eachRoomProducer.producer) {
|
|
275
|
+
producers.push(eachRoomProducer.producer)
|
|
276
|
+
}
|
|
277
|
+
})
|
|
278
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.userId!, this.preapreClientMessageBody(true, this.selfParticipant, this.preapreWebSocketMessageBody(SFUMessageType.OnAllProducers, { producers: producers })));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
}
|
|
282
|
+
private async onProducerClosed(payload: SFUMessageBody) {
|
|
283
|
+
const producerId = payload.message.producerId
|
|
284
|
+
if (producerId) {
|
|
285
|
+
const producerToBeClosed = this.producers.find((eachProducer) => eachProducer.id === producerId)
|
|
286
|
+
if (producerToBeClosed) {
|
|
287
|
+
console.log("onProducerClosed", payload.message)
|
|
288
|
+
producerToBeClosed.close()
|
|
289
|
+
const response = { producerId: producerId }
|
|
290
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.roomId!, this.preapreClientMessageBody(false, this.selfParticipant, this.preapreWebSocketMessageBody(SFUMessageType.OnTrackEnded, response)));
|
|
291
|
+
this.producers = this.producers.filter((eachProducer) => eachProducer.id !== producerId)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private async createWebrtcSendTransport(participant: Participant) {
|
|
298
|
+
if (!this.sendRouter) {
|
|
299
|
+
console.error("createWebrtcRecieveTransport no send router found ", participant.roomId!)
|
|
300
|
+
return
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
if (!this.webrtcSendTransport || this.webrtcSendTransport.closed) {
|
|
305
|
+
this.webrtcSendTransport = await this.sendRouter?.createWebRtcTransport(await webrtcTransportConfiguration())
|
|
306
|
+
if (this.webrtcSendTransport) {
|
|
307
|
+
this.webrtcSendTransport!.appData.type = 'send'
|
|
308
|
+
this.webrtcSendTransport!.appData.userId = participant.userId
|
|
309
|
+
this.webrtcSendTransport!.appData.participant = participant
|
|
310
|
+
this.webrtcSendTransport!.on("@newproducer", (produce) => {
|
|
311
|
+
})
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
console.error("createWebrtcSendTransport Transport not created ", participant.roomId!)
|
|
315
|
+
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (this.webrtcSendTransport) {
|
|
319
|
+
const transportOptionForClient = {
|
|
320
|
+
iceRole: this.webrtcSendTransport.iceRole,
|
|
321
|
+
iceParameters: this.webrtcSendTransport.iceParameters,
|
|
322
|
+
iceCandidates: this.webrtcSendTransport.iceCandidates,
|
|
323
|
+
iceState: this.webrtcSendTransport.iceState,
|
|
324
|
+
iceSelectedTuple: this.webrtcSendTransport.iceSelectedTuple,
|
|
325
|
+
dtlsParameters: this.webrtcSendTransport.dtlsParameters,
|
|
326
|
+
dtlsState: this.webrtcSendTransport.dtlsState,
|
|
327
|
+
dtlsRemoteCert: this.webrtcSendTransport.dtlsRemoteCert,
|
|
328
|
+
sctpParameters: this.webrtcSendTransport.sctpParameters,
|
|
329
|
+
sctpState: this.webrtcSendTransport.sctpState,
|
|
330
|
+
id: this.webrtcSendTransport.id,
|
|
331
|
+
appData: this.webrtcSendTransport.appData
|
|
332
|
+
}
|
|
333
|
+
this.redisBroadcastMessageToTopic(participant.userId!, this.preapreClientMessageBody(true, participant, this.preapreWebSocketMessageBody(SFUMessageType.OnSendTransport, { transport: transportOptionForClient })));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
private async createWebrtcRecieveTransport(participant: Participant) {
|
|
337
|
+
if (!this.recvRouter) {
|
|
338
|
+
console.error("createWebrtcRecieveTransport no recieve router found ", participant.roomId!)
|
|
339
|
+
return
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (!this.webrtcRecieveTransport || this.webrtcRecieveTransport.closed) {
|
|
343
|
+
this.webrtcRecieveTransport = await this.sendRouter?.createWebRtcTransport(await webrtcTransportConfiguration())
|
|
344
|
+
if (this.webrtcRecieveTransport) {
|
|
345
|
+
this.webrtcRecieveTransport!.appData.type = 'consumer'
|
|
346
|
+
this.webrtcRecieveTransport!.appData.userId = participant.userId
|
|
347
|
+
this.webrtcRecieveTransport!.appData.participant = participant
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
console.error("webrtcRecieveTransport Transport not created ", participant.roomId!)
|
|
351
|
+
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (this.webrtcRecieveTransport) {
|
|
355
|
+
const transportOptionForClient = {
|
|
356
|
+
iceRole: this.webrtcRecieveTransport.iceRole,
|
|
357
|
+
iceParameters: this.webrtcRecieveTransport.iceParameters,
|
|
358
|
+
iceCandidates: this.webrtcRecieveTransport.iceCandidates,
|
|
359
|
+
iceState: this.webrtcRecieveTransport.iceState,
|
|
360
|
+
iceSelectedTuple: this.webrtcRecieveTransport.iceSelectedTuple,
|
|
361
|
+
dtlsParameters: this.webrtcRecieveTransport.dtlsParameters,
|
|
362
|
+
dtlsState: this.webrtcRecieveTransport.dtlsState,
|
|
363
|
+
dtlsRemoteCert: this.webrtcRecieveTransport.dtlsRemoteCert,
|
|
364
|
+
sctpParameters: this.webrtcRecieveTransport.sctpParameters,
|
|
365
|
+
sctpState: this.webrtcRecieveTransport.sctpState,
|
|
366
|
+
id: this.webrtcRecieveTransport.id,
|
|
367
|
+
appData: this.webrtcRecieveTransport.appData
|
|
368
|
+
}
|
|
369
|
+
this.redisBroadcastMessageToTopic(participant.userId!, this.preapreClientMessageBody(true, participant, this.preapreWebSocketMessageBody(SFUMessageType.OnConsumeTransport, { transport: transportOptionForClient })));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
private async connectTransport(payload: SFUMessageBody) {
|
|
375
|
+
let transport;
|
|
376
|
+
if (payload.message.type && payload.message.type === 'send') {
|
|
377
|
+
transport = this.webrtcSendTransport
|
|
378
|
+
}
|
|
379
|
+
if (payload.message.type && payload.message.type === 'consumer') {
|
|
380
|
+
transport = this.webrtcRecieveTransport
|
|
381
|
+
}
|
|
382
|
+
const dtlsParameters = payload.message.dtlsParameters
|
|
383
|
+
if (transport) {
|
|
384
|
+
await transport.connect({ dtlsParameters })
|
|
385
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.userId!, this.preapreClientMessageBody(true, this.selfParticipant, this.preapreWebSocketMessageBody(SFUMessageType.OnTransportConnectDone, { transportId: transport.id })));
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
console.error("connectTransport no transport found ", payload)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
private async onRestartIceCandidate(payload: SFUMessageBody) {
|
|
395
|
+
const transportId = payload.message.transportId
|
|
396
|
+
if (transportId) {
|
|
397
|
+
let transport: WebRtcTransport | undefined = undefined
|
|
398
|
+
if (this.webrtcRecieveTransport?.id === transportId) {
|
|
399
|
+
transport = this.webrtcRecieveTransport
|
|
400
|
+
}
|
|
401
|
+
else if (this.webrtcSendTransport?.id === transportId) {
|
|
402
|
+
transport = this.webrtcSendTransport
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (transport) {
|
|
406
|
+
const iceParameters = await transport.restartIce()
|
|
407
|
+
const response = { iceParameters: iceParameters, transportId: transportId }
|
|
408
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.userId!, this.preapreClientMessageBody(true, this.selfParticipant, this.preapreWebSocketMessageBody(SFUMessageType.OnRestartIceCandidateResponse, response)));
|
|
409
|
+
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
const response = { transportId: transportId }
|
|
413
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.userId!, this.preapreClientMessageBody(true, this.selfParticipant, this.preapreWebSocketMessageBody(SFUMessageType.OnTransportNotFound, response)));
|
|
414
|
+
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
private async checkAllProducerAndConsuer() {
|
|
420
|
+
if (this.isUserPresentInRoom) {
|
|
421
|
+
try {
|
|
422
|
+
// console.log("checkAllProducerAndConsuer")
|
|
423
|
+
this.producers.forEach((eachProducer) => {
|
|
424
|
+
if (eachProducer && eachProducer.closed === true) {
|
|
425
|
+
const response = { producerId: eachProducer.id }
|
|
426
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.roomId!, this.preapreClientMessageBody(false, this.selfParticipant, this.preapreWebSocketMessageBody(SFUMessageType.OnTrackEnded, response)));
|
|
427
|
+
}
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
this.producers = this.producers.filter((eachProducer) =>
|
|
431
|
+
eachProducer.closed === false
|
|
432
|
+
)
|
|
433
|
+
}
|
|
434
|
+
catch (err) {
|
|
435
|
+
console.error(err)
|
|
436
|
+
}
|
|
437
|
+
if (this.isUserPresentInRoom) {
|
|
438
|
+
setTimeout(this.checkAllProducerAndConsuer, 8000)
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
public cleanUp() {
|
|
447
|
+
this.isUserPresentInRoom = false
|
|
448
|
+
this.roomHandlerDataSource = undefined
|
|
449
|
+
this.producers.forEach(producer => {
|
|
450
|
+
producer.close()
|
|
451
|
+
});
|
|
452
|
+
this.consumers.forEach(consumer => {
|
|
453
|
+
consumer.close()
|
|
454
|
+
});
|
|
455
|
+
if (this.webrtcSendTransport) {
|
|
456
|
+
this.webrtcSendTransport.close()
|
|
457
|
+
this.webrtcSendTransport = undefined
|
|
458
|
+
}
|
|
459
|
+
if (this.webrtcRecieveTransport) {
|
|
460
|
+
this.webrtcRecieveTransport.close()
|
|
461
|
+
this.webrtcRecieveTransport = undefined
|
|
462
|
+
}
|
|
463
|
+
this.recvRouter = this.sendRouter = undefined
|
|
464
|
+
this.producers = []
|
|
465
|
+
this.consumers = []
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import { Worker } from 'mediasoup/node/lib/Worker';
|
|
4
|
+
import { SFUEachRoomHandler } from './SFUEachRoomHandler';
|
|
5
|
+
import { Participant } from '../models/Participant';
|
|
6
|
+
import { ServerHandler } from '../ServerHandler';
|
|
7
|
+
const mediasoup = require("mediasoup");
|
|
8
|
+
|
|
9
|
+
export class SFUHandler {
|
|
10
|
+
|
|
11
|
+
static instance = new SFUHandler()
|
|
12
|
+
private workers: Worker[] = []
|
|
13
|
+
private sfuRooms = new Map<string, SFUEachRoomHandler>()
|
|
14
|
+
|
|
15
|
+
static getInstance() {
|
|
16
|
+
return SFUHandler.instance;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
this.init = this.init.bind(this)
|
|
21
|
+
this.createWorkers = this.createWorkers.bind(this)
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
public async init() {
|
|
28
|
+
|
|
29
|
+
await this.createWorkers()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public async checkAndSetupRoom(roomId: string) {
|
|
33
|
+
if (this.sfuRooms.has(roomId) === false) {
|
|
34
|
+
console.log("On New Room")
|
|
35
|
+
const sfuEachRoomHandler = new SFUEachRoomHandler(roomId, this.workers, (this.sfuRooms.size % this.workers.length))
|
|
36
|
+
this.sfuRooms.set(roomId, sfuEachRoomHandler)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public checkIfCanCloseRoom(roomId: string) {
|
|
41
|
+
const room = this.getRoomSFUHandler(roomId)
|
|
42
|
+
if (room && room.roomPaticipants.size === 0) {
|
|
43
|
+
room.cleanUp()
|
|
44
|
+
this.sfuRooms.delete(roomId)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public getRoomSFUHandler(roomId: string): SFUEachRoomHandler | undefined {
|
|
49
|
+
if (this.sfuRooms.has(roomId)) {
|
|
50
|
+
return this.sfuRooms.get(roomId)
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
console.error("No Room Found During SFU Message", roomId)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private async createWorkers() {
|
|
58
|
+
while (this.workers.length < os.cpus().length) {
|
|
59
|
+
mediasoup.logLevel = "info"
|
|
60
|
+
const worker: Worker = await mediasoup.createWorker({ rtcMinPort: ServerHandler.getInstance().serverStartRequest.rtcMinPort, rtcMaxPort: ServerHandler.getInstance().serverStartRequest.rtcMaxPort })
|
|
61
|
+
worker.updateSettings({ logLevel: "debug" })
|
|
62
|
+
this.workers.push(worker)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { RtpCodecCapability } from "mediasoup/node/lib/RtpParameters";
|
|
2
|
+
import { WebRtcTransportOptions } from "mediasoup/node/lib/WebRtcTransport";
|
|
3
|
+
import { ServerHandler } from "../ServerHandler";
|
|
4
|
+
const publicIp = require('public-ip');
|
|
5
|
+
let publicIpAddress : string | undefined;
|
|
6
|
+
(async () => {
|
|
7
|
+
publicIpAddress = await publicIp.v4();
|
|
8
|
+
console.log(publicIpAddress)
|
|
9
|
+
})();
|
|
10
|
+
export const mediaCodecs: RtpCodecCapability[] = [
|
|
11
|
+
{
|
|
12
|
+
kind: 'audio',
|
|
13
|
+
mimeType: 'audio/opus',
|
|
14
|
+
clockRate: 48000,
|
|
15
|
+
channels: 2
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
kind: 'video',
|
|
19
|
+
mimeType: 'video/VP8',
|
|
20
|
+
clockRate: 90000,
|
|
21
|
+
parameters: {
|
|
22
|
+
'x-google-start-bitrate': 1000
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
kind: 'video',
|
|
27
|
+
mimeType: 'video/VP9',
|
|
28
|
+
clockRate: 90000,
|
|
29
|
+
parameters: {
|
|
30
|
+
'profile-id': 2,
|
|
31
|
+
'x-google-start-bitrate': 1000
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
kind: 'video',
|
|
36
|
+
mimeType: 'video/h264',
|
|
37
|
+
clockRate: 90000,
|
|
38
|
+
parameters: {
|
|
39
|
+
'packetization-mode': 1,
|
|
40
|
+
'profile-level-id': '42001f',
|
|
41
|
+
'level-asymmetry-allowed': 1,
|
|
42
|
+
'x-google-start-bitrate': 1000
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
export default async function webrtcTransportConfiguration() {
|
|
49
|
+
if(!publicIpAddress){
|
|
50
|
+
publicIpAddress = await publicIp.v4();
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
|
|
54
|
+
listenIps: [{ ip: '0.0.0.0', announcedIp: publicIpAddress }],
|
|
55
|
+
enableUdp: true,
|
|
56
|
+
enableTcp: true,
|
|
57
|
+
preferUdp: !ServerHandler.getInstance().serverStartRequest.isTCPConnection ,
|
|
58
|
+
preferTcp: ServerHandler.getInstance().serverStartRequest.isTCPConnection,
|
|
59
|
+
maxSctpMessageSize: 262144 * 10,
|
|
60
|
+
sctpSendBufferSize: 262144 * 10,
|
|
61
|
+
enableSctp: true,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { VaniEventListener } from "./VaniEventListener";
|
|
2
|
+
|
|
3
|
+
export class EventEmitterHandler {
|
|
4
|
+
|
|
5
|
+
static instance = new EventEmitterHandler()
|
|
6
|
+
public vaniEventEmitter : VaniEventListener = new VaniEventListener()
|
|
7
|
+
static getInstance(): EventEmitterHandler {
|
|
8
|
+
return EventEmitterHandler.instance;
|
|
9
|
+
}
|
|
10
|
+
}
|