Add to allow stream hints in case probing fails
This commit is contained in:
parent
9277f04b4b
commit
486d64ff19
200
src/misc/modals/Hint.js
Normal file
200
src/misc/modals/Hint.js
Normal file
@ -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 (
|
||||||
|
<Grid container spacing={1}>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Select label={<Trans>Type</Trans>} value={props.stream.type} onChange={handleChange('type')} disabled={props.locked}>
|
||||||
|
<MenuItem value="audio">Audio</MenuItem>
|
||||||
|
<MenuItem value="video">Video</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</Grid>
|
||||||
|
{props.stream.type === 'audio' ? (
|
||||||
|
<React.Fragment>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Select label={<Trans>Codec</Trans>} value={props.stream.codec} onChange={handleChange('codec')}>
|
||||||
|
<MenuItem value="aac">AAC</MenuItem>
|
||||||
|
<MenuItem value="mp3">MP3</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Audio.Sampling value={props.stream.sampling_hz} onChange={handleChange('sampling_hz')} allowCustom />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Audio.Layout value={props.stream.layout} onChange={handleChange('layout')} allowCustom />
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
<React.Fragment>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Select label={<Trans>Codec</Trans>} value={props.stream.codec} onChange={handleChange('codec')}>
|
||||||
|
<MenuItem value="h264">H264</MenuItem>
|
||||||
|
<MenuItem value="hevc">HEVC</MenuItem>
|
||||||
|
<MenuItem value="vp9">VP9</MenuItem>
|
||||||
|
<MenuItem value="av1">AV1</MenuItem>
|
||||||
|
<MenuItem value="vp8">VP8</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Video.Size value={props.stream.width + 'x' + props.stream.height} onChange={handleChange('size')} allowCustom />
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<Grid container spacing={1}>
|
||||||
|
{props.streams.map((stream, index) => (
|
||||||
|
<Grid key={stream.index + ':' + stream.stream} item xs={12}>
|
||||||
|
<Stack spacing={1}>
|
||||||
|
<Typography>Stream {stream.stream}</Typography>
|
||||||
|
<Stream stream={stream} onChange={handleChange(index)} locked={index === 0} />
|
||||||
|
{index > 0 && (
|
||||||
|
<Button variant="outlined" color="secondary" onClick={handleRemoveStream(index)}>
|
||||||
|
<Trans>Remove Stream</Trans>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Button variant="outlined" color="default" onClick={handleAddStream}>
|
||||||
|
<Trans>Add Stream</Trans>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Streams.defaultProps = {
|
||||||
|
streams: [],
|
||||||
|
type: '',
|
||||||
|
onChange: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Component = function (props) {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={props.open}
|
||||||
|
onClose={props.onClose}
|
||||||
|
title={props.title}
|
||||||
|
buttonsLeft={
|
||||||
|
<Button variant="outlined" color="secondary" onClick={props.onClose}>
|
||||||
|
<Trans>Close</Trans>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
buttonsRight={
|
||||||
|
<Button variant="outlined" color="default" onClick={props.onDone}>
|
||||||
|
<Trans>Save</Trans>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Streams type={props.type} streams={props.streams} onChange={props.onChange} />
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Component;
|
||||||
|
|
||||||
|
Component.defaultProps = {
|
||||||
|
open: false,
|
||||||
|
title: '',
|
||||||
|
streams: [],
|
||||||
|
type: '',
|
||||||
|
onClose: null,
|
||||||
|
onDone: () => {},
|
||||||
|
onHelp: null,
|
||||||
|
};
|
||||||
@ -15,6 +15,7 @@ import BoxText from '../../misc/BoxText';
|
|||||||
import EncodingSelect from '../../misc/EncodingSelect';
|
import EncodingSelect from '../../misc/EncodingSelect';
|
||||||
import PaperFooter from '../../misc/PaperFooter';
|
import PaperFooter from '../../misc/PaperFooter';
|
||||||
import ProbeModal from '../../misc/modals/Probe';
|
import ProbeModal from '../../misc/modals/Probe';
|
||||||
|
import HintModal from '../../misc/modals/Hint';
|
||||||
import SourceSelect from './SourceSelect';
|
import SourceSelect from './SourceSelect';
|
||||||
import StreamSelect from './StreamSelect';
|
import StreamSelect from './StreamSelect';
|
||||||
|
|
||||||
@ -39,10 +40,15 @@ export default function Profile(props) {
|
|||||||
status: 'none',
|
status: 'none',
|
||||||
});
|
});
|
||||||
const [$skillsRefresh, setSkillsRefresh] = React.useState(false);
|
const [$skillsRefresh, setSkillsRefresh] = React.useState(false);
|
||||||
const [$modal, setModal] = React.useState({
|
const [$probeModal, setProbeModal] = React.useState({
|
||||||
open: false,
|
open: false,
|
||||||
data: '',
|
data: '',
|
||||||
});
|
});
|
||||||
|
const [$hintModal, setHintModal] = React.useState({
|
||||||
|
open: false,
|
||||||
|
type: '',
|
||||||
|
streams: [],
|
||||||
|
});
|
||||||
const [$activeStep, setActiveStep] = React.useState(props.startWith === 'audio' ? 1 : 0);
|
const [$activeStep, setActiveStep] = React.useState(props.startWith === 'audio' ? 1 : 0);
|
||||||
const [$ready, setReady] = React.useState(false);
|
const [$ready, setReady] = React.useState(false);
|
||||||
|
|
||||||
@ -106,6 +112,12 @@ export default function Profile(props) {
|
|||||||
|
|
||||||
const res = await props.onProbe(inputs);
|
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);
|
let status = M.analyzeStreams(type, res.streams);
|
||||||
|
|
||||||
if (type === 'video') {
|
if (type === 'video') {
|
||||||
@ -194,8 +206,6 @@ export default function Profile(props) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return status === 'success';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRefresh = async () => {
|
const handleRefresh = async () => {
|
||||||
@ -242,24 +252,24 @@ export default function Profile(props) {
|
|||||||
props.onAbort();
|
props.onAbort();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleModal = (type) => (event) => {
|
const handleProbeLogModal = (type) => (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (type === 'video') {
|
if (type === 'video') {
|
||||||
setModal({
|
setProbeModal({
|
||||||
...$modal,
|
...$probeModal,
|
||||||
open: true,
|
open: true,
|
||||||
data: $videoProbe.log.join('\n'),
|
data: $videoProbe.log.join('\n'),
|
||||||
});
|
});
|
||||||
} else if (type === 'audio') {
|
} else if (type === 'audio') {
|
||||||
setModal({
|
setProbeModal({
|
||||||
...$modal,
|
...$probeModal,
|
||||||
open: true,
|
open: true,
|
||||||
data: $audioProbe.log.join('\n'),
|
data: $audioProbe.log.join('\n'),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setModal({
|
setProbeModal({
|
||||||
...$modal,
|
...$probeModal,
|
||||||
open: false,
|
open: false,
|
||||||
data: '',
|
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) {
|
if ($ready === false) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -383,12 +491,21 @@ export default function Profile(props) {
|
|||||||
<Typography>
|
<Typography>
|
||||||
<Trans>
|
<Trans>
|
||||||
Failed to probe the source. Please check the{' '}
|
Failed to probe the source. Please check the{' '}
|
||||||
<Link color="textSecondary" href="#!" onClick={handleModal('video')}>
|
<Link color="textSecondary" href="#!" onClick={handleProbeLogModal('video')}>
|
||||||
probe details
|
probe details
|
||||||
</Link>
|
</Link>
|
||||||
.
|
.
|
||||||
</Trans>
|
</Trans>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Typography>
|
||||||
|
<Trans>
|
||||||
|
In order to proceed anyways, you can provide{' '}
|
||||||
|
<Link color="textSecondary" href="#!" onClick={handleHintModal('video', [])}>
|
||||||
|
hints
|
||||||
|
</Link>{' '}
|
||||||
|
about the available streams.
|
||||||
|
</Trans>
|
||||||
|
</Typography>
|
||||||
</BoxText>
|
</BoxText>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
@ -399,7 +516,7 @@ export default function Profile(props) {
|
|||||||
<Typography>
|
<Typography>
|
||||||
<Trans>
|
<Trans>
|
||||||
The source doesn't provide any video streams. Please check the{' '}
|
The source doesn't provide any video streams. Please check the{' '}
|
||||||
<Link href="#!" onClick={handleModal('video')}>
|
<Link href="#!" onClick={handleProbeLogModal('video')}>
|
||||||
probe details
|
probe details
|
||||||
</Link>
|
</Link>
|
||||||
.
|
.
|
||||||
@ -421,7 +538,7 @@ export default function Profile(props) {
|
|||||||
<Grid item xs={12} align="right">
|
<Grid item xs={12} align="right">
|
||||||
<Typography>
|
<Typography>
|
||||||
<Trans>
|
<Trans>
|
||||||
<Link href="#!" onClick={handleModal('video')}>
|
<Link href="#!" onClick={handleProbeLogModal('video')}>
|
||||||
Show probe details
|
Show probe details
|
||||||
</Link>
|
</Link>
|
||||||
</Trans>
|
</Trans>
|
||||||
@ -533,7 +650,7 @@ export default function Profile(props) {
|
|||||||
<Typography>
|
<Typography>
|
||||||
<Trans>
|
<Trans>
|
||||||
Failed to probe the source. Please check the{' '}
|
Failed to probe the source. Please check the{' '}
|
||||||
<Link href="#!" onClick={handleModal('audio')}>
|
<Link href="#!" onClick={handleProbeLogModal('audio')}>
|
||||||
probe details
|
probe details
|
||||||
</Link>
|
</Link>
|
||||||
.
|
.
|
||||||
@ -549,7 +666,7 @@ export default function Profile(props) {
|
|||||||
<Typography>
|
<Typography>
|
||||||
<Trans>
|
<Trans>
|
||||||
The source doesn't provide any audio streams. Please check the{' '}
|
The source doesn't provide any audio streams. Please check the{' '}
|
||||||
<Link href="#!" onClick={handleModal('audio')}>
|
<Link href="#!" onClick={handleProbeLogModal('audio')}>
|
||||||
probe details
|
probe details
|
||||||
</Link>
|
</Link>
|
||||||
.
|
.
|
||||||
@ -571,7 +688,7 @@ export default function Profile(props) {
|
|||||||
<Grid item xs={12} align="right">
|
<Grid item xs={12} align="right">
|
||||||
<Typography>
|
<Typography>
|
||||||
<Trans>
|
<Trans>
|
||||||
<Link href="#!" onClick={handleModal('audio')}>
|
<Link href="#!" onClick={handleProbeLogModal('audio')}>
|
||||||
Show probe details
|
Show probe details
|
||||||
</Link>
|
</Link>
|
||||||
</Trans>
|
</Trans>
|
||||||
@ -629,7 +746,16 @@ export default function Profile(props) {
|
|||||||
<Backdrop open={$videoProbe.probing || $audioProbe.probing || $skillsRefresh}>
|
<Backdrop open={$videoProbe.probing || $audioProbe.probing || $skillsRefresh}>
|
||||||
<CircularProgress color="inherit" />
|
<CircularProgress color="inherit" />
|
||||||
</Backdrop>
|
</Backdrop>
|
||||||
<ProbeModal open={$modal.open} onClose={handleModal('none')} data={$modal.data} />
|
<ProbeModal open={$probeModal.open} onClose={handleProbeLogModal('none')} data={$probeModal.data} />
|
||||||
|
<HintModal
|
||||||
|
open={$hintModal.open}
|
||||||
|
onClose={handleHintCancel}
|
||||||
|
onChange={handleHintChange}
|
||||||
|
onDone={handleHintDone}
|
||||||
|
title="Stream hints"
|
||||||
|
type={$hintModal.type}
|
||||||
|
streams={$hintModal.streams}
|
||||||
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user