From c9d1c206c50c19c0eb6c44ff063afe007401508c Mon Sep 17 00:00:00 2001 From: Ingo Oppermann Date: Tue, 26 Nov 2024 12:10:05 +0100 Subject: [PATCH] Fix H264 nvenc presets, add HEVC and AV1 nvenc --- src/misc/coders/Encoders/index.js | 4 + src/misc/coders/Encoders/video/av1_nvenc.js | 222 ++++++++++++++++++ src/misc/coders/Encoders/video/h264_nvenc.js | 42 ++-- src/misc/coders/Encoders/video/hevc_nvenc.js | 229 +++++++++++++++++++ 4 files changed, 480 insertions(+), 17 deletions(-) create mode 100644 src/misc/coders/Encoders/video/av1_nvenc.js create mode 100644 src/misc/coders/Encoders/video/hevc_nvenc.js diff --git a/src/misc/coders/Encoders/index.js b/src/misc/coders/Encoders/index.js index e6bf7ee..17b8a30 100644 --- a/src/misc/coders/Encoders/index.js +++ b/src/misc/coders/Encoders/index.js @@ -18,6 +18,7 @@ import * as H264V4L2M2M from './video/h264_v4l2m2m'; import * as H264VAAPI from './video/h264_vaapi'; import * as HEVCVAAPI from './video/hevc_vaapi'; import * as HEVCVideoToolbox from './video/hevc_videotoolbox'; +import * as HEVCNVENC from './video/hevc_nvenc'; import * as VP9VPX from './video/vp9_libvpx'; import * as VP9VAAPI from './video/vp9_vaapi'; import * as VideoCopy from './video/copy'; @@ -25,6 +26,7 @@ import * as VideoNone from './video/none'; import * as VideoRaw from './video/rawvideo'; import * as AV1Rav1e from './video/av1_librav1e'; import * as AV1AOM from './video/av1_libaom'; +import * as AV1NVENC from './video/av1_nvenc'; class Registry { constructor(type) { @@ -136,10 +138,12 @@ videoRegistry.Register(H264VAAPI); videoRegistry.Register(X265); videoRegistry.Register(HEVCVAAPI); videoRegistry.Register(HEVCVideoToolbox); +videoRegistry.Register(HEVCNVENC); videoRegistry.Register(VP9VPX); videoRegistry.Register(VP9VAAPI); videoRegistry.Register(AV1AOM); videoRegistry.Register(AV1Rav1e); +videoRegistry.Register(AV1NVENC); videoRegistry.Register(VideoRaw); export { audioRegistry as Audio, videoRegistry as Video }; diff --git a/src/misc/coders/Encoders/video/av1_nvenc.js b/src/misc/coders/Encoders/video/av1_nvenc.js new file mode 100644 index 0000000..2fbc208 --- /dev/null +++ b/src/misc/coders/Encoders/video/av1_nvenc.js @@ -0,0 +1,222 @@ +import React from 'react'; + +import Grid from '@mui/material/Grid'; +import MenuItem from '@mui/material/MenuItem'; + +import { Trans } from '@lingui/macro'; + +import Select from '../../../Select'; +import Video from '../../settings/Video'; +import Helper from '../../helper'; + +function init(initialState) { + const state = { + bitrate: '4096', + fps: '25', + gop: '2', + preset: 'p4', + tune: 'll', + level: 'auto', + rc: 'auto', + ...initialState, + }; + + return state; +} + +function createMapping(settings, stream, skills) { + stream = Helper.InitStream(stream); + skills = Helper.InitSkills(skills); + + const local = [ + '-codec:v', + 'av1_nvenc', + '-preset:v', + `${settings.preset}`, + '-tune:v', + `${settings.tune}`, + '-b:v', + `${settings.bitrate}k`, + '-maxrate', + `${settings.bitrate}k`, + '-bufsize', + `${settings.bitrate}k`, + '-r', + `${settings.fps}`, + '-pix_fmt', + 'yuv420p', + ]; + + if (settings.gop !== 'auto') { + local.push('-g', `${Math.round(parseInt(settings.fps) * parseInt(settings.gop)).toFixed(0)}`); + } + + if (settings.level !== 'auto') { + local.push('-level:v', `${settings.level}`); + } + + if (settings.rc !== 'auto') { + local.push('-rc:v', `${settings.rc}`); + } + + const mapping = { + global: [], + local: local, + filter: [], + }; + + return mapping; +} + +function Preset({ value = '', onChange = function (event) {} }) { + return ( + + ); +} + +function Tune({ value = '', onChange = function (event) {} }) { + return ( + + ); +} + +function Level({ value = '', onChange = function (event) {} }) { + return ( + + ); +} + +function RateControl({ value = '', onChange = function (event) {} }) { + return ( + + ); +} + +function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) { + settings = init(settings); + stream = Helper.InitStream(stream); + skills = Helper.InitSkills(skills); + + const handleChange = (newSettings) => { + let automatic = false; + if (!newSettings) { + newSettings = settings; + automatic = true; + } + + onChange(newSettings, createMapping(newSettings, stream, skills), automatic); + }; + + const update = (what) => (event) => { + const newSettings = { + ...settings, + [what]: event.target.value, + }; + + handleChange(newSettings); + }; + + React.useEffect(() => { + handleChange(null); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +const coder = 'av1_nvenc'; +const name = 'AV1 (NVENC)'; +const codec = 'av1'; +const type = 'video'; +const hwaccel = true; + +function summarize(settings) { + return `${name}, ${settings.bitrate} kbit/s, ${settings.fps} FPS, Profile: ${settings.profile}`; +} + +function defaults(stream, skills) { + const settings = init({}); + + return { + settings: settings, + mapping: createMapping(settings, stream, skills), + }; +} + +export { coder, name, codec, type, hwaccel, summarize, defaults, Coder as component }; diff --git a/src/misc/coders/Encoders/video/h264_nvenc.js b/src/misc/coders/Encoders/video/h264_nvenc.js index f8bf818..db2c1e5 100644 --- a/src/misc/coders/Encoders/video/h264_nvenc.js +++ b/src/misc/coders/Encoders/video/h264_nvenc.js @@ -14,7 +14,8 @@ function init(initialState) { bitrate: '4096', fps: '25', gop: '2', - preset: 'medium', + preset: 'p4', + tune: 'll', profile: 'auto', level: 'auto', rc: 'auto', @@ -33,6 +34,8 @@ function createMapping(settings, stream, skills) { 'h264_nvenc', '-preset:v', `${settings.preset}`, + '-tune:v', + `${settings.tune}`, '-b:v', `${settings.bitrate}k`, '-maxrate', @@ -73,19 +76,24 @@ function createMapping(settings, stream, skills) { function Preset({ value = '', onChange = function (event) {} }) { return ( + ); +} + +function Tune({ value = '', onChange = function (event) {} }) { + return ( + ); } @@ -139,9 +147,6 @@ function RateControl({ value = '', onChange = function (event) {} }) { constqp vbr cbr - cbr_ld_hq - cbr_hq - vbr_hq ); } @@ -189,6 +194,9 @@ function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (s + + + diff --git a/src/misc/coders/Encoders/video/hevc_nvenc.js b/src/misc/coders/Encoders/video/hevc_nvenc.js new file mode 100644 index 0000000..e51f3ab --- /dev/null +++ b/src/misc/coders/Encoders/video/hevc_nvenc.js @@ -0,0 +1,229 @@ +import React from 'react'; + +import Grid from '@mui/material/Grid'; +import MenuItem from '@mui/material/MenuItem'; + +import { Trans } from '@lingui/macro'; + +import Select from '../../../Select'; +import Video from '../../settings/Video'; +import Helper from '../../helper'; + +function init(initialState) { + const state = { + bitrate: '4096', + fps: '25', + gop: '2', + preset: 'p4', + tune: 'll', + profile: 'auto', + level: 'auto', + rc: 'auto', + ...initialState, + }; + + return state; +} + +function createMapping(settings, stream, skills) { + stream = Helper.InitStream(stream); + skills = Helper.InitSkills(skills); + + const local = [ + '-codec:v', + 'hevc_nvenc', + '-preset:v', + `${settings.preset}`, + '-tune:v', + `${settings.tune}`, + '-b:v', + `${settings.bitrate}k`, + '-maxrate', + `${settings.bitrate}k`, + '-bufsize', + `${settings.bitrate}k`, + '-r', + `${settings.fps}`, + '-pix_fmt', + 'yuv420p', + ]; + + if (settings.gop !== 'auto') { + local.push('-g', `${Math.round(parseInt(settings.fps) * parseInt(settings.gop)).toFixed(0)}`); + } + + if (settings.profile !== 'auto') { + local.push('-profile:v', `${settings.profile}`); + } + + if (settings.level !== 'auto') { + local.push('-level:v', `${settings.level}`); + } + + if (settings.rc !== 'auto') { + local.push('-rc:v', `${settings.rc}`); + } + + const mapping = { + global: [], + local: local, + filter: [], + }; + + return mapping; +} + +function Preset({ value = '', onChange = function (event) {} }) { + return ( + + ); +} + +function Tune({ value = '', onChange = function (event) {} }) { + return ( + + ); +} + +function Profile({ value = '', onChange = function (event) {} }) { + return ( + + ); +} + +function Level({ value = '', onChange = function (event) {} }) { + return ( + + ); +} + +function RateControl({ value = '', onChange = function (event) {} }) { + return ( + + ); +} + +function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) { + settings = init(settings); + stream = Helper.InitStream(stream); + skills = Helper.InitSkills(skills); + + const handleChange = (newSettings) => { + let automatic = false; + if (!newSettings) { + newSettings = settings; + automatic = true; + } + + onChange(newSettings, createMapping(newSettings, stream, skills), automatic); + }; + + const update = (what) => (event) => { + const newSettings = { + ...settings, + [what]: event.target.value, + }; + + handleChange(newSettings); + }; + + React.useEffect(() => { + handleChange(null); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +const coder = 'hevc_nvenc'; +const name = 'HEVC (NVENC)'; +const codec = 'hevc'; +const type = 'video'; +const hwaccel = true; + +function summarize(settings) { + return `${name}, ${settings.bitrate} kbit/s, ${settings.fps} FPS, Profile: ${settings.profile}`; +} + +function defaults(stream, skills) { + const settings = init({}); + + return { + settings: settings, + mapping: createMapping(settings, stream, skills), + }; +} + +export { coder, name, codec, type, hwaccel, summarize, defaults, Coder as component };