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 (
+
+ );
+ });
+
+ options.unshift(
+ ,
+ );
+
+ options.push(
+ ,
+ );
+
form = (
-
- Send stream to this address:
-
-
-
-
-
-
+
+ } onClick={props.onRefresh} sx={{ float: 'right' }}>
+ Refresh
+
+ {props.settings.push.name === config.rtmp.name && (
+
+
+
+ Address:
+
+
+
+
+
+
+
+
+ )}
-
+
Probe
@@ -921,7 +978,18 @@ function PushRTMP(props) {
return form;
}
+PushRTMP.defaultProps = {
+ knownDevices: [],
+ settings: {},
+ config: {},
+ skills: null,
+ onChange: function (settings) {},
+ onProbe: function (settings, inputs) {},
+ onRefresh: function () {},
+};
+
function PushSRT(props) {
+ const { i18n } = useLingui();
const classes = useStyles();
const navigate = useNavigate();
const config = props.config;
@@ -946,21 +1014,54 @@ function PushSRT(props) {
} else {
const SRT = getSRT(config);
+ const filteredDevices = props.knownDevices.filter((device) => device.media === 'srt');
+ const options = filteredDevices.map((device) => {
+ return (
+
+ );
+ });
+
+ options.unshift(
+ ,
+ );
+
+ options.push(
+ ,
+ );
+
form = (
-
- Send stream to this address:
-
-
-
-
-
-
+
+ } onClick={props.onRefresh} sx={{ float: 'right' }}>
+ Refresh
+
+ {props.settings.push.name === config.srt.name && (
+
+
+
+ Address:
+
+
+
+
+
+
+
+
+ )}
-
+
Probe
@@ -971,11 +1072,21 @@ function PushSRT(props) {
return form;
}
+PushSRT.defaultProps = {
+ knownDevices: [],
+ settings: {},
+ config: {},
+ skills: null,
+ onChange: function (settings) {},
+ onProbe: function (settings, inputs) {},
+ onRefresh: function () {},
+};
+
function Source(props) {
const classes = useStyles();
const { i18n } = useLingui();
- const settings = initSettings(props.settings);
const config = initConfig(props.config);
+ const settings = initSettings(props.settings);
const skills = initSkills(props.skills);
const handleChange = (section, what) => (event) => {
@@ -1001,6 +1112,9 @@ function Source(props) {
}
} else if (section === 'push') {
settings.push[what] = value;
+ if (what === 'type') {
+ settings.push.name = 'none';
+ }
} else {
settings[what] = value;
}
@@ -1014,6 +1128,10 @@ function Source(props) {
props.onProbe(settings, createInputs(settings, config, skills));
};
+ const handleRefresh = () => {
+ props.onRefresh();
+ };
+
return (
@@ -1027,13 +1145,22 @@ function Source(props) {
{settings.mode === 'pull' ? (
) : (
-
+
)}
);
}
Source.defaultProps = {
+ knownDevices: [],
settings: {},
config: {},
skills: null,