Node-Media-Server
A Node.js implementation of RTMP/HTTP-FLV/WS-FLV/HLS/DASH Media Server
中文介绍
If you like this project you can support me.
Web Admin Panel Source
https://github.com/illuspas/Node-Media-Server-Admin
Web Admin Panel Screenshot
Features
- Cross platform support Windows/Linux/Unix
- Support H.264/AAC/MP3/SPEEX/NELLYMOSER/G.711
- Extension support H.265(flv_id=12)/VP8(flv_id=10)/VP9(flv_id=11)/OPUS(flv_id=13)
- Support GOP cache
- Support remux to LIVE-HTTP/WS-FLV, Support NodePlayer.js playback
- Support remux to HLS/DASH/MP4
- Support xycdn style authentication
- Support event callback
- Support https/wss
- Support Server Monitor
- Support Rtsp/Rtmp relay
- Support api control relay
- Support real-time multi-resolution transcoding
Usage
npx
npx node-media-server
install as a global program
npm i node-media-server -gnode-media-server
docker version
docker run --name nms -d -p 1935:1935 -p 8000:8000 -p 8443:8443 illuspas/node-media-server
npm version (recommended)
mkdir nmscd nmsnpm install node-media-servervi app.js
const NodeMediaServer = require('node-media-server');const config = { rtmp: { port: 1935, chunk_size: 60000, gop_cache: true, ping: 30, ping_timeout: 60 }, http: { port: 8000, allow_origin: '*' }};var nms = new NodeMediaServer(config)nms.run();
node app.js
Publishing live streams
From FFmpeg
If you have a video file with H.264 video and AAC audio:
ffmpeg -re -i INPUT_FILE_NAME -c copy -f flv rtmp://localhost/live/STREAM_NAME
Or if you have a video file that is encoded in other audio/video format:
ffmpeg -re -i INPUT_FILE_NAME -c:v libx264 -preset veryfast -tune zerolatency -c:a aac -ar 44100 -f flv rtmp://localhost/live/STREAM_NAME
From OBS
Settings -> Stream
Stream Type : Custom Streaming Server
URL : rtmp://localhost/live
Stream key : STREAM_NAME?sign=expires-HashValue (sign parameter required only if publish auth is enabled)
Accessing the live stream
RTMP
rtmp://localhost/live/STREAM_NAME
http-flv
http://localhost:8000/live/STREAM_NAME.flv
websocket-flv
ws://localhost:8000/live/STREAM_NAME.flv
HLS
http://localhost:8000/live/STREAM_NAME/index.m3u8
DASH
http://localhost:8000/live/STREAM_NAME/index.mpd
via flv.js over http-flv
<script src="https://cdn.bootcss.com/flv.js/1.5.0/flv.min.js"></script><video id="videoElement"></video><script> if (flvjs.isSupported()) { var videoElement = document.getElementById('videoElement'); var flvPlayer = flvjs.createPlayer({ type: 'flv', url: 'http://localhost:8000/live/STREAM_NAME.flv' }); flvPlayer.attachMediaElement(videoElement); flvPlayer.load(); flvPlayer.play(); }</script>
via flv.js over websocket-flv
<script src="https://cdn.bootcss.com/flv.js/1.5.0/flv.min.js"></script><video id="videoElement"></video><script> if (flvjs.isSupported()) { var videoElement = document.getElementById('videoElement'); var flvPlayer = flvjs.createPlayer({ type: 'flv', url: 'ws://localhost:8000/live/STREAM_NAME.flv' }); flvPlayer.attachMediaElement(videoElement); flvPlayer.load(); flvPlayer.play(); }</script>
Logging
Modify the logging type
It is now possible to modify the logging type which determines which console outputs are shown.
There are a total of 4 possible options:
- 0 - Don't log anything
- 1 - Log errors
- 2 - Log errors and generic info
- 3 - Log everything (debug)
Modifying the logging type is easy - just add a new value logType
in the config and set it to a value between 0 and 4.By default, this is set to show errors and generic info internally (setting 2).
const NodeMediaServer = require('node-media-server');const config = { logType: 3, rtmp: { port: 1935, chunk_size: 60000, gop_cache: true, ping: 30, ping_timeout: 60 }, http: { port: 8000, allow_origin: '*' }};var nms = new NodeMediaServer(config)nms.run();
Authentication
Encryption URL consists of:
rtmp://hostname:port/appname/stream?sign=expires-HashValue
http://hostname:port/appname/stream.flv?sign=expires-HashValue
ws://hostname:port/appname/stream.flv?sign=expires-HashValue
1.Publish or play address:
rtmp://192.168.0.10/live/stream
2.Config set auth->secret: 'nodemedia2017privatekey'
const config = { rtmp: { port: 1935, chunk_size: 60000, gop_cache: true, ping: 30, ping_timeout: 60 }, http: { port: 8000, allow_origin: '*' }, auth: { play: true, publish: true, secret: 'nodemedia2017privatekey' }}
3.expiration time: 2017/8/23 11:25:21 ,The calculated expiration timestamp is
1503458721
4.The combination HashValue is:
HashValue = md5("/live/stream-1503458721-nodemedia2017privatekey”)
HashValue = 80c1d1ad2e0c2ab63eebb50eed64201a
5.Final request address
rtmp://192.168.0.10/live/stream?sign=1503458721-80c1d1ad2e0c2ab63eebb50eed64201a
The 'sign' keyword can not be modified
H.265 over RTMP
- Play:NodeMediaClient-Android and NodeMediaClient-iOS
- Commercial Pure JavaScrip live stream player: NodePlayer.js
- OpenSource Pure JavaScrip live stream player: pro-flv.js
VP8 VP9 OPUS over RTMP
Event callback
......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)}`); // let session = nms.getSession(id); // session.reject();});nms.on('postPlay', (id, StreamPath, args) => { console.log('[NodeEvent on postPlay]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);});nms.on('donePlay', (id, StreamPath, args) => { console.log('[NodeEvent on donePlay]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);});
Https/Wss
Generate certificate
openssl genrsa -out privatekey.pem 1024openssl req -new -key privatekey.pem -out certrequest.csr openssl x509 -req -in certrequest.csr -signkey privatekey.pem -out certificate.pem
Config https
const NodeMediaServer = require('node-media-server');const config = { rtmp: { port: 1935, chunk_size: 60000, gop_cache: true, ping: 30, ping_timeout: 60 }, http: { port: 8000, allow_origin: '*' }, https: { port: 8443, key:'./privatekey.pem', cert:'./certificate.pem', }};var nms = new NodeMediaServer(config)nms.run();
Accessing
https://localhost:8443/live/STREAM_NAME.flvwss://localhost:8443/live/STREAM_NAME.flv
In the browser environment, Self-signed certificates need to be added with trust before they can be accessed.
API
Protected API
const config = { ....... auth: { api : true, api_user: 'admin', api_pass: 'nms2018', }, ......}
Based on the basic auth,Please change your password.The default is not turned on
Server stats
http://localhost:8000/api/server
{ "os": { "arch": "x64", "platform": "darwin", "release": "16.7.0" }, "cpu": { "num": 8, "load": 12, "model": "Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz", "speed": 3592 }, "mem": { "totle": 8589934592, "free": 754126848 }, "net": { "inbytes": 6402345, "outbytes": 6901489 }, "nodejs": { "uptime": 109, "version": "v8.9.0", "mem": { "rss": 59998208, "heapTotal": 23478272, "heapUsed": 15818096, "external": 3556366 } }, "clients": { "accepted": 207, "active": 204, "idle": 0, "rtmp": 203, "http": 1, "ws": 0 }}
Streams stats
http://localhost:8000/api/streams
{ "live": { "s": { "publisher": { "app": "live", "stream": "s", "clientId": "U3UYQ02P", "connectCreated": "2017-12-21T02:29:13.594Z", "bytes": 190279524, "ip": "::1", "audio": { "codec": "AAC", "profile": "LC", "samplerate": 48000, "channels": 6 }, "video": { "codec": "H264", "width": 1920, "height": 1080, "profile": "Main", "level": 4.1, "fps": 24 } }, "subscribers": [ { "app": "live", "stream": "s", "clientId": "H227P4IR", "connectCreated": "2017-12-21T02:31:35.278Z", "bytes": 18591846, "ip": "::ffff:127.0.0.1", "protocol": "http" }, { "app": "live", "stream": "s", "clientId": "ZNULPE9K", "connectCreated": "2017-12-21T02:31:45.394Z", "bytes": 8744478, "ip": "::ffff:127.0.0.1", "protocol": "ws" }, { "app": "live", "stream": "s", "clientId": "C5G8NJ30", "connectCreated": "2017-12-21T02:31:51.736Z", "bytes": 2046073, "ip": "::ffff:192.168.0.91", "protocol": "rtmp" } ] }, "stream": { "publisher": null, "subscribers": [ { "app": "live", "stream": "stream", "clientId": "KBH4PCWB", "connectCreated": "2017-12-21T02:31:30.245Z", "bytes": 0, "ip": "::ffff:127.0.0.1", "protocol": "http" } ] } }}
Remux to HLS/DASH live stream
const NodeMediaServer = require('node-media-server');const config = { rtmp: { port: 1935, chunk_size: 60000, gop_cache: true, ping: 30, ping_timeout: 60 }, http: { port: 8000, mediaroot: './media', allow_origin: '*' }, trans: { ffmpeg: '/usr/local/bin/ffmpeg', tasks: [ { app: 'live', hls: true, hlsFlags: '[hls_time=2:hls_list_size=3:hls_flags=delete_segments]', hlsKeep: true, // to prevent hls file delete after end the stream dash: true, dashFlags: '[f=dash:window_size=3:extra_window_size=5]', dashKeep: true // to prevent dash file delete after end the stream } ] }};var nms = new NodeMediaServer(config)nms.run();
Remux to RTMP/HLS/DASH live stream with audio transcode
const NodeMediaServer = require('node-media-server');const config = { rtmp: { port: 1935, chunk_size: 60000, gop_cache: true, ping: 30, ping_timeout: 60 }, http: { port: 8000, mediaroot: './media', allow_origin: '*' }, trans: { ffmpeg: '/usr/local/bin/ffmpeg', tasks: [ { app: 'live', vc: "copy", vcParam: [], ac: "aac", acParam: ['-ab', '64k', '-ac', '1', '-ar', '44100'], rtmp:true, rtmpApp:'live2', hls: true, hlsFlags: '[hls_time=2:hls_list_size=3:hls_flags=delete_segments]', dash: true, dashFlags: '[f=dash:window_size=3:extra_window_size=5]' } ] }};var nms = new NodeMediaServer(config)nms.run();
Remux to RTMP cannot use the same app name
Record to MP4
const NodeMediaServer = require('node-media-server');const config = { rtmp: { port: 1935, chunk_size: 60000, gop_cache: true, ping: 30, ping_timeout: 60 }, http: { port: 8000, mediaroot: './media', allow_origin: '*' }, trans: { ffmpeg: '/usr/local/bin/ffmpeg', tasks: [ { app: 'live', mp4: true, mp4Flags: '[movflags=frag_keyframe+empty_moov]', } ] }};var nms = new NodeMediaServer(config)nms.run();
Rtsp/Rtmp Relay
NodeMediaServer implement RTSP and RTMP relay with ffmpeg.
Static pull
The static pull mode is executed at service startup and reconnect after failure.It could be a live stream or a file. In theory, it is not limited to RTSP or RTMP protocol.
relay: { ffmpeg: '/usr/local/bin/ffmpeg', tasks: [ { app: 'cctv', mode: 'static', edge: 'rtsp://admin:admin888@192.168.0.149:554/ISAPI/streaming/channels/101', name: '0_149_101', rtsp_transport : 'tcp' //['udp', 'tcp', 'udp_multicast', 'http'] }, { app: 'iptv', mode: 'static', edge: 'rtmp://live.hkstv.hk.lxdns.com/live/hks', name: 'hks' }, { app: 'mv', mode: 'static', edge: '/Volumes/ExtData/Movies/Dancing.Queen-SD.mp4', name: 'dq' } ]}
Dynamic pull
When the local server receives a play request.If the stream does not exist, pull the stream from the configured edge server to local.When the stream is not played by the client, it automatically disconnects.
relay: { ffmpeg: '/usr/local/bin/ffmpeg', tasks: [ { app: 'live', mode: 'pull', edge: 'rtmp://192.168.0.20', } ]}
Dynamic push
When the local server receives a publish request.Automatically push the stream to the edge server.
relay: { ffmpeg: '/usr/local/bin/ffmpeg', tasks: [ { app: 'live', mode: 'push', edge: 'rtmp://192.168.0.10', } ]}
Fission
Real-time transcoding multi-resolution output
fission: { ffmpeg: '/usr/local/bin/ffmpeg', tasks: [ { rule: "game/*", model: [ { ab: "128k", vb: "1500k", vs: "1280x720", vf: "30", }, { ab: "96k", vb: "1000k", vs: "854x480", vf: "24", }, { ab: "96k", vb: "600k", vs: "640x360", vf: "20", }, ] }, { rule: "show/*", model: [ { ab: "128k", vb: "1500k", vs: "720x1280", vf: "30", }, { ab: "96k", vb: "1000k", vs: "480x854", vf: "24", }, { ab: "64k", vb: "600k", vs: "360x640", vf: "20", }, ] }, ]}
Publisher and Player App/SDK
Android Livestream App
https://play.google.com/store/apps/details?id=cn.nodemedia.qlive
http://www.nodemedia.cn/uploads/qlive-release.apk
Android SDK
https://github.com/NodeMedia/NodeMediaClient-Android
iOS SDK
https://github.com/NodeMedia/NodeMediaClient-iOS
React-Native SDK
https://github.com/NodeMedia/react-native-nodemediaclient
NodePlayer.js HTML5 live player
- Implemented with asm.js / wasm
- http-flv/ws-flv
- H.264/H.265 + AAC/Nellymoser/G.711 decoder
- Ultra low latency (Support for iOS safari browser)
http://www.nodemedia.cn/products/node-media-player
Windows browser plugin(ActiveX/NPAPI)
- H.264/H.265+AAC rtmp publisher
- Camera/Desktop + Microphone capture
- Nvidia/AMD/Intel Hardware acceleration Encoder/Decoder
- Ultra low latency rtmp/rtsp/http live player
- Only 6MB installation package
http://www.nodemedia.cn/products/node-media-client/win
Thanks
Sorng Sothearith, standifer1023, floatflower, Christopher Thomas, strive, jaysonF, 匿名, 李勇, 巴草根, ZQL, 陈勇至, -Y, 高山流水, 老郭, 孙建, 不说本可以, Jacky, 人走茶凉,树根, 疯狂的台灯, 枫叶, lzq, 番茄, smicroz , kasra.shahram, 熊科辉, Ken Lee , Erik Herz, Javier Gomez, trustfarm, leeoxiang, Aaron Turner, Anonymous
Thank you for your support.