Compare commits

...

83 Commits

Author SHA1 Message Date
32692f2390 replace classic build for docker 2025-04-03 21:19:15 +00:00
755df1ec5a Merge pull request 'LINXD-2850: Add handler on icestatechange failed/disconnected to send 'connection-failed' event to participants' (#39) from LINXD-2850-connection-failed-handler into develop
Reviewed-on: #39
Reviewed-by: Cristi Ene <cristi.ene@safemobile.com>
2025-03-12 15:22:30 +00:00
cd91d40dd0 LINXD-2850: Added image in doc 2025-03-12 09:56:09 +02:00
642dba8dac LINXD-2850: Add handler on icestatechange failed/disconnected to send 'connection-failed' event to participants 2025-03-12 09:31:36 +02:00
4f044555ba Merge pull request 'Update node version in Dockerfile' (#38) from update-build into develop
Reviewed-on: #38
2025-03-07 09:35:02 +00:00
230d5b6bce Merge branch 'develop' into update-build 2025-03-07 09:34:33 +00:00
73c7700ded Update node version in Dockerfile 2025-03-07 11:33:47 +02:00
9e66772b53 Merge pull request 'update-build' (#37) from update-build into develop
Reviewed-on: #37
2025-03-07 09:14:06 +00:00
5ef66af3f8 Update build.sh 2025-03-07 11:12:56 +02:00
1b29d43580 Update build.sh 2025-03-07 11:12:12 +02:00
dd5af12c7c Merge pull request 'LINXD-2842-log-protocol-change' (#36) from LINXD-2842-log-protocol-change into develop
Reviewed-on: #36
Reviewed-by: Cristi Ene <cristi.ene@safemobile.com>
2025-03-05 21:40:24 +00:00
af21774d17 LINXD-2842: Update mediasoup version; Add handler for icestatechange; Add env PREFER_TCP; Add 3s ICE consent timeout 2025-03-05 23:37:35 +02:00
fc250c248e LINXD-2842: Update README 2025-03-04 17:01:54 +02:00
fc39d5bead LINXD-2842: Log message when protocol is changed 2025-03-03 22:30:41 +02:00
a5c00ba10a update documentation 2025-02-27 13:34:55 +02:00
f7053359a5 Merge pull request 'LINXD-2836: Add connection type(udp, tcp) configurable from env' (#35) from LINXD-2836-mediasoup-connection-type-env into develop
Reviewed-on: #35
Reviewed-by: Cristi Ene <cristi.ene@safemobile.com>
2025-02-26 09:45:03 +00:00
056858d412 Merge pull request 'LINXD-2570: Add unit to dispatcher video call diagram' (#34) from LINXD-2570-mobile-to-dispatcher-calls into develop
Reviewed-on: #34
Reviewed-by: Cristi Ene <cristi.ene@safemobile.com>
2025-02-26 09:40:55 +00:00
3f40baad3e LINX-2836: Update certs 2025-02-26 10:26:28 +02:00
Sergiu Toma
b1c8419af5 LINXD-2836: Add connection type(udp, tcp) configurable from env 2025-02-26 10:24:09 +02:00
Sergiu Toma
271768065c LINXD-2570: Add unit to dispatcher video call diagram 2023-12-06 10:26:21 +02:00
ce1fe3ca8d Merge pull request 'Update 'app.js'' (#33) from sergiu-patch-1 into develop
Reviewed-on: #33
Reviewed-by: bmamihai <mihai.bozieru@safemobile.com>
2023-06-07 12:51:12 +00:00
96b770d9c5 Update 'app.js' 2023-06-07 12:50:24 +00:00
4b1f4fab70 Merge pull request 'LH-284: Update duplex documentation workflow diagrams' (#32) from LH-284-update-and-centralize-diagrams-for-video-calls into develop
Reviewed-on: #32
Reviewed-by: Adriana <adriana.epure@safemobile.com>
2023-05-30 11:27:09 +00:00
ba7c2186af LH-284: Update duplex documentation workflow diagrams 2023-05-30 11:57:02 +03:00
636e8a9fab Merge pull request 'added build commit no' (#31) from build-commit-no into develop
Reviewed-on: #31
Reviewed-by: Cristi Ene <cristi.ene@safemobile.com>
2023-04-10 23:36:34 +00:00
5da2eb6927 update build.sh after review 2023-04-05 01:57:58 +03:00
97948a5d8c added build commit no 2023-04-02 01:17:18 +03:00
b94b1bff86 Merge pull request 'docker' (#18) from docker into develop
Reviewed-on: #18
Reviewed-by: Cristi Ene <cristi.ene@safemobile.com>
2023-02-26 22:11:02 +00:00
5c4f2bf13e Merge branch 'develop' into docker 2023-02-26 22:10:34 +00:00
df7f1987f6 Merge pull request 'LH-276: Add close-producer event handler; Update client' (#30) from LH-276-close-producer into develop
Reviewed-on: #30
Reviewed-by: Cristi Ene <cristi.ene@safemobile.com>
2023-02-23 09:46:41 +00:00
7842953faf LH-276: Refactor consumer-resume 2023-02-22 18:57:57 +02:00
4591617b1e LH-276: Fix video consume when initiator is not set yet(at start) 2023-02-22 18:50:37 +02:00
9b3f2f94c8 LH-276: Fix audio when initiator is not set yet(at start) 2023-02-22 18:46:01 +02:00
29a4cd7227 LH-276: Format code 2023-02-22 18:40:39 +02:00
c80265fe25 LH-276: Format code 2023-02-22 18:36:50 +02:00
abb1533c9b LH-276: Format code 2023-02-22 18:32:41 +02:00
6e3ce9fbb3 LH-276: Format code 2023-02-22 18:30:28 +02:00
4d8adf9eac LH-276: Refactor consume(consumeAudio/consumeVideo); Format code 2023-02-22 18:21:53 +02:00
bde02fe250 LH-276: Add close-producer event handler; Update client 2023-02-21 02:41:48 +02:00
0ddb43b4b5 Merge pull request 'LINXD-2342-dispatcher-audio' (#29) from LINXD-2342-dispatcher-audio into develop
Reviewed-on: #29
Reviewed-by: Cristi Ene <cristi.ene@safemobile.com>
2023-02-15 09:14:57 +00:00
f02d6af726 LINXD-2342: Added documentation for kind 2023-02-15 11:14:02 +02:00
7ee93d7963 LINXD-2342: Update video client 2023-02-15 10:06:39 +02:00
98212a78cb Update client to consume audio correctly 2023-02-11 21:32:53 +02:00
33ea44ac02 Update client to consume audio correctly 2023-02-11 21:30:21 +02:00
6b822142d0 Add the kind of producer on new-producer event 2023-02-11 21:13:57 +02:00
50e95b93a4 Update client 2023-02-08 19:37:32 +02:00
6eca0808c9 Merge pull request 'Update web client to work with dev' (#27) from update-web-client into develop
Reviewed-on: #27
Reviewed-by: bmamihai <mihai.bozieru@safemobile.com>
2023-01-23 20:37:27 +00:00
f728b23b7f Update web client to work with dev 2023-01-23 20:45:08 +02:00
33c0234fb0 Merge pull request 'LINXD-2303: Added client-to-client workflow; Added client-mediadoup workflow' (#26) from LINXD-2303-video-documentation-mobile-mobile into develop
Reviewed-on: #26
Reviewed-by: Cristi Ene <cristi.ene@safemobile.com>
2023-01-19 14:44:24 +00:00
f81d5b2dfa LINXD-2303: Update diagrams Mediasoup connect & produce; Update diagram Client-Client workflow 2023-01-19 16:43:46 +02:00
ae87b5bd71 LINXD-2303: Added client-to-client workflow; Added client-mediadoup workflow 2023-01-19 16:31:16 +02:00
09e82fb224 Merge pull request 'Moved 'new-producer' in 'transport-produce' handler' (#25) from new-producer-in-transport-produce into develop
Reviewed-on: #25
Reviewed-by: Cristi Ene <cristi.ene@safemobile.com>
2023-01-19 06:54:53 +00:00
1da0170261 Moved 'new-producer' in 'transport-produce' handler 2023-01-18 11:49:02 +02:00
aca0a6eac0 improved build to get also the git log to know what is on server 2023-01-17 16:00:52 +00:00
23ca56c8c6 rever build.sh 2023-01-17 15:51:55 +00:00
a2d0b6771b Merge pull request 'Add new-producer event; Update client to consume when receives new-producer event' (#24) from LAPI-675-generate-new-producer-in-mediasoup-and-client into develop
Reviewed-on: #24
Reviewed-by: Cristi Ene <cristi.ene@safemobile.com>
2023-01-16 15:40:06 +00:00
9382fd11fb LAPI-675: Remove commented code 2023-01-16 17:39:15 +02:00
fb99fd57f8 LAPI-675: Refactor transport-connect new-producer 2023-01-16 17:32:31 +02:00
0f4342777a LAPI-675: Update bundle 2023-01-13 20:12:59 +02:00
69c167e4e9 Add new-producer event; Update client to consume when receives new-producer event 2023-01-13 20:11:24 +02:00
ad4c1f27e7 Merge pull request 'LAPI-674: Parse transport-recv-connect and consume param' (#23) from LAPI-674-change-events-for-consumer-in-mediasoup-server into develop
Reviewed-on: #23
Reviewed-by: Cristi Ene <cristi.ene@safemobile.com>
2023-01-13 07:52:27 +00:00
b2128c4754 fix build 2023-01-12 16:42:44 +00:00
af53dc1610 fix build 2023-01-12 16:30:39 +00:00
f48fe4f9aa LAPI-674: Parse transport-recv-connect and consume param 2023-01-12 16:23:30 +02:00
6fd6ca9755 Merge pull request 'comsume-undefined' (#22) from comsume-undefined into develop
Reviewed-on: #22
Reviewed-by: Cristi Ene <cristi.ene@safemobile.com>
2023-01-12 13:31:46 +00:00
1b72941a32 Moved callId in consumer-resume 2023-01-12 15:09:21 +02:00
c037240f47 Refactor consumer-resume 2023-01-12 14:32:48 +02:00
9716bddd71 Added log 2023-01-12 12:50:40 +02:00
e6c08a2c1d Refactor consumer-resume v3 2023-01-12 12:46:46 +02:00
1342e812e6 Refactor consumer-resume 2023-01-12 12:45:23 +02:00
d8405eccc7 Refactor consumer-resume 2023-01-12 12:44:11 +02:00
5bf31d452f Check for initiatorConsumerAudio before resume() 2023-01-12 12:38:40 +02:00
c034610471 Rollback 2023-01-12 12:38:01 +02:00
df5b3eab90 Check for initiatorConsumerAudio before resume() 2023-01-12 12:31:52 +02:00
b0293230b6 Merge pull request 'add-diagrams-doc' (#21) from add-diagrams-doc into develop
Reviewed-on: #21
Reviewed-by: Cristi Ene <cristi.ene@safemobile.com>
2023-01-10 17:51:05 +00:00
48eee903a5 added command for limiting cpu/memory 2023-01-05 14:31:51 +02:00
d5bc0cd1d3 run under host network 2023-01-05 13:18:13 +02:00
1c353d7c88 Merge branch 'develop' into docker 2023-01-05 01:14:56 +02:00
3e31ba21bd replace individual copy with copy all 2022-12-15 13:20:22 +02:00
cdf02756d3 fix start 2022-12-14 01:32:17 +02:00
b2f9f5affa remove pm2 and watchify 2022-12-14 00:48:14 +02:00
5b9bfeaa01 improve docker 2022-12-10 03:21:59 +02:00
e3bef9b3e5 New dockerfile 2022-12-10 02:38:09 +02:00
36 changed files with 2048 additions and 839 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
node_modules
doc

12
.env
View File

@ -1,7 +1,11 @@
PORT=3000 PORT=3000
IP=0.0.0.0 # Listening IPv4 or IPv6. IP=0.0.0.0 # Listening IPv4 or IPv6.
ANNOUNCED_IP=185.8.154.190 # Announced IPv4 or IPv6 (useful when running mediasoup behind NAT with private IP). ANNOUNCED_IP=192.168.1.199 # Announced IPv4 or IPv6 (useful when running mediasoup behind NAT with private IP).
RTC_MIN_PORT=2000 RTC_MIN_PORT=40000
RTC_MAX_PORT=2020 RTC_MAX_PORT=49999
SERVER_CERT="./server/ssl/cert.pem" SERVER_CERT="./server/ssl/cert.pem"
SERVER_KEY="./server/ssl/key.pem" SERVER_KEY="./server/ssl/key.pem"
ENABLE_UDP=true
ENABLE_TCP=true
PREFER_UDP=true
PREFER_TCP=false

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
/node_modules /node_modules
/dist /dist
.idea

View File

@ -1,11 +1,25 @@
FROM ubuntu FROM ubuntu:22.04
WORKDIR /app
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y build-essential pip net-tools iputils-ping iproute2 curl apt-get install -y build-essential pip net-tools iputils-ping iproute2 curl
RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
RUN apt-get install -y nodejs RUN apt-get install -y nodejs
RUN npm install -g watchify
EXPOSE 3000 COPY . /app/
EXPOSE 2000-2020
EXPOSE 10000-10100 RUN npm install
EXPOSE 3000/tcp
EXPOSE 2000-2200/udp
CMD node app.js
#docker build -t linx-video .
# docker run -it -d --restart always -p 3000:3000/tcp -p 2000-2200:2000-2200/udp linx-video
#Run under host network
# docker run -it -d --network host --restart always -p 3000:3000/tcp -p 2000-2200:2000-2200/udp linx-video
#https://docs.docker.com/config/containers/resource_constraints/
#docker run -it -d --network host --cpus="0.25" --memory="512m" --restart always -p 3000:3000/tcp -p 2000-2200:2000-2200/udp linx-video

View File

@ -6,36 +6,54 @@
1. Go to `/server/ssl` 1. Go to `/server/ssl`
2. Execute `openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem` 2. Execute `openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem`
### Development ### Development
##### To start in development mode you must: ##### To start in development mode you must:
1. Install the dependencies `npm install`.
2. Run the `npm start:dev` command to start the server in dev mode.
(Any change will trigger a refresh of the server)
1. Install the dependencies `npm install`.
2. Go to the `linx-devops/scaling-tools/private-system-truste-cert` project and generate a new server certificate and key:
sh create_certificate_for_domain.sh 192.168.1.110 #local IP
# generates files
nginx-selfsigned.crt
device.key
3. You need to update the Video Server in the provisioning to point to your private IP. ex: https://192.168.1.199:3000
4. The generated files must be moved to server/ssl and renamed as follows:
cp device.key {mediasoup_project}/server/ssl/key.pem
cp nginx-selfsigned.crt {mediosup_project}/server/ssl/cert.pem
5. Go to https://dev.linx.safemobile.com/dispatcher/resources/help/LINXHelp.html#safemobile-certificate-import and import the certificate for your system type
6. The ANNOUNCED IP in .env must be configured to use the same private IP used in generating the certificate.
7. Run the `npm start:dev` command to start the server in dev mode.
(Any change will trigger a refresh of the server)
### Production ### Production
##### To start in production mode you must: ##### To start in production mode you must:
1. Install the dependencies `npm install`. 1. Install the dependencies `npm install`.
2. Run the `npm start:prod` command to start the server in production mode. 2. Run the `npm start:prod` command to start the server in production mode.
(To connect to the terminal, use `pm2 log video-server`) (To connect to the terminal, use `pm2 log video-server`)
### Web client ### Web client
- The server will start by default on port 3000, and the ssl certificates will have to be configured - The server will start by default on port 3000, and the ssl certificates will have to be configured
- The web client can be accessed using the /sfu path - The web client can be accessed using the /sfu path
ex: https://HOST/sfu/?assetId=1&&accountId=1&producer=true&dest_asset_id=75&assetName=Adi ex: https://HOST/sfu/?assetId=1&&accountId=1&producer=true&dest_asset_id=75&assetName=Adi
assetId = asset id of the unit on which you are doing the test assetId = asset id of the unit on which you are doing the test
accountId = account id of the unit on which you are doing the test accountId = account id of the unit on which you are doing the test
producer = it will always be true because you are the producer producer = it will always be true because you are the producer
(it's possible to put false, but then you have to have another client with producer true) (it's possible to put false, but then you have to have another client with producer true)
assetName = asset name of the unit on which you are doing the test assetName = asset name of the unit on which you are doing the test
dest_asset_id= the addressee with whom the call is made dest_asset_id= the addressee with whom the call is made
- To make a call using this client, you need a microphone and permission to use it - To make a call using this client, you need a microphone and permission to use it
- For any changes related to the client, the command `npm run watch' will have to be used to generate the bundle.js used by the web client - For any changes related to the client, the command `npm run watch' will have to be used to generate the bundle.js used by the web client
### Demo project ### Demo project
The demo project used initially and then modified for our needs `https://github.com/jamalag/mediasoup2` The demo project used initially and then modified for our needs `https://github.com/jamalag/mediasoup2`

530
app.js
View File

@ -1,4 +1,4 @@
require('dotenv').config() require('dotenv').config();
const express = require('express'); const express = require('express');
const app = express(); const app = express();
@ -12,6 +12,11 @@ try {
console.log('https support is disabled!'); console.log('https support is disabled!');
} }
const mediasoup = require('mediasoup'); const mediasoup = require('mediasoup');
const isUdpEnabled = process.env.ENABLE_UDP === 'true';
const isTcpEnabled = process.env.ENABLE_TCP === 'true';
const isUdpPreferred = process.env.PREFER_UDP === 'true';
const isTcpPreferred = process.env.PREFER_TCP === 'true';
let currentConnectionType = isUdpPreferred ? 'udp' : 'tcp';
let worker; let worker;
/** /**
@ -28,8 +33,8 @@ let worker;
* initiatorConsumerVideo: Consumer, * initiatorConsumerVideo: Consumer,
* initiatorConsumerAudio: Consumer, * initiatorConsumerAudio: Consumer,
* initiatorConsumerTransport: Consumer Transport * initiatorConsumerTransport: Consumer Transport
* initiatorSockerId * initiatorSocket
* receiverSocketId * receiverSocket
* } * }
* *
**/ **/
@ -37,7 +42,7 @@ let videoCalls = {};
let socketDetails = {}; let socketDetails = {};
app.get('/', (_req, res) => { app.get('/', (_req, res) => {
res.send('Hello from mediasoup app!') res.send('OK');
}); });
app.use('/sfu', express.static(path.join(__dirname, 'public'))); app.use('/sfu', express.static(path.join(__dirname, 'public')));
@ -52,7 +57,7 @@ const httpsServer = https.createServer(options, app);
const io = new Server(httpsServer, { const io = new Server(httpsServer, {
allowEIO3: true, allowEIO3: true,
origins: ["*:*"] origins: ['*:*'],
}); });
httpsServer.listen(process.env.PORT, () => { httpsServer.listen(process.env.PORT, () => {
@ -66,19 +71,19 @@ const createWorker = async () => {
worker = await mediasoup.createWorker({ worker = await mediasoup.createWorker({
rtcMinPort: parseInt(process.env.RTC_MIN_PORT), rtcMinPort: parseInt(process.env.RTC_MIN_PORT),
rtcMaxPort: parseInt(process.env.RTC_MAX_PORT), rtcMaxPort: parseInt(process.env.RTC_MAX_PORT),
}) });
console.log(`[createWorker] worker pid ${worker.pid}`); console.log(`[createWorker] worker pid ${worker.pid}`);
worker.on('died', error => { worker.on('died', (error) => {
// This implies something serious happened, so kill the application // This implies something serious happened, so kill the application
console.error('mediasoup worker has died', error); console.error('mediasoup worker has died', error);
setTimeout(() => process.exit(1), 2000); // exit in 2 seconds setTimeout(() => process.exit(1), 2000); // exit in 2 seconds
}) });
return worker; return worker;
} catch (error) { } catch (error) {
console.log(`ERROR | createWorker | ${error.message}`); console.error(`[createWorker] | ERROR | error: ${error.message}`);
} }
} };
// We create a Worker as soon as our application starts // We create a Worker as soon as our application starts
worker = createWorker(); worker = createWorker();
@ -87,57 +92,53 @@ worker = createWorker();
// https://mediasoup.org/documentation/v3/mediasoup/rtp-parameters-and-capabilities/#RtpCodecCapability // https://mediasoup.org/documentation/v3/mediasoup/rtp-parameters-and-capabilities/#RtpCodecCapability
// list of media codecs supported by mediasoup ... // list of media codecs supported by mediasoup ...
// https://github.com/versatica/mediasoup/blob/v3/src/supportedRtpCapabilities.ts // https://github.com/versatica/mediasoup/blob/v3/src/supportedRtpCapabilities.ts
const mediaCodecs = [ const mediaCodecs = [
{ {
kind : 'audio', kind: 'audio',
mimeType : 'audio/opus', mimeType: 'audio/opus',
clockRate : 48000, clockRate: 48000,
channels : 2 channels: 2,
}, },
{ {
kind : 'video', kind: 'video',
mimeType : 'video/VP8', mimeType: 'video/VP8',
clockRate : 90000, clockRate: 90000,
parameters : parameters: {
{ 'x-google-start-bitrate': 1000,
'x-google-start-bitrate' : 1000
}, },
channels : 2 channels: 2,
}, },
{ {
kind : 'video', kind: 'video',
mimeType : 'video/VP9', mimeType: 'video/VP9',
clockRate : 90000, clockRate: 90000,
parameters : parameters: {
{ 'profile-id': 2,
'profile-id' : 2, 'x-google-start-bitrate': 1000,
'x-google-start-bitrate' : 1000 },
}
}, },
{ {
kind : 'video', kind: 'video',
mimeType : 'video/h264', mimeType: 'video/h264',
clockRate : 90000, clockRate: 90000,
parameters : parameters: {
{ 'packetization-mode': 1,
'packetization-mode' : 1, 'profile-level-id': '4d0032',
'profile-level-id' : '4d0032', 'level-asymmetry-allowed': 1,
'level-asymmetry-allowed' : 1, 'x-google-start-bitrate': 1000,
'x-google-start-bitrate' : 1000 },
}
}, },
{ {
kind : 'video', kind: 'video',
mimeType : 'video/h264', mimeType: 'video/h264',
clockRate : 90000, clockRate: 90000,
parameters : parameters: {
{ 'packetization-mode': 1,
'packetization-mode' : 1, 'profile-level-id': '42e01f',
'profile-level-id' : '42e01f', 'level-asymmetry-allowed': 1,
'level-asymmetry-allowed' : 1, 'x-google-start-bitrate': 1000,
'x-google-start-bitrate' : 1000 },
} },
}
]; ];
const closeCall = (callId) => { const closeCall = (callId) => {
@ -152,24 +153,23 @@ const closeCall = (callId) => {
videoCalls[callId]?.receiverProducerTransport?.close(); videoCalls[callId]?.receiverProducerTransport?.close();
videoCalls[callId]?.router?.close(); videoCalls[callId]?.router?.close();
delete videoCalls[callId]; delete videoCalls[callId];
} else { console.log(`[closeCall] | callId: ${callId}`);
console.log(`The call with id ${callId} has already been deleted`);
} }
} catch (error) { } catch (error) {
console.log(`ERROR | closeCall | callid ${callId} | ${error.message}`); console.error(`[closeCall] | ERROR | callId: ${callId} | error: ${error.message}`);
} }
} };
/* /*
- Handlers for WS events - Handlers for WS events
- These are created only when we have a connection with a peer - These are created only when we have a connection with a peer
*/ */
peers.on('connection', async socket => { peers.on('connection', async (socket) => {
console.log('[connection] socketId:', socket.id); console.log('[connection] socketId:', socket.id);
// After making the connection successfully, we send the client a 'connection-success' event // After making the connection successfully, we send the client a 'connection-success' event
socket.emit('connection-success', { socket.emit('connection-success', {
socketId: socket.id socketId: socket.id,
}); });
// It is triggered when the peer is disconnected // It is triggered when the peer is disconnected
@ -192,24 +192,22 @@ peers.on('connection', async socket => {
if (callId) { if (callId) {
console.log(`[createRoom] socket.id ${socket.id} callId ${callId}`); console.log(`[createRoom] socket.id ${socket.id} callId ${callId}`);
if (!videoCalls[callId]) { if (!videoCalls[callId]) {
console.log('[createRoom] callId', callId); videoCalls[callId] = { router: await worker.createRouter({ mediaCodecs }) };
videoCalls[callId] = { router: await worker.createRouter({ mediaCodecs }) } console.log(`[createRoom] Generate Router ID: ${videoCalls[callId].router.id}`);
console.log(`[createRoom] Router ID: ${videoCalls[callId].router.id}`); videoCalls[callId].receiverSocket = socket;
videoCalls[callId].receiverSocketId = socket.id
} else { } else {
videoCalls[callId].initiatorSockerId = socket.id videoCalls[callId].initiatorSocket = socket;
} }
socketDetails[socket.id] = callId; socketDetails[socket.id] = callId;
// rtpCapabilities is set for callback // rtpCapabilities is set for callback
console.log('[getRtpCapabilities] callId', callId);
callbackResponse = { callbackResponse = {
rtpCapabilities :videoCalls[callId].router.rtpCapabilities rtpCapabilities: videoCalls[callId].router.rtpCapabilities,
}; };
} else { } else {
console.log(`[createRoom] missing callId ${callId}`); console.log(`[createRoom] missing callId: ${callId}`);
} }
} catch (error) { } catch (error) {
console.log(`ERROR | createRoom | callId ${callId} | ${error.message}`); console.error(`[createRoom] | ERROR | callId: ${callId} | error: ${error.message}`);
} finally { } finally {
callback(callbackResponse); callback(callbackResponse);
} }
@ -228,23 +226,27 @@ peers.on('connection', async socket => {
const callId = socketDetails[socket.id]; const callId = socketDetails[socket.id];
console.log(`[createWebRtcTransport] socket ${socket.id} | sender ${sender} | callId ${callId}`); console.log(`[createWebRtcTransport] socket ${socket.id} | sender ${sender} | callId ${callId}`);
if (sender) { if (sender) {
if(!videoCalls[callId].receiverProducerTransport && !isInitiator(callId, socket.id)) { if (!videoCalls[callId].receiverProducerTransport && !isInitiator(callId, socket.id)) {
videoCalls[callId].receiverProducerTransport = await createWebRtcTransportLayer(callId, callback); videoCalls[callId].receiverProducerTransport = await createWebRtcTransportLayer(callId, callback);
} else if(!videoCalls[callId].initiatorProducerTransport && isInitiator(callId, socket.id)) { } else if (!videoCalls[callId].initiatorProducerTransport && isInitiator(callId, socket.id)) {
videoCalls[callId].initiatorProducerTransport = await createWebRtcTransportLayer(callId, callback); videoCalls[callId].initiatorProducerTransport = await createWebRtcTransportLayer(callId, callback);
} else { } else {
console.log(`producerTransport has already been defined | callId ${callId}`); console.log(`producerTransport has already been defined | callId ${callId}`);
callback(null); callback(null);
} }
} else if (!sender) { } else if (!sender) {
if(!videoCalls[callId].receiverConsumerTransport && !isInitiator(callId, socket.id)) { if (!videoCalls[callId].receiverConsumerTransport && !isInitiator(callId, socket.id)) {
videoCalls[callId].receiverConsumerTransport = await createWebRtcTransportLayer(callId, callback); videoCalls[callId].receiverConsumerTransport = await createWebRtcTransportLayer(callId, callback);
} else if(!videoCalls[callId].initiatorConsumerTransport && isInitiator(callId, socket.id)) { } else if (!videoCalls[callId].initiatorConsumerTransport && isInitiator(callId, socket.id)) {
videoCalls[callId].initiatorConsumerTransport = await createWebRtcTransportLayer(callId, callback); videoCalls[callId].initiatorConsumerTransport = await createWebRtcTransportLayer(callId, callback);
} }
} }
} catch (error) { } catch (error) {
console.log(`ERROR | createWebRtcTransport | callId ${socketDetails[socket.id]} | sender ${sender} | ${error.message}`); console.error(
`[createWebRtcTransport] | ERROR | callId: ${socketDetails[socket.id]} | sender: ${sender} | error: ${
error.message
}`
);
callback(error); callback(error);
} }
}); });
@ -259,13 +261,12 @@ peers.on('connection', async socket => {
if (typeof dtlsParameters === 'string') dtlsParameters = JSON.parse(dtlsParameters); if (typeof dtlsParameters === 'string') dtlsParameters = JSON.parse(dtlsParameters);
console.log(`[transport-connect] socket ${socket.id} | callId ${callId}`); console.log(`[transport-connect] socket ${socket.id} | callId ${callId}`);
if (!isInitiator(callId, socket.id)) {
await videoCalls[callId].receiverProducerTransport.connect({ dtlsParameters }); isInitiator(callId, socket.id)
} else { ? await videoCalls[callId].initiatorProducerTransport.connect({ dtlsParameters })
await videoCalls[callId].initiatorProducerTransport.connect({ dtlsParameters }); : await videoCalls[callId].receiverProducerTransport.connect({ dtlsParameters });
}
} catch (error) { } catch (error) {
console.log(`ERROR | transport-connect | callId ${socketDetails[socket.id]} | ${error.message}`); console.error(`[transport-connect] | ERROR | callId: ${socketDetails[socket.id]} | error: ${error.message}`);
} }
}); });
@ -279,7 +280,8 @@ peers.on('connection', async socket => {
const callId = socketDetails[socket.id]; const callId = socketDetails[socket.id];
if (typeof rtpParameters === 'string') rtpParameters = JSON.parse(rtpParameters); if (typeof rtpParameters === 'string') rtpParameters = JSON.parse(rtpParameters);
console.log(`[transport-produce] kind: ${kind} | socket: ${socket.id} | callId: ${callId}`); console.log(`[transport-produce] callId: ${callId} | kind: ${kind} | socket: ${socket.id}`);
if (kind === 'video') { if (kind === 'video') {
if (!isInitiator(callId, socket.id)) { if (!isInitiator(callId, socket.id)) {
videoCalls[callId].receiverVideoProducer = await videoCalls[callId].receiverProducerTransport.produce({ videoCalls[callId].receiverVideoProducer = await videoCalls[callId].receiverProducerTransport.produce({
@ -287,35 +289,31 @@ peers.on('connection', async socket => {
rtpParameters, rtpParameters,
}); });
console.log(`[transport-produce] receiverVideoProducer Producer ID: ${videoCalls[callId].receiverVideoProducer.id} | kind: ${videoCalls[callId].receiverVideoProducer.kind}`);
videoCalls[callId].receiverVideoProducer.on('transportclose', () => { videoCalls[callId].receiverVideoProducer.on('transportclose', () => {
const callId = socketDetails[socket.id]; console.log('transport for this producer closed', callId);
console.log('transport for this producer closed', callId)
closeCall(callId); closeCall(callId);
}); });
// Send back to the client the Producer's id // Send back to the client the Producer's id
callback && callback({ callback &&
id: videoCalls[callId].receiverVideoProducer.id callback({
}); id: videoCalls[callId].receiverVideoProducer.id,
});
} else { } else {
videoCalls[callId].initiatorVideoProducer = await videoCalls[callId].initiatorProducerTransport.produce({ videoCalls[callId].initiatorVideoProducer = await videoCalls[callId].initiatorProducerTransport.produce({
kind, kind,
rtpParameters, rtpParameters,
}); });
console.log(`[transport-produce] initiatorVideoProducer Producer ID: ${videoCalls[callId].initiatorVideoProducer.id} | kind: ${videoCalls[callId].initiatorVideoProducer.kind}`);
videoCalls[callId].initiatorVideoProducer.on('transportclose', () => { videoCalls[callId].initiatorVideoProducer.on('transportclose', () => {
const callId = socketDetails[socket.id]; console.log('transport for this producer closed', callId);
console.log('transport for this producer closed', callId)
closeCall(callId); closeCall(callId);
}); });
callback && callback({ callback &&
id: videoCalls[callId].initiatorVideoProducer.id callback({
}); id: videoCalls[callId].initiatorVideoProducer.id,
});
} }
} else if (kind === 'audio') { } else if (kind === 'audio') {
if (!isInitiator(callId, socket.id)) { if (!isInitiator(callId, socket.id)) {
@ -324,40 +322,44 @@ peers.on('connection', async socket => {
rtpParameters, rtpParameters,
}); });
console.log(`[transport-produce] receiverAudioProducer Producer ID: ${videoCalls[callId].receiverAudioProducer.id} | kind: ${videoCalls[callId].receiverAudioProducer.kind}`);
videoCalls[callId].receiverAudioProducer.on('transportclose', () => { videoCalls[callId].receiverAudioProducer.on('transportclose', () => {
const callId = socketDetails[socket.id]; console.log('transport for this producer closed', callId);
console.log('transport for this producer closed', callId)
closeCall(callId); closeCall(callId);
}); });
// Send back to the client the Producer's id // Send back to the client the Producer's id
callback && callback({ callback &&
id: videoCalls[callId].receiverAudioProducer.id callback({
}); id: videoCalls[callId].receiverAudioProducer.id,
});
} else { } else {
videoCalls[callId].initiatorAudioProducer = await videoCalls[callId].initiatorProducerTransport.produce({ videoCalls[callId].initiatorAudioProducer = await videoCalls[callId].initiatorProducerTransport.produce({
kind, kind,
rtpParameters, rtpParameters,
}); });
console.log(`[transport-produce] initiatorAudioProducer Producer ID: ${videoCalls[callId].initiatorAudioProducer.id} | kind: ${videoCalls[callId].initiatorAudioProducer.kind}`);
videoCalls[callId].initiatorAudioProducer.on('transportclose', () => { videoCalls[callId].initiatorAudioProducer.on('transportclose', () => {
const callId = socketDetails[socket.id]; console.log('transport for this producer closed', callId);
console.log('transport for this producer closed', callId)
closeCall(callId); closeCall(callId);
}); });
// Send back to the client the Producer's id // Send back to the client the Producer's id
callback && callback({ callback &&
id: videoCalls[callId].initiatorAudioProducer.id callback({
}); id: videoCalls[callId].initiatorAudioProducer.id,
});
} }
} }
const socketToEmit = isInitiator(callId, socket.id)
? videoCalls[callId].receiverSocket
: videoCalls[callId].initiatorSocket;
// callId - Id of the call
// kind - producer type: audio/video
socketToEmit?.emit('new-producer', { callId, kind });
} catch (error) { } catch (error) {
console.log(`ERROR | transport-produce | callId ${socketDetails[socket.id]} | ${error.message}`); console.error(`[transport-produce] | ERROR | callId: ${socketDetails[socket.id]} | error: ${error.message}`);
} }
}); });
@ -369,16 +371,17 @@ peers.on('connection', async socket => {
try { try {
const callId = socketDetails[socket.id]; const callId = socketDetails[socket.id];
console.log(`[transport-recv-connect] socket ${socket.id} | callId ${callId}`); console.log(`[transport-recv-connect] socket ${socket.id} | callId ${callId}`);
if (typeof dtlsParameters === 'string') dtlsParameters = JSON.parse(dtlsParameters);
// await videoCalls[callId].consumerTransport.connect({ dtlsParameters }); // await videoCalls[callId].consumerTransport.connect({ dtlsParameters });
if(!isInitiator(callId, socket.id)) { if (!isInitiator(callId, socket.id)) {
await videoCalls[callId].receiverConsumerTransport.connect({ dtlsParameters }); await videoCalls[callId].receiverConsumerTransport.connect({ dtlsParameters });
} else if(isInitiator(callId, socket.id)) { } else if (isInitiator(callId, socket.id)) {
await videoCalls[callId].initiatorConsumerTransport.connect({ dtlsParameters }); await videoCalls[callId].initiatorConsumerTransport.connect({ dtlsParameters });
} }
} catch (error) { } catch (error) {
console.log(`ERROR | transport-recv-connect | callId ${socketDetails[socket.id]} | ${error.message}`); console.error(`[transport-recv-connect] | ERROR | callId: ${socketDetails[socket.id]} | error: ${error.message}`);
} }
}) });
/* /*
- The customer consumes after successfully connecting to consumerTransport - The customer consumes after successfully connecting to consumerTransport
@ -388,196 +391,164 @@ peers.on('connection', async socket => {
- The consumer does consumerTransport.consume(params) - The consumer does consumerTransport.consume(params)
*/ */
socket.on('consume', async ({ rtpCapabilities }, callback) => { socket.on('consume', async ({ rtpCapabilities }, callback) => {
try { const callId = socketDetails[socket.id];
const callId = socketDetails[socket.id]; const socketId = socket.id;
console.log(`[consume] socket ${socket.id} | callId ${callId} | rtpCapabilities: ${JSON.stringify(rtpCapabilities)}`);
console.log('[consume] callId', callId); console.log(`[consume] socket ${socketId} | callId: ${callId}`);
let canConsumeVideo, canConsumeAudio;
if (isInitiator(callId, socket.id)) {
canConsumeVideo = !!videoCalls[callId].receiverVideoProducer && !!videoCalls[callId].router.canConsume({
producerId: videoCalls[callId].receiverVideoProducer.id,
rtpCapabilities
});
canConsumeAudio = !!videoCalls[callId].receiverAudioProducer && !!videoCalls[callId].router.canConsume({
producerId: videoCalls[callId].receiverAudioProducer.id,
rtpCapabilities
});
} else { if (typeof rtpCapabilities === 'string') rtpCapabilities = JSON.parse(rtpCapabilities);
canConsumeVideo = !!videoCalls[callId].initiatorVideoProducer && !!videoCalls[callId].router.canConsume({
producerId: videoCalls[callId].initiatorVideoProducer.id,
rtpCapabilities
});
canConsumeAudio = !!videoCalls[callId].initiatorAudioProducer && !!videoCalls[callId].router.canConsume({ callback({
producerId: videoCalls[callId].initiatorAudioProducer.id, videoParams: await consumeVideo({ callId, socketId, rtpCapabilities }),
rtpCapabilities audioParams: await consumeAudio({ callId, socketId, rtpCapabilities }),
}); });
}
console.log('[consume] canConsumeVideo', canConsumeVideo);
console.log('[consume] canConsumeAudio', canConsumeAudio);
if (canConsumeVideo && !canConsumeAudio) {
const videoParams = await consumeVideo(callId, socket.id, rtpCapabilities)
callback({ videoParams, audioParams: null });
} else if (canConsumeVideo && canConsumeAudio) {
const videoParams = await consumeVideo(callId, socket.id, rtpCapabilities)
const audioParams = await consumeAudio(callId, socket.id, rtpCapabilities)
callback({ videoParams, audioParams });
} else if (!canConsumeVideo && canConsumeAudio) {
const audioParams = await consumeAudio(callId, socket.id, rtpCapabilities)
const data = { videoParams: null, audioParams };
callback(data);
} else {
console.log(`[consume] Can't consume | callId ${callId}`);
callback(null);
}
} catch (error) {
console.log(`ERROR | consume | callId ${socketDetails[socket.id]} | ${error.message}`)
callback({ params: { error } });
}
}); });
/* /*
- Event sent by the consumer after consuming to resume the pause - Event sent by the consumer after consuming to resume the pause
- When consuming on consumerTransport, it is initially done with paused: true, here we will resume - When consuming on consumerTransport, it is initially done with paused: true, here we will resume
- For the initiator we resume the initiatorConsumerAUDIO/VIDEO and for receiver the receiverConsumerAUDIO/VIDEO
*/ */
socket.on('consumer-resume', async () => { socket.on('consumer-resume', () => {
try { try {
const callId = socketDetails[socket.id]; const callId = socketDetails[socket.id];
console.log(`[consumer-resume] callId ${callId}`) const isInitiatorValue = isInitiator(callId, socket.id);
console.log(`[consumer-resume] callId: ${callId} | isInitiator: ${isInitiatorValue}`);
const consumerVideo = isInitiatorValue
? videoCalls[callId].initiatorConsumerVideo
: videoCalls[callId].receiverConsumerVideo;
const consumerAudio = isInitiatorValue
? videoCalls[callId].initiatorConsumerAudio
: videoCalls[callId].receiverConsumerAudio;
consumerVideo?.resume();
consumerAudio?.resume();
} catch (error) {
console.error(
`[consumer-resume] | ERROR | callId: ${socketDetails[socket.id]} | isInitiator: ${isInitiator} | error: ${
error.message
}`
);
}
});
socket.on('close-producer', ({ callId, kind }) => {
try {
if (isInitiator(callId, socket.id)) { if (isInitiator(callId, socket.id)) {
console.log(`[consumer-resume] isInitiator true`); console.log(`[close-producer] initiator --EMIT--> receiver | callId: ${callId} | kind: ${kind}`);
await videoCalls[callId].initiatorConsumerVideo.resume(); videoCalls[callId].receiverSocket.emit('close-producer', { callId, kind });
await videoCalls[callId].initiatorConsumerAudio.resume();
} else { } else {
console.log(`[consumer-resume] isInitiator false`); console.log(`[close-producer] receiver --EMIT--> initiator | callId: ${callId} | kind: ${kind}`);
(videoCalls[callId].receiverConsumerVideo) && await videoCalls[callId].receiverConsumerVideo.resume(); videoCalls[callId].initiatorSocket.emit('close-producer', { callId, kind });
(videoCalls[callId].receiverConsumerVideo) && await videoCalls[callId].receiverConsumerAudio.resume();
} }
} catch (error) { } catch (error) {
console.log(`ERROR | consumer-resume | callId ${socketDetails[socket.id]} | ${error.message}`); console.error(`[close-producer] | ERROR | callId: ${socketDetails[socket.id]} | error: ${error.message}`);
} }
}); });
}); });
const consumeVideo = async (callId, socketId, rtpCapabilities) => { const canConsume = ({ callId, producerId, rtpCapabilities }) => {
return !!videoCalls[callId].router.canConsume({
producerId,
rtpCapabilities,
});
};
const consumeVideo = async ({ callId, socketId, rtpCapabilities }) => {
// Handlers for consumer transport https://mediasoup.org/documentation/v3/mediasoup/api/#consumer-on-transportclose
if (isInitiator(callId, socketId) && videoCalls[callId].receiverVideoProducer) {
const producerId = videoCalls[callId].receiverVideoProducer.id;
if (!canConsume({ callId, producerId, rtpCapabilities })) return null;
if(isInitiator(callId, socketId)) {
videoCalls[callId].initiatorConsumerVideo = await videoCalls[callId].initiatorConsumerTransport.consume({ videoCalls[callId].initiatorConsumerVideo = await videoCalls[callId].initiatorConsumerTransport.consume({
producerId: videoCalls[callId].receiverVideoProducer.id, producerId,
rtpCapabilities, rtpCapabilities,
paused: true, paused: true,
}); });
// https://mediasoup.org/documentation/v3/mediasoup/api/#consumer-on-transportclose
videoCalls[callId].initiatorConsumerVideo.on('transportclose', () => {
const callId = socketDetails[socket.id];
console.log('transport close from consumer', callId);
closeCall(callId);
});
// https://mediasoup.org/documentation/v3/mediasoup/api/#consumer-on-producerclose
videoCalls[callId].initiatorConsumerVideo.on('producerclose', () => {
const callId = socketDetails[socket.id];
console.log('producer of consumer closed', callId);
closeCall(callId);
});
return { return {
id: videoCalls[callId].initiatorConsumerVideo.id, id: videoCalls[callId].initiatorConsumerVideo.id,
producerId: videoCalls[callId].receiverVideoProducer.id, producerId,
kind: 'video', kind: 'video',
rtpParameters: videoCalls[callId].initiatorConsumerVideo.rtpParameters, rtpParameters: videoCalls[callId].initiatorConsumerVideo.rtpParameters,
} };
} else { } else if (videoCalls[callId].initiatorVideoProducer) {
const producerId = videoCalls[callId].initiatorVideoProducer.id;
if (!canConsume({ callId, producerId, rtpCapabilities })) return null;
videoCalls[callId].receiverConsumerVideo = await videoCalls[callId].receiverConsumerTransport.consume({ videoCalls[callId].receiverConsumerVideo = await videoCalls[callId].receiverConsumerTransport.consume({
producerId: videoCalls[callId].initiatorVideoProducer.id, producerId,
rtpCapabilities, rtpCapabilities,
paused: true, paused: true,
}); });
videoCalls[callId].receiverConsumerVideo.on('transportclose', () => {
const callId = socketDetails[socket.id];
console.log('transport close from consumer', callId);
closeCall(callId);
});
videoCalls[callId].receiverConsumerVideo.on('producerclose', () => {
const callId = socketDetails[socket.id];
console.log('producer of consumer closed', callId);
closeCall(callId);
});
return { return {
id: videoCalls[callId].receiverConsumerVideo.id, id: videoCalls[callId].receiverConsumerVideo.id,
producerId: videoCalls[callId].initiatorVideoProducer.id, producerId,
kind: 'video', kind: 'video',
rtpParameters: videoCalls[callId].receiverConsumerVideo.rtpParameters, rtpParameters: videoCalls[callId].receiverConsumerVideo.rtpParameters,
} };
}
}
const consumeAudio = async (callId, socketId, rtpCapabilities) => {
if(isInitiator(callId, socketId)) {
videoCalls[callId].initiatorConsumerAudio = await videoCalls[callId].initiatorConsumerTransport.consume({
producerId: videoCalls[callId].receiverAudioProducer.id,
rtpCapabilities,
paused: true,
});
// https://mediasoup.org/documentation/v3/mediasoup/api/#consumer-on-transportclose
videoCalls[callId].initiatorConsumerAudio.on('transportclose', () => {
const callId = socketDetails[socket.id];
console.log('transport close from consumer', callId);
closeCall(callId);
});
// https://mediasoup.org/documentation/v3/mediasoup/api/#consumer-on-producerclose
videoCalls[callId].initiatorConsumerAudio.on('producerclose', () => {
const callId = socketDetails[socket.id];
console.log('producer of consumer closed', callId);
closeCall(callId);
});
return {
id: videoCalls[callId].initiatorConsumerAudio.id,
producerId: videoCalls[callId].receiverAudioProducer.id,
kind: 'audio',
rtpParameters: videoCalls[callId].initiatorConsumerAudio.rtpParameters,
}
} else { } else {
videoCalls[callId].receiverConsumerAudio = await videoCalls[callId].receiverConsumerTransport.consume({ return null;
producerId: videoCalls[callId].initiatorAudioProducer.id,
rtpCapabilities,
paused: true,
});
videoCalls[callId].receiverConsumerAudio.on('transportclose', () => {
const callId = socketDetails[socket.id];
console.log('transport close from consumer', callId);
closeCall(callId);
});
videoCalls[callId].receiverConsumerAudio.on('producerclose', () => {
const callId = socketDetails[socket.id];
console.log('producer of consumer closed', callId);
closeCall(callId);
});
return {
id: videoCalls[callId].receiverConsumerAudio.id,
producerId: videoCalls[callId].initiatorAudioProducer.id,
kind: 'audio',
rtpParameters: videoCalls[callId].receiverConsumerAudio.rtpParameters,
}
} }
} };
const consumeAudio = async ({ callId, socketId, rtpCapabilities }) => {
try {
// Handlers for consumer transport https://mediasoup.org/documentation/v3/mediasoup/api/#consumer-on-transportclose
if (isInitiator(callId, socketId) && videoCalls[callId].receiverAudioProducer) {
const producerId = videoCalls[callId].receiverAudioProducer.id;
if (!canConsume({ callId, producerId, rtpCapabilities })) return null;
videoCalls[callId].initiatorConsumerAudio = await videoCalls[callId].initiatorConsumerTransport.consume({
producerId,
rtpCapabilities,
paused: true,
});
return {
id: videoCalls[callId].initiatorConsumerAudio.id,
producerId,
kind: 'audio',
rtpParameters: videoCalls[callId].initiatorConsumerAudio.rtpParameters,
};
} else if (videoCalls[callId].initiatorAudioProducer) {
const producerId = videoCalls[callId].initiatorAudioProducer.id;
if (!canConsume({ callId, producerId, rtpCapabilities })) return null;
videoCalls[callId].receiverConsumerAudio = await videoCalls[callId].receiverConsumerTransport.consume({
producerId,
rtpCapabilities,
paused: true,
});
return {
id: videoCalls[callId].receiverConsumerAudio.id,
producerId,
kind: 'audio',
rtpParameters: videoCalls[callId].receiverConsumerAudio.rtpParameters,
};
} else {
return null;
}
} catch (error) {
console.error(`[consumeAudio] | ERROR | error: ${error}`);
}
};
const isInitiator = (callId, socketId) => { const isInitiator = (callId, socketId) => {
return (videoCalls[callId].initiatorSockerId === socketId); return videoCalls[callId]?.initiatorSocket?.id === socketId;
};
const emitToParticipants = (callId, event, message) => {
try {
videoCalls[callId].receiverSocket.emit(event, message);
videoCalls[callId].initiatorSocket.emit(event, message);
} catch (error) {
console.error(`[emitToParticipants] | ERROR | callId: ${callId} | error: ${error.message}`);
}
} }
/* /*
@ -589,26 +560,45 @@ const isInitiator = (callId, socketId) => {
*/ */
const createWebRtcTransportLayer = async (callId, callback) => { const createWebRtcTransportLayer = async (callId, callback) => {
try { try {
console.log('[createWebRtcTransportLayer] callId', callId); console.log(`[createWebRtcTransportLayer] callId: ${callId}`);
// https://mediasoup.org/documentation/v3/mediasoup/api/#WebRtcTransportOptions // https://mediasoup.org/documentation/v3/mediasoup/api/#WebRtcTransportOptions
const webRtcTransport_options = { const webRtcTransport_options = {
listenIps: [ listenIps: [
{ {
ip: process.env.IP, // Listening IPv4 or IPv6. ip: process.env.IP, // Listening IPv4 or IPv6.
announcedIp: process.env.ANNOUNCED_IP, // Announced IPv4 or IPv6 (useful when running mediasoup behind NAT with private IP). announcedIp: process.env.ANNOUNCED_IP, // Announced IPv4 or IPv6 (useful when running mediasoup behind NAT with private IP).
} },
], ],
enableUdp: true, enableUdp: isUdpEnabled,
enableTcp: true, enableTcp: isTcpEnabled,
preferUdp: true, preferUdp: isUdpPreferred,
preferTcp: isTcpPreferred,
iceConsentTimeout: 3
}; };
// https://mediasoup.org/documentation/v3/mediasoup/api/#router-createWebRtcTransport // https://mediasoup.org/documentation/v3/mediasoup/api/#router-createWebRtcTransport
let transport = await videoCalls[callId].router.createWebRtcTransport(webRtcTransport_options) let transport = await videoCalls[callId].router.createWebRtcTransport(webRtcTransport_options);
console.log(`callId: ${callId} | transport id: ${transport.id}`)
// `iceselectedtuplechange`: Fires when ICE switches transport (e.g., UDP → TCP).
transport.on('iceselectedtuplechange', (selectedTuple) => {
const { protocol } = selectedTuple;
if (currentConnectionType !== protocol) {
console.warn(`⚠️ ${currentConnectionType.toUpperCase()} blocked! Switching to ${protocol.toUpperCase()} for callId: ${callId}`);
currentConnectionType = protocol;
}
});
// `icestatechange`: Fires when ICE connection state changes (e.g., new, connected, failed).
transport.on('icestatechange', (iceState) => {
console.log(`[ICE STATE CHANGE] callId: ${callId} | State: ${iceState}`);
if (iceState === 'failed' || iceState === 'disconnected') {
console.warn(`⚠️ ICE failure detected for callId: ${callId}! Possible UDP blockage.`);
emitToParticipants(callId, 'connection-failed', { callId });
}
});
// Handler for when DTLS(Datagram Transport Layer Security) changes // Handler for when DTLS(Datagram Transport Layer Security) changes
transport.on('dtlsstatechange', dtlsState => { transport.on('dtlsstatechange', (dtlsState) => {
console.log(`transport | dtlsstatechange | calldId ${callId} | dtlsState ${dtlsState}`); console.log(`transport | dtlsstatechange | calldId ${callId} | dtlsState ${dtlsState}`);
if (dtlsState === 'closed') { if (dtlsState === 'closed') {
transport.close(); transport.close();
@ -627,15 +617,15 @@ const createWebRtcTransportLayer = async (callId, callback) => {
dtlsParameters: transport.dtlsParameters, dtlsParameters: transport.dtlsParameters,
}; };
console.log('[createWebRtcTransportLayer] callback params', params);
// Send back to the client the params // Send back to the client the params
callback({ params }); callback({ params });
// Set transport to producerTransport or consumerTransport // Set transport to producerTransport or consumerTransport
return transport; return transport;
} catch (error) { } catch (error) {
console.log(`ERROR | createWebRtcTransportLayer | callId ${socketDetails[socket.id]} | ${error.message}`); console.error(
`[createWebRtcTransportLayer] | ERROR | callId: ${socketDetails[socket.id]} | error: ${error.message}`
);
callback({ params: { error } }); callback({ params: { error } });
} }
} };

View File

@ -1,4 +1,40 @@
#/!bin/bash #/!bin/bash
## FUNCTIONS
function getGitVersion(){
version=$(git describe)
count=$(echo ${version%%-*} | grep -o "\." | wc -l)
if (( $count > 1 )); then
version=${version%%-*}
elif (( $count == 0 ));then
echo -e "Error: Git version \"${version%%-*}\" not respecting Safemobile standard.\n Must be like 4.xx or 4.xx.xx"
version="0.0.0"
else
if [[ "$1" == "dev" ]];then
cleanprefix=${version#*-} # remove everything before `-` including `-`
cleansuffix=${cleanprefix%-*} # remove everything after `-` including `-`
version="${version%%-*}.${cleansuffix}"
else
version="${version%%-*}.0" # one `%` remove everything after last `-`, two `%%` remove everything after all `-`
fi
fi
}
function addVersionPm2(){
file_pkg="package.json"
key=" \"version\": \""
if [ -f "$file_pkg" ] && [ ! -z "$version" ]; then
versionApp=" \"version\": \"$version\","
sed -i "s|^.*$key.*|${versionApp//\//\\/}|g" $file_pkg
text=$(cat $file_pkg | grep -c "$version")
if [ $text -eq 0 ]; then
echo "Version couldn't be set"
else
echo "Version $version successfully applied to App"
fi
fi
}
## PREBUILD PROCESS ## PREBUILD PROCESS
# check dist dir to be present and empty # check dist dir to be present and empty
if [ ! -d "dist" ]; then if [ ! -d "dist" ]; then
@ -9,37 +45,30 @@ else
## CLEANUP ## CLEANUP
rm -fr dist/* rm -fr dist/*
fi fi
if [ -d "node_modules" ]; then
rm -fr node_modules
fi
# Install dependencies # Install dependencies
#npm install #npm install
## PROJECT NEEDS ## PROJECT NEEDS
echo "Building app... from $(git rev-parse --abbrev-ref HEAD)" echo "Building app... from $(git rev-parse --abbrev-ref HEAD)"
#npm run-script build #npm run-script build
cp -r {.env,app.js,package.json,server,public} dist/ cp -r {.env,app.js,package.json,server,public,doc,Dockerfile,tsconfig.json,.dockerignore} dist/
#cp -r ./* dist/
#Add version control for pm2 # Generate Git log
dateString=$(date +"%Y%m%d-%H%M%S")
git log --pretty=format:"%ad%x09%an%x09%s" --no-merges -20 > "dist/git-$dateString.log"
# Get Git version control
getGitVersion $1
# Add version control for pm2
cd dist cd dist
#Add version control for pm2 addVersionPm2
version=$(git describe)
file_pkg="package.json"
key=" \"version\": \""
count=$(echo ${version%%-*} | grep -o "\." | wc -l)
if (( $count > 1 )); then
version=${version%%-*}
else
version="${version%%-*}.0"
fi
if [ -f "$file_pkg" ] && [ ! -z "$version" ]; then
version=" \"version\": \"$version\","
sed -i "s|^.*$key.*|${version//\//\\/}|g" $file_pkg
text=$(cat $file_pkg | grep -c "$version")
if [ $text -eq 0 ]; then
echo "Version couldn't be set"
else
echo "Version $version successfully applied to App"
fi
fi
## POST BUILD ## POST BUILD

Binary file not shown.

Before

Width:  |  Height:  |  Size: 614 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 968 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 571 KiB

1150
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,7 @@
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
"express": "^4.18.1", "express": "^4.18.1",
"mediasoup": "^3.10.4", "mediasoup": "^3.15.5",
"mediasoup-client": "^3.6.54", "mediasoup-client": "^3.6.54",
"parcel": "^2.7.0", "parcel": "^2.7.0",
"socket.io": "^2.0.3", "socket.io": "^2.0.3",

View File

@ -20353,7 +20353,7 @@ module.exports = yeast;
},{}],94:[function(require,module,exports){ },{}],94:[function(require,module,exports){
module.exports = { module.exports = {
hubAddress: 'https://hub.dev.linx.safemobile.com/', hubAddress: 'https://hub.dev.linx.safemobile.com/',
mediasoupAddress: 'https://testing.video.safemobile.org', mediasoupAddress: 'https://testing.video.safemobile.org/',
} }
},{}],95:[function(require,module,exports){ },{}],95:[function(require,module,exports){
const io = require('socket.io-client') const io = require('socket.io-client')
@ -20449,10 +20449,23 @@ setTimeout(() => {
console.log(`[MEDIA] ${config.mediasoupAddress} | connected: ${socket.connected} | existsProducer: ${existsProducer}`) console.log(`[MEDIA] ${config.mediasoupAddress} | connected: ${socket.connected} | existsProducer: ${existsProducer}`)
if (!IS_PRODUCER && existsProducer && consumer === undefined) { if (!IS_PRODUCER && existsProducer && consumer === undefined) {
goConnect() goConnect()
// document.getElementById('btnRecvSendTransport').click();
} }
if (IS_PRODUCER && urlParams.get('testing') === 'true') { getLocalStream() } if (IS_PRODUCER && urlParams.get('testing') === 'true') { getLocalStream() }
}) })
socket.on('new-producer', ({ callId, kind }) => {
console.log(`🟢 new-producer | callId: ${callId} | kind: ${kind} | Ready to consume`);
connectRecvTransport();
})
socket.on('close-producer', ({ callId, kind }) => {
console.log(`🔴 close-producer | callId: ${callId} | kind: ${kind}`);
if (kind === 'video') {
consumerVideo.close()
remoteVideo.srcObject = null
}
else if (kind === 'audio') consumerAudio.close()
})
} }
if (IS_PRODUCER === true) { if (IS_PRODUCER === true) {
@ -20731,6 +20744,8 @@ const connectSendTransport = async () => {
// Enable Close call button // Enable Close call button
const closeCallBtn = document.getElementById('btnCloseCall'); const closeCallBtn = document.getElementById('btnCloseCall');
closeCallBtn.removeAttribute('disabled'); closeCallBtn.removeAttribute('disabled');
createRecvTransport();
} }
const createRecvTransport = async () => { const createRecvTransport = async () => {
@ -20770,7 +20785,8 @@ const createRecvTransport = async () => {
errback(error) errback(error)
} }
}) })
connectRecvTransport() // We call it in new-rpoducer, we don't need it here anymore
// connectRecvTransport()
}) })
} }
@ -20826,7 +20842,7 @@ const connectRecvTransport = async () => {
console.log('remoteVideo PLAY') console.log('remoteVideo PLAY')
}) })
.catch((error) => { .catch((error) => {
displayError(`remoteVideo PLAY ERROR | ${error.message}`) console.error(`remoteVideo PLAY ERROR | ${error.message}`)
}) })
}) })
} }
@ -20839,10 +20855,6 @@ const getVideoTrask = async (videoParams) => {
rtpParameters: videoParams.rtpParameters rtpParameters: videoParams.rtpParameters
}) })
consumerVideo.on('transportclose', () => {
console.log('transport closed so consumer closed')
})
return consumerVideo.track return consumerVideo.track
} }
@ -20903,31 +20915,31 @@ const closeCall = () => {
resetCallSettings() resetCallSettings()
} }
const consume = async () => { // const consume = async (kind) => {
console.log('[consume]') // console.log(`[consume] kind: ${kind}`)
console.log('createRecvTransport Consumer') // console.log('createRecvTransport Consumer')
await socket.emit('createWebRtcTransport', { sender: false, callId, dispatcher: true }, ({ params }) => { // await socket.emit('createWebRtcTransport', { sender: false, callId, dispatcher: true }, ({ params }) => {
if (params.error) { // if (params.error) {
console.log('createRecvTransport | createWebRtcTransport | Error', params.error) // console.log('createRecvTransport | createWebRtcTransport | Error', params.error)
return // return
} // }
consumerTransport = device.createRecvTransport(params) // consumerTransport = device.createRecvTransport(params)
consumerTransport.on('connect', async ({ dtlsParameters }, callback, errback) => { // consumerTransport.on('connect', async ({ dtlsParameters }, callback, errback) => {
try { // try {
await socket.emit('transport-recv-connect', { // await socket.emit('transport-recv-connect', {
dtlsParameters, // dtlsParameters,
}) // })
callback() // callback()
} catch (error) { // } catch (error) {
errback(error) // errback(error)
} // }
}) // })
connectRecvTransport() // connectRecvTransport()
}) // })
} // }
btnLocalVideo.addEventListener('click', getLocalStream) btnLocalVideo.addEventListener('click', getLocalStream)
btnRecvSendTransport.addEventListener('click', consume) // btnRecvSendTransport.addEventListener('click', consume)
btnCloseCall.addEventListener('click', closeCall) btnCloseCall.addEventListener('click', closeCall)
},{"./config":94,"mediasoup-client":66,"socket.io-client":82}]},{},[95]); },{"./config":94,"mediasoup-client":66,"socket.io-client":82}]},{},[95]);

View File

@ -1,4 +1,4 @@
module.exports = { module.exports = {
hubAddress: 'https://hub.dev.linx.safemobile.com/', hubAddress: 'https://hub.dev.linx.safemobile.com/',
mediasoupAddress: 'https://testing.video.safemobile.org', mediasoupAddress: 'https://testing.video.safemobile.org/',
} }

View File

@ -75,7 +75,7 @@
</td> </td>
<td> <td>
<div id="sharedBtns"> <div id="sharedBtns">
<button id="btnRecvSendTransport">Consume</button> <!-- <button id="btnRecvSendTransport">Consume</button> -->
<button id="remoteSoundControl">Unmute</button> <button id="remoteSoundControl">Unmute</button>
</div> </div>
</td> </td>

View File

@ -91,10 +91,23 @@ setTimeout(() => {
console.log(`[MEDIA] ${config.mediasoupAddress} | connected: ${socket.connected} | existsProducer: ${existsProducer}`) console.log(`[MEDIA] ${config.mediasoupAddress} | connected: ${socket.connected} | existsProducer: ${existsProducer}`)
if (!IS_PRODUCER && existsProducer && consumer === undefined) { if (!IS_PRODUCER && existsProducer && consumer === undefined) {
goConnect() goConnect()
// document.getElementById('btnRecvSendTransport').click();
} }
if (IS_PRODUCER && urlParams.get('testing') === 'true') { getLocalStream() } if (IS_PRODUCER && urlParams.get('testing') === 'true') { getLocalStream() }
}) })
socket.on('new-producer', ({ callId, kind }) => {
console.log(`🟢 new-producer | callId: ${callId} | kind: ${kind} | Ready to consume`);
connectRecvTransport();
})
socket.on('close-producer', ({ callId, kind }) => {
console.log(`🔴 close-producer | callId: ${callId} | kind: ${kind}`);
if (kind === 'video') {
consumerVideo.close()
remoteVideo.srcObject = null
}
else if (kind === 'audio') consumerAudio.close()
})
} }
if (IS_PRODUCER === true) { if (IS_PRODUCER === true) {
@ -373,6 +386,8 @@ const connectSendTransport = async () => {
// Enable Close call button // Enable Close call button
const closeCallBtn = document.getElementById('btnCloseCall'); const closeCallBtn = document.getElementById('btnCloseCall');
closeCallBtn.removeAttribute('disabled'); closeCallBtn.removeAttribute('disabled');
createRecvTransport();
} }
const createRecvTransport = async () => { const createRecvTransport = async () => {
@ -412,7 +427,8 @@ const createRecvTransport = async () => {
errback(error) errback(error)
} }
}) })
connectRecvTransport() // We call it in new-rpoducer, we don't need it here anymore
// connectRecvTransport()
}) })
} }
@ -468,7 +484,7 @@ const connectRecvTransport = async () => {
console.log('remoteVideo PLAY') console.log('remoteVideo PLAY')
}) })
.catch((error) => { .catch((error) => {
displayError(`remoteVideo PLAY ERROR | ${error.message}`) console.error(`remoteVideo PLAY ERROR | ${error.message}`)
}) })
}) })
} }
@ -481,10 +497,6 @@ const getVideoTrask = async (videoParams) => {
rtpParameters: videoParams.rtpParameters rtpParameters: videoParams.rtpParameters
}) })
consumerVideo.on('transportclose', () => {
console.log('transport closed so consumer closed')
})
return consumerVideo.track return consumerVideo.track
} }
@ -545,30 +557,30 @@ const closeCall = () => {
resetCallSettings() resetCallSettings()
} }
const consume = async () => { // const consume = async (kind) => {
console.log('[consume]') // console.log(`[consume] kind: ${kind}`)
console.log('createRecvTransport Consumer') // console.log('createRecvTransport Consumer')
await socket.emit('createWebRtcTransport', { sender: false, callId, dispatcher: true }, ({ params }) => { // await socket.emit('createWebRtcTransport', { sender: false, callId, dispatcher: true }, ({ params }) => {
if (params.error) { // if (params.error) {
console.log('createRecvTransport | createWebRtcTransport | Error', params.error) // console.log('createRecvTransport | createWebRtcTransport | Error', params.error)
return // return
} // }
consumerTransport = device.createRecvTransport(params) // consumerTransport = device.createRecvTransport(params)
consumerTransport.on('connect', async ({ dtlsParameters }, callback, errback) => { // consumerTransport.on('connect', async ({ dtlsParameters }, callback, errback) => {
try { // try {
await socket.emit('transport-recv-connect', { // await socket.emit('transport-recv-connect', {
dtlsParameters, // dtlsParameters,
}) // })
callback() // callback()
} catch (error) { // } catch (error) {
errback(error) // errback(error)
} // }
}) // })
connectRecvTransport() // connectRecvTransport()
}) // })
} // }
btnLocalVideo.addEventListener('click', getLocalStream) btnLocalVideo.addEventListener('click', getLocalStream)
btnRecvSendTransport.addEventListener('click', consume) // btnRecvSendTransport.addEventListener('click', consume)
btnCloseCall.addEventListener('click', closeCall) btnCloseCall.addEventListener('click', closeCall)

22
server/ssl/cert-old.pem Normal file
View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIUfpwrZVz3ogv3YeXbtL5wqIGEXGMwDQYJKoZIhvcNAQEL
BQAwZzELMAkGA1UEBhMCUk8xDTALBgNVBAgMBEFsYmExDTALBgNVBAcMBEFsYmEx
DDAKBgNVBAoMA0FBQTEQMA4GA1UEAwwHQUFBIENPTTEaMBgGCSqGSIb3DQEJARYL
YXNkQGFzZC5jb20wHhcNMjUwMjE4MTAwMDM5WhcNMzUwMjE2MTAwMDM5WjBnMQsw
CQYDVQQGEwJSTzENMAsGA1UECAwEQWxiYTENMAsGA1UEBwwEQWxiYTEMMAoGA1UE
CgwDQUFBMRAwDgYDVQQDDAdBQUEgQ09NMRowGAYJKoZIhvcNAQkBFgthc2RAYXNk
LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL7itjfKeuH5+f7c
43gAI+ppmxiwvzqhHLmkmlQrVbSC+P93yGekHIuXpbM3sqGRnvSJL3c9SIEdtVVj
yfJCs6KIsujxtiGn3hgQD01B6LqzFjSKnfYSGz8XDsjFW8cnpD1yRi3J7DhUjleM
bhQ0ileu9joS2OOhf84mtOkXJyY8q9xJH4ypimogcR98eM6ewnrb5Vhjo8YDaix2
6rceNmO/g4biknhXnBGc58/MnyAHtwzZxsu/k1IYtZuBYMPcAo7CQEX4XxXqQpaF
zaaoEUYB8KzVDlsr+i5SJzLtrHkyiuJijHq6YyOFkTwUULuJ7Wz0YL1redDCZV4i
EIVzBAcCAwEAAaNTMFEwHQYDVR0OBBYEFErSYY3J7ukx2KaRcHmazbMlKNBlMB8G
A1UdIwQYMBaAFErSYY3J7ukx2KaRcHmazbMlKNBlMA8GA1UdEwEB/wQFMAMBAf8w
DQYJKoZIhvcNAQELBQADggEBAC3TQY6jMGeHIEDEYS7sUbNZxe+azdDlx0DdwgLK
t+Zo2O40F55nVTZOUfypjCnLJnZitekptl5P6CPGrp2VX4/C0Ok4swwr+xamsjWt
9RR9yG0IpVfnCEziT4dpBPhNf/6ilgdpnkJUWY3LO3BJhM4Js7rfP4D9NgEYHeSR
YDN3TuEbi//bp43bhDh8EBQtDx9lPGOSUiKd3I7KfRttsxvLG2wBz3M5HXRc++6p
pHE+64YfkwV5xZDvU2M/EqePLp7DdQ9g+vQ68FxI6jMCegBoz+ueyE9RhZOk/cUh
uIXwIdFowjkUXgNncuGrR1gWf1mJVCHOsdnGZf3VSykGdWg=
-----END CERTIFICATE-----

View File

@ -1,25 +1,24 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIEJTCCAw2gAwIBAgIURHg2am+RarQxIVY1f3CicUQgRowwDQYJKoZIhvcNAQEL MIID9TCCAt2gAwIBAgIJAJZHglUuIBjtMA0GCSqGSIb3DQEBCwUAMIGZMQswCQYD
BQAwgaExCzAJBgNVBAYTAlJPMRIwEAYDVQQIDAlCdWNoYXJlc3QxEjAQBgNVBAcM VQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxGDAWBgNVBAcMD1JvbGxpbmcgTWVh
CUJ1Y2hhcmVzdDETMBEGA1UECgwKU2FmZW1vYmlsZTETMBEGA1UECwwKU2FmZW1v ZGl3czEYMBYGA1UECgwPU2FmZW1vYmlsZSBMTEMuMQ0wCwYDVQQLDARMSU5YMQ0w
YmlsZTETMBEGA1UEAwwKU2FmZW1vYmlsZTErMCkGCSqGSIb3DQEJARYcbWloYWku CwYDVQQDDARMSU5YMSUwIwYJKoZIhvcNAQkBFhZzdXBwb3J0QHNhZmVtb2JpbGUu
Ym96aWVydUBzYWZlbW9iaWxlLmNvbTAeFw0yMjA4MDEyMjA0MjFaFw0zMjA3Mjky Y29tMB4XDTI1MDIyNDEwMTAzNFoXDTM1MDIyMjEwMTAzNFowXjELMAkGA1UEBhMC
MjA0MjFaMIGhMQswCQYDVQQGEwJSTzESMBAGA1UECAwJQnVjaGFyZXN0MRIwEAYD VVMxETAPBgNVBAgMCElsbGlub2lzMRgwFgYDVQQHDA9Sb2xsaW5nIE1lYWRpd3Mx
VQQHDAlCdWNoYXJlc3QxEzARBgNVBAoMClNhZmVtb2JpbGUxEzARBgNVBAsMClNh EzARBgNVBAoMClNhZmVtb2JpbGUxDTALBgNVBAMMBExJTlgwggEiMA0GCSqGSIb3
ZmVtb2JpbGUxEzARBgNVBAMMClNhZmVtb2JpbGUxKzApBgkqhkiG9w0BCQEWHG1p DQEBAQUAA4IBDwAwggEKAoIBAQDEd8LMvdkD4CyZkwVYh4V/RIBMH8d9jK1Yvozd
aGFpLmJvemllcnVAc2FmZW1vYmlsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB 0kPSGrC+ZXemmF7qHAD5g8RDkg1odkVuZa+jj0KlKHKtReF0p9OB/J6fNavlD7mM
DwAwggEKAoIBAQCSEk80aBAbmWtPBLcTjFLbvVmxuzDgzrjH7h2Hg/ly8lE/o2nZ UiAtEpEgoKx3VlhrYEtIoFk+EJWaN1WObhYNfPtEw8Ncfww1cyDNmOnsifkLg+yh
1T2ESSuaQFsxw54ukqbj1ooQXF1DoIxSp+CiNzf/FTB6BaMkaG0ayE2Wnm2wkjKp +aNxXzrR3toRF7pxFehrTpMRxx4LiIN2z4vHCdelvu9yJspzRAWd5QSQ6eGr3OPY
POnAzZgTabJoB/qeUlr9i4xiAyBhiQDk5KjdWYHxeZnSznqfIOPzAdw7ZJVYvqvT yn+9v4XfN0YgnWSbH8aJ24bysIB3vOtsULjOfNJivNcx+/gQ9yP4AFhycperiDcu
GciHnoina5TzPUbpnLcR2LvHcLxuSuWQ6dTz/sfdZRx8lkbR3qltUazmJX+yxJJr GGTiw+fwk1y6e04XulQ65mgxGTXNHlnM2ZvDyOwDZqL89Uf3AgMBAAGjejB4MB8G
kagq2V3cfpfLM8DOzPPEzuKHM6sK6ZgTqbc4ti+ul7Q1V+e0v2xNDtuYHkbaOuyd A1UdIwQYMBaAFGQaM9lRXGKKghjag6SPD+uHK3K5MAkGA1UdEwQCMAAwCwYDVR0P
ucmaZ3R++0ryoWWan5OFWZIKjttKy/yq8MUrAgMBAAGjUzBRMB0GA1UdDgQWBBSM BAQDAgTwMB4GA1UdEQQXMBWHBMCoAceCDTE5Mi4xNjguMS4xOTkwHQYDVR0OBBYE
nlDraef71C/filHpA7dDpwmB7zAfBgNVHSMEGDAWgBSMnlDraef71C/filHpA7dD FHNfBgu/Ixj2j6yDTiDluw6/i3cEMA0GCSqGSIb3DQEBCwUAA4IBAQAojT+cdzfU
pwmB7zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBNySms9mXG sVq/ODttG8wS23Du20W2iNdvlAwkgni0UgxTJQ12odtIH9WZAVS46G++t2so87Ki
PVOmFAm9YjMjRY+cUpa0Gm6saxp9VOyrAg2KzdwG6LNGgauNsIra1ytM40NASspN gp6OK25AWtsQ7oLFK2P6VpaVGH6FwRSODFTly7Wv+7US7NmB/DO215+rG7q7C7Ag
r+L49gUCmASUGOqeZCpJjkKAsGspQ4WQKKI6YW8h5dsSuud2qyQtm+w1RKDq+wih J6zrsQgLPR6M6rZRrOs9Hd12oX6zRgEaYnbIc/Z1DVPRmCDiQISE5M8LihIO4sOW
A+B82xWXcFFd52gp6nerib4Pf9ATooOmBMCHFZwC+74sKCv7fXDlzLGdCII8lmI4 gmDacLhM9lbuMvbHEkCNnOuAzdWRmvR06CyBXmu/9iusyWYvgwI6bHFAZRYCOTmB
uq5eFrSS1NeT3iQCwGb9SHfyFkCliaEdpskqmWhonckN0tJVV118SvknV/h9oIsw +poHSGAlTmivcbNhHyZjS63NafRU7sSuc0JDzWQAdkA1AclSokfC8dJC6fnKBujY
uEMIib6YOBlrU+FInnpqpc8VuR4vv0Yro9XrvmurzLuN8k/lVVkr6NMzyNY9mbkF o2WFKJMFrFS3
9p/Sxd5yIeam
-----END CERTIFICATE----- -----END CERTIFICATE-----

28
server/ssl/key-old.pem Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+4rY3ynrh+fn+
3ON4ACPqaZsYsL86oRy5pJpUK1W0gvj/d8hnpByLl6WzN7KhkZ70iS93PUiBHbVV
Y8nyQrOiiLLo8bYhp94YEA9NQei6sxY0ip32Ehs/Fw7IxVvHJ6Q9ckYtyew4VI5X
jG4UNIpXrvY6EtjjoX/OJrTpFycmPKvcSR+MqYpqIHEffHjOnsJ62+VYY6PGA2os
duq3HjZjv4OG4pJ4V5wRnOfPzJ8gB7cM2cbLv5NSGLWbgWDD3AKOwkBF+F8V6kKW
hc2mqBFGAfCs1Q5bK/ouUicy7ax5MoriYox6umMjhZE8FFC7ie1s9GC9a3nQwmVe
IhCFcwQHAgMBAAECggEAMJWSjGuwUCDoZNqC2PGsMoczjxq5aWpFXejL0P2AoGOv
jZJGwz5Nd6ge6BkWkbH3M8VQ+/fwotBVbYjrBwq8HvPNGaYf1bwctqIryt2qJw7a
6X+Yid986NdtD2PQIsXvsyYJP7FDuuimnBjlkaX3yi6BhDF026co2OcYJ7WZZM0e
nc6JR7wGFZM3Dw3ybFvGrK4k7/Iq2N6wqedzCOvDbLXUC16UtmRVIOuiuNm+THrl
BiD37AKwB/LZRcdSQ1HeiWlK42Zc+IikHPJhl0PACcJNFNB3u2rdP8maSu5aMLku
yHnKCz6w9C1vDKrI/iszW2QCky+mGBD9WKK2u6hxFQKBgQDtcfL8hMKj6Ki/dsqR
McGPs1rLgZFAH9axubUth0uLdsEQDZtkoJIzXt8RLS3exuHMKt+Ln6YAOEhKm8Cl
OqIg0E/8SNi7QryU9yfqFqcE2QBZL0QVtvYZeUuiHIOrpc0bmTdNvp8i7zWw/oz6
ymeJ6vpEWKDpOvUnfm79XJbh+wKBgQDNzVjUNfo5s6QNnZlJvwI3J2mAsfLMVQxp
++P41f+dUCoAsEPujxASthdDxRND9oIfsTodA+VkrlLhs1JyTe4PlPcfSl7D5QSV
ayXVHF9iLbGM8fWMf6zBTebdaw9GqY3KTOHBH+X+JOHPP9dI6a4l7Ok8tFE9ia8M
G8Ce7djUZQKBgDSfGDaWRXyFx0AHV4Ut/bOXD/whzsrjQ3VHrrtUTI2v18FzAoke
fMgdslngJVZFxSy2I6yRyPwrfPnr4pm7kMqs380NZ9q4Q4rP62yZcJJGdSlOrEwT
rB6hHv3iS9vydq4zGmqEYEghs0hyYVQDH0cVaDlVWvPVORdzka1co6OZAoGBAJHl
TV/DlExrqZVtcEnzeyKWchimDjYE5PQNeiPhsYBYYC50xvPLv91D8WI9x9aaXs0Q
2t3O8URawK74bS5TSL0LIdWw51WAeatjdkKKBqSXOBNvRGAB8vpmu4+kYgP6F2ae
8jvy3R06EErYO0qZPrfsJ7y9KAq0HMA8vGTuwJRxAoGBAMfWJLseheDXKUXndnR9
ovNA+spTTFECtoLwhWxwgoL3GYVqSA96RfnmdKHY4d5isQ1g/JN05Uo6bL7HKJCG
BwS9WCsa6fHhbJR31fP16UQNNknNSwTtUoeJavwarQ7MB5CT9Fz5HsaC4NGaQkve
86Barwb6tt4iu4Y8a2bcG/sE
-----END PRIVATE KEY-----

View File

@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY----- -----BEGIN PRIVATE KEY-----
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
xvrhZIQLy0ujoDPPBE8+3Q== P4Hmmp4712UC4HkhQLSJ6HU=
-----END PRIVATE KEY----- -----END PRIVATE KEY-----