Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to customize ffmpeg output options in relay and fission t… #559

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ nms.run();
```

# Rtsp/Rtmp Relay
NodeMediaServer implement RTSP and RTMP relay with ffmpeg.
NodeMediaServer implements RTSP and RTMP relay with ffmpeg.

## Static pull
The static pull mode is executed at service startup and reconnect after failure.
Expand Down Expand Up @@ -668,6 +668,22 @@ relay: {
}
```

## Transcoding Options
FFmpeg output options could be added for the pull/push modes above. Default behavior is `-c copy`.
```
relay: {
ffmpeg: '/usr/local/bin/ffmpeg',
tasks: [
{
app: 'live',
mode: 'push',
options: ['-vf', 'scale=1920:-1', '-c:v', 'libx264', '-b:v', '2m', '-c:a', 'copy'],
edge: 'rtmp://192.168.0.10',
}
]
}
```

# Fission
Real-time transcoding multi-resolution output
![fission](https://raw.githubusercontent.com/illuspas/resources/master/img/admin_panel_fission.png)
Expand Down Expand Up @@ -725,6 +741,29 @@ fission: {
}
```

## Custom Transcoding Options
Custom FFmpeg output options could be specified directly. In this case, a suffix should also be provided for the transcoded stream.
```
fission: {
ffmpeg: '/usr/local/bin/ffmpeg',
tasks: [
{
rule: 'live/*',
model: [
{
options: ['-c:v', 'hevc_nvenc', '-b:v', '10m', '-vf', 'scale=3840:-1', '-c:a', 'copy'],
suffix: 'uhd5'
},
{
options: ['-c:v', 'libx264', '-b:v', '4m', '-vf', 'scale=1920:-1', '-c:a', 'copy'],
suffix: 'hd4'
}
]
}
]
}
```

# Publisher and Player App/SDK

## Android Livestream App
Expand Down
39 changes: 39 additions & 0 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,22 @@ relay: {
}
```

## 转码选项
可以为上面的推流和拉流模式配置FFmpeg输出选项。默认值是 `-c copy`.
```
relay: {
ffmpeg: '/usr/local/bin/ffmpeg',
tasks: [
{
app: 'live',
mode: 'push',
options: ['-vf', 'scale=1920:-1', '-c:v', 'libx264', '-b:v', '2m', '-c:a', 'copy'],
edge: 'rtmp://192.168.0.10',
}
]
}
```

# 实时多分辨率转码
![fission](https://raw.githubusercontent.com/illuspas/resources/master/img/admin_panel_fission.png)
```
Expand Down Expand Up @@ -618,6 +634,29 @@ fission: {
}
```

## 自定义FFmpeg选项
可以直接配置FFmpeg输出选项,同时需要为转码后的流设置后缀。
```
fission: {
ffmpeg: '/usr/local/bin/ffmpeg',
tasks: [
{
rule: 'live/*',
model: [
{
options: ['-c:v', 'hevc_nvenc', '-b:v', '10m', '-vf', 'scale=3840:-1', '-c:a', 'copy'],
suffix: 'uhd5'
},
{
options: ['-c:v', 'libx264', '-b:v', '4m', '-vf', 'scale=1920:-1', '-c:a', 'copy'],
suffix: 'hd4'
}
]
}
]
}
```

# 推流与播放 App/SDK

## Android Livestream App
Expand Down
9 changes: 7 additions & 2 deletions src/api/controllers/relay.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ function getStreams(req, res, next) {
stats[app][name]['relays'].push({
app: app,
name: name,
options: session.conf.options,
path: session.conf.inPath,
url: session.conf.ouPath,
mode: session.conf.mode,
Expand All @@ -56,6 +57,7 @@ function getStreamByID(req, res, next) {
const relays = relaySession.map((item) => ({
app: item.conf.app,
name: item.conf.name,
options: item.conf.options,
path: item.conf.inPath,
url: item.conf.ouPath,
mode: item.conf.mode,
Expand All @@ -81,6 +83,7 @@ function getStreamByName(req, res, next) {
const relays = relaySession.map((item) => ({
app: item.conf.app,
name: item.conf.name,
options: item.conf.options,
url: item.conf.ouPath,
mode: item.conf.mode,
ts: item.ts,
Expand Down Expand Up @@ -118,9 +121,10 @@ async function pullStream(req, res, next) {
let url = req.body.url;
let app = req.body.app;
let name = req.body.name;
let options = req.body.options ? req.body.options : null;
let rtsp_transport = req.body.rtsp_transport ? req.body.rtsp_transport : null;
if (url && app && name) {
process.nextTick(() => this.nodeEvent.emit('relayPull', url, app, name, rtsp_transport));
process.nextTick(() => this.nodeEvent.emit('relayPull', url, app, name, options, rtsp_transport));
let ret = await once(this.nodeEvent, 'relayPullDone');
res.send(ret[0]);

Expand All @@ -139,8 +143,9 @@ async function pushStream(req, res, next) {
let url = req.body.url;
let app = req.body.app;
let name = req.body.name;
let options = req.body.options ? req.body.options : null;
if (url && app && name) {
process.nextTick(() => this.nodeEvent.emit('relayPush', url, app, name));
process.nextTick(() => this.nodeEvent.emit('relayPush', url, app, name, options));
let ret = await once(this.nodeEvent, 'relayPushDone');
res.send(ret[0]);
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/node_fission_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class NodeFissionServer {
conf.streamApp = app;
conf.streamName = name;
conf.args = args;
let session = new NodeFissionSession(conf);
let session = new NodeFissionSession(id, conf);
this.fissionSessions.set(id, session);
session.on('end', () => {
this.fissionSessions.delete(id);
Expand Down
24 changes: 17 additions & 7 deletions src/node_fission_session.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,34 @@ const EventEmitter = require('events');
const { spawn } = require('child_process');

class NodeFissionSession extends EventEmitter {
constructor(conf) {
constructor(id, conf) {
super();
this.id = id;
this.conf = conf;
}

run() {
let inPath = 'rtmp://127.0.0.1:' + this.conf.rtmpPort + this.conf.streamPath;
let argv = ['-i', inPath];
for (let m of this.conf.model) {
let x264 = ['-c:v', 'libx264', '-preset', 'veryfast', '-tune', 'zerolatency', '-maxrate', m.vb, '-bufsize', m.vb, '-g', parseInt(m.vf) * 2, '-r', m.vf, '-s', m.vs];
let aac = ['-c:a', 'aac', '-b:a', m.ab];
let outPath = ['-f', 'flv', 'rtmp://127.0.0.1:' + this.conf.rtmpPort + '/' + this.conf.streamApp + '/' + this.conf.streamName + '_' + m.vs.split('x')[1]];
argv.splice(argv.length, 0, ...x264);
argv.splice(argv.length, 0, ...aac);
argv.splice(argv.length, 0, ...outPath);
if (m.options) {
argv.splice(argv.length, 0, ...m.options);
let outPath = ['-f', 'flv', 'rtmp://127.0.0.1:' + this.conf.rtmpPort + '/' + this.conf.streamApp + '/' + this.conf.streamName + '_' + m.suffix];
argv.splice(argv.length, 0, ...outPath);
} else {
let x264 = ['-c:v', 'libx264', '-preset', 'veryfast', '-tune', 'zerolatency', '-maxrate', m.vb, '-bufsize', m.vb, '-g', parseInt(m.vf) * 2, '-r', m.vf, '-s', m.vs];
let aac = ['-c:a', 'aac', '-b:a', m.ab];
let outPath = ['-f', 'flv', 'rtmp://127.0.0.1:' + this.conf.rtmpPort + '/' + this.conf.streamApp + '/' + this.conf.streamName + '_' + m.vs.split('x')[1]];
argv.splice(argv.length, 0, ...x264);
argv.splice(argv.length, 0, ...aac);
argv.splice(argv.length, 0, ...outPath);
}
}

argv = argv.filter((n) => { return n; });

Logger.log('[fission task] id=' + this.id, 'cmd=ffmpeg', argv.join(' '));

this.ffmpeg_exec = spawn(this.conf.ffmpeg, argv);
this.ffmpeg_exec.on('error', (e) => {
Logger.ffdebug(e);
Expand Down
6 changes: 4 additions & 2 deletions src/node_relay_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,11 @@ class NodeRelayServer {
}

//从远端拉推到本地
onRelayPull(url, app, name, rtsp_transport) {
onRelayPull(url, app, name, options, rtsp_transport) {
let conf = {};
conf.app = app;
conf.name = name;
conf.options = options;
conf.mode = 'pull';
conf.ffmpeg = this.config.relay.ffmpeg;
conf.inPath = url;
Expand All @@ -124,10 +125,11 @@ class NodeRelayServer {
}

//从本地拉推到远端
onRelayPush(url, app, name) {
onRelayPush(url, app, name, options) {
let conf = {};
conf.app = app;
conf.name = name;
conf.options = options;
conf.mode = 'push';
conf.ffmpeg = this.config.relay.ffmpeg;
conf.inPath = `rtmp://127.0.0.1:${this.config.rtmp.port}/${app}/${name}`;
Expand Down
3 changes: 2 additions & 1 deletion src/node_relay_session.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ class NodeRelaySession extends EventEmitter {

run() {
let format = this.conf.ouPath.startsWith('rtsp://') ? 'rtsp' : 'flv';
let argv = ['-re', '-i', this.conf.inPath, '-c', 'copy', '-f', format, this.conf.ouPath];
let options = this.conf.options ? this.conf.options: ['-c', 'copy'];
let argv = ['-re', '-i', this.conf.inPath, ...options, '-f', format, this.conf.ouPath];
if (this.conf.inPath[0] === '/' || this.conf.inPath[1] === ':') {
argv.unshift('-1');
argv.unshift('-stream_loop');
Expand Down