From d74438e300b949943e2a31cba57a759bb3c0ae37 Mon Sep 17 00:00:00 2001 From: Ingo Oppermann Date: Fri, 19 Jan 2024 20:33:48 +0100 Subject: [PATCH] Allow to select from publishing RTMP and SRT streams --- src/utils/api.js | 33 ++++++ src/utils/restreamer.js | 50 +++++++++ src/views/Edit/Sources/Network.js | 181 +++++++++++++++++++++++++----- 3 files changed, 237 insertions(+), 27 deletions(-) diff --git a/src/utils/api.js b/src/utils/api.js index 81c31f1..a5f4f41 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -443,6 +443,39 @@ class API { expect: 'json', }); } + + async RTMPChannels() { + const res = await this._GET('/v3/rtmp', { + expect: 'json', + }); + + if (res.err !== null) { + return res; + } + + res.val = res.val.map((f) => f.name); + + return res; + } + + async SRTChannels() { + const res = await this._GET('/v3/srt', { + expect: 'json', + }); + + if (res.err !== null) { + return res; + } + + const val = res.val; + res.val = []; + + for (let path in val.publisher) { + res.val.push(path); + } + + return res; + } } export default API; diff --git a/src/utils/restreamer.js b/src/utils/restreamer.js index 3b6e146..6558402 100644 --- a/src/utils/restreamer.js +++ b/src/utils/restreamer.js @@ -649,6 +649,26 @@ class Restreamer { } } + let channels = (await this.ListRTMPChannels()).map((name) => { + return { + media: 'rtmp', + id: name, + name: name, + }; + }); + + skills.sources['network'].push(...channels); + + channels = (await this.ListSRTChannels()).map((name) => { + return { + media: 'srt', + id: name, + name: name, + }; + }); + + skills.sources['network'].push(...channels); + this.skills = skills; } @@ -2773,6 +2793,18 @@ class Restreamer { return data; } + // RTMP + + async ListRTMPChannels() { + return await this._listRTMPChannels(); + } + + // SRT + + async ListSRTChannels() { + return await this._listSRTChannels(); + } + // Expert Mode IsExpert() { @@ -3268,6 +3300,24 @@ class Restreamer { return val.map((f) => f.name); } + async _listRTMPChannels() { + const [val, err] = await this._call(this.api.RTMPChannels); + if (err !== null) { + return []; + } + + return val; + } + + async _listSRTChannels() { + const [val, err] = await this._call(this.api.SRTChannels); + if (err !== null) { + return []; + } + + return val; + } + async _getAboutDebug() { const about = await this.About(); diff --git a/src/views/Edit/Sources/Network.js b/src/views/Edit/Sources/Network.js index 3cb8c59..21f9d7d 100644 --- a/src/views/Edit/Sources/Network.js +++ b/src/views/Edit/Sources/Network.js @@ -14,6 +14,7 @@ import Button from '@mui/material/Button'; import Grid from '@mui/material/Grid'; import Icon from '@mui/icons-material/AccountTree'; import MenuItem from '@mui/material/MenuItem'; +import RefreshIcon from '@mui/icons-material/Refresh'; import TextField from '@mui/material/TextField'; import Typography from '@mui/material/Typography'; import WarningIcon from '@mui/icons-material/Warning'; @@ -53,6 +54,7 @@ const initSettings = (initialSettings) => { settings.push = { type: 'rtmp', + name: 'none', ...settings.push, }; @@ -178,6 +180,7 @@ const initSkills = (initialSkills) => { }; const createInputs = (settings, config, skills) => { + settings = initSettings(settings); config = initConfig(config); skills = initSkills(skills); @@ -192,12 +195,22 @@ const createInputs = (settings, config, skills) => { }; if (settings.mode === 'push') { + let name = settings.push.name; if (settings.push.type === 'hls') { - input.address = getLocalHLS(config); + if (name === 'none') { + name = config.hls.name; + } + input.address = getLocalHLS(name); } else if (settings.push.type === 'rtmp') { - input.address = getLocalRTMP(config); + if (name === config.rtmp.name) { + name += '.stream'; + } + input.address = getLocalRTMP(name); } else if (settings.push.type === 'srt') { - input.address = getLocalSRT(config); + if (name === config.srt.name) { + name += '.stream'; + } + input.address = getLocalSRT(name); } else { input.address = ''; } @@ -462,12 +475,12 @@ const getLocalHLS = (config, name) => { return url; }; -const getLocalRTMP = (config) => { - return '{rtmp,name=' + config.rtmp.name + '.stream}'; +const getLocalRTMP = (name) => { + return '{rtmp,name=' + name + '}'; }; -const getLocalSRT = (config) => { - return '{srt,name=' + config.srt.name + '.stream,mode=request}'; +const getLocalSRT = (name) => { + return '{srt,name=' + name + ',mode=request}'; }; const isValidURL = (address) => { @@ -843,6 +856,16 @@ function Push(props) { ); } +Push.defaultProps = { + knownDevices: [], + settings: {}, + config: {}, + skills: null, + onChange: function (settings) {}, + onProbe: function (settings, inputs) {}, + onRefresh: function () {}, +}; + function PushHLS(props) { const classes = useStyles(); const config = props.config; @@ -872,6 +895,7 @@ function PushHLS(props) { } function PushRTMP(props) { + const { i18n } = useLingui(); const classes = useStyles(); const navigate = useNavigate(); const config = props.config; @@ -896,21 +920,54 @@ function PushRTMP(props) { } else { const RTMP = getRTMP(config); + const filteredDevices = props.knownDevices.filter((device) => device.media === 'rtmp'); + const options = filteredDevices.map((device) => { + return ( + + {device.name} + + ); + }); + + options.unshift( + + {i18n._(t`Choose an input stream ...`)} + , + ); + + options.push( + + {i18n._(t`Send stream to address ...`)} + , + ); + form = ( - - Send stream to this address: - - - - -