diff --git a/src/misc/EncodingSelect.js b/src/misc/EncodingSelect.js index e15be0c..0ef6838 100644 --- a/src/misc/EncodingSelect.js +++ b/src/misc/EncodingSelect.js @@ -239,5 +239,5 @@ EncodingSelect.defaultProps = { codecs: [], availableEncoders: [], availableDecoders: [], - onChange: function (encoder, decoder) {}, + onChange: function (encoder, decoder, automatic) {}, }; diff --git a/src/misc/FilterSelect.js b/src/misc/FilterSelect.js index 0d6ccc8..516a40e 100644 --- a/src/misc/FilterSelect.js +++ b/src/misc/FilterSelect.js @@ -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( - + ); } } @@ -121,7 +128,7 @@ export default function FilterSelect(props) { FilterSelect.defaultProps = { type: '', - filters: [], + profile: {}, availableFilters: [], - onChange: function (filter) {}, + onChange: function (filter, automatic) {}, }; diff --git a/src/misc/coders/Encoders/audio/AAC.js b/src/misc/coders/Encoders/audio/AAC.js index a3eea58..4784c7b 100644 --- a/src/misc/coders/Encoders/audio/AAC.js +++ b/src/misc/coders/Encoders/audio/AAC.js @@ -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'); diff --git a/src/misc/coders/Encoders/audio/AACAudioToolbox.js b/src/misc/coders/Encoders/audio/AACAudioToolbox.js index f8e0525..94fe217 100644 --- a/src/misc/coders/Encoders/audio/AACAudioToolbox.js +++ b/src/misc/coders/Encoders/audio/AACAudioToolbox.js @@ -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'); diff --git a/src/misc/coders/Encoders/audio/Libopus.js b/src/misc/coders/Encoders/audio/Libopus.js index bb6d9d9..a7bd2ce 100644 --- a/src/misc/coders/Encoders/audio/Libopus.js +++ b/src/misc/coders/Encoders/audio/Libopus.js @@ -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: [], diff --git a/src/misc/coders/Encoders/audio/Libvorbis.js b/src/misc/coders/Encoders/audio/Libvorbis.js index 4ceafbc..bb7aa4b 100644 --- a/src/misc/coders/Encoders/audio/Libvorbis.js +++ b/src/misc/coders/Encoders/audio/Libvorbis.js @@ -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: [], diff --git a/src/misc/coders/Encoders/audio/MP3.js b/src/misc/coders/Encoders/audio/MP3.js index 7a394c7..0e7fc49 100644 --- a/src/misc/coders/Encoders/audio/MP3.js +++ b/src/misc/coders/Encoders/audio/MP3.js @@ -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: [], diff --git a/src/misc/coders/Encoders/audio/Opus.js b/src/misc/coders/Encoders/audio/Opus.js index 17f62ed..c5fe8a6 100644 --- a/src/misc/coders/Encoders/audio/Opus.js +++ b/src/misc/coders/Encoders/audio/Opus.js @@ -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); diff --git a/src/misc/coders/Encoders/audio/Vorbis.js b/src/misc/coders/Encoders/audio/Vorbis.js index c484720..462a508 100644 --- a/src/misc/coders/Encoders/audio/Vorbis.js +++ b/src/misc/coders/Encoders/audio/Vorbis.js @@ -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: [], diff --git a/src/misc/filters/audio/Loudnorm.js b/src/misc/filters/audio/Loudnorm.js index 5b2c70a..7b0ec22 100644 --- a/src/misc/filters/audio/Loudnorm.js +++ b/src/misc/filters/audio/Loudnorm.js @@ -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 ( - Loudness Normalization} checked={settings.value} onChange={update('value')} /> + Loudness Normalization} checked={settings.enabled} onChange={update('enabled')} /> ); @@ -86,7 +86,7 @@ function defaults() { return { settings: settings, - mapping: createMapping(settings), + graph: createGraph(settings), }; } diff --git a/src/misc/filters/audio/Volume.js b/src/misc/filters/audio/Volume.js index ab2bffc..06cc660 100644 --- a/src/misc/filters/audio/Volume.js +++ b/src/misc/filters/audio/Volume.js @@ -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), }; } diff --git a/src/misc/filters/video/HFlip.js b/src/misc/filters/video/HFlip.js index 34b8fff..52d8ff2 100644 --- a/src/misc/filters/video/HFlip.js +++ b/src/misc/filters/video/HFlip.js @@ -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 ( - Horizontal Flip} checked={settings.value} onChange={update('value')} /> + Horizontal Flip} checked={settings.enabled} onChange={update('enabled')} /> ); } @@ -84,7 +84,7 @@ function defaults() { return { settings: settings, - mapping: createMapping(settings), + graph: createGraph(settings), }; } diff --git a/src/misc/filters/video/Transpose.js b/src/misc/filters/video/Transpose.js index 8f99731..2c5157b 100644 --- a/src/misc/filters/video/Transpose.js +++ b/src/misc/filters/video/Transpose.js @@ -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), }; } diff --git a/src/misc/filters/video/VFlip.js b/src/misc/filters/video/VFlip.js index 4d74c20..9c76f94 100644 --- a/src/misc/filters/video/VFlip.js +++ b/src/misc/filters/video/VFlip.js @@ -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 ( - Vertical Flip} checked={settings.value} onChange={update('value')} /> + Vertical Flip} checked={settings.enabled} onChange={update('enabled')} /> ); } @@ -84,7 +84,7 @@ function defaults() { return { settings: settings, - mapping: createMapping(settings), + graph: createGraph(settings), }; } diff --git a/src/utils/metadata.js b/src/utils/metadata.js index e99be7b..3bfda79 100644 --- a/src/utils/metadata.js +++ b/src/utils/metadata.js @@ -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, }; diff --git a/src/views/Edit/Profile.js b/src/views/Edit/Profile.js index 4afb778..02f644c 100644 --- a/src/views/Edit/Profile.js +++ b/src/views/Edit/Profile.js @@ -408,12 +408,11 @@ export default function Source(props) { onChange={handleEncoding('video')} /> - {($profile.video.encoder.coder !== 'none' && $profile.video.encoder.coder !== 'copy') && ( + {$profile.video.encoder.coder !== 'none' && $profile.video.encoder.coder !== 'copy' && ( @@ -481,7 +480,7 @@ export default function Source(props) { onChange={handleEncoding('audio')} /> - {($profile.audio.encoder.coder !== 'none' && $profile.audio.encoder.coder !== 'copy') && ( + {$profile.audio.encoder.coder !== 'none' && $profile.audio.encoder.coder !== 'copy' && ( - {($profile.audio.encoder.coder !== 'none' && $profile.audio.encoder.coder !== 'copy') && ( + {$profile.audio.encoder.coder !== 'none' && $profile.audio.encoder.coder !== 'copy' && (