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 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) {
|
||||
<Typography>
|
||||
<Trans>
|
||||
Failed to probe the source. Please check the{' '}
|
||||
<Link color="textSecondary" href="#!" onClick={handleModal('video')}>
|
||||
<Link color="textSecondary" href="#!" onClick={handleProbeLogModal('video')}>
|
||||
probe details
|
||||
</Link>
|
||||
.
|
||||
</Trans>
|
||||
</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>
|
||||
</Grid>
|
||||
)}
|
||||
@ -399,7 +516,7 @@ export default function Profile(props) {
|
||||
<Typography>
|
||||
<Trans>
|
||||
The source doesn't provide any video streams. Please check the{' '}
|
||||
<Link href="#!" onClick={handleModal('video')}>
|
||||
<Link href="#!" onClick={handleProbeLogModal('video')}>
|
||||
probe details
|
||||
</Link>
|
||||
.
|
||||
@ -421,7 +538,7 @@ export default function Profile(props) {
|
||||
<Grid item xs={12} align="right">
|
||||
<Typography>
|
||||
<Trans>
|
||||
<Link href="#!" onClick={handleModal('video')}>
|
||||
<Link href="#!" onClick={handleProbeLogModal('video')}>
|
||||
Show probe details
|
||||
</Link>
|
||||
</Trans>
|
||||
@ -533,7 +650,7 @@ export default function Profile(props) {
|
||||
<Typography>
|
||||
<Trans>
|
||||
Failed to probe the source. Please check the{' '}
|
||||
<Link href="#!" onClick={handleModal('audio')}>
|
||||
<Link href="#!" onClick={handleProbeLogModal('audio')}>
|
||||
probe details
|
||||
</Link>
|
||||
.
|
||||
@ -549,7 +666,7 @@ export default function Profile(props) {
|
||||
<Typography>
|
||||
<Trans>
|
||||
The source doesn't provide any audio streams. Please check the{' '}
|
||||
<Link href="#!" onClick={handleModal('audio')}>
|
||||
<Link href="#!" onClick={handleProbeLogModal('audio')}>
|
||||
probe details
|
||||
</Link>
|
||||
.
|
||||
@ -571,7 +688,7 @@ export default function Profile(props) {
|
||||
<Grid item xs={12} align="right">
|
||||
<Typography>
|
||||
<Trans>
|
||||
<Link href="#!" onClick={handleModal('audio')}>
|
||||
<Link href="#!" onClick={handleProbeLogModal('audio')}>
|
||||
Show probe details
|
||||
</Link>
|
||||
</Trans>
|
||||
@ -629,7 +746,16 @@ export default function Profile(props) {
|
||||
<Backdrop open={$videoProbe.probing || $audioProbe.probing || $skillsRefresh}>
|
||||
<CircularProgress color="inherit" />
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user