Merge branch 'dev' into dev-internal-srt-latency

This commit is contained in:
Ingo Oppermann 2025-01-22 13:52:21 +01:00
commit e0744f8e07
No known key found for this signature in database
GPG Key ID: 2AB32426E9DD229E
8 changed files with 161 additions and 35 deletions

View File

@ -114,36 +114,23 @@
var player = videojs('player', config);
if(playerConfig.logo.image.length != 0) {
var overlay = null;
var imgTag = new Image();
imgTag.onLoad = function () {
imgTag.setAttribute('width', this.width);
imgTag.setAttribute('height'.this.height);
const imgTag = new Image();
imgTag.src = playerConfig.logo.image;
imgTag.onload = function () {
const logoWidth = imgTag.width;
const logoHeight = imgTag.height;
const overlayPosition = playerConfig.logo.position || 'top-left';
const overlayLink = playerConfig.logo.link || '#';
player.overlay({
overlays: [{
content: `<a href="${overlayLink}" target="_blank"><img src="${playerConfig.logo.image + '?' + Math.random()}" alt="Logo" width="${logoWidth}" height="${logoHeight}"></a>`,
start: 'play',
end: 'pause',
align: overlayPosition,
showBackground: false
}]
});
};
imgTag.src = playerConfig.logo.image + '?' + Math.random();
if (playerConfig.logo.link.length !== 0) {
var aTag = document.createElement('a');
aTag.setAttribute('href', playerConfig.logo.link);
aTag.setAttribute('target', '_blank');
aTag.appendChild(imgTag);
overlay = aTag.outerHTML;
} else {
overlay = imgTag.outerHTML;
}
player.overlay({
align: playerConfig.logo.position,
overlays: [
{
showBackground: false,
content: overlay,
start: 'play',
end: 'pause',
},
],
});
}
player.ready(function() {

View File

@ -43,6 +43,8 @@ function createMapping(settings, stream, skills) {
`${settings.fps}`,
'-sc_threshold',
'0',
'-forced-idr',
'1',
'-pix_fmt',
'yuv420p',
];

View File

@ -381,9 +381,9 @@ class Restreamer {
return;
}
await this._initSkills();
await this._initConfig();
await this._discoverChannels();
await this._initSkills();
}
_setTokenRefresh(expiresIn) {
@ -514,6 +514,9 @@ class Restreamer {
virtualvideo: [],
videoloop: [],
audioloop: [],
channel: [],
noaudio: [],
sdp: [],
},
sinks: {},
};
@ -677,6 +680,15 @@ class Restreamer {
skills.sources['network'].push(...channels);
channels = this.ListChannels().map((channel) => {
return {
id: channel.channelid,
name: channel.name,
};
});
skills.sources['channel'].push(...channels);
this.skills = skills;
}

View File

@ -78,10 +78,6 @@ export default function Profile({
}, []);
const load = async () => {
// Add pseudo sources
skills.sources.noaudio = [];
skills.sources.sdp = [];
let audio = $sources.audio;
let hasAudio = false;

View File

@ -0,0 +1,125 @@
import React from 'react';
import { useLingui } from '@lingui/react';
import { Trans, t } from '@lingui/macro';
import VideocamIcon from '@mui/icons-material/Videocam';
import makeStyles from '@mui/styles/makeStyles';
import Button from '@mui/material/Button';
import Grid from '@mui/material/Grid';
import RefreshIcon from '@mui/icons-material/Refresh';
import Typography from '@mui/material/Typography';
import FormInlineButton from '../../../misc/FormInlineButton';
import SelectCustom from '../../../misc/SelectCustom';
const useStyles = makeStyles((theme) => ({
gridContainer: {
marginTop: '0.5em',
},
}));
const initSettings = (initialSettings) => {
if (!initialSettings) {
initialSettings = {};
}
const settings = {
channelid: 'none',
...initialSettings,
};
return settings;
};
const createInputs = (settings) => {
const address = `{fs:mem}/${settings.channelid}.m3u8`;
const input = {
address: address,
options: [],
};
return [input];
};
function Source({ knownDevices = [], settings = {}, onChange = function (settings) {}, onProbe = function (settings, inputs) {}, onRefresh = function () {} }) {
const classes = useStyles();
const { i18n } = useLingui();
settings = initSettings(settings);
const handleChange = (what) => (event) => {
let data = {};
if (['channelid'].includes(what)) {
data[what] = event.target.value;
}
onChange({
...settings,
...data,
});
};
const handleRefresh = () => {
onRefresh();
};
const handleProbe = () => {
onProbe(settings, createInputs(settings));
};
const options = knownDevices.map((device) => {
return {
value: device.id,
label: device.name + ' (' + device.id + ')',
};
});
options.unshift({
value: 'none',
label: i18n._(t`Choose an input channel ...`),
disabled: true,
});
return (
<Grid container alignItems="flex-start" spacing={2} className={classes.gridContainer}>
<Grid item xs={12}>
<Typography>
<Trans>Select a channel:</Trans>
</Typography>
</Grid>
<Grid item xs={12}>
<SelectCustom
options={options}
label={<Trans>Video device</Trans>}
value={settings.channelid}
onChange={handleChange('channelid')}
variant="outlined"
/>
<Button size="small" startIcon={<RefreshIcon />} onClick={handleRefresh} sx={{ float: 'right' }}>
<Trans>Refresh</Trans>
</Button>
</Grid>
<Grid item xs={12}>
<FormInlineButton onClick={handleProbe} disabled={settings.channelid === 'none'}>
<Trans>Probe</Trans>
</FormInlineButton>
</Grid>
</Grid>
);
}
function SourceIcon(props) {
return <VideocamIcon style={{ color: '#FFF' }} {...props} />;
}
const id = 'channel';
const name = <Trans>Channel</Trans>;
const capabilities = ['audio', 'video'];
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0 || ^7.0.0';
const func = {
initSettings,
createInputs,
};
export { id, name, capabilities, ffversion, SourceIcon as icon, Source as component, func };

View File

@ -1,8 +1,8 @@
import React from 'react';
import { useLingui } from '@lingui/react';
import ScreenshotMonitorIcon from '@mui/icons-material/ScreenshotMonitor';
import { Trans, t } from '@lingui/macro';
import ScreenshotMonitorIcon from '@mui/icons-material/ScreenshotMonitor';
import makeStyles from '@mui/styles/makeStyles';
import Button from '@mui/material/Button';
import Grid from '@mui/material/Grid';

View File

@ -11,6 +11,7 @@ import * as VideoLoop from './VideoLoop';
import * as VirtualAudio from './VirtualAudio';
import * as VirtualVideo from './VirtualVideo';
import * as X11grab from './X11grab';
import * as Channel from './Channel';
class Registry {
constructor() {
@ -54,5 +55,6 @@ registry.Register(VideoLoop);
registry.Register(AudioLoop);
registry.Register(SDP);
registry.Register(X11grab);
registry.Register(Channel);
export default registry;

View File

@ -112,6 +112,7 @@ export default function Edit({ restreamer = null }) {
});
const skills = await restreamer.Skills();
skills.sources['channel'] = skills.sources['channel'].filter((channel) => channel.id !== _channelid);
setSkills(skills);
const config = await restreamer.ConfigActive();
@ -208,6 +209,7 @@ export default function Edit({ restreamer = null }) {
await restreamer.RefreshSkills();
const skills = await restreamer.Skills();
skills.sources['channel'] = skills.sources['channel'].filter((channel) => channel.id !== _channelid);
setSkills(skills);
};