[mirotalksfu] - add RTMP server and multi-source streaming!, update dep
هذا الالتزام موجود في:
26
rtmpServers/node-media-server/Dockerfile
Normal file
26
rtmpServers/node-media-server/Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
# Use a lightweight Node.js image
|
||||
FROM node:20-slim
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package.json and install npm dependencies
|
||||
COPY package.json .
|
||||
RUN npm install
|
||||
|
||||
# Cleanup unnecessary packages and files
|
||||
RUN npm cache clean --force \
|
||||
&& rm -rf /tmp/* /var/tmp/* /usr/share/doc/*
|
||||
|
||||
# Copy the application code
|
||||
COPY src src
|
||||
|
||||
# Rtmp port
|
||||
EXPOSE 1935
|
||||
# Http port
|
||||
EXPOSE 8081
|
||||
# Https port
|
||||
EXPOSE 8043
|
||||
|
||||
# Set default command to start the application
|
||||
CMD ["npm", "start"]
|
||||
47
rtmpServers/node-media-server/README.md
Normal file
47
rtmpServers/node-media-server/README.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# RTMP Streaming
|
||||
|
||||

|
||||
|
||||
For running an `RTMP` (Real-Time Messaging Protocol) server in Node, **[Node-Media-Server](https://github.com/illuspas/Node-Media-Server)** is one of the best options.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```sh
|
||||
# Create the config file for the server
|
||||
$ cp config.template.js config.js
|
||||
# Install the dependencies
|
||||
$ npm install
|
||||
# Start the RTMP Server
|
||||
$ npm start
|
||||
```
|
||||
|
||||
## Using Docker
|
||||
|
||||
```sh
|
||||
# Create the config file for the server
|
||||
$ cp config.template.js config.js
|
||||
|
||||
# Copy the docker.compose.yml
|
||||
$ cp docker-compose.template.yml docker-compose.yml
|
||||
|
||||
# Pull the official mirotalk rtmp image
|
||||
$ docker pull mirotalk/nms:latest
|
||||
|
||||
# Create and start containers
|
||||
$ docker-compose up -d
|
||||
|
||||
# Check the logs
|
||||
$ docker logs -f mirotalk-nms
|
||||
|
||||
# To stop and remove resources
|
||||
$ docker-compose down
|
||||
```
|
||||
|
||||
## Dashboard & API
|
||||
|
||||
[http://localhost:8081/admin](http://localhost:8081/admin)
|
||||
[http://localhost:8081/api/server](http://localhost:8081/api/server)
|
||||
|
||||
## Custom Configuration
|
||||
|
||||
Modify the `config.js` to suit your specific needs.
|
||||
13
rtmpServers/node-media-server/docker-compose.template.yml
Normal file
13
rtmpServers/node-media-server/docker-compose.template.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
mirotalk-nms:
|
||||
container_name: mirotalk-nms
|
||||
image: mirotalk/nms:latest
|
||||
volumes:
|
||||
- ./src/config.js/:/app/src/config.js/:ro
|
||||
ports:
|
||||
- '1935:1935'
|
||||
- '8081:8081'
|
||||
- '8043:8043'
|
||||
restart: unless-stopped
|
||||
26
rtmpServers/node-media-server/package.json
Normal file
26
rtmpServers/node-media-server/package.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "mirotalk-rtmp-nms",
|
||||
"version": "1.0.0",
|
||||
"description": "MiroTalk RTMP Node Media Server",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node src/server.js",
|
||||
"start-dev": "nodemon src/server.js"
|
||||
},
|
||||
"keywords": [
|
||||
"rtmp",
|
||||
"node",
|
||||
"media",
|
||||
"server"
|
||||
],
|
||||
"author": "Miroslav Pejic",
|
||||
"license": "AGPLv3",
|
||||
"dependencies": {
|
||||
"crypto": "^1.0.1",
|
||||
"node-media-server": "^2.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"uuid": "10.0.0",
|
||||
"nodemon": "^3.1.4"
|
||||
}
|
||||
}
|
||||
21
rtmpServers/node-media-server/src/cert.pem
Normal file
21
rtmpServers/node-media-server/src/cert.pem
Normal file
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDfzCCAmcCFHLVDcza5/3VZ8U5Vd2LnWRvwME1MA0GCSqGSIb3DQEBCwUAMHsx
|
||||
CzAJBgNVBAYTAklUMQ4wDAYDVQQIDAVJdGFseTERMA8GA1UECgwITWlyb1RhbGsx
|
||||
HTAbBgNVBAMMFE1pcm9UYWxrIFJUTVAgU2VydmVyMSowKAYJKoZIhvcNAQkBFhtt
|
||||
aXJvc2xhdi5wZWppYy44NUBnbWFpbC5jb20wIBcNMjQwNjIwMjE1MDQ4WhgPMjA1
|
||||
MTExMDUyMTUwNDhaMHsxCzAJBgNVBAYTAklUMQ4wDAYDVQQIDAVJdGFseTERMA8G
|
||||
A1UECgwITWlyb1RhbGsxHTAbBgNVBAMMFE1pcm9UYWxrIFJUTVAgU2VydmVyMSow
|
||||
KAYJKoZIhvcNAQkBFhttaXJvc2xhdi5wZWppYy44NUBnbWFpbC5jb20wggEiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJfFke3Df+0tgei1djU1Af0i4OukeI
|
||||
UHxWzr3mibVxJ8qtNwukXc0F9XnRXA9kF7WJa+vzuQXQgClH75mrrTNzwV1IWXSZ
|
||||
nKokogyweHC+XZ/Frdv+yCWyJcC7YrIJVTJNCU+Z4wDBLira35Z2tBTd7UV9gURB
|
||||
oQZhqeG0b653D1kvb9oEzTJFbmcst5YzEHUfs4CeF6RvOK4q0wD1CXImJYpXrJ02
|
||||
YbpLyZ8hLZuGq3uDdPOjHXQApxmLhSUgJHnIcHxR/xVFdLyAqP+aTKnozLCS3PZC
|
||||
yB1lJbuEmPO5WfeQDmL7W3COEtdAdlCNF8VZ09z1AlCKLnp75vi9M04hAgMBAAEw
|
||||
DQYJKoZIhvcNAQELBQADggEBAHOVKxSt+BGtDFynltp0pfHGyFo1sr5ULUams67s
|
||||
LQuiOm0Iuw1kXRA9Yf/hAcL12/taEBfNqYxveQXe8xbodwobkOpHmyYYLZ+50a8I
|
||||
+hP15UkmlJb0iy7OkjoalDqVFFN2WQTJK3OqMg4RdJlTMpzDibNYzZWZ6Xaxl670
|
||||
FDh3xJO9/MweHO/ScGS5RVIdYIdDbFGzzcYHiWpsbcYgYdvsNVofNsZpotWd37/x
|
||||
CbYImc1RKhRnBQTcnnK0u+6ugD26Yho3eB5f0nbj2gkikDYueYYZG+7uV2w+9QKI
|
||||
e+nipac/6/ACwo1ZMsEYR3arjdLN8Rxr39s5PStP63EkGv4=
|
||||
-----END CERTIFICATE-----
|
||||
30
rtmpServers/node-media-server/src/config.template.js
Normal file
30
rtmpServers/node-media-server/src/config.template.js
Normal file
@@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
const config = {
|
||||
rtmp: {
|
||||
port: 1935,
|
||||
chunk_size: 60000,
|
||||
gop_cache: true,
|
||||
ping: 60,
|
||||
ping_timeout: 30,
|
||||
},
|
||||
http: {
|
||||
port: 8081,
|
||||
allow_origin: '*',
|
||||
},
|
||||
https: {
|
||||
port: 8043,
|
||||
key: __dirname + '/key.pem',
|
||||
cert: __dirname + '/cert.pem',
|
||||
},
|
||||
auth: {
|
||||
api: true,
|
||||
api_user: 'mirotalk',
|
||||
api_pass: 'mirotalkRtmpPassword', // http://localhost:8081/admin
|
||||
play: false, // Require authentication for playing streams
|
||||
publish: false, // Require authentication for publishing streams
|
||||
secret: 'mirotalkRtmpSecret', // Check the sign.js file to generate a valid RTMP URL
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
27
rtmpServers/node-media-server/src/key.pem
Normal file
27
rtmpServers/node-media-server/src/key.pem
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAyXxZHtw3/tLYHotXY1NQH9IuDrpHiFB8Vs695om1cSfKrTcL
|
||||
pF3NBfV50VwPZBe1iWvr87kF0IApR++Zq60zc8FdSFl0mZyqJKIMsHhwvl2fxa3b
|
||||
/sglsiXAu2KyCVUyTQlPmeMAwS4q2t+WdrQU3e1FfYFEQaEGYanhtG+udw9ZL2/a
|
||||
BM0yRW5nLLeWMxB1H7OAnhekbziuKtMA9QlyJiWKV6ydNmG6S8mfIS2bhqt7g3Tz
|
||||
ox10AKcZi4UlICR5yHB8Uf8VRXS8gKj/mkyp6Mywktz2QsgdZSW7hJjzuVn3kA5i
|
||||
+1twjhLXQHZQjRfFWdPc9QJQii56e+b4vTNOIQIDAQABAoIBAQC98pSyGzpO6ccF
|
||||
RKfl460t0p/JEqRNRlNyIwW0SS7ctn7EPZikJCoc7Acj8H4yBogGPc/7vPpWTfyc
|
||||
7K0aw/Y1sp2Wj371MlTUpFECLQlc7japzfYQg+/FuwGvpqPhWIhLR/PbR752YGfW
|
||||
X+MhlTP25LEWWL9Yf83cVKOLz53Sbt9KH/baJhpFhXrrxl/1rcR6U7MhZOLlq1aa
|
||||
XbYwglzd/l20cED1wbCzbxVwl/M73ZIOgn+2vgSxkNCxkZVpGYvE0Tx9S9b3rmtl
|
||||
OD9WqTS5beoKABXbMPTMdbyekPAnK2pAUs+WmKD8iJ69djKeMj/lO1vQBZOBJzlL
|
||||
pxteIS7hAoGBAPvbO27SPnMI76lfXDAECNHCq1YLiVpqhctFvcJlM1aQMbWKqYOX
|
||||
XVho+drGlh/Mf9JpY3rtfd7VNZKbJQRT/6Wf7j7L7WOldClxIhBXxziqOJ/bemJP
|
||||
ELRau321q5x2aNLGZbioaDgB9fzEm/aPyjRC8JnIvePyksAzXJNw2mtzAoGBAMzM
|
||||
9w7nyfa16pG14hAdiYkCtB052jZ71sz+Y9XbA12D36EDLxkQ3l5I6FNrvWvu30N8
|
||||
snG+SMmk8LSjy3b4bv5DPP1Bnh+HQG/5quoG61uODkRC7aIgLCgdbmnggWrI+gV7
|
||||
E+YM6HMZFVk3Lvo1GobyxsBCLBRCPdfW15nQ0eMbAoGBAJiXtIOpeFLEOEibUUR6
|
||||
PUmxs5N3e+m/Hn8RKy6LmDY7ORLwB1KGM/Ur7S3jIfP0OAGo/q/tElUfQs0nmJ7t
|
||||
sbeMlZGQhqzYAvBU7jmOpVKst5ALLzQ/CTTswCojFu2+RDZoJBtkVXiRn5NdH82c
|
||||
Qvu1Dwdtu7dPMiCnPdDLEFsHAoGBAJjHhr7N13J+f0C4CK6w+jsFk0wCLnFarQE7
|
||||
/Uo6GiaXDCrXbzkpxllb1kT1KNft2QxFZ/FGXJJgw1heoJhd+J8hlcvwOX+XrFBc
|
||||
Vk5DXyxrquTtcMzzZz19xzKg0qrQxwNzr4J8uqOyYKSvcBIjr2hgkDg4pR1v1SbB
|
||||
FRGgIBNlAoGAeMJrhQy5RU6xCG9l8+jH42PhG4+F9pV5EQI0v421KQ4hklgY+pT6
|
||||
KrTuZp6tjX7hErYNNd77ELDRLZ3p8VlqxuvF3UI6s+I7rRxpXpjSve3si8USYS4L
|
||||
aKAp6qDc3Vt1e6uin9NwZS6jtDvH8VOIMOHTQYJwUTnjpSLuYIxOzU0=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
81
rtmpServers/node-media-server/src/server.js
Normal file
81
rtmpServers/node-media-server/src/server.js
Normal file
@@ -0,0 +1,81 @@
|
||||
'use strict';
|
||||
|
||||
const NodeMediaServer = require('node-media-server');
|
||||
|
||||
const config = require('./config');
|
||||
|
||||
console.log('Rtmp Server config', {
|
||||
config: config,
|
||||
http: {
|
||||
admin: 'http://localhost:8081/admin',
|
||||
stats: 'http://localhost:8081/api/server',
|
||||
streams: 'http://localhost:8081/api/streams',
|
||||
},
|
||||
https: {
|
||||
admin: 'https://localhost:8043/admin',
|
||||
stats: 'https://localhost:8043/api/server',
|
||||
streams: 'http://localhost:8043/api/streams',
|
||||
},
|
||||
});
|
||||
|
||||
const nms = new NodeMediaServer(config);
|
||||
|
||||
nms.run();
|
||||
|
||||
nms.on('preConnect', (id, args) => {
|
||||
console.log('[NodeEvent on preConnect]', `id=${id} args=${JSON.stringify(args)}`);
|
||||
// let session = nms.getSession(id);
|
||||
// session.reject();
|
||||
});
|
||||
|
||||
nms.on('postConnect', (id, args) => {
|
||||
console.log('[NodeEvent on postConnect]', `id=${id} args=${JSON.stringify(args)}`);
|
||||
});
|
||||
|
||||
nms.on('doneConnect', (id, args) => {
|
||||
console.log('[NodeEvent on doneConnect]', `id=${id} args=${JSON.stringify(args)}`);
|
||||
});
|
||||
|
||||
nms.on('prePublish', (id, StreamPath, args) => {
|
||||
console.log('[NodeEvent on prePublish]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
|
||||
// let session = nms.getSession(id);
|
||||
// session.reject();
|
||||
});
|
||||
|
||||
nms.on('postPublish', (id, StreamPath, args) => {
|
||||
console.log('[NodeEvent on postPublish]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
|
||||
});
|
||||
|
||||
nms.on('donePublish', (id, StreamPath, args) => {
|
||||
console.log('[NodeEvent on donePublish]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
|
||||
});
|
||||
|
||||
nms.on('prePlay', (id, StreamPath, args) => {
|
||||
console.log('[NodeEvent on prePlay]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
|
||||
});
|
||||
|
||||
nms.on('postPlay', (id, StreamPath, args) => {
|
||||
console.log('[NodeEvent on postPlay]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
|
||||
// let session = nms.getSession(id);
|
||||
// session.reject();
|
||||
});
|
||||
|
||||
nms.on('donePlay', (id, StreamPath, args) => {
|
||||
console.log('[NodeEvent on donePlay]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
|
||||
});
|
||||
|
||||
nms.on('logMessage', (...args) => {
|
||||
// custom logger log message handler
|
||||
});
|
||||
|
||||
nms.on('errorMessage', (...args) => {
|
||||
// custom logger error message handler
|
||||
});
|
||||
|
||||
nms.on('debugMessage', (...args) => {
|
||||
// custom logger debug message handler
|
||||
});
|
||||
|
||||
nms.on('ffDebugMessage', (...args) => {
|
||||
// custom logger ffmpeg debug message handler
|
||||
});
|
||||
58
rtmpServers/node-media-server/src/sign.js
Normal file
58
rtmpServers/node-media-server/src/sign.js
Normal file
@@ -0,0 +1,58 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto-js');
|
||||
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
/**
|
||||
* Generates an RTMP URL with an expiration timestamp and a hash value.
|
||||
*
|
||||
* @param {string} baseURL - The base URL of the RTMP server.
|
||||
* @param {string} streamPath - The path to the stream.
|
||||
* @param {string} secretKey - The secret key used for generating the hash.
|
||||
* @param {number} expirationHours - The number of hours until the URL expires.
|
||||
* @returns {string} - The generated RTMP URL for Node Media Server.
|
||||
*/
|
||||
function generateRTMPUrl(baseURL, streamPath, secretKey, expirationHours = 8) {
|
||||
// Current timestamp in seconds
|
||||
const currentTime = Math.floor(Date.now() / 1000);
|
||||
|
||||
// Expiration time (current time + expirationHours in seconds)
|
||||
const expirationTime = currentTime + expirationHours * 3600;
|
||||
|
||||
// Generate the hash value
|
||||
const hashValue = crypto.MD5(`${streamPath}-${expirationTime}-${secretKey}`).toString();
|
||||
|
||||
// Construct the final request address
|
||||
const rtmpUrl = `${baseURL}${streamPath}?sign=${expirationTime}-${hashValue}`;
|
||||
|
||||
// Print some log
|
||||
log.debug('generateRTMPUrl', {
|
||||
currentTime: currentTime,
|
||||
expirationTime: expirationTime,
|
||||
hashValue: hashValue,
|
||||
rtmpUrl: rtmpUrl,
|
||||
});
|
||||
|
||||
return rtmpUrl;
|
||||
}
|
||||
|
||||
// Example usage
|
||||
const baseURL = 'rtmp://localhost:1935';
|
||||
const streamKey = uuidv4();
|
||||
const streamPath = '/live/' + streamKey; // path/stream-key
|
||||
const secretKey = 'mirotalkRtmpSecret';
|
||||
const expirationHours = 8;
|
||||
|
||||
// Run: node sign.js
|
||||
const rtmpUrl = generateRTMPUrl(baseURL, streamPath, secretKey, expirationHours);
|
||||
console.log('Generated RTMP URL:', rtmpUrl);
|
||||
|
||||
/*
|
||||
OBS:
|
||||
- Server: rtmp://localhost:1935/live
|
||||
- StreamKey: demo?sign=1719169535-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
|
||||
FFMPEG:
|
||||
- ffmpeg -re -i input.mp4 -c:v libx264 -preset veryfast -maxrate 3000k -bufsize 6000k -vf "scale=-2:720" -g 50 -c:a aac -b:a 128k -f flv "rtmp://localhost:1935/live/demo?sign=1719169535-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
*/
|
||||
المرجع في مشكلة جديدة
حظر مستخدم