Allow to select from publishing RTMP and SRT streams
This commit is contained in:
parent
42013ec2c1
commit
d74438e300
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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 (
|
||||
<MenuItem key={device.id} value={device.id}>
|
||||
{device.name}
|
||||
</MenuItem>
|
||||
);
|
||||
});
|
||||
|
||||
options.unshift(
|
||||
<MenuItem key="none" value="none" disabled>
|
||||
{i18n._(t`Choose an input stream ...`)}
|
||||
</MenuItem>,
|
||||
);
|
||||
|
||||
options.push(
|
||||
<MenuItem key={config.rtmp.name} value={config.rtmp.name}>
|
||||
{i18n._(t`Send stream to address ...`)}
|
||||
</MenuItem>,
|
||||
);
|
||||
|
||||
form = (
|
||||
<Grid container alignItems="flex-start" spacing={2} className={classes.gridContainer}>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Send stream to this address:</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<BoxTextarea>
|
||||
<Textarea rows={1} value={RTMP} readOnly allowCopy />
|
||||
</BoxTextarea>
|
||||
<Select type="select" label={<Trans>Input stream</Trans>} value={props.settings.push.name} onChange={props.onChange('push', 'name')}>
|
||||
{options}
|
||||
</Select>
|
||||
<Button size="small" startIcon={<RefreshIcon />} onClick={props.onRefresh} sx={{ float: 'right' }}>
|
||||
<Trans>Refresh</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
{props.settings.push.name === config.rtmp.name && (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Address:</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<BoxTextarea>
|
||||
<Textarea rows={1} value={RTMP} readOnly allowCopy />
|
||||
</BoxTextarea>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
<AdvancedSettings {...props} />
|
||||
<Grid item xs={12}>
|
||||
<FormInlineButton onClick={props.onProbe}>
|
||||
<FormInlineButton onClick={props.onProbe} disabled={props.settings.push.name === 'none'}>
|
||||
<Trans>Probe</Trans>
|
||||
</FormInlineButton>
|
||||
</Grid>
|
||||
@ -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 (
|
||||
<MenuItem key={device.id} value={device.id}>
|
||||
{device.name}
|
||||
</MenuItem>
|
||||
);
|
||||
});
|
||||
|
||||
options.unshift(
|
||||
<MenuItem key="none" value="none" disabled>
|
||||
{i18n._(t`Choose an input stream ...`)}
|
||||
</MenuItem>,
|
||||
);
|
||||
|
||||
options.push(
|
||||
<MenuItem key={config.srt.name} value={config.srt.name}>
|
||||
{i18n._(t`Send stream to address ...`)}
|
||||
</MenuItem>,
|
||||
);
|
||||
|
||||
form = (
|
||||
<Grid container alignItems="flex-start" spacing={2} className={classes.gridContainer}>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Send stream to this address:</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<BoxTextarea>
|
||||
<Textarea rows={1} value={SRT} readOnly allowCopy />
|
||||
</BoxTextarea>
|
||||
<Select type="select" label={<Trans>Input stream</Trans>} value={props.settings.push.name} onChange={props.onChange('push', 'name')}>
|
||||
{options}
|
||||
</Select>
|
||||
<Button size="small" startIcon={<RefreshIcon />} onClick={props.onRefresh} sx={{ float: 'right' }}>
|
||||
<Trans>Refresh</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
{props.settings.push.name === config.srt.name && (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Address:</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<BoxTextarea>
|
||||
<Textarea rows={1} value={SRT} readOnly allowCopy />
|
||||
</BoxTextarea>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
<AdvancedSettings {...props} />
|
||||
<Grid item xs={12}>
|
||||
<FormInlineButton onClick={props.onProbe}>
|
||||
<FormInlineButton onClick={props.onProbe} disabled={props.settings.push.name === 'none'}>
|
||||
<Trans>Probe</Trans>
|
||||
</FormInlineButton>
|
||||
</Grid>
|
||||
@ -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 (
|
||||
<React.Fragment>
|
||||
<Grid container alignItems="flex-start" spacing={2} className={classes.gridContainer}>
|
||||
@ -1027,13 +1145,22 @@ function Source(props) {
|
||||
{settings.mode === 'pull' ? (
|
||||
<Pull settings={settings} config={config} skills={skills} onChange={handleChange} onProbe={handleProbe} />
|
||||
) : (
|
||||
<Push settings={settings} config={config} skills={skills} onChange={handleChange} onProbe={handleProbe} />
|
||||
<Push
|
||||
settings={settings}
|
||||
config={config}
|
||||
skills={skills}
|
||||
knownDevices={props.knownDevices}
|
||||
onChange={handleChange}
|
||||
onProbe={handleProbe}
|
||||
onRefresh={handleRefresh}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
Source.defaultProps = {
|
||||
knownDevices: [],
|
||||
settings: {},
|
||||
config: {},
|
||||
skills: null,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user