Node-Media-Server (相对稳定可用性高)

主要应用Node.js 实现的RTSP(结合ffmpeg)/RTMP/HTTP/WebSocket/HLS/DASH流媒体服务器

特性

  • 跨平台支持 Windows/Linux/Unix
  • 支持的音视频编码 H.264/H.265/AAC/SPEEX/NELLYMOSER
  • 支持缓存最近一个关键帧间隔数据,实现RTMP协议秒开
  • 支持事件回调
  • 支持https/wss加密传输
  • 支持服务器和流媒体信息统计
  • 支持RTMP直播流转HLS,DASH直播流
  • 支持RTMP直播流录制为MP4文件并开启faststart
  • 支持RTMP/RTSP中继(关键)
  • 支持多核集群模式
  • 支持录制为MP4回放
  • 支持实时转码(关键)
  • 支持低延迟HLS/DASH
  • 支持on_connect/on_publish/on_play/on_done 事件回调

用法

git 版本

简单运行方式

npm i
node app.js

多核模式运行

node cluster.js

npm 版本(推荐)

单核模式

mkdir nms
cd nms
npm install node-media-server
const { NodeMediaServer } = require('node-media-server');

const config = {
rtmp: {
port: 1935,
chunk_size: 60000,
gop_cache: true,
ping: 60,
ping_timeout: 30
},
http: {
port: 8000,
allow_origin: '*'
}
}; var nms = new NodeMediaServer(config)
nms.run();

多核模式

mkdir nms
cd nms
npm install node-media-server
const { NodeMediaCluster } = require('node-media-server');
const numCPUs = require('os').cpus().length;
const config = {
rtmp: {
port: 1935,
chunk_size: 60000,
gop_cache: true,
ping: 60,
ping_timeout: 30
},
http: {
port: 8000,
allow_origin: '*'
},
cluster: {
num: numCPUs
}
}; var nmcs = new NodeMediaCluster(config)
nmcs.run();

鉴权验证(更安全)

加密后的 URL 形式:

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.原始推流或播放地址:

rtmp://192.168.0.10/live/stream

2.配置验证秘钥为: 'nodemedia2017privatekey',同时打开播放和发布的鉴权开关

const config = {
rtmp: {
port: 1935,
chunk_size: 60000,
gop_cache: true,
ping: 60,
ping_timeout: 30
},
http: {
port: 8000,
allow_origin: '*'
},
auth: {
play: true,
publish: true,
secret: 'nodemedia2017privatekey'
}
}

3.请求过期时间为: 2017/8/23 11:25:21 ,则请求过期时间戳为:

1503458721

4.md5计算结合“完整流地址-失效时间-密钥”的字符串:

HashValue = md5("/live/stream-1503458721-nodemedia2017privatekey”)

HashValue = 80c1d1ad2e0c2ab63eebb50eed64201a

5.最终请求地址为

rtmp://192.168.0.10/live/stream?sign=1503458721-80c1d1ad2e0c2ab63eebb50eed64201a

注意:'sign' 关键字不能修改为其他的

RTMP协议传输H.265视频

H.265并没有在Adobe的官方规范里实现,这里使用id 12作为标识,也是国内绝大多数云服务商使用的id号

PC转码推流: ffmpeg-hw-win32

纯JavaScrip 直播播放器: NodePlayer.js

事件回调

......
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 视频加密传输

生成证书

openssl genrsa -out privatekey.pem 1024
openssl req -new -key privatekey.pem -out certrequest.csr
openssl x509 -req -in certrequest.csr -signkey privatekey.pem -out certificate.pem

配置 https支持

const { NodeMediaServer } = require('node-media-server');

const config = {
rtmp: {
port: 1935,
chunk_size: 60000,
gop_cache: true,
ping: 60,
ping_timeout: 30
},
http: {
port: 8000,
allow_origin: '*'
},
https: {
port: 8443,
key:'./privatekey.pem',
cert:'./certificate.pem',
}
}; var nms = new NodeMediaServer(config)
nms.run();

播放加密传输视频

https://localhost:8443/live/STREAM_NAME.flv
wss://localhost:8443/live/STREAM_NAME.flv

注意:Web浏览器播放自签名的证书需先添加信任才能访问

API

保护API

const config = {
.......
auth: {
api : true,
api_user: 'admin',
api_pass: 'nms2018',
}, ......
}

注意:基于Basic auth提供验证,请注意修改密码,默认并未开启。

服务器信息统计

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
}
}

流信息统计

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"
}
]
}
}
}

转 HLS/DASH 直播流

const { NodeMediaServer } = require('node-media-server');

const config = {
rtmp: {
port: 1935,
chunk_size: 60000,
gop_cache: true,
ping: 60,
ping_timeout: 30
},
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]',
dash: true,
dashFlags: '[f=dash:window_size=3:extra_window_size=5]'
}
]
}
}; var nms = new NodeMediaServer(config)
nms.run();

直播录制为MP4文件

const { NodeMediaServer } = require('node-media-server');

const config = {
rtmp: {
port: 1935,
chunk_size: 60000,
gop_cache: true,
ping: 60,
ping_timeout: 30
},
http: {
port: 8000,
mediaroot: './media',
allow_origin: '*'
},
trans: {
ffmpeg: '/usr/local/bin/ffmpeg',
tasks: [
{
app: 'vod',
mp4: true,
mp4Flags: '[movflags=faststart]',
}
]
}
}; var nms = new NodeMediaServer(config)
nms.run();

Rtsp/Rtmp 中继

NodeMediaServer 使用ffmpeg实现RTMP/RTSP的中继服务。(我们主要应用这里将rtsp转为rtmp)

静态拉流

静态拉流模式在服务启动时执行,当发生错误时自动重连。可以是一个直播流,也可以是一个本地文件。理论上并不限制是RTSP或RTMP协议

relay: {
ffmpeg: '/usr/local/bin/ffmpeg',
tasks: [
{
app: 'cctv',
mode: 'static',
edge: 'rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov',//rtsp
name: 'BigBuckBunny'
rtsp_transport : 'tcp' //['udp', 'tcp', 'udp_multicast', 'http']
}, {
app: 'iptv',
mode: 'static',
edge: 'rtmp://live.hkstv.hk.lxdns.com/live/hks',//rtmp
name: 'hks'
}, {
app: 'mv',
mode: 'static',
edge: '/Volumes/ExtData/Movies/Dancing.Queen-SD.mp4',//本地文件
name: 'dq'
}
]
}

动态拉流

当本地服务器收到一个播放请求,如果这个流不存在,则从配置的边缘服务器拉取这个流。当没有客户端播放这个流时,自动断开。

relay: {
ffmpeg: '/usr/local/bin/ffmpeg',
tasks: [
{
app: 'live',
mode: 'pull',
edge: 'rtmp://192.168.0.20',
}
]
}

动态推流

当本地服务器收到一个发布请求,自动将这个流推送到边缘服务器。

relay: {
ffmpeg: '/usr/local/bin/ffmpeg',
tasks: [
{
app: 'live',
mode: 'push',
edge: 'rtmp://192.168.0.10',
}
]
}

安装应用流程

安装ffmpeg(版本要求4.0.0以上)

# 进入/usr/local/src目录安装yasm
cd /usr/local/src
wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
tar zxvf yasm-1.3.0.tar.gz
cd yasm-1.3.0/
./configure
make && make install
# 进入/usr/local/src目录安装
cd /usr/local/src
wget https://ffmpeg.org/releases/ffmpeg-4.0.tar.bz2
tar jxvf ffmpeg-4.0.tar.bz2
cd ffmpeg-4.0/
./configure
make && make install
# 查看是否安装成功
cd /usr/local/bin
ll (存在ffmpeg可执行文件)

安装Node-Media-Server

# 克隆工程到本地/usr/local/src目录
cd /usr/local/src
git clone git@github.com:qq1126176532/Node-Media-Server.git # 开始安装npm
curl --silent --location https://rpm.nodesource.com/setup_10.x | bash -
yum install -y nodejs
npm install -g cnpm --registry=https://registry.npm.taobao.org # 查看版本确认安装成功
npm -v #执行安装
cd Node-Media-Server/
npm i #修改启动脚本(这里以简单工作模式启动为例,多核心集群模式结合中继模式存在一些问题)
vim app.js:
---
const { NodeMediaServer } = require('./index'); const config = {
rtmp: {
port: 1935,
chunk_size: 60000,
gop_cache: true,
ping: 60,
ping_timeout: 30
},
http: {
port: 8000,
webroot: './public',
mediaroot: './media',
allow_origin: '*'
},
https: {
port: 8443,
key: './privatekey.pem',
cert: './certificate.pem',
},
auth: {
api: true,
api_user: 'admin',
api_pass: 'admin',
play: false,
publish: false,
secret: 'nodemedia2017privatekey'
},
//引入中继模式任务
relay: {
//指定ffmpeg可执行文件位置
ffmpeg: '/usr/local/bin/ffmpeg',
tasks: [
{
//应用名称
app: 'iptv',
//工作模式 静态即可
mode: 'static',
//中继地址
edge: 'rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov',
//访问资源名称
name: 'rtsp',
//传输协议
rtsp_transport : 'tcp' //['udp', 'tcp', 'udp_multicast', 'http']
}
]
},
}; let 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)}`);
// 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)}`);
});
---
# 保存退出 # 启动服务
nohup node app.js & # 查看服务是否启动成功
ps -ef | grep "app.js"
[root@localhost Node-Media-Server]# ps -ef | grep "app.js"
root 37745 127550 7 10:31 pts/1 00:00:00 node app.js
tail -f nohup.out(查看启动日志)
# 三个所用(1935 8000 8443)端口暴露
firewall-cmd --zone=public --add-port=1935/tcp --permanent
firewall-cmd --zone=public --add-port=8000/tcp --permanent
firewall-cmd --zone=public --add-port=8443/tcp --permanent #(关闭端口 如果开错了备用还原)
firewall-cmd --zone=public --remove-port=8080/tcp --permanent # 重新加载配置
firewall-cmd --reload # 重启防火墙
systemctl restart firewalld.service # 查看所有开放端口
firewall-cmd --zone=public --list-ports

客户端

引用资料及思考:

感谢如下链接提供文章资源

前期准备和思考

  • 1.之前没做高这方面的内容,起始渡鸟狗哥都找不到帮助太多的有价值的资料。有时候java搞起来不方便也可以倾向于考虑一下其他脚本语言或者方式的实现。
  • 2.ws-socket:此方案最终摒弃原因如下:需要对接外网收费服务,免费服务不稳定,需要定期更新密钥,连接数有限。docker服务地址官网
  • 3.Node-Media-Server方法切合实际需求,可离线内网使用,画面问题需后期持续优化。
  • 4.应用Node.js基于Chrome V8 引擎的 JavaScript 运行环境,使用事件驱动、非阻塞式 I/O 的模型轻量高效。
  • 5.服务稳定,支持多协议方便后期扩展,服务单一避免冗余和服务带来的代码臃肿,rtsp推流方式多样,提供加密传输机制更加安全。

Node-Media-Server的更多相关文章

  1. 资料整理:基于node push server实现push notification

    chat example based on ionic/ socket.io/ redis https://github.com/jbavari/ionic-socket.io-redis-chat ...

  2. Adobe Flash Media Server安装

    Flash Media Server(FMS)是一个流媒体服务器 使用 实时消息传送协议(RTMP),RTMP是一种未加密的TCP/IP协议,专门设计用来高速传送音频.视频和数据信息. 3.5版32位 ...

  3. Windows Server 2003从入门到精通之Windows Media Server流媒体服务器架建[转]

    今天我们来做一个windows media server流媒体格式文件的流媒体服务器. 现在市面上能够买到的一些电影文件有 rm格式和wmv格式.还有一些是DivX技术的avi格式,要想让你的服务器对 ...

  4. android media server 解析1-media player service 结构部分

    下面为media server注册的四个服务之一:MediaPlayerService的结构图 1.图中没有MediaPlayerService的代理对象BpMediaPlayerService部分, ...

  5. mac media server

    近日在mac osx基于开源组件nginx-rtmp-module架设了一台默认的media server,以下是过程笔记 下载https://github.com/arut/nginx-rtmp-m ...

  6. Flash Media Server 5.0 (FMS)注册码

    flash media server 4.5 及最新 flash media server 5.0 注册码 防止图片打不开时: Name:tam/CORE Serial:1652-5580-8001- ...

  7. Adobe/Flash Media Server 5.0 linux 64位系统下的安装

    一.下载 Adobe/Flash MS5.0下载地址: http://fs1.d-h.st/download/00036/VOt/adobemediaserver_5_ls1_linux64.tar. ...

  8. 使用Flash Media Server(FMS)录制mp4格式的视频

    最近在做一个有关视频直播和点播的项目,客户的一个需求就是可以控制对直播流的录制,直播的实现采用的是Adobe的Flash Media Server,具体方式就是:视频采集端采集视频并编码->rt ...

  9. windows media server 组件安装后流媒体服务器启动失败

    做好的web应用,去客户现场部署的时候发现流媒体服务器不能启动.(现场服务器系统为windows server2008 R2) 自己测试的时候搭建环境没什么问题.从来没有遇到安装windows med ...

  10. (转)ubuntu 12.04搭建Adobe Flash Media Server服务

    破解版传送门:http://fms45.cuplayer.com/fms4download.html 福利:1462-5247-1705-7678-8379-5590 下载解压 cd进目录,./ins ...

随机推荐

  1. 图层的使用要点(CALayer)

    A,图层和路径 基本图层 CALayer 动画的主角 形状图层 CAShapeLayer 绘制不规则图形 渐变图层 CAGradientLayer 颜色渐变.阴影 复制图层 CAReplicatorL ...

  2. Salesforce中Html的转义,InputField和RemoteAction

    在Salesforce的开发中,有时候需要在对象中插入记录,其中有的字段需要插入Html,但是对于输入Html的域,大多数框架和网站都需要做Html的转义处理,防止XSS或者SQL注入攻击.有时候我们 ...

  3. 50条常用liunx命令整理

    1.pwd命令 :确定自己在那个目录 使用方法:在liunx命令输入框里面输入pwd,自动就会显示出自己现在在那个目录下 操作截图: 此时正处在root目录里面 2.cd命令:切换目录的意思 使用方法 ...

  4. 微信公众平台Java版极速SDK

    JEEWX-API 是第一个微信公众平台Java版极速SDK,基于 jeewx-api 开发可以立即拥有简单易用的API,让开发更加轻松自如,节省更多时间 http://www.jeewx.com/

  5. 前端使用canvas绘制立体三角形

    前端绘制立体效果的三角形的demo 在移动端使用时,需要自适应屏幕.canvas上无法设置rem,所以在canvas外加一个父级元素设置为rem,再将canvas的宽高设置为100% 100%. 如果 ...

  6. Linux 日志分析工具(logwatch)安装及使用

    Linux 日志分析工具(logwatch)安装及使用 日志是非常重要的系统文件,管理员每天的重要工作就是分析和查看服务器的日志,判断服务器的健康状态.但是日志管理又是一项非常枯燥的工作,如果需要管理 ...

  7. JavaScript&jQuery获取url参数方法

    JavaScript&jQuery获取url参数方法 function getUrlParam(name){ var reg = new RegExp("(^|&)" ...

  8. web.xml context-param配置

    context-param 为上下文初始化参数 解析:每个<context-param>元素含有一对参数名和参数值(param-name和param-value),用作应用的Servlet ...

  9. 20145211《网络渗透》Adobe阅读器渗透攻击

    20145211<网络渗透>Adobe阅读器渗透攻击 实验准备 1.用了一个kali,一个English Winxp3,并保证能相互ping通 2.开启显示隐藏文件 实验步骤: 1.开启m ...

  10. Apache ab 测试结果的分析

    以前安装好APACHE总是不知道该如何测试APACHE的性能,现在总算找到一个测试工具了.就是APACHE自带的测试工具AB(apache benchmark).在APACHE的bin目录下.格式: ...