Fix wrong call to encoder defaults (datarhei/restreamer#467)
This commit is contained in:
parent
d6b19e0e0f
commit
ec4edf2f47
@ -15,6 +15,8 @@ import H from '../utils/help';
|
||||
export default function EncodingSelect(props) {
|
||||
const { i18n } = useLingui();
|
||||
|
||||
console.log(props.streams);
|
||||
|
||||
const profile = props.profile;
|
||||
let availableEncoders = [];
|
||||
let availableDecoders = [];
|
||||
@ -28,6 +30,7 @@ export default function EncodingSelect(props) {
|
||||
|
||||
const handleDecoderChange = (event) => {
|
||||
const decoder = profile.decoder;
|
||||
const stream = props.streams[profile.stream];
|
||||
decoder.coder = event.target.value;
|
||||
|
||||
// If the coder changes, use the coder's default settings
|
||||
@ -39,7 +42,7 @@ export default function EncodingSelect(props) {
|
||||
}
|
||||
|
||||
if (c !== null) {
|
||||
const defaults = c.defaults(props.skills);
|
||||
const defaults = c.defaults(stream, props.skills);
|
||||
decoder.settings = defaults.settings;
|
||||
decoder.mapping = defaults.mapping;
|
||||
}
|
||||
@ -58,6 +61,7 @@ export default function EncodingSelect(props) {
|
||||
|
||||
const handleEncoderChange = (event) => {
|
||||
const encoder = profile.encoder;
|
||||
const stream = props.streams[profile.stream];
|
||||
encoder.coder = event.target.value;
|
||||
|
||||
// If the coder changes, use the coder's default settings
|
||||
@ -69,7 +73,7 @@ export default function EncodingSelect(props) {
|
||||
}
|
||||
|
||||
if (c !== null) {
|
||||
const defaults = c.defaults(props.skills);
|
||||
const defaults = c.defaults(stream, props.skills);
|
||||
encoder.settings = defaults.settings;
|
||||
encoder.mapping = defaults.mapping;
|
||||
}
|
||||
@ -198,6 +202,8 @@ export default function EncodingSelect(props) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: in case there's no decoder for a codec it should be mentioned.
|
||||
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: [],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = [];
|
||||
const type = 'audio';
|
||||
const hwaccel = false;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: [],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = [];
|
||||
const type = 'video';
|
||||
const hwaccel = false;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'h264_cuvid'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -53,12 +53,12 @@ const codecs = ['h264'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'h264_mmal'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = ['h264'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'hevc_cuvid'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = ['hevc'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'mjpeg_cuvid'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = ['mjpeg'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'mpeg1_cuvid'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = ['mpeg1'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'mpeg2_cuvid'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = ['mpeg2'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'mpeg2_mmal'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = ['mpeg2'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'mpeg4_cuvid'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = ['mpeg4'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'mpeg4_mmal'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = ['mpeg4'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-hwaccel', 'cuda', '-hwaccel_output_format', 'cuda'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -53,12 +53,12 @@ const codecs = ['h264', 'hevc', 'mpeg1', 'mpeg2', 'mpeg4', 'vp8', 'vp9', 'vc1'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'vc1_cuvid'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = ['vc1'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'vc1_mmal'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = ['vc1'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'vp8_cuvid'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = ['vp8'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'vp9_cuvid'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = ['vp9'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-hwaccel', 'videotoolbox'],
|
||||
@ -27,7 +27,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -51,12 +51,12 @@ const codecs = ['h264'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings, stream) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const local = ['-codec:a', 'aac', '-b:a', `${settings.bitrate}k`, '-shortest'];
|
||||
|
||||
if (stream.codec === 'aac') {
|
||||
@ -30,7 +30,6 @@ function createMapping(settings, stream) {
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = props.stream;
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -39,7 +38,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -84,12 +83,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s`;
|
||||
}
|
||||
|
||||
function defaults(stream) {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings, stream),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings, stream) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const local = ['-codec:a', 'aac_at', '-b:a', `${settings.bitrate}k`, '-shortest'];
|
||||
|
||||
if (stream.codec === 'aac') {
|
||||
@ -30,7 +30,6 @@ function createMapping(settings, stream) {
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = props.stream;
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -39,7 +38,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -84,12 +83,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s`;
|
||||
}
|
||||
|
||||
function defaults(stream) {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings, stream),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
function createMapping(settings, stream) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const local = ['-codec:a', 'copy'];
|
||||
|
||||
//if (stream.codec === 'aac') {
|
||||
@ -17,7 +17,6 @@ function createMapping(settings, stream) {
|
||||
|
||||
function Coder(props) {
|
||||
const settings = {};
|
||||
const stream = props.stream;
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -26,7 +25,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -54,12 +53,12 @@ function summarize(settings) {
|
||||
return `${name}`;
|
||||
}
|
||||
|
||||
function defaults(stream) {
|
||||
function defaults(stream, skills) {
|
||||
const settings = {};
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings, stream),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings, stream) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const local = ['-codec:a', 'libopus', '-b:a', `${settings.bitrate}k`, '-shortest'];
|
||||
|
||||
const mapping = {
|
||||
@ -26,7 +26,6 @@ function createMapping(settings, stream) {
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = props.stream;
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +34,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -80,12 +79,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s`;
|
||||
}
|
||||
|
||||
function defaults(stream) {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings, stream),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings, stream) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const local = ['-codec:a', 'libvorbis', '-b:a', `${settings.bitrate}k`, '-shortest'];
|
||||
|
||||
const mapping = {
|
||||
@ -26,7 +26,6 @@ function createMapping(settings, stream) {
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = props.stream;
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +34,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -80,12 +79,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s`;
|
||||
}
|
||||
|
||||
function defaults(stream) {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings, stream),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings, stream) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
// '-qscale:a', '6'
|
||||
const local = ['-codec:a', 'libmp3lame', '-b:a', `${settings.bitrate}k`, '-shortest'];
|
||||
|
||||
@ -27,7 +27,6 @@ function createMapping(settings, stream) {
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = props.stream;
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -36,7 +35,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -81,12 +80,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s`;
|
||||
}
|
||||
|
||||
function defaults(stream) {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings, stream),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
function createMapping(settings, stream) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const local = ['-an'];
|
||||
|
||||
const mapping = {
|
||||
@ -13,7 +13,6 @@ function createMapping(settings, stream) {
|
||||
|
||||
function Coder(props) {
|
||||
const settings = {};
|
||||
const stream = props.stream;
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -22,7 +21,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -50,12 +49,12 @@ function summarize(settings) {
|
||||
return `${name}`;
|
||||
}
|
||||
|
||||
function defaults(stream) {
|
||||
function defaults(stream, skills) {
|
||||
const settings = {};
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings, stream),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings, stream) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
let sampling = settings.sampling;
|
||||
let layout = settings.layout;
|
||||
|
||||
@ -90,7 +90,6 @@ Delay.defaultProps = {
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = props.stream;
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -99,7 +98,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -147,12 +146,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s`;
|
||||
}
|
||||
|
||||
function defaults(stream) {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings, stream),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings, stream) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const local = ['-codec:a', 'vorbis', '-b:a', `${settings.bitrate}k`, '-qscale:a', '3', '-shortest'];
|
||||
|
||||
const mapping = {
|
||||
@ -26,7 +26,6 @@ function createMapping(settings, stream) {
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = props.stream;
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +34,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -80,12 +79,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s`;
|
||||
}
|
||||
|
||||
function defaults(stream) {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings, stream),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const local = ['-codec:v', 'copy'];
|
||||
|
||||
const mapping = {
|
||||
@ -21,7 +21,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -43,10 +43,10 @@ function summarize(settings) {
|
||||
return `${name}`;
|
||||
}
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
return {
|
||||
settings: {},
|
||||
mapping: createMapping({}),
|
||||
mapping: createMapping({}, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const local = [
|
||||
'-codec:v',
|
||||
'h264_nvenc',
|
||||
@ -171,7 +171,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -232,12 +232,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s, ${settings.fps} FPS, Profile: ${settings.profile}`;
|
||||
}
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const local = [
|
||||
'-codec:v',
|
||||
'h264_omx',
|
||||
@ -58,7 +58,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -110,12 +110,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s, ${settings.fps} FPS, Profile: ${settings.profile}`;
|
||||
}
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -75,7 +75,7 @@ Codec Controls
|
||||
4: High
|
||||
*/
|
||||
|
||||
function createMapping(settings, skills) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
let ffversion = 4;
|
||||
if (SemverSatisfies(skills.ffmpeg.version, '^5.0.0')) {
|
||||
ffversion = 5;
|
||||
@ -143,7 +143,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, props.skills), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -216,12 +216,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s, ${settings.fps} FPS, Profile: ${settings.profile}`;
|
||||
}
|
||||
|
||||
function defaults(skills) {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings, skills),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const global = [];
|
||||
const local = [];
|
||||
|
||||
@ -105,7 +105,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -163,12 +163,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s, ${settings.fps} FPS, Profile: ${settings.profile}`;
|
||||
}
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const local = [
|
||||
'-codec:v',
|
||||
'h264_videotoolbox',
|
||||
@ -84,7 +84,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -139,12 +139,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s, ${settings.fps} FPS, Profile: ${settings.profile}`;
|
||||
}
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const global = [];
|
||||
const local = [];
|
||||
|
||||
@ -105,7 +105,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -163,12 +163,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s, ${settings.fps} FPS, Profile: ${settings.profile}`;
|
||||
}
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
function createMapping(settings, stream) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const local = ['-vn'];
|
||||
|
||||
const mapping = {
|
||||
@ -13,7 +13,6 @@ function createMapping(settings, stream) {
|
||||
|
||||
function Coder(props) {
|
||||
const settings = {};
|
||||
const stream = props.stream;
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -22,7 +21,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -50,12 +49,12 @@ function summarize(settings) {
|
||||
return `${name}`;
|
||||
}
|
||||
|
||||
function defaults(stream) {
|
||||
function defaults(stream, skills) {
|
||||
const settings = {};
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings, stream),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const local = ['-codec:v', 'rawvideo'];
|
||||
|
||||
const mapping = {
|
||||
@ -21,7 +21,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -43,10 +43,10 @@ function summarize(settings) {
|
||||
return `${name}`;
|
||||
}
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
return {
|
||||
settings: {},
|
||||
mapping: createMapping({}),
|
||||
mapping: createMapping({}, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings, skills) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
let ffversion = 4;
|
||||
if (SemverSatisfies(skills.ffmpeg.version, '^5.0.0')) {
|
||||
ffversion = 5;
|
||||
@ -75,7 +75,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, props.skills), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -129,12 +129,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s, ${settings.fps} FPS, Preset: ${settings.preset}, Profile: ${settings.profile}`;
|
||||
}
|
||||
|
||||
function defaults(skills) {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings, skills),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
const global = [];
|
||||
const local = [];
|
||||
|
||||
@ -105,7 +105,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -163,12 +163,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s, ${settings.fps} FPS, Profile: ${settings.profile}`;
|
||||
}
|
||||
|
||||
function defaults() {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings, skills) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
let ffversion = 4;
|
||||
if (SemverSatisfies(skills.ffmpeg.version, '^5.0.0')) {
|
||||
ffversion = 5;
|
||||
@ -132,7 +132,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, props.skills), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -195,12 +195,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s, ${settings.fps} FPS, Preset: ${settings.preset}, Profile: ${settings.profile}`;
|
||||
}
|
||||
|
||||
function defaults(skills) {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings, skills),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ function init(initialState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
function createMapping(settings, skills) {
|
||||
function createMapping(settings, stream, skills) {
|
||||
let ffversion = 4;
|
||||
if (SemverSatisfies(skills.ffmpeg.version, '^5.0.0')) {
|
||||
ffversion = 5;
|
||||
@ -132,7 +132,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, props.skills), automatic);
|
||||
props.onChange(newSettings, createMapping(newSettings, props.stream, props.skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
@ -195,12 +195,12 @@ function summarize(settings) {
|
||||
return `${name}, ${settings.bitrate} kbit/s, ${settings.fps} FPS, Preset: ${settings.preset}, Profile: ${settings.profile}`;
|
||||
}
|
||||
|
||||
function defaults(skills) {
|
||||
function defaults(stream, skills) {
|
||||
const settings = init({});
|
||||
|
||||
return {
|
||||
settings: settings,
|
||||
mapping: createMapping(settings, skills),
|
||||
mapping: createMapping(settings, stream, skills),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ const topics = {
|
||||
en: 'https://docs.datarhei.com/restreamer/knowledge-base/manual/edit-livestream/license',
|
||||
de: 'https://docs.datarhei.com/restreamer/v/de/wissensdatenbank/user-guides/streameinstellungen/lizenz',
|
||||
},
|
||||
'login': {
|
||||
login: {
|
||||
en: 'https://docs.datarhei.com/restreamer/knowledge-base/manual/login',
|
||||
de: 'https://docs.datarhei.com/restreamer/v/de/wissensdatenbank/user-guides/dashboard',
|
||||
},
|
||||
'main': {
|
||||
main: {
|
||||
en: 'https://docs.datarhei.com/restreamer/knowledge-base/manual/main-screen',
|
||||
de: 'https://docs.datarhei.com/restreamer/v/de/wissensdatenbank/user-guides/hauptbildschirm',
|
||||
},
|
||||
@ -73,7 +73,7 @@ const topics = {
|
||||
en: 'https://docs.datarhei.com/restreamer/knowledge-base/manual/process-report',
|
||||
de: 'https://docs.datarhei.com/restreamer/v/de/wissensdatenbank/user-guides/prozess-details',
|
||||
},
|
||||
'publication': {
|
||||
publication: {
|
||||
en: 'https://docs.datarhei.com/restreamer/knowledge-base/manual/publications',
|
||||
de: 'https://docs.datarhei.com/restreamer/v/de/wissensdatenbank/user-guides/publication-services',
|
||||
},
|
||||
|
||||
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles';
|
||||
import { HashRouter as DOMRouter } from 'react-router-dom';
|
||||
import { MemoryRouter, Route, Routes } from 'react-router-dom';
|
||||
import theme from '../theme';
|
||||
import I18n from '../I18n';
|
||||
|
||||
@ -30,21 +30,60 @@ More about "render" and "screen" on https://testing-library.com/docs
|
||||
|
||||
For testing certain conditions, check out https://jestjs.io/docs/expect and for
|
||||
running tests, check out https://jestjs.io/docs/api.
|
||||
|
||||
As an alternative to fireEvent there is the userEvent which may simulate better
|
||||
a user clicking around. Example:
|
||||
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
test("radio", () => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<form>
|
||||
<label>
|
||||
First <input type="radio" name="radio1" value="first" />
|
||||
</label>
|
||||
<label>
|
||||
Second <input type="radio" name="radio1" value="second" />
|
||||
</label>
|
||||
</form>
|
||||
)
|
||||
|
||||
await user.click(screen.getByLabelText("Second"));
|
||||
});
|
||||
*/
|
||||
|
||||
const AllTheProviders = ({ children }) => {
|
||||
return (
|
||||
<StyledEngineProvider injectFirst>
|
||||
<ThemeProvider theme={theme}>
|
||||
<I18n>
|
||||
<DOMRouter>{children}</DOMRouter>
|
||||
</I18n>
|
||||
</ThemeProvider>
|
||||
</StyledEngineProvider>
|
||||
);
|
||||
const NoRoute = (props) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const customRender = (ui, options) => render(ui, { wrapper: AllTheProviders, ...options });
|
||||
const AllTheProviders =
|
||||
(initialEntries, path) =>
|
||||
({ children }) => {
|
||||
if (typeof initialEntries === 'undefined') {
|
||||
initialEntries = '/';
|
||||
}
|
||||
if (typeof path === 'undefined') {
|
||||
path = '/';
|
||||
}
|
||||
return (
|
||||
<StyledEngineProvider injectFirst>
|
||||
<ThemeProvider theme={theme}>
|
||||
<I18n>
|
||||
<MemoryRouter initialEntries={[initialEntries]}>
|
||||
<Routes>
|
||||
<Route path={path} element={children} />
|
||||
<Route path="*" element={<NoRoute />} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
</I18n>
|
||||
</ThemeProvider>
|
||||
</StyledEngineProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const customRender = (ui, options, initialEntries, path) => render(ui, { wrapper: AllTheProviders(initialEntries, path), ...options });
|
||||
|
||||
// re-export everything
|
||||
export * from '@testing-library/react';
|
||||
|
||||
58
src/views/Edit/Wizard/Abort.js
Normal file
58
src/views/Edit/Wizard/Abort.js
Normal file
@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Button from '@mui/material/Button';
|
||||
|
||||
import Paper from '../../../misc/Paper';
|
||||
import PaperHeader from '../../../misc/PaperHeader';
|
||||
|
||||
export default function Abort(props) {
|
||||
return (
|
||||
<Paper xs={12} sm={8} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={3} variant="h1" title={<Trans>Abort</Trans>} onHelp={props.onHelp} />
|
||||
<Grid container spacing={3}>
|
||||
{props.nchannels <= 1 ? (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>You can't abort the wizard because at least one input must be defined.</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Button variant="outlined" color="primary" fullWidth onClick={props.onBack}>
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Are you sure you want to abort the wizard?</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Button variant="outlined" color="default" fullWidth onClick={props.onNext}>
|
||||
<Trans>Yes</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Button variant="outlined" color="primary" fullWidth onClick={props.onBack}>
|
||||
<Trans>No</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
Abort.defaultProps = {
|
||||
onHelp: () => {},
|
||||
onBack: () => {},
|
||||
onNext: () => {},
|
||||
nchannels: 0,
|
||||
};
|
||||
136
src/views/Edit/Wizard/Audio.js
Normal file
136
src/views/Edit/Wizard/Audio.js
Normal file
@ -0,0 +1,136 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import Button from '@mui/material/Button';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import Radio from '@mui/material/Radio';
|
||||
import RadioGroup from '@mui/material/RadioGroup';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
|
||||
import BoxText from '../../../misc/BoxText';
|
||||
|
||||
import Paper from '../../../misc/Paper';
|
||||
import PaperHeader from '../../../misc/PaperHeader';
|
||||
import Select from '../../../misc/Select';
|
||||
|
||||
export default function Audio(props) {
|
||||
const { i18n } = useLingui();
|
||||
|
||||
return (
|
||||
<Paper xs={12} sm={9} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" title={<Trans>Audio setup</Trans>} onAbort={props.onAbort} onHelp={props.onHelp} />
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{props.status === 'error' && (
|
||||
<BoxText color="dark">
|
||||
<WarningIcon fontSize="large" color="error" />
|
||||
<Typography textAlign="center">
|
||||
<Trans>Failed to verify the source. Please check the address.</Trans>
|
||||
</Typography>
|
||||
</BoxText>
|
||||
)}
|
||||
{props.status === 'nostream' && (
|
||||
<BoxText color="dark">
|
||||
<WarningIcon fontSize="large" color="error" />
|
||||
<Typography textAlign="center">
|
||||
<Trans>The source doesn't provide any audio streams.</Trans>
|
||||
</Typography>
|
||||
</BoxText>
|
||||
)}
|
||||
{props.status === 'nocoder' && (
|
||||
<BoxText color="dark">
|
||||
<WarningIcon fontSize="large" color="error" />
|
||||
<Typography textAlign="center">
|
||||
<Trans>The source doesn't provide any compatible audio streams.</Trans>
|
||||
</Typography>
|
||||
</BoxText>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<RadioGroup row value={props.source} onChange={props.onSource}>
|
||||
<Grid container spacing={2}>
|
||||
{props.streamList.length === 0 && (
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>
|
||||
The video source doesn't provide any compatible audio stream. <strong>Silence audio</strong> is recommended.
|
||||
Services e.g. YouTube, Facebook & Co. require an audio channel.
|
||||
</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
{props.streamList.length !== 0 && (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel value="video" control={<Radio />} label={i18n._(t`Audio from device`)} />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Select label={<Trans>Stream</Trans>} value={props.stream} onChange={props.onAudioStreamChange}>
|
||||
{props.streamList}
|
||||
</Select>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{props.deviceList.length !== 0 && (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel value="alsa" control={<Radio />} label={i18n._(t`Audio from device`)} />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Select label={<Trans>Device</Trans>} value={props.address} onChange={props.onAudioDeviceChange}>
|
||||
{props.deviceList}
|
||||
</Select>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<div>
|
||||
<FormControlLabel value="silence" control={<Radio />} label={i18n._(t`Silence Audio`)} />
|
||||
</div>
|
||||
<div>
|
||||
<FormControlLabel value="none" control={<Radio />} label={i18n._(t`No audio`)} />
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</RadioGroup>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Button variant="outlined" color="default" fullWidth onClick={props.onBack}>
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<Button variant="outlined" fullWidth color="primary" onClick={props.onNext}>
|
||||
<Trans>Next</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
Audio.defaultProps = {
|
||||
onAbort: () => {},
|
||||
onHelp: () => {},
|
||||
onBack: () => {},
|
||||
onNext: () => {},
|
||||
onSource: () => {},
|
||||
source: '',
|
||||
onAudioStreamChange: () => {},
|
||||
onAudioDeviceChange: () => {},
|
||||
streamList: [],
|
||||
deviceList: [],
|
||||
status: '',
|
||||
stream: 0,
|
||||
address: {},
|
||||
};
|
||||
38
src/views/Edit/Wizard/Error.js
Normal file
38
src/views/Edit/Wizard/Error.js
Normal file
@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Button from '@mui/material/Button';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
|
||||
import BoxText from '../../../misc/BoxText';
|
||||
import Paper from '../../../misc/Paper';
|
||||
import PaperHeader from '../../../misc/PaperHeader';
|
||||
|
||||
export default function Error(props) {
|
||||
return (
|
||||
<Paper xs={12} sm={8} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={3} variant="h1" title={<Trans>Error</Trans>} onAbort={props.onAbort} onHelp={props.onHelp} />
|
||||
<Grid container spacing={3}>
|
||||
<BoxText color="dark">
|
||||
<WarningIcon fontSize="large" color="error" />
|
||||
<Typography textAlign="center">
|
||||
<Trans>There was an error setting up the stream.</Trans>
|
||||
</Typography>
|
||||
</BoxText>
|
||||
<Grid item xs={12}>
|
||||
<Button variant="outlined" fullWidth color="primary" onClick={props.onNext}>
|
||||
<Trans>Retry</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
Error.defaultProps = {
|
||||
onAbort: () => {},
|
||||
onHelp: () => {},
|
||||
onNext: () => {},
|
||||
};
|
||||
58
src/views/Edit/Wizard/License.js
Normal file
58
src/views/Edit/Wizard/License.js
Normal file
@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import Button from '@mui/material/Button';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import LicenseControl from '../../../misc/controls/License';
|
||||
|
||||
import Paper from '../../../misc/Paper';
|
||||
import PaperHeader from '../../../misc/PaperHeader';
|
||||
|
||||
export default function License(props) {
|
||||
return (
|
||||
<Paper xs={12} sm={9} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" title={<Trans>License</Trans>} onAbort={props.onAbort} onHelp={props.onHelp} />
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>
|
||||
Use your copyright and choose the correct image license. Whether free for all or highly restricted. Briefly discuss what others are
|
||||
allowed to do with your image.
|
||||
</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<LicenseControl license={props.license} onChange={props.onChange} />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Button variant="outlined" color="default" fullWidth onClick={props.onBack}>
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<Button variant="outlined" color="primary" fullWidth onClick={props.onNext}>
|
||||
<Trans>Save</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
License.defaultProps = {
|
||||
onAbort: () => {},
|
||||
onHelp: () => {},
|
||||
onBack: () => {},
|
||||
onNext: () => {},
|
||||
onChange: (license) => {},
|
||||
license: '',
|
||||
};
|
||||
55
src/views/Edit/Wizard/Metadata.js
Normal file
55
src/views/Edit/Wizard/Metadata.js
Normal file
@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import Button from '@mui/material/Button';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import MetadataControl from '../../../misc/controls/Metadata';
|
||||
|
||||
import Paper from '../../../misc/Paper';
|
||||
import PaperHeader from '../../../misc/PaperHeader';
|
||||
|
||||
export default function Metadata(props) {
|
||||
return (
|
||||
<Paper xs={12} sm={9} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" title={<Trans>Metadata</Trans>} onAbort={props.onAbort} onHelp={props.onHelp} />
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Briefly describe what the audience will see during the live stream.</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<MetadataControl settings={props.metadata} onChange={props.onChange} />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Button variant="outlined" color="default" fullWidth onClick={props.onBack}>
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<Button variant="outlined" fullWidth color="primary" onClick={props.onNext}>
|
||||
<Trans>Next</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
Metadata.defaultProps = {
|
||||
onAbort: () => {},
|
||||
onHelp: () => {},
|
||||
onBack: () => {},
|
||||
onNext: () => {},
|
||||
onChange: (metadata) => {},
|
||||
metadata: {},
|
||||
};
|
||||
31
src/views/Edit/Wizard/Probe.js
Normal file
31
src/views/Edit/Wizard/Probe.js
Normal file
@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import Paper from '../../../misc/Paper';
|
||||
import PaperHeader from '../../../misc/PaperHeader';
|
||||
|
||||
export default function Probe(props) {
|
||||
return (
|
||||
<Paper xs={12} md={5} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" onAbort={props.onAbort} />
|
||||
<Grid container justifyContent="center" spacing={2} align="center">
|
||||
<Grid item xs={12}>
|
||||
<CircularProgress color="inherit" />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography textAlign="center">
|
||||
<Trans>Please wait. Probe stream data ...</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
Probe.defaultProps = {
|
||||
onAbort: () => {},
|
||||
};
|
||||
31
src/views/Edit/Wizard/Saving.js
Normal file
31
src/views/Edit/Wizard/Saving.js
Normal file
@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import Paper from '../../../misc/Paper';
|
||||
import PaperHeader from '../../../misc/PaperHeader';
|
||||
|
||||
export default function Saving(props) {
|
||||
return (
|
||||
<Paper xs={12} md={5} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" onAbort={props.onAbort} />
|
||||
<Grid container justifyContent="center" spacing={2} align="center">
|
||||
<Grid item xs={12}>
|
||||
<CircularProgress color="inherit" />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Please wait. Setting up the stream ...</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
Saving.defaultProps = {
|
||||
onAbort: () => {},
|
||||
};
|
||||
51
src/views/Edit/Wizard/Source.js
Normal file
51
src/views/Edit/Wizard/Source.js
Normal file
@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import Button from '@mui/material/Button';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import Paper from '../../../misc/Paper';
|
||||
import PaperHeader from '../../../misc/PaperHeader';
|
||||
|
||||
export default function Source(props) {
|
||||
return (
|
||||
<Paper xs={12} sm={9} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" title={<Trans>Video setup</Trans>} onAbort={props.Abort} onHelp={props.onHelp} />
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>
|
||||
Select whether you pull the stream from a <strong>network source</strong> (such as a network camera) or the{' '}
|
||||
<strong>internal RTMP server</strong> (e.g., OBS streams to the Restreamer).
|
||||
</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={2}>
|
||||
{props.sources}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Button variant="outlined" fullWidth color="default" onClick={props.onAdvanced}>
|
||||
<Trans>Advanced setup</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
Source.defaultProps = {
|
||||
onAbort: () => {},
|
||||
onHelp: () => {},
|
||||
onAdvanced: () => {},
|
||||
sources: [],
|
||||
};
|
||||
87
src/views/Edit/Wizard/Video.js
Normal file
87
src/views/Edit/Wizard/Video.js
Normal file
@ -0,0 +1,87 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import Button from '@mui/material/Button';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Link from '@mui/material/Link';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
|
||||
import BoxText from '../../../misc/BoxText';
|
||||
|
||||
import Paper from '../../../misc/Paper';
|
||||
import PaperHeader from '../../../misc/PaperHeader';
|
||||
|
||||
export default function Video(props) {
|
||||
return (
|
||||
<Paper xs={12} sm={9} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" title={<Trans>Video setup</Trans>} onAbort={props.onAbort} onHelp={props.onHelp} />
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
{props.children}
|
||||
<Grid item xs={12}>
|
||||
{props.status === 'error' && (
|
||||
<BoxText color="dark">
|
||||
<WarningIcon fontSize="large" color="error" />
|
||||
<Typography textAlign="center">
|
||||
{props.sourceid === 'rtmp' || props.sourceid === 'hls' ? (
|
||||
<Trans>No live stream was detected. Please check the software that sends the stream.</Trans>
|
||||
) : (
|
||||
<Trans>Failed to verify the source. Please check the address.</Trans>
|
||||
)}
|
||||
</Typography>
|
||||
</BoxText>
|
||||
)}
|
||||
{props.status === 'nostream' && (
|
||||
<BoxText color="dark">
|
||||
<WarningIcon fontSize="large" color="error" />
|
||||
<Typography textAlign="center">
|
||||
<Trans>The source doesn't provide any video streams. Please check the device.</Trans>
|
||||
</Typography>
|
||||
</BoxText>
|
||||
)}
|
||||
{props.status === 'nocoder' && (
|
||||
<BoxText color="dark">
|
||||
<WarningIcon fontSize="large" color="error" />
|
||||
<Typography textAlign="center">
|
||||
<Trans>
|
||||
The source doesn't provide any compatible video streams. Please check the{' '}
|
||||
<Link color="secondary" target="_blank" href="https://github.com/datarhei/restreamer">
|
||||
requirements
|
||||
</Link>
|
||||
.
|
||||
</Trans>
|
||||
</Typography>
|
||||
</BoxText>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Button variant="outlined" color="default" fullWidth onClick={props.onBack}>
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<Button variant="outlined" fullWidth color="primary" disabled={!props.ready} onClick={props.onNext}>
|
||||
<Trans>Next</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
Video.defaultProps = {
|
||||
onAbort: () => {},
|
||||
onHelp: () => {},
|
||||
onBack: () => {},
|
||||
onNext: () => {},
|
||||
sourceid: '',
|
||||
status: '',
|
||||
ready: false,
|
||||
};
|
||||
101
src/views/Edit/Wizard/VideoProfile.js
Normal file
101
src/views/Edit/Wizard/VideoProfile.js
Normal file
@ -0,0 +1,101 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import Button from '@mui/material/Button';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import Paper from '../../../misc/Paper';
|
||||
import PaperHeader from '../../../misc/PaperHeader';
|
||||
import Select from '../../../misc/Select';
|
||||
|
||||
export default function VideoProfile(props) {
|
||||
return (
|
||||
<Paper xs={12} sm={9} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" title={<Trans>Video setup</Trans>} onAbort={props.onAbort} onHelp={props.onHelp} />
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>The video source is compatible. Select the desired resolution:</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Select label={<Trans>Profile</Trans>} value={props.stream} onChange={props.onStreamChange}>
|
||||
{props.streamList}
|
||||
</Select>
|
||||
</Grid>
|
||||
{props.compatible === false && (
|
||||
<React.Fragment>
|
||||
{props.encodersList.length === 0 ? (
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Your stream needs to be encoded, but there's no suitable encoder available.</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Your stream needs to be encoded. Choose the desired encoder:</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
{props.decodersList.length >= 2 && (
|
||||
<Grid item xs={12}>
|
||||
<Select label={<Trans>Decoder</Trans>} value={props.decoder} onChange={props.onDecoderChange}>
|
||||
{props.decodersList}
|
||||
</Select>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<Select label={<Trans>Encoder</Trans>} value={props.encoder} onChange={props.onEncoderChange}>
|
||||
{props.encodersList}
|
||||
</Select>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Button variant="outlined" color="default" fullWidth onClick={props.onBack}>
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
color="primary"
|
||||
disabled={props.compatible === false && props.encodersList.length === 0}
|
||||
onClick={props.onNext}
|
||||
>
|
||||
<Trans>Next</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
VideoProfile.defaultProps = {
|
||||
onAbort: () => {},
|
||||
onHelp: () => {},
|
||||
onBack: () => {},
|
||||
onNext: () => {},
|
||||
compatible: false,
|
||||
onStreamChange: () => {},
|
||||
streamList: [],
|
||||
stream: 0,
|
||||
onDecoderChange: () => {},
|
||||
decodersList: [],
|
||||
decoder: '',
|
||||
onEncoderChange: () => {},
|
||||
encodersList: [],
|
||||
encoder: '',
|
||||
};
|
||||
@ -2,33 +2,32 @@ import React from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import { t } from '@lingui/macro';
|
||||
import Backdrop from '@mui/material/Backdrop';
|
||||
import Button from '@mui/material/Button';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Link from '@mui/material/Link';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import Radio from '@mui/material/Radio';
|
||||
import RadioGroup from '@mui/material/RadioGroup';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
|
||||
import * as Decoders from '../../../misc/coders/Decoders';
|
||||
import * as Encoders from '../../../misc/coders/Encoders';
|
||||
import * as M from '../../../utils/metadata';
|
||||
import BoxText from '../../../misc/BoxText';
|
||||
import FullSources from '../Sources';
|
||||
import H from '../../../utils/help';
|
||||
import LicenseControl from '../../../misc/controls/License';
|
||||
import MetadataControl from '../../../misc/controls/Metadata';
|
||||
import NotifyContext from '../../../contexts/Notify';
|
||||
import Paper from '../../../misc/Paper';
|
||||
import PaperHeader from '../../../misc/PaperHeader';
|
||||
import Sources from './Sources';
|
||||
import Select from '../../../misc/Select';
|
||||
|
||||
import Source from './Source';
|
||||
import Video from './Video';
|
||||
import VideoProfile from './VideoProfile';
|
||||
import Audio from './Audio';
|
||||
import Abort from './Abort';
|
||||
import Error from './Error';
|
||||
import Saving from './Saving';
|
||||
import Probe from './Probe';
|
||||
import License from './License';
|
||||
import Metadata from './Metadata';
|
||||
|
||||
export default function Wizard(props) {
|
||||
const { i18n } = useLingui();
|
||||
@ -281,37 +280,7 @@ export default function Wizard(props) {
|
||||
}
|
||||
|
||||
// STEP 1 - Source Type Selection
|
||||
return (
|
||||
<Paper xs={12} sm={9} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" title={<Trans>Video setup</Trans>} onAbort={handleAbort} onHelp={handleHelp('video-setup')} />
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>
|
||||
Select whether you pull the stream from a <strong>network source</strong> (such as a network camera) or the{' '}
|
||||
<strong>internal RTMP server</strong> (e.g., OBS streams to the Restreamer).
|
||||
</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={2}>
|
||||
{availableSources}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Button variant="outlined" fullWidth color="default" onClick={handleAdvanced}>
|
||||
<Trans>Advanced setup</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
return <Source onAbort={handleAbort} onHelp={handleHelp('video-setup')} onAdvanced={handleAdvanced} sources={availableSources} />;
|
||||
} else if ($step === 'VIDEO SETTINGS') {
|
||||
handleNext = async () => {
|
||||
// probing ...
|
||||
@ -372,106 +341,45 @@ export default function Wizard(props) {
|
||||
|
||||
// STEP 2 - Source Settings
|
||||
return (
|
||||
<Paper xs={12} sm={9} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" title={<Trans>Video setup</Trans>} onAbort={handleAbort} onHelp={handleHelp('video-settings')} />
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Component
|
||||
knownDevices={$skills.sources[s.type]}
|
||||
config={$config.source[s.type]}
|
||||
settings={$sources.video.settings}
|
||||
skills={$skills}
|
||||
onChange={handleChange}
|
||||
onRefresh={handleRefresh}
|
||||
/>
|
||||
<Grid item xs={12}>
|
||||
{$probe.status === 'error' && (
|
||||
<BoxText color="dark">
|
||||
<WarningIcon fontSize="large" color="error" />
|
||||
<Typography textAlign="center">
|
||||
{$sourceid === 'rtmp' || $sourceid === 'hls' ? (
|
||||
<Trans>No live stream was detected. Please check the software that sends the stream.</Trans>
|
||||
) : (
|
||||
<Trans>Failed to verify the source. Please check the address.</Trans>
|
||||
)}
|
||||
</Typography>
|
||||
</BoxText>
|
||||
)}
|
||||
{$probe.status === 'nostream' && (
|
||||
<BoxText color="dark">
|
||||
<WarningIcon fontSize="large" color="error" />
|
||||
<Typography textAlign="center">
|
||||
<Trans>The source doesn't provide any video streams. Please check the device.</Trans>
|
||||
</Typography>
|
||||
</BoxText>
|
||||
)}
|
||||
{$probe.status === 'nocoder' && (
|
||||
<BoxText color="dark">
|
||||
<WarningIcon fontSize="large" color="error" />
|
||||
<Typography textAlign="center">
|
||||
<Trans>
|
||||
The source doesn't provide any compatible video streams. Please check the{' '}
|
||||
<Link color="secondary" target="_blank" href="https://github.com/datarhei/restreamer">
|
||||
requirements
|
||||
</Link>
|
||||
.
|
||||
</Trans>
|
||||
</Typography>
|
||||
</BoxText>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Button variant="outlined" color="default" fullWidth onClick={handleBack}>
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<Button variant="outlined" fullWidth color="primary" disabled={!$sources.video.ready} onClick={handleNext}>
|
||||
<Trans>Next</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Video
|
||||
onAbort={handleAbort}
|
||||
onHelp={handleHelp('video-settings')}
|
||||
onBack={handleBack}
|
||||
onNext={handleNext}
|
||||
status={$probe.status}
|
||||
sourceid={$sourceid}
|
||||
ready={$sources.video.ready}
|
||||
>
|
||||
<Component
|
||||
knownDevices={$skills.sources[s.type]}
|
||||
config={$config.source[s.type]}
|
||||
settings={$sources.video.settings}
|
||||
skills={$skills}
|
||||
onChange={handleChange}
|
||||
onRefresh={handleRefresh}
|
||||
/>
|
||||
<Backdrop open={$skillsRefresh}>
|
||||
<CircularProgress color="inherit" />
|
||||
</Backdrop>
|
||||
</Paper>
|
||||
</Video>
|
||||
);
|
||||
}
|
||||
// STEP 3 - Source Probe
|
||||
else if ($step === 'VIDEO PROBE') {
|
||||
return (
|
||||
<Paper xs={12} md={5} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" onAbort={handleAbort} />
|
||||
<Grid container justifyContent="center" spacing={2} align="center">
|
||||
<Grid item xs={12}>
|
||||
<CircularProgress color="inherit" />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography textAlign="center">
|
||||
<Trans>Please wait. Probe stream data ...</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
return <Probe onAbort={handleAbort} />;
|
||||
} else if ($step === 'VIDEO RESULT') {
|
||||
handleNext = () => {
|
||||
const streams = $sources.video.streams;
|
||||
const videoprofile = $profile.video;
|
||||
|
||||
const encoder = Encoders.Video.Get(videoprofile.encoder.coder);
|
||||
let defaults = encoder.defaults();
|
||||
let defaults = encoder.defaults({}, $skills);
|
||||
|
||||
videoprofile.encoder.settings = defaults.settings;
|
||||
videoprofile.encoder.mapping = defaults.mapping;
|
||||
|
||||
const decoder = Decoders.Video.Get(videoprofile.decoder.coder);
|
||||
defaults = decoder.defaults();
|
||||
defaults = decoder.defaults({}, $skills);
|
||||
|
||||
videoprofile.decoder.settings = defaults.settings;
|
||||
videoprofile.decoder.mapping = defaults.mapping;
|
||||
@ -613,68 +521,22 @@ export default function Wizard(props) {
|
||||
|
||||
// STEP 4 - Video Profile Selection
|
||||
return (
|
||||
<Paper xs={12} sm={9} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" title={<Trans>Video setup</Trans>} onAbort={handleAbort} onHelp={handleHelp('video-result')} />
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>The video source is compatible. Select the desired resolution:</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Select label={<Trans>Profile</Trans>} value={$profile.video.stream} onChange={handleStreamChange}>
|
||||
{streamList}
|
||||
</Select>
|
||||
</Grid>
|
||||
{compatible === false && (
|
||||
<React.Fragment>
|
||||
{encodersList.length === 0 ? (
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Your stream needs to be encoded, but there's no suitable encoder available.</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Your stream needs to be encoded. Choose the desired encoder:</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
{decodersList.length >= 2 && (
|
||||
<Grid item xs={12}>
|
||||
<Select label={<Trans>Decoder</Trans>} value={$profile.video.decoder.coder} onChange={handleDecoderChange}>
|
||||
{decodersList}
|
||||
</Select>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<Select label={<Trans>Encoder</Trans>} value={$profile.video.encoder.coder} onChange={handleEncoderChange}>
|
||||
{encodersList}
|
||||
</Select>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Button variant="outlined" color="default" fullWidth onClick={handleBack}>
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<Button variant="outlined" fullWidth color="primary" disabled={compatible === false && encodersList.length === 0} onClick={handleNext}>
|
||||
<Trans>Next</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
<VideoProfile
|
||||
onAbort={handleAbort}
|
||||
onHelp={handleHelp('video-result')}
|
||||
onBack={handleBack}
|
||||
onNext={handleNext}
|
||||
compatible={compatible}
|
||||
stream={$profile.video.stream}
|
||||
streamList={streamList}
|
||||
onStreamChange={handleStreamChange}
|
||||
decoder={$profile.video.decoder.coder}
|
||||
decodersList={decodersList}
|
||||
onDecoderChange={handleDecoderChange}
|
||||
encoder={$profile.video.encoder.coder}
|
||||
encodersList={encodersList}
|
||||
onEncoderChange={handleEncoderChange}
|
||||
/>
|
||||
);
|
||||
} else if ($step === 'AUDIO SETTINGS') {
|
||||
handleNext = async () => {
|
||||
@ -845,118 +707,24 @@ export default function Wizard(props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper xs={12} sm={9} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" title={<Trans>Audio setup</Trans>} onAbort={handleAbort} onHelp={handleHelp('audio-settings')} />
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{$probe.status === 'error' && (
|
||||
<BoxText color="dark">
|
||||
<WarningIcon fontSize="large" color="error" />
|
||||
<Typography textAlign="center">
|
||||
<Trans>Failed to verify the source. Please check the address.</Trans>
|
||||
</Typography>
|
||||
</BoxText>
|
||||
)}
|
||||
{$probe.status === 'nostream' && (
|
||||
<BoxText color="dark">
|
||||
<WarningIcon fontSize="large" color="error" />
|
||||
<Typography textAlign="center">
|
||||
<Trans>The source doesn't provide any audio streams.</Trans>
|
||||
</Typography>
|
||||
</BoxText>
|
||||
)}
|
||||
{$probe.status === 'nocoder' && (
|
||||
<BoxText color="dark">
|
||||
<WarningIcon fontSize="large" color="error" />
|
||||
<Typography textAlign="center">
|
||||
<Trans>The source doesn't provide any compatible audio streams.</Trans>
|
||||
</Typography>
|
||||
</BoxText>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<RadioGroup row value={radioValue} onChange={handleStream}>
|
||||
<Grid container spacing={2}>
|
||||
{streamList.length === 0 && (
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>
|
||||
The video source doesn't provide any compatible audio stream. <strong>Silence audio</strong> is recommended.
|
||||
Services e.g. YouTube, Facebook & Co. require an audio channel.
|
||||
</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
{streamList.length !== 0 && (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel value="video" control={<Radio />} label={i18n._(t`Audio from device`)} />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Select label={<Trans>Stream</Trans>} value={profile.stream} onChange={handleAudioStreamChange}>
|
||||
{streamList}
|
||||
</Select>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{deviceList.length !== 0 && (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel value="alsa" control={<Radio />} label={i18n._(t`Audio from device`)} />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Select label={<Trans>Device</Trans>} value={source.settings.address} onChange={handleAudioDeviceChange}>
|
||||
{deviceList}
|
||||
</Select>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<div>
|
||||
<FormControlLabel value="silence" control={<Radio />} label={i18n._(t`Silence Audio`)} />
|
||||
</div>
|
||||
<div>
|
||||
<FormControlLabel value="none" control={<Radio />} label={i18n._(t`No audio`)} />
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</RadioGroup>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Button variant="outlined" color="default" fullWidth onClick={handleBack}>
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<Button variant="outlined" fullWidth color="primary" onClick={handleNext}>
|
||||
<Trans>Next</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
<Audio
|
||||
onAbort={handleAbort}
|
||||
onHelp={handleHelp('audio-settings')}
|
||||
onBack={handleBack}
|
||||
onNext={handleNext}
|
||||
status={$probe.status}
|
||||
source={radioValue}
|
||||
onSource={handleStream}
|
||||
streamList={streamList}
|
||||
deviceList={deviceList}
|
||||
stream={profile.stream}
|
||||
onAudioStreamChange={handleAudioStreamChange}
|
||||
address={source.settings.address}
|
||||
onAudioDeviceChange={handleAudioDeviceChange}
|
||||
/>
|
||||
);
|
||||
} else if ($step === 'AUDIO PROBE') {
|
||||
return (
|
||||
<Paper xs={12} md={5} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" onAbort={handleAbort} />
|
||||
<Grid container justifyContent="center" spacing={2} align="center">
|
||||
<Grid item xs={12}>
|
||||
<CircularProgress color="inherit" />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Please wait. Probe stream data ...</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
return <Probe onAbort={handleAbort} />;
|
||||
} else if ($step === 'AUDIO RESULT') {
|
||||
handleNext = () => {
|
||||
let stream = null;
|
||||
@ -981,7 +749,7 @@ export default function Wizard(props) {
|
||||
}
|
||||
|
||||
const encoder = Encoders.Audio.Get(profile.coder);
|
||||
const defaults = encoder.defaults(stream);
|
||||
const defaults = encoder.defaults(stream, $skills);
|
||||
|
||||
profile.encoder.settings = defaults.settings;
|
||||
profile.encoder.mapping = defaults.mapping;
|
||||
@ -1023,35 +791,14 @@ export default function Wizard(props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Paper xs={12} sm={9} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" title={<Trans>Metadata</Trans>} onAbort={handleAbort} onHelp={handleHelp('audio-result')} />
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Briefly describe what the audience will see during the live stream.</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<MetadataControl settings={$data.meta} onChange={handleMetadataChange} />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Button variant="outlined" color="default" fullWidth onClick={handleBack}>
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<Button variant="outlined" fullWidth color="primary" onClick={handleNext}>
|
||||
<Trans>Next</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
<Metadata
|
||||
onAbort={handleAbort}
|
||||
onHelp={handleHelp('audio-result')}
|
||||
onBack={handleBack}
|
||||
onNext={handleNext}
|
||||
onChange={handleMetadataChange}
|
||||
metadata={$data.meta}
|
||||
/>
|
||||
);
|
||||
} else if ($step === 'LICENSE') {
|
||||
handleNext = async () => {
|
||||
@ -1078,55 +825,17 @@ export default function Wizard(props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Paper xs={12} sm={9} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" title={<Trans>License</Trans>} onAbort={handleAbort} onHelp={handleHelp('license')} />
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>
|
||||
Use your copyright and choose the correct image license. Whether free for all or highly restricted. Briefly discuss what others
|
||||
are allowed to do with your image.
|
||||
</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<LicenseControl license={$data.license} onChange={handleLicenseChange} />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Button variant="outlined" color="default" fullWidth onClick={handleBack}>
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<Button variant="outlined" color="primary" fullWidth onClick={handleNext}>
|
||||
<Trans>Save</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
<License
|
||||
onAbort={handleAbort}
|
||||
onHelp={handleHelp('license')}
|
||||
onBack={handleBack}
|
||||
onNext={handleNext}
|
||||
onChange={handleLicenseChange}
|
||||
license={$data.license}
|
||||
/>
|
||||
);
|
||||
} else if ($step === 'SAVING') {
|
||||
return (
|
||||
<Paper xs={12} md={5} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={2} variant="h1" onAbort={handleAbort} />
|
||||
<Grid container justifyContent="center" spacing={2} align="center">
|
||||
<Grid item xs={12}>
|
||||
<CircularProgress color="inherit" />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Please wait. Setting up the stream ...</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
return <Saving onAbort={handleAbort} />;
|
||||
} else if ($step === 'DONE') {
|
||||
return null;
|
||||
} else if ($step === 'ERROR') {
|
||||
@ -1134,24 +843,7 @@ export default function Wizard(props) {
|
||||
setStep('TYPE');
|
||||
};
|
||||
|
||||
return (
|
||||
<Paper xs={12} sm={8} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={3} variant="h1" title={<Trans>Error</Trans>} onAbort={handleAbort} onHelp={handleHelp('error')} />
|
||||
<Grid container spacing={3}>
|
||||
<BoxText color="dark">
|
||||
<WarningIcon fontSize="large" color="error" />
|
||||
<Typography textAlign="center">
|
||||
<Trans>There was an error setting up the stream.</Trans>
|
||||
</Typography>
|
||||
</BoxText>
|
||||
<Grid item xs={12}>
|
||||
<Button variant="outlined" fullWidth color="primary" onClick={handleNext}>
|
||||
<Trans>Retry</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
return <Error onAbort={handleAbort} onHelp={handleHelp('error')} />;
|
||||
} else if ($step === 'ABORT') {
|
||||
const nchannels = props.restreamer.ListChannels().length;
|
||||
|
||||
@ -1169,46 +861,12 @@ export default function Wizard(props) {
|
||||
navigate(`/`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Paper xs={12} sm={8} md={6} marginBottom="6em" className="PaperM">
|
||||
<PaperHeader spacing={3} variant="h1" title={<Trans>Abort</Trans>} onHelp={handleHelp('abort')} />
|
||||
<Grid container spacing={3}>
|
||||
{nchannels <= 1 ? (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>You can't abort the wizard because at least one input must be defined.</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Button variant="outlined" color="primary" fullWidth onClick={handleBack}>
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography>
|
||||
<Trans>Are you sure you want to abort the wizard?</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Button variant="outlined" color="default" fullWidth onClick={handleNext}>
|
||||
<Trans>Yes</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Button variant="outlined" color="primary" fullWidth onClick={handleBack}>
|
||||
<Trans>No</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
return <Abort onHelp={handleHelp('abort')} onBack={handleBack} onNext={handleNext} nchannels={nchannels} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Wizard.defaultProps = {
|
||||
restreamer: null,
|
||||
};
|
||||
|
||||
906
src/views/Edit/Wizard/index.test.js
Normal file
906
src/views/Edit/Wizard/index.test.js
Normal file
@ -0,0 +1,906 @@
|
||||
import React from 'react';
|
||||
import { render, fireEvent, act, screen } from '../../../utils/testing';
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import Wizard from './index';
|
||||
|
||||
const restreamer = {
|
||||
SelectChannel: () => {
|
||||
return 'test';
|
||||
},
|
||||
GetChannel: () => {
|
||||
return {
|
||||
id: 'test',
|
||||
name: 'test',
|
||||
};
|
||||
},
|
||||
Skills: () => {
|
||||
return {
|
||||
ffmpeg: {
|
||||
version: '5.1.2',
|
||||
},
|
||||
formats: {
|
||||
demuxers: ['rtsp'],
|
||||
},
|
||||
protocols: {
|
||||
input: ['http', 'https', 'rtmp', 'rtmps', 'srt'],
|
||||
},
|
||||
sources: {
|
||||
network: {},
|
||||
},
|
||||
encoders: {
|
||||
audio: ['copy', 'none', 'aac'],
|
||||
video: ['copy', 'none', 'libx264'],
|
||||
},
|
||||
decoders: {
|
||||
audio: ['default'],
|
||||
video: ['default'],
|
||||
},
|
||||
};
|
||||
},
|
||||
RefreshSkills: () => {},
|
||||
ConfigActive: () => {
|
||||
return {
|
||||
source: {
|
||||
network: {
|
||||
rtmp: {
|
||||
enabled: true,
|
||||
app: '/live',
|
||||
token: 'foobar',
|
||||
},
|
||||
srt: {
|
||||
enabled: true,
|
||||
token: 'foobar',
|
||||
passphrase: 'bazfoobazfoo',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
Probe: (id, inputs) => {
|
||||
let streams = [];
|
||||
|
||||
if (inputs[0].address === 'rtmp://localhost/live/external.stream?token=foobar') {
|
||||
streams.push({
|
||||
url: inputs[0].address,
|
||||
format: 'rtmp',
|
||||
index: 0,
|
||||
stream: 0,
|
||||
language: 'und',
|
||||
type: 'video',
|
||||
codec: 'h264',
|
||||
coder: '',
|
||||
bitrate_kbps: 0,
|
||||
duration_sec: 0,
|
||||
fps: 0,
|
||||
pix_fmt: 'yuvj420p',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
sampling_hz: 0,
|
||||
layout: '',
|
||||
channels: 0,
|
||||
});
|
||||
streams.push({
|
||||
url: inputs[0].address,
|
||||
format: 'rtmp',
|
||||
index: 0,
|
||||
stream: 1,
|
||||
language: 'und',
|
||||
type: 'audio',
|
||||
codec: 'aac',
|
||||
coder: '',
|
||||
bitrate_kbps: 0,
|
||||
duration_sec: 0,
|
||||
fps: 0,
|
||||
pix_fmt: 'yuvj420p',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
sampling_hz: 44100,
|
||||
layout: 'stereo',
|
||||
channels: 2,
|
||||
});
|
||||
} else if (inputs[0].address === 'srt://localhost?mode=caller&transtype=live&streamid=external,mode:request,token:foobar&passphrase=bazfoobazfoo') {
|
||||
streams.push({
|
||||
url: inputs[0].address,
|
||||
format: 'srt',
|
||||
index: 0,
|
||||
stream: 0,
|
||||
language: 'und',
|
||||
type: 'video',
|
||||
codec: 'h264',
|
||||
coder: '',
|
||||
bitrate_kbps: 0,
|
||||
duration_sec: 0,
|
||||
fps: 0,
|
||||
pix_fmt: 'yuvj420p',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
sampling_hz: 0,
|
||||
layout: '',
|
||||
channels: 0,
|
||||
});
|
||||
streams.push({
|
||||
url: inputs[0].address,
|
||||
format: 'srt',
|
||||
index: 0,
|
||||
stream: 1,
|
||||
language: 'und',
|
||||
type: 'audio',
|
||||
codec: 'aac',
|
||||
coder: '',
|
||||
bitrate_kbps: 0,
|
||||
duration_sec: 0,
|
||||
fps: 0,
|
||||
pix_fmt: 'yuvj420p',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
sampling_hz: 44100,
|
||||
layout: 'stereo',
|
||||
channels: 2,
|
||||
});
|
||||
} else if (inputs[0].address === 'anullsrc=r=44100:cl=stereo') {
|
||||
streams.push({
|
||||
url: inputs[0].address,
|
||||
format: 'lavfi',
|
||||
index: 0,
|
||||
stream: 0,
|
||||
language: 'und',
|
||||
type: 'audio',
|
||||
codec: 'pcm',
|
||||
coder: '',
|
||||
bitrate_kbps: 0,
|
||||
duration_sec: 0,
|
||||
fps: 0,
|
||||
pix_fmt: 'yuvj420p',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
sampling_hz: 44100,
|
||||
layout: 'stereo',
|
||||
channels: 2,
|
||||
});
|
||||
} else {
|
||||
const name = inputs[0].address.split('/').pop();
|
||||
const [video, audio] = name.split('-');
|
||||
|
||||
switch (video) {
|
||||
case 'none':
|
||||
break;
|
||||
case 'h264':
|
||||
streams.push({
|
||||
url: 'rtsp://127.0.0.1/live/stream',
|
||||
format: 'rtsp',
|
||||
index: 0,
|
||||
stream: 0,
|
||||
language: 'und',
|
||||
type: 'video',
|
||||
codec: 'h264',
|
||||
coder: '',
|
||||
bitrate_kbps: 0,
|
||||
duration_sec: 0,
|
||||
fps: 0,
|
||||
pix_fmt: 'yuvj420p',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
sampling_hz: 0,
|
||||
layout: '',
|
||||
channels: 0,
|
||||
});
|
||||
break;
|
||||
case 'other':
|
||||
streams.push({
|
||||
url: 'rtsp://127.0.0.1/live/stream',
|
||||
format: 'rtsp',
|
||||
index: 0,
|
||||
stream: 0,
|
||||
language: 'und',
|
||||
type: 'video',
|
||||
codec: 'mjpeg',
|
||||
coder: '',
|
||||
bitrate_kbps: 0,
|
||||
duration_sec: 0,
|
||||
fps: 0,
|
||||
pix_fmt: 'yuvj420p',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
sampling_hz: 0,
|
||||
layout: '',
|
||||
channels: 0,
|
||||
});
|
||||
break;
|
||||
case 'unknown':
|
||||
streams.push({
|
||||
url: 'rtsp://127.0.0.1/live/stream',
|
||||
format: 'rtsp',
|
||||
index: 0,
|
||||
stream: 0,
|
||||
language: 'und',
|
||||
type: 'video',
|
||||
codec: 'xxx',
|
||||
coder: '',
|
||||
bitrate_kbps: 0,
|
||||
duration_sec: 0,
|
||||
fps: 0,
|
||||
pix_fmt: 'yuvj420p',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
sampling_hz: 0,
|
||||
layout: '',
|
||||
channels: 0,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (audio) {
|
||||
case 'none':
|
||||
break;
|
||||
case 'aac':
|
||||
streams.push({
|
||||
url: 'rtsp://127.0.0.1/live/stream',
|
||||
format: 'rtsp',
|
||||
index: 0,
|
||||
stream: 1,
|
||||
language: 'und',
|
||||
type: 'audio',
|
||||
codec: 'aac',
|
||||
coder: '',
|
||||
bitrate_kbps: 0,
|
||||
duration_sec: 0,
|
||||
fps: 0,
|
||||
pix_fmt: 'yuvj420p',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
sampling_hz: 44100,
|
||||
layout: 'stereo',
|
||||
channels: 2,
|
||||
});
|
||||
break;
|
||||
case 'ogg':
|
||||
streams.push({
|
||||
url: 'rtsp://127.0.0.1/live/stream',
|
||||
format: 'rtsp',
|
||||
index: 0,
|
||||
stream: 1,
|
||||
language: 'und',
|
||||
type: 'audio',
|
||||
codec: 'ogg',
|
||||
coder: '',
|
||||
bitrate_kbps: 0,
|
||||
duration_sec: 0,
|
||||
fps: 0,
|
||||
pix_fmt: 'yuvj420p',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
sampling_hz: 44100,
|
||||
layout: 'stereo',
|
||||
channels: 2,
|
||||
});
|
||||
break;
|
||||
case 'unknown':
|
||||
streams.push({
|
||||
url: 'rtsp://127.0.0.1/live/stream',
|
||||
format: 'rtsp',
|
||||
index: 0,
|
||||
stream: 1,
|
||||
language: 'und',
|
||||
type: 'audio',
|
||||
codec: 'xxx',
|
||||
coder: '',
|
||||
bitrate_kbps: 0,
|
||||
duration_sec: 0,
|
||||
fps: 0,
|
||||
pix_fmt: 'yuvj420p',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
sampling_hz: 44100,
|
||||
layout: 'stereo',
|
||||
channels: 2,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [{ streams: streams }, null];
|
||||
},
|
||||
UpsertIngest: (_channelid, global, inputs, outputs, control) => {
|
||||
return [{}, null];
|
||||
},
|
||||
SetIngestMetadata: (_channelid, data) => {},
|
||||
UpsertIngestSnapshot: (_channelid, control) => {},
|
||||
UpdatePlayer: (_channelid) => {},
|
||||
UpdatePlayersite: () => {},
|
||||
};
|
||||
|
||||
test('wizard', async () => {
|
||||
await act(async () => {
|
||||
render(<Wizard restreamer={restreamer} />, {}, '/wizard/test', '/wizard/:channelid');
|
||||
});
|
||||
|
||||
expect(await screen.findByText(/Select whether you pull the stream from a/)).toBeInTheDocument();
|
||||
|
||||
expect(screen.queryByText('Network source')).toBeInTheDocument();
|
||||
expect(screen.queryByText('RTMP server')).toBeInTheDocument();
|
||||
expect(screen.queryByText('SRT server')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('wizard: rtmp source video h264-aac', async () => {
|
||||
await act(async () => {
|
||||
render(<Wizard restreamer={restreamer} />, {}, '/wizard/test', '/wizard/:channelid');
|
||||
});
|
||||
|
||||
// Choose network source
|
||||
let button = screen.getByRole('button', { name: 'RTMP server' });
|
||||
fireEvent.click(button);
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/The video source is compatible. Select the desired resolution:/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Your stream needs to be encoded. Choose the desired encoder:/)).not.toBeInTheDocument();
|
||||
|
||||
// Select suggested video stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/Audio from device/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Silence Audio/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/No audio/)).toBeInTheDocument();
|
||||
|
||||
// Confirm selected audio stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/Metadata/)).toBeInTheDocument();
|
||||
|
||||
// Confirm metadata
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/License/)).toBeInTheDocument();
|
||||
|
||||
// Confirm license
|
||||
button = screen.getByRole('button', { name: 'Save' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
});
|
||||
|
||||
test('wizard: srt source video h264-aac', async () => {
|
||||
await act(async () => {
|
||||
render(<Wizard restreamer={restreamer} />, {}, '/wizard/test', '/wizard/:channelid');
|
||||
});
|
||||
|
||||
// Choose network source
|
||||
let button = screen.getByRole('button', { name: 'SRT server' });
|
||||
fireEvent.click(button);
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/The video source is compatible. Select the desired resolution:/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Your stream needs to be encoded. Choose the desired encoder:/)).not.toBeInTheDocument();
|
||||
|
||||
// Select suggested video stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/Audio from device/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Silence Audio/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/No audio/)).toBeInTheDocument();
|
||||
|
||||
// Confirm selected audio stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/Metadata/)).toBeInTheDocument();
|
||||
|
||||
// Confirm metadata
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/License/)).toBeInTheDocument();
|
||||
|
||||
// Confirm license
|
||||
button = screen.getByRole('button', { name: 'Save' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
});
|
||||
|
||||
test('wizard: network source error', async () => {
|
||||
await act(async () => {
|
||||
render(<Wizard restreamer={restreamer} />, {}, '/wizard/test', '/wizard/:channelid');
|
||||
});
|
||||
|
||||
// Choose network source
|
||||
let button = screen.getByRole('button', { name: 'Network source' });
|
||||
fireEvent.click(button);
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeDisabled();
|
||||
|
||||
// Add a stream address
|
||||
let input = screen.getByLabelText('Address');
|
||||
fireEvent.change(input, { target: { value: 'rtsp://127.0.0.1/live/none-none' } });
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/Failed to verify the source. Please check the address./)).toBeInTheDocument();
|
||||
|
||||
// Add a stream address
|
||||
input = screen.getByLabelText('Address');
|
||||
fireEvent.change(input, { target: { value: 'rtsp://127.0.0.1/live/none-aac' } });
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/The source doesn't provide any video streams. Please check the device./)).toBeInTheDocument();
|
||||
|
||||
/*
|
||||
// TODO: This message appears only if there's no h264 encoder. However it should appear if there's no
|
||||
// suitable decoder for the detected codec. The question is, if this is actually possible, because
|
||||
// if FFmpeg doesn't know the codec, how can it detect it?
|
||||
|
||||
input = screen.getByLabelText('Address');
|
||||
fireEvent.change(input, { target: { value: 'rtsp://127.0.0.1/live/unknown-none' } });
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/The source doesn't provide any compatible video streams./)).toBeInTheDocument();
|
||||
*/
|
||||
});
|
||||
|
||||
test('wizard: network source video h264', async () => {
|
||||
await act(async () => {
|
||||
render(<Wizard restreamer={restreamer} />, {}, '/wizard/test', '/wizard/:channelid');
|
||||
});
|
||||
|
||||
// Choose network source
|
||||
let button = screen.getByRole('button', { name: 'Network source' });
|
||||
fireEvent.click(button);
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeDisabled();
|
||||
|
||||
// Add a stream address
|
||||
let input = screen.getByLabelText('Address');
|
||||
fireEvent.change(input, { target: { value: 'rtsp://127.0.0.1/live/h264-none' } });
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/The video source is compatible. Select the desired resolution:/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Your stream needs to be encoded. Choose the desired encoder:/)).not.toBeInTheDocument();
|
||||
|
||||
// Select suggested video stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/The video source doesn't provide any compatible audio stream./)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Silence Audio/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/No audio/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('wizard: network source video non-h264', async () => {
|
||||
await act(async () => {
|
||||
render(<Wizard restreamer={restreamer} />, {}, '/wizard/test', '/wizard/:channelid');
|
||||
});
|
||||
|
||||
// Choose network source
|
||||
let button = screen.getByRole('button', { name: 'Network source' });
|
||||
fireEvent.click(button);
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeDisabled();
|
||||
|
||||
// Add a stream address
|
||||
let input = screen.getByLabelText('Address');
|
||||
fireEvent.change(input, { target: { value: 'rtsp://127.0.0.1/live/other-none' } });
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/The video source is compatible. Select the desired resolution:/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Your stream needs to be encoded. Choose the desired encoder:/)).toBeInTheDocument();
|
||||
|
||||
// Select suggested video stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/The video source doesn't provide any compatible audio stream./)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Silence Audio/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/No audio/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('wizard: network source audio aac', async () => {
|
||||
await act(async () => {
|
||||
render(<Wizard restreamer={restreamer} />, {}, '/wizard/test', '/wizard/:channelid');
|
||||
});
|
||||
|
||||
// Choose network source
|
||||
let button = screen.getByRole('button', { name: 'Network source' });
|
||||
fireEvent.click(button);
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeDisabled();
|
||||
|
||||
// Add a stream address
|
||||
let input = screen.getByLabelText('Address');
|
||||
fireEvent.change(input, { target: { value: 'rtsp://127.0.0.1/live/h264-aac' } });
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
// Select suggested video stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/Audio from device/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Silence Audio/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/No audio/)).toBeInTheDocument();
|
||||
|
||||
// Select suggested audio stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/Metadata/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('wizard: network source audio non-aac', async () => {
|
||||
await act(async () => {
|
||||
render(<Wizard restreamer={restreamer} />, {}, '/wizard/test', '/wizard/:channelid');
|
||||
});
|
||||
|
||||
// Choose network source
|
||||
let button = screen.getByRole('button', { name: 'Network source' });
|
||||
fireEvent.click(button);
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeDisabled();
|
||||
|
||||
// Add a stream address
|
||||
let input = screen.getByLabelText('Address');
|
||||
fireEvent.change(input, { target: { value: 'rtsp://127.0.0.1/live/h264-ogg' } });
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
// Select suggested video stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/Audio from device/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Silence Audio/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/No audio/)).toBeInTheDocument();
|
||||
|
||||
// Select suggested audio stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/Metadata/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('wizard: network source silence audio', async () => {
|
||||
await act(async () => {
|
||||
render(<Wizard restreamer={restreamer} />, {}, '/wizard/test', '/wizard/:channelid');
|
||||
});
|
||||
|
||||
// Choose network source
|
||||
let button = screen.getByRole('button', { name: 'Network source' });
|
||||
fireEvent.click(button);
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeDisabled();
|
||||
|
||||
// Add a stream address
|
||||
let input = screen.getByLabelText('Address');
|
||||
fireEvent.change(input, { target: { value: 'rtsp://127.0.0.1/live/h264-aac' } });
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
// Select suggested video stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/Audio from device/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Silence Audio/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/No audio/)).toBeInTheDocument();
|
||||
|
||||
expect(screen.queryByLabelText('Audio from device')).toBeChecked();
|
||||
expect(screen.queryByLabelText('Silence Audio')).not.toBeChecked();
|
||||
expect(screen.queryByLabelText('No audio')).not.toBeChecked();
|
||||
|
||||
input = screen.queryByLabelText('Silence Audio');
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(input);
|
||||
});
|
||||
|
||||
expect(screen.queryByLabelText('Audio from device')).not.toBeChecked();
|
||||
expect(screen.queryByLabelText('Silence Audio')).toBeChecked();
|
||||
expect(screen.queryByLabelText('No audio')).not.toBeChecked();
|
||||
|
||||
// Confirm selected audio stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/Metadata/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('wizard: network source no audio', async () => {
|
||||
await act(async () => {
|
||||
render(<Wizard restreamer={restreamer} />, {}, '/wizard/test', '/wizard/:channelid');
|
||||
});
|
||||
|
||||
// Choose network source
|
||||
let button = screen.getByRole('button', { name: 'Network source' });
|
||||
fireEvent.click(button);
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeDisabled();
|
||||
|
||||
// Add a stream address
|
||||
let input = screen.getByLabelText('Address');
|
||||
fireEvent.change(input, { target: { value: 'rtsp://127.0.0.1/live/h264-aac' } });
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
// Select suggested video stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/Audio from device/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Silence Audio/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/No audio/)).toBeInTheDocument();
|
||||
|
||||
expect(screen.queryByLabelText('Audio from device')).toBeChecked();
|
||||
expect(screen.queryByLabelText('Silence Audio')).not.toBeChecked();
|
||||
expect(screen.queryByLabelText('No audio')).not.toBeChecked();
|
||||
|
||||
input = screen.queryByLabelText('No audio');
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(input);
|
||||
});
|
||||
|
||||
expect(screen.queryByLabelText('Audio from device')).not.toBeChecked();
|
||||
expect(screen.queryByLabelText('Silence Audio')).not.toBeChecked();
|
||||
expect(screen.queryByLabelText('No audio')).toBeChecked();
|
||||
|
||||
// Confirm selected audio stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/Metadata/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('wizard: metadata', async () => {
|
||||
await act(async () => {
|
||||
render(<Wizard restreamer={restreamer} />, {}, '/wizard/test', '/wizard/:channelid');
|
||||
});
|
||||
|
||||
// Choose network source
|
||||
let button = screen.getByRole('button', { name: 'Network source' });
|
||||
fireEvent.click(button);
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeDisabled();
|
||||
|
||||
// Add a stream address
|
||||
let input = screen.getByLabelText('Address');
|
||||
fireEvent.change(input, { target: { value: 'rtsp://127.0.0.1/live/h264-aac' } });
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
// Select suggested video stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
// Confirm selected audio stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/Metadata/)).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('Name')).toHaveValue('test');
|
||||
expect(screen.queryByLabelText('Description')).toHaveValue('Live from earth. Powered by datarhei Restreamer.');
|
||||
|
||||
// Confirm metadata
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/License/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('wizard: license', async () => {
|
||||
await act(async () => {
|
||||
render(<Wizard restreamer={restreamer} />, {}, '/wizard/test', '/wizard/:channelid');
|
||||
});
|
||||
|
||||
// Choose network source
|
||||
let button = screen.getByRole('button', { name: 'Network source' });
|
||||
fireEvent.click(button);
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeDisabled();
|
||||
|
||||
// Add a stream address
|
||||
let input = screen.getByLabelText('Address');
|
||||
fireEvent.change(input, { target: { value: 'rtsp://127.0.0.1/live/h264-aac' } });
|
||||
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
// Select suggested video stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
// Confirm selected audio stream
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
// Confirm metadata
|
||||
button = screen.getByRole('button', { name: 'Next' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/License/)).toBeInTheDocument();
|
||||
|
||||
// Confirm license
|
||||
button = screen.getByRole('button', { name: 'Save' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user