diff --git a/src/misc/modals/Hint.js b/src/misc/modals/Hint.js new file mode 100644 index 0000000..4f790e3 --- /dev/null +++ b/src/misc/modals/Hint.js @@ -0,0 +1,200 @@ +import React from 'react'; + +import { useLingui } from '@lingui/react'; +import { Trans, t } from '@lingui/macro'; +import Button from '@mui/material/Button'; +import Grid from '@mui/material/Grid'; +import Stack from '@mui/material/Stack'; +import MenuItem from '@mui/material/MenuItem'; +import Typography from '@mui/material/Typography'; + +import Dialog from './Dialog'; +import Select from '../Select'; +import Video from '../coders/settings/Video'; +import Audio from '../coders/settings/Audio'; + +const Stream = function (props) { + const { i18n } = useLingui(); + + const handleChange = (what) => (event) => { + const value = event.target.value; + + let stream = { + ...props.stream, + }; + + if (what === 'type') { + if (value === 'audio') { + stream.codec = 'aac'; + if (stream.sampling_hz === 0) { + stream.sampling_hz = 44100; + } + if (stream.layout === '') { + stream.layout = 'stereo'; + stream.channels = 2; + } + } else { + stream.codec = 'h264'; + if (stream.width === 0) { + stream.width = 1920; + stream.height = 1080; + } + } + stream.type = value; + } else if (what === 'size') { + const [width, height] = value.split('x'); + + stream.width = width; + stream.height = height; + } else { + stream[what] = value; + } + + props.onChange(stream); + }; + + return ( + + + + + {props.stream.type === 'audio' ? ( + + + + + + + + + + + + ) : ( + + + + + + + + + )} + + ); +}; + +Stream.defaultProps = { + stream: {}, + locked: false, + onChange: () => {}, +}; + +const Streams = function (props) { + const handleChange = (index) => (stream) => { + const streams = props.streams.slice(); + + streams[index] = stream; + + props.onChange(streams); + }; + + const handleAddStream = () => { + const streams = props.streams.slice(); + + streams.push({ + index: props.type === 'video' ? 0 : 1, + stream: streams.length, + type: 'audio', + codec: 'aac', + width: 0, + height: 0, + sampling_hz: 44100, + layout: 'stereo', + channels: 2, + }); + + props.onChange(streams); + }; + + const handleRemoveStream = (index) => () => { + const streams = props.streams.toSpliced(index, 1); + + props.onChange(streams); + }; + + return ( + + {props.streams.map((stream, index) => ( + + + Stream {stream.stream} + + {index > 0 && ( + + )} + + + ))} + + + + + ); +}; + +Streams.defaultProps = { + streams: [], + type: '', + onChange: () => {}, +}; + +const Component = function (props) { + return ( + + Close + + } + buttonsRight={ + + } + > + + + ); +}; + +export default Component; + +Component.defaultProps = { + open: false, + title: '', + streams: [], + type: '', + onClose: null, + onDone: () => {}, + onHelp: null, +}; diff --git a/src/views/Edit/Profile.js b/src/views/Edit/Profile.js index 13c45fc..aae5f92 100644 --- a/src/views/Edit/Profile.js +++ b/src/views/Edit/Profile.js @@ -15,6 +15,7 @@ import BoxText from '../../misc/BoxText'; import EncodingSelect from '../../misc/EncodingSelect'; import PaperFooter from '../../misc/PaperFooter'; import ProbeModal from '../../misc/modals/Probe'; +import HintModal from '../../misc/modals/Hint'; import SourceSelect from './SourceSelect'; import StreamSelect from './StreamSelect'; @@ -39,10 +40,15 @@ export default function Profile(props) { status: 'none', }); const [$skillsRefresh, setSkillsRefresh] = React.useState(false); - const [$modal, setModal] = React.useState({ + const [$probeModal, setProbeModal] = React.useState({ open: false, data: '', }); + const [$hintModal, setHintModal] = React.useState({ + open: false, + type: '', + streams: [], + }); const [$activeStep, setActiveStep] = React.useState(props.startWith === 'audio' ? 1 : 0); const [$ready, setReady] = React.useState(false); @@ -106,6 +112,12 @@ export default function Profile(props) { const res = await props.onProbe(inputs); + const status = handleProbeStreams(type, device, settings, inputs, res); + + return status === 'success'; + }; + + const handleProbeStreams = (type, device, settings, inputs, res) => { let status = M.analyzeStreams(type, res.streams); if (type === 'video') { @@ -194,8 +206,6 @@ export default function Profile(props) { }, }); } - - return status === 'success'; }; const handleRefresh = async () => { @@ -242,24 +252,24 @@ export default function Profile(props) { props.onAbort(); }; - const handleModal = (type) => (event) => { + const handleProbeLogModal = (type) => (event) => { event.preventDefault(); if (type === 'video') { - setModal({ - ...$modal, + setProbeModal({ + ...$probeModal, open: true, data: $videoProbe.log.join('\n'), }); } else if (type === 'audio') { - setModal({ - ...$modal, + setProbeModal({ + ...$probeModal, open: true, data: $audioProbe.log.join('\n'), }); } else { - setModal({ - ...$modal, + setProbeModal({ + ...$probeModal, open: false, data: '', }); @@ -348,6 +358,104 @@ export default function Profile(props) { }); }; + const handleHintModal = (type, streams) => (event) => { + if (event) { + event.preventDefault(); + } + + if (!streams) { + streams = []; + } + + if (streams.length > 0) { + return streams; + } + + if (type === 'video') { + streams = [ + { + index: 0, + stream: 0, + type: 'video', + codec: 'h264', + width: 1920, + height: 1080, + sampling_hz: 0, + layout: '', + channels: 0, + }, + ]; + } else if (type === 'audio') { + streams = [ + { + index: 1, + stream: 0, + type: 'audio', + codec: 'aac', + width: 0, + height: 0, + sampling_hz: 44100, + layout: 'stereo', + channels: 2, + }, + ]; + } + + if (type === 'video') { + setHintModal({ + ...$hintModal, + open: true, + type: type, + streams: streams, + }); + } else if (type === 'audio') { + setHintModal({ + ...$hintModal, + open: true, + type: type, + streams: streams, + }); + } else { + setHintModal({ + ...$hintModal, + open: false, + type: '', + streams: [], + }); + } + }; + + const handleHintChange = (streams) => { + setHintModal({ + ...$hintModal, + streams: streams, + }); + }; + + const handleHintCancel = () => { + setHintModal({ + streams: [], + }); + + handleHintModal('none')(null); + }; + + const handleHintDone = () => { + const type = $hintModal.type; + + const device = $sources[type].type; + const settings = $sources[type].settings; + const inputs = $sources[type].inputs; + const probe = { + streams: $hintModal.streams, + log: ['Stream hints'], + }; + + handleProbeStreams(type, device, settings, inputs, probe); + + handleHintModal('none')(null); + }; + if ($ready === false) { return null; } @@ -383,12 +491,21 @@ export default function Profile(props) { Failed to probe the source. Please check the{' '} - + probe details . + + + In order to proceed anyways, you can provide{' '} + + hints + {' '} + about the available streams. + + )} @@ -399,7 +516,7 @@ export default function Profile(props) { The source doesn't provide any video streams. Please check the{' '} - + probe details . @@ -421,7 +538,7 @@ export default function Profile(props) { - + Show probe details @@ -533,7 +650,7 @@ export default function Profile(props) { Failed to probe the source. Please check the{' '} - + probe details . @@ -549,7 +666,7 @@ export default function Profile(props) { The source doesn't provide any audio streams. Please check the{' '} - + probe details . @@ -571,7 +688,7 @@ export default function Profile(props) { - + Show probe details @@ -629,7 +746,16 @@ export default function Profile(props) { - + + ); }