Use graphs instead of mappings for filters, use -filter:(a|v) in command line

This commit is contained in:
Ingo Oppermann 2022-07-14 16:35:59 +02:00
parent 7639c4de18
commit a91e86bc66
No known key found for this signature in database
GPG Key ID: 2AB32426E9DD229E
16 changed files with 104 additions and 92 deletions

View File

@ -239,5 +239,5 @@ EncodingSelect.defaultProps = {
codecs: [],
availableEncoders: [],
availableDecoders: [],
onChange: function (encoder, decoder) {},
onChange: function (encoder, decoder, automatic) {},
};

View File

@ -17,36 +17,38 @@ export default function FilterSelect(props) {
// what: Filter name
// settings (component settings): {Key: Value}
// mapping (FFmpeg -af/-vf args): ['String', ...]
const handleFilterSettingsChange = (what) => (settings, mapping, automatic) => {
const handleFilterSettingsChange = (what) => (settings, graph, automatic) => {
const filter = profile.filter;
// Store mapping/settings per component
filter.settings[what] = {
mapping: mapping,
settings: settings,
graph: graph,
};
// Combine FFmpeg args
let settings_mapping = [];
for (let i in filter.settings) {
if (filter.settings[i].mapping.length !== 0) {
settings_mapping.push(filter.settings[i].mapping);
}
}
// Create the real filter mapping
// ['-af/-vf', 'args,args']
if (settings_mapping.length !== 0) {
if (props.type === 'video') {
filter.mapping = ['-vf', settings_mapping.join(',')];
} else if (props.type === 'audio') {
filter.mapping = ['-af', settings_mapping.join(',')];
}
// Get the order of the filters
let filterOrder = [];
if (props.type === 'video') {
filterOrder = Filters.Video.Filters();
} else {
filter.mapping = [];
filterOrder = Filters.Audio.Filters();
}
props.onChange(profile.filter, filter, automatic);
// Create the filter graph in the order as the filters are registered
const graphs = [];
for (let f of filterOrder) {
if (!(f in filter.settings)) {
continue;
}
if (filter.settings[f].graph.length !== 0) {
graphs.push(filter.settings[f].graph);
}
}
filter.graph = graphs.join(',');
props.onChange(filter, automatic);
};
// Set filterRegistry by type
@ -64,8 +66,8 @@ export default function FilterSelect(props) {
let hwaccel = false;
if (props.type === 'video') {
encoderRegistry = Encoders.Video;
for (let i in encoderRegistry.List()) {
if (encoderRegistry.List()[i].codec === props.videoProfile.encoder.coder && encoderRegistry.List()[i].hwaccel) {
for (let encoder of encoderRegistry.List()) {
if (encoder.codec === props.profile.encoder.coder && encoder.hwaccel) {
hwaccel = true;
}
}
@ -79,12 +81,17 @@ export default function FilterSelect(props) {
if (props.availableFilters.includes(c.filter)) {
const Settings = c.component;
if (!(c.filter in profile.filter.settings)) {
profile.filter.settings[c.filter] = c.defaults();
} else {
profile.filter.settings[c.filter] = {
...c.defaults(),
...profile.filter.settings[c.filter],
};
}
filterSettings.push(
<Settings
key={c.filter}
settings={profile.filter.settings[c.filter] ? profile.filter.settings[c.filter].settings : []}
onChange={handleFilterSettingsChange(c.filter)}
/>
<Settings key={c.filter} settings={profile.filter.settings[c.filter].settings} onChange={handleFilterSettingsChange(c.filter)} />
);
}
}
@ -121,7 +128,7 @@ export default function FilterSelect(props) {
FilterSelect.defaultProps = {
type: '',
filters: [],
profile: {},
availableFilters: [],
onChange: function (filter) {},
onChange: function (filter, automatic) {},
};

View File

@ -28,7 +28,7 @@ function createMapping(settings, stream) {
layout = stream.layout;
}
const local = ['-codec:a', 'aac', '-b:a', `${settings.bitrate}k`, '-shortest', '-af', `aresample=osr=${sampling}:ocl=${layout}`];
const local = ['-codec:a', 'aac', '-b:a', `${settings.bitrate}k`, '-shortest', '-filter:a', `aresample=osr=${sampling}:ocl=${layout}`];
if (stream.codec === 'aac') {
local.push('-bsf:a', 'aac_adtstoasc');

View File

@ -28,7 +28,7 @@ function createMapping(settings, stream) {
layout = stream.layout;
}
const local = ['-codec:a', 'aac_at', '-b:a', `${settings.bitrate}k`, '-shortest', '-af', `aresample=osr=${sampling}:ocl=${layout}`];
const local = ['-codec:a', 'aac_at', '-b:a', `${settings.bitrate}k`, '-shortest', '-filter:a', `aresample=osr=${sampling}:ocl=${layout}`];
if (stream.codec === 'aac') {
local.push('-bsf:a', 'aac_adtstoasc');

View File

@ -28,7 +28,7 @@ function createMapping(settings, stream) {
layout = stream.layout;
}
const local = ['-codec:a', 'libopus', '-b:a', `${settings.bitrate}k`, '-shortest', '-af', `aresample=osr=${sampling}:ocl=${layout}`];
const local = ['-codec:a', 'libopus', '-b:a', `${settings.bitrate}k`, '-shortest', '-filter:a', `aresample=osr=${sampling}:ocl=${layout}`];
const mapping = {
global: [],

View File

@ -28,7 +28,7 @@ function createMapping(settings, stream) {
layout = stream.layout;
}
const local = ['-codec:a', 'libvorbis', '-b:a', `${settings.bitrate}k`, '-shortest', '-af', `aresample=osr=${sampling}:ocl=${layout}`];
const local = ['-codec:a', 'libvorbis', '-b:a', `${settings.bitrate}k`, '-shortest', '-filter:a', `aresample=osr=${sampling}:ocl=${layout}`];
const mapping = {
global: [],

View File

@ -29,7 +29,7 @@ function createMapping(settings, stream) {
}
// '-qscale:a', '6'
const local = ['-codec:a', 'libmp3lame', '-b:a', `${settings.bitrate}k`, '-shortest', '-af', `aresample=osr=${sampling}:ocl=${layout}`];
const local = ['-codec:a', 'libmp3lame', '-b:a', `${settings.bitrate}k`, '-shortest', '-filter:a', `aresample=osr=${sampling}:ocl=${layout}`];
const mapping = {
global: [],

View File

@ -34,7 +34,7 @@ function createMapping(settings, stream) {
layout = stream.layout;
}
const local = ['-codec:a', 'opus', '-b:a', `${settings.bitrate}k`, '-vbr', 'on', '-shortest', '-af', `aresample=osr=${sampling}:ocl=${layout}`];
const local = ['-codec:a', 'opus', '-b:a', `${settings.bitrate}k`, '-vbr', 'on', '-shortest', '-filter:a', `aresample=osr=${sampling}:ocl=${layout}`];
if (settings.delay !== 'auto') {
local.push('opus_delay', settings.delay);

View File

@ -28,7 +28,7 @@ function createMapping(settings, stream) {
layout = stream.layout;
}
const local = ['-codec:a', 'vorbis', '-b:a', `${settings.bitrate}k`, '-qscale:a', '3', '-shortest', '-af', `aresample=osr=${sampling}:ocl=${layout}`];
const local = ['-codec:a', 'vorbis', '-b:a', `${settings.bitrate}k`, '-qscale:a', '3', '-shortest', '-filter:a', `aresample=osr=${sampling}:ocl=${layout}`];
const mapping = {
global: [],

View File

@ -10,21 +10,21 @@ import Checkbox from '../../Checkbox';
function init(initialState) {
const state = {
value: false,
enabled: false,
...initialState,
};
return state;
}
function createMapping(settings) {
function createGraph(settings) {
const mapping = [];
if (settings.value) {
if (settings.enabled) {
mapping.push('loudnorm');
}
return mapping;
return mapping.join(',');
}
function Filter(props) {
@ -37,15 +37,15 @@ function Filter(props) {
automatic = true;
}
props.onChange(newSettings, createMapping(newSettings), automatic);
props.onChange(newSettings, createGraph(newSettings), automatic);
};
const update = (what) => (event) => {
const newSettings = {
...settings,
};
if (['value'].includes(what)) {
newSettings[what] = !settings.value;
if (['enabled'].includes(what)) {
newSettings[what] = !settings.enabled;
} else {
newSettings[what] = event.target.value;
}
@ -61,7 +61,7 @@ function Filter(props) {
return (
<React.Fragment>
<Grid item>
<Checkbox label={<Trans>Loudness Normalization</Trans>} checked={settings.value} onChange={update('value')} />
<Checkbox label={<Trans>Loudness Normalization</Trans>} checked={settings.enabled} onChange={update('enabled')} />
</Grid>
</React.Fragment>
);
@ -86,7 +86,7 @@ function defaults() {
return {
settings: settings,
mapping: createMapping(settings),
graph: createGraph(settings),
};
}

View File

@ -20,7 +20,7 @@ function init(initialState) {
return state;
}
function createMapping(settings) {
function createGraph(settings) {
const mapping = [];
switch (settings.level) {
@ -34,7 +34,7 @@ function createMapping(settings) {
break;
}
return mapping;
return mapping.join(',');
}
function VolumeLevel(props) {
@ -95,7 +95,7 @@ function Filter(props) {
automatic = true;
}
props.onChange(newSettings, createMapping(newSettings), automatic);
props.onChange(newSettings, createGraph(newSettings), automatic);
};
const update = (what) => (event) => {
@ -143,7 +143,7 @@ function defaults() {
return {
settings: settings,
mapping: createMapping(settings),
graph: createGraph(settings),
};
}

View File

@ -10,21 +10,21 @@ import Checkbox from '../../Checkbox';
function init(initialState) {
const state = {
value: false,
enabled: false,
...initialState,
};
return state;
}
function createMapping(settings) {
function createGraph(settings) {
const mapping = [];
if (settings.value) {
if (settings.enabled) {
mapping.push('hflip');
}
return mapping;
return mapping.join(',');
}
function Filter(props) {
@ -37,15 +37,15 @@ function Filter(props) {
automatic = true;
}
props.onChange(newSettings, createMapping(newSettings), automatic);
props.onChange(newSettings, createGraph(newSettings), automatic);
};
const update = (what) => (event) => {
const newSettings = {
...settings,
};
if (['value'].includes(what)) {
newSettings[what] = !settings.value;
if (['enabled'].includes(what)) {
newSettings[what] = !settings.enabled;
} else {
newSettings[what] = event.target.value;
}
@ -60,7 +60,7 @@ function Filter(props) {
return (
<Grid item>
<Checkbox label={<Trans>Horizontal Flip</Trans>} checked={settings.value} onChange={update('value')} />
<Checkbox label={<Trans>Horizontal Flip</Trans>} checked={settings.enabled} onChange={update('enabled')} />
</Grid>
);
}
@ -84,7 +84,7 @@ function defaults() {
return {
settings: settings,
mapping: createMapping(settings),
graph: createGraph(settings),
};
}

View File

@ -18,7 +18,7 @@ function init(initialState) {
return state;
}
function createMapping(settings) {
function createGraph(settings) {
const mapping = [];
switch (settings.value) {
@ -35,7 +35,7 @@ function createMapping(settings) {
break;
}
return mapping;
return mapping.join(',');
}
// filter
@ -65,7 +65,7 @@ function Filter(props) {
automatic = true;
}
props.onChange(newSettings, createMapping(newSettings), automatic);
props.onChange(newSettings, createGraph(newSettings), automatic);
};
const update = (what) => (event) => {
@ -108,7 +108,7 @@ function defaults() {
return {
settings: settings,
mapping: createMapping(settings),
graph: createGraph(settings),
};
}

View File

@ -10,21 +10,21 @@ import Checkbox from '../../Checkbox';
function init(initialState) {
const state = {
value: false,
enabled: false,
...initialState,
};
return state;
}
function createMapping(settings) {
function createGraph(settings) {
const mapping = [];
if (settings.value) {
if (settings.enabled) {
mapping.push('vflip');
}
return mapping;
return mapping.join(',');
}
function Filter(props) {
@ -37,15 +37,15 @@ function Filter(props) {
automatic = true;
}
props.onChange(newSettings, createMapping(newSettings), automatic);
props.onChange(newSettings, createGraph(newSettings), automatic);
};
const update = (what) => (event) => {
const newSettings = {
...settings,
};
if (['value'].includes(what)) {
newSettings[what] = !settings.value;
if (['enabled'].includes(what)) {
newSettings[what] = !settings.enabled;
} else {
newSettings[what] = event.target.value;
}
@ -60,7 +60,7 @@ function Filter(props) {
return (
<Grid item>
<Checkbox label={<Trans>Vertical Flip</Trans>} checked={settings.value} onChange={update('value')} />
<Checkbox label={<Trans>Vertical Flip</Trans>} checked={settings.enabled} onChange={update('enabled')} />
</Grid>
);
}
@ -84,7 +84,7 @@ function defaults() {
return {
settings: settings,
mapping: createMapping(settings),
graph: createGraph(settings),
};
}

View File

@ -597,15 +597,19 @@ const createInputsOutputs = (sources, profiles) => {
global = [...global, ...profile.video.encoder.mapping.global];
// Merge video filters
for (let i = 0; i < profile.video.encoder.mapping.local.length; i++) {
if (profile.video.encoder.mapping.local[i] === '-vf' && profile.video.filter.mapping.length !== 0) {
profile.video.encoder.mapping.local[i + 1] = profile.video.encoder.mapping.local[i + 1] + ',' + profile.video.filter.mapping[1];
profile.video.filter.mapping = [];
const local = profile.video.encoder.mapping.local.slice();
if (profile.video.filter.graph.length !== 0) {
// Check if there's already a video filter in the local mapping
let filterIndex = local.indexOf('-filter:v');
if (filterIndex !== -1) {
local[filterIndex + 1] += ',' + profile.video.filter.graph;
} else {
local.unshift('-filter:v', profile.video.filter.graph);
}
}
const options = ['-map', index + ':' + stream.stream, ...profile.video.filter.mapping, ...profile.video.encoder.mapping.local];
const options = ['-map', index + ':' + stream.stream, ...local];
if (profile.audio.encoder.coder !== 'none' && profile.audio.source !== -1 && profile.audio.stream !== -1) {
global = [...global, ...profile.audio.decoder.mapping.global];
@ -627,15 +631,19 @@ const createInputsOutputs = (sources, profiles) => {
global = [...global, ...profile.audio.encoder.mapping.global];
// Merge audio filters
for (let i = 0; i < profile.audio.encoder.mapping.local.length; i++) {
if (profile.audio.encoder.mapping.local[i] === '-af' && profile.audio.filter.mapping.length !== 0) {
profile.audio.encoder.mapping.local[i + 1] = profile.audio.encoder.mapping.local[i + 1] + ',' + profile.audio.filter.mapping[1];
profile.audio.filter.mapping = [];
const local = profile.audio.encoder.mapping.local.slice();
if (profile.audio.filter.graph.length !== 0) {
// Check if there's already a audio filter in the local mapping
let filterIndex = local.indexOf('-filter:a');
if (filterIndex !== -1) {
local[filterIndex + 1] += ',' + profile.audio.filter.graph;
} else {
local.unshift('-filter:a', profile.audio.filter.graph);
}
}
options.push('-map', index + ':' + stream.stream, ...profile.audio.filter.mapping, ...profile.audio.encoder.mapping.local);
options.push('-map', index + ':' + stream.stream, ...local);
} else {
options.push('-an');
}
@ -807,9 +815,8 @@ const initProfile = (initialProfile) => {
}
profile.video.filter = {
filter: 'default',
graph: '',
settings: {},
mapping: {},
...profile.video.filter,
};
@ -863,9 +870,8 @@ const initProfile = (initialProfile) => {
}
profile.audio.filter = {
filter: 'default',
graph: '',
settings: {},
mapping: {},
...profile.audio.filter,
};

View File

@ -408,12 +408,11 @@ export default function Source(props) {
onChange={handleEncoding('video')}
/>
</Grid>
{($profile.video.encoder.coder !== 'none' && $profile.video.encoder.coder !== 'copy') && (
{$profile.video.encoder.coder !== 'none' && $profile.video.encoder.coder !== 'copy' && (
<Grid item xs={12}>
<FilterSelect
type="video"
profile={$profile.video}
videoProfile={$profile.video}
availableFilters={props.skills.filter}
onChange={handleFilter('video')}
/>
@ -481,7 +480,7 @@ export default function Source(props) {
onChange={handleEncoding('audio')}
/>
</Grid>
{($profile.audio.encoder.coder !== 'none' && $profile.audio.encoder.coder !== 'copy') && (
{$profile.audio.encoder.coder !== 'none' && $profile.audio.encoder.coder !== 'copy' && (
<Grid item xs={12}>
<FilterSelect
type="audio"
@ -558,7 +557,7 @@ export default function Source(props) {
onChange={handleEncoding('audio')}
/>
</Grid>
{($profile.audio.encoder.coder !== 'none' && $profile.audio.encoder.coder !== 'copy') && (
{$profile.audio.encoder.coder !== 'none' && $profile.audio.encoder.coder !== 'copy' && (
<Grid item xs={12}>
<FilterSelect
type="audio"