diff --git a/src/utils/restreamer.js b/src/utils/restreamer.js index 5f120a8..f8a785f 100644 --- a/src/utils/restreamer.js +++ b/src/utils/restreamer.js @@ -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; } diff --git a/src/views/Edit/Profile.js b/src/views/Edit/Profile.js index 1f7d986..8c64abe 100644 --- a/src/views/Edit/Profile.js +++ b/src/views/Edit/Profile.js @@ -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; diff --git a/src/views/Edit/Sources/Channel.js b/src/views/Edit/Sources/Channel.js new file mode 100644 index 0000000..f6996de --- /dev/null +++ b/src/views/Edit/Sources/Channel.js @@ -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 ( + + + + Select a channel: + + + + Video device} + value={settings.channelid} + onChange={handleChange('channelid')} + variant="outlined" + /> + + + + + Probe + + + + ); +} + +function SourceIcon(props) { + return ; +} + +const id = 'channel'; +const name = Channel; +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 }; diff --git a/src/views/Edit/Sources/X11grab.js b/src/views/Edit/Sources/X11grab.js index 508abc1..a861eb8 100644 --- a/src/views/Edit/Sources/X11grab.js +++ b/src/views/Edit/Sources/X11grab.js @@ -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'; diff --git a/src/views/Edit/Sources/index.js b/src/views/Edit/Sources/index.js index cf56731..e8293a3 100644 --- a/src/views/Edit/Sources/index.js +++ b/src/views/Edit/Sources/index.js @@ -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; diff --git a/src/views/Edit/index.js b/src/views/Edit/index.js index 2ab3773..0f39df5 100644 --- a/src/views/Edit/index.js +++ b/src/views/Edit/index.js @@ -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); };