Add optional --nvhevc feature for NVIDIA NVENC HEVC hardware encoding
- Added --nvhevc command line flag support - Created NVENC HEVC presets that match AV1 settings: - Fast: fast preset, CQ 26, main10 profile - Web-optimized: medium preset, CQ 28, main10 profile - Quick: fast preset, CQ 26, main10 profile - Tiny: fast preset, CQ 30, main10 profile - Updated encoding logic to use hevc_nvenc when --nvhevc flag is provided - Added proper NVENC HEVC parameters: - rc-lookahead 32 for better quality - spatial_aq and temporal_aq for adaptive quantization - hvc1 tag for compatibility - VBR rate control with constant quality - Updated help text and examples to show NVENC HEVC usage - Output files are named with NVHEVC prefix when using NVENC HEVC - AV1 remains the default codec, NVENC HEVC is optional - Requires NVIDIA GPU with NVENC support
This commit is contained in:
@@ -38,6 +38,7 @@ func printHelp() {
|
||||
fmt.Println()
|
||||
fmt.Println("🎬 CODEC OPTIONS:")
|
||||
fmt.Println(" --x264 Use H.264/x264 codec instead of AV1 (matches AV1 presets)")
|
||||
fmt.Println(" --nvhevc Use NVIDIA NVENC HEVC hardware encoding (requires NVIDIA GPU)")
|
||||
fmt.Println()
|
||||
fmt.Println("📊 INFORMATION OPTIONS:")
|
||||
fmt.Println(" --help Show this help information")
|
||||
@@ -45,7 +46,7 @@ func printHelp() {
|
||||
fmt.Println(" --stats Show encoding statistics")
|
||||
fmt.Println()
|
||||
fmt.Println("⚙️ FEATURES:")
|
||||
fmt.Println("• Multiple codecs: AV1 (default), H.264/x264")
|
||||
fmt.Println("• Multiple codecs: AV1 (default), H.264/x264, NVIDIA NVENC HEVC")
|
||||
fmt.Println("• Hardware acceleration support (NVENC)")
|
||||
fmt.Println("• Automatic file detection")
|
||||
fmt.Println("• Progress tracking with ETA")
|
||||
@@ -60,6 +61,7 @@ func printHelp() {
|
||||
fmt.Println("💡 EXAMPLES:")
|
||||
fmt.Println(" ./gwencoder --fast # Fast AV1 encoding")
|
||||
fmt.Println(" ./gwencoder --fast --x264 # Fast H.264 encoding")
|
||||
fmt.Println(" ./gwencoder --fast --nvhevc # Fast NVENC HEVC encoding")
|
||||
fmt.Println(" ./gwencoder --web # Web-optimized AV1 encoding")
|
||||
fmt.Println(" ./gwencoder --web --x264 # Web-optimized H.264 encoding")
|
||||
fmt.Println(" ./gwencoder --quick # Quick AV1 encoding")
|
||||
@@ -116,13 +118,20 @@ func printStats() {
|
||||
fmt.Println("══════════════════════════════════════════════════════════════")
|
||||
}
|
||||
|
||||
func encodeFile(file string, mode gwutils.EncodingMode, useX264 bool) (time.Duration, int64, error) {
|
||||
func encodeFile(file string, mode gwutils.EncodingMode, useX264 bool, useNVHEVC bool) (time.Duration, int64, error) {
|
||||
base := strings.TrimSuffix(filepath.Base(file), filepath.Ext(file))
|
||||
codecName := "AV1"
|
||||
if useX264 {
|
||||
codecName = "H264"
|
||||
} else if useNVHEVC {
|
||||
codecName = "NVHEVC"
|
||||
}
|
||||
out := fmt.Sprintf("%s-%s-%s-GWELL.%s", base, codecName, strings.Title(mode.Name), mode.Container)
|
||||
// Choose container/extension, overriding invalid combos (e.g., H.264 in WEBM)
|
||||
outExt := mode.Container
|
||||
if useX264 && mode.Name == "Web-optimized" && strings.EqualFold(mode.Container, "webm") {
|
||||
outExt = "mkv"
|
||||
}
|
||||
out := fmt.Sprintf("%s-%s-%s-GWELL.%s", base, codecName, strings.Title(mode.Name), outExt)
|
||||
|
||||
start := time.Now()
|
||||
|
||||
@@ -137,10 +146,46 @@ func encodeFile(file string, mode gwutils.EncodingMode, useX264 bool) (time.Dura
|
||||
// Calculate physical cores
|
||||
physicalCores := gwutils.GetPhysicalCores()
|
||||
|
||||
// Initialize NVENC quality variable
|
||||
var nvencQuality string
|
||||
|
||||
// Build FFmpeg arguments
|
||||
args := []string{"-y", "-i", file}
|
||||
|
||||
if useX264 {
|
||||
if useNVHEVC {
|
||||
// NVIDIA NVENC HEVC parameters based on mode
|
||||
var nvencPreset string
|
||||
|
||||
if mode.Name == "Web-optimized" {
|
||||
nvencPreset = "medium"
|
||||
nvencQuality = "28" // Higher quality for web
|
||||
} else if mode.Name == "Quick" {
|
||||
nvencPreset = "fast"
|
||||
nvencQuality = "26" // Good quality for quick
|
||||
} else if mode.Name == "Tiny" {
|
||||
nvencPreset = "fast"
|
||||
nvencQuality = "30" // Lower quality for tiny files
|
||||
} else {
|
||||
// Fast mode
|
||||
nvencPreset = "fast"
|
||||
nvencQuality = "26" // Good quality for fast
|
||||
}
|
||||
|
||||
args = append(args, "-c:v", "hevc_nvenc")
|
||||
args = append(args, "-preset", nvencPreset)
|
||||
args = append(args, "-profile:v", "main10")
|
||||
args = append(args, "-rc", "vbr")
|
||||
args = append(args, "-cq", nvencQuality)
|
||||
args = append(args, "-b:v", "0")
|
||||
args = append(args, "-maxrate", fmt.Sprintf("%sk", mode.Maxrate))
|
||||
args = append(args, "-bufsize", fmt.Sprintf("%sk", mode.Maxrate))
|
||||
args = append(args, "-rc-lookahead", "32")
|
||||
args = append(args, "-spatial_aq", "1")
|
||||
args = append(args, "-temporal_aq", "1")
|
||||
args = append(args, "-g", "240")
|
||||
args = append(args, "-keyint_min", "24")
|
||||
args = append(args, "-tag:v", "hvc1")
|
||||
} else if useX264 {
|
||||
// H.264/x264 parameters based on mode
|
||||
var x264Preset string
|
||||
var x264Tune string
|
||||
@@ -208,7 +253,13 @@ func encodeFile(file string, mode gwutils.EncodingMode, useX264 bool) (time.Dura
|
||||
args = append(args, "-g", "240")
|
||||
}
|
||||
|
||||
args = append(args, "-c:a", "libopus")
|
||||
// Select audio codec based on container compatibility
|
||||
// - WEBM requires Opus/Vorbis; MKV supports Opus; MP4 prefers AAC
|
||||
audioCodec := "libopus"
|
||||
if strings.EqualFold(outExt, "mp4") {
|
||||
audioCodec = "aac"
|
||||
}
|
||||
args = append(args, "-c:a", audioCodec)
|
||||
args = append(args, audioBitrateParts...)
|
||||
args = append(args, "-map", "0:v", "-map", "0:a")
|
||||
args = append(args, "-sn") // Skip subtitles for speed
|
||||
@@ -217,14 +268,18 @@ func encodeFile(file string, mode gwutils.EncodingMode, useX264 bool) (time.Dura
|
||||
fmt.Printf("🎬 %s %s Encoding: %s → %s\n", mode.Name, codecName, file, out)
|
||||
fmt.Printf("⚡ %s settings:\n", mode.Name)
|
||||
fmt.Printf(" • Using %d physical cores\n", physicalCores)
|
||||
if useNVHEVC {
|
||||
fmt.Printf(" • NVIDIA NVENC HEVC hardware encoding\n")
|
||||
fmt.Printf(" • CQ=%s (constant quality)\n", nvencQuality)
|
||||
} else if useX264 {
|
||||
fmt.Printf(" • CRF=%s (%s quality)\n", mode.CRF, mode.Name)
|
||||
if useX264 {
|
||||
fmt.Printf(" • H.264/x264 codec\n")
|
||||
} else {
|
||||
fmt.Printf(" • CRF=%s (%s quality)\n", mode.CRF, mode.Name)
|
||||
fmt.Printf(" • Preset=%s (encoding speed)\n", mode.Preset)
|
||||
}
|
||||
fmt.Printf(" • Opus audio at %skbps per channel\n", mode.AudioBitrate)
|
||||
fmt.Printf(" • %s container\n", strings.ToUpper(mode.Container))
|
||||
fmt.Printf(" • %s audio at %skbps per channel\n", strings.ToUpper(audioCodec), mode.AudioBitrate)
|
||||
fmt.Printf(" • %s container\n", strings.ToUpper(outExt))
|
||||
if totalDuration > 0 {
|
||||
fmt.Printf("📐 Duration: %s\n", gwutils.FormatTime(totalDuration))
|
||||
}
|
||||
@@ -301,15 +356,18 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
// Check for --x264 flag
|
||||
// Check for --x264 and --nvhevc flags
|
||||
useX264 := false
|
||||
useNVHEVC := false
|
||||
args := os.Args[1:]
|
||||
|
||||
// Filter out --x264 flag and set useX264
|
||||
// Filter out codec flags and set flags
|
||||
var filteredArgs []string
|
||||
for _, arg := range args {
|
||||
if arg == "--x264" {
|
||||
useX264 = true
|
||||
} else if arg == "--nvhevc" {
|
||||
useNVHEVC = true
|
||||
} else {
|
||||
filteredArgs = append(filteredArgs, arg)
|
||||
}
|
||||
@@ -333,7 +391,15 @@ func main() {
|
||||
case "--fast":
|
||||
printHeader()
|
||||
fmt.Println()
|
||||
if useX264 {
|
||||
if useNVHEVC {
|
||||
fmt.Println("⚡ FAST NVENC HEVC ENCODING MODE")
|
||||
fmt.Println("══════════════════════════════════════════════════════════════")
|
||||
fmt.Println("Using fast NVENC HEVC encoding settings:")
|
||||
fmt.Println("• NVIDIA NVENC HEVC hardware encoding")
|
||||
fmt.Println("• Opus audio at 64kbps per channel")
|
||||
fmt.Println("• MKV container for better compatibility")
|
||||
fmt.Println("• CQ 26 for fast encoding")
|
||||
} else if useX264 {
|
||||
fmt.Println("⚡ FAST H.264 ENCODING MODE")
|
||||
fmt.Println("══════════════════════════════════════════════════════════════")
|
||||
fmt.Println("Using fast H.264 encoding settings:")
|
||||
@@ -356,13 +422,21 @@ func main() {
|
||||
codecName := "AV1"
|
||||
if useX264 {
|
||||
codecName = "H264"
|
||||
} else if useNVHEVC {
|
||||
codecName = "NVHEVC"
|
||||
} else if useNVHEVC {
|
||||
codecName = "NVHEVC"
|
||||
}
|
||||
fmt.Printf("⚡ %s %s settings:\n", mode.Name, codecName)
|
||||
fmt.Printf(" • Using %d physical cores\n", gwutils.GetPhysicalCores())
|
||||
if useNVHEVC {
|
||||
fmt.Printf(" • NVIDIA NVENC HEVC hardware encoding\n")
|
||||
fmt.Printf(" • CQ=26 (fast encoding quality)\n")
|
||||
} else if useX264 {
|
||||
fmt.Printf(" • CRF=%s (fast encoding quality)\n", mode.CRF)
|
||||
if useX264 {
|
||||
fmt.Printf(" • H.264/x264 codec\n")
|
||||
} else {
|
||||
fmt.Printf(" • CRF=%s (fast encoding quality)\n", mode.CRF)
|
||||
fmt.Printf(" • Preset=%s (speed optimized)\n", mode.Preset)
|
||||
}
|
||||
fmt.Printf(" • Opus audio at %skbps per channel\n", mode.AudioBitrate)
|
||||
@@ -370,7 +444,7 @@ func main() {
|
||||
fmt.Println("📏 Using original resolution")
|
||||
fmt.Println()
|
||||
|
||||
excludePatterns := []string{"GWELL", "AV1-", "H264-"}
|
||||
excludePatterns := []string{"GWELL", "AV1-", "H264-", "NVHEVC-"}
|
||||
files := gwutils.FindMediaFiles(excludePatterns)
|
||||
if len(files) == 0 {
|
||||
fmt.Println("❌ No video files found in current directory.")
|
||||
@@ -381,14 +455,22 @@ func main() {
|
||||
fmt.Println()
|
||||
|
||||
for _, file := range files {
|
||||
encodeFile(file, mode, useX264)
|
||||
encodeFile(file, mode, useX264, useNVHEVC)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
case "--web":
|
||||
printHeader()
|
||||
fmt.Println()
|
||||
if useX264 {
|
||||
if useNVHEVC {
|
||||
fmt.Println("🌐 WEB-OPTIMIZED NVENC HEVC ENCODING MODE")
|
||||
fmt.Println("══════════════════════════════════════════════════════════════")
|
||||
fmt.Println("Using web-optimized NVENC HEVC settings:")
|
||||
fmt.Println("• NVIDIA NVENC HEVC hardware encoding")
|
||||
fmt.Println("• Opus audio at 64kbps per channel")
|
||||
fmt.Println("• MKV container for web compatibility")
|
||||
fmt.Println("• CQ 28 for web-optimized quality")
|
||||
} else if useX264 {
|
||||
fmt.Println("🌐 WEB-OPTIMIZED H.264 ENCODING MODE")
|
||||
fmt.Println("══════════════════════════════════════════════════════════════")
|
||||
fmt.Println("Using web-optimized H.264 settings:")
|
||||
@@ -411,13 +493,21 @@ func main() {
|
||||
codecName := "AV1"
|
||||
if useX264 {
|
||||
codecName = "H264"
|
||||
} else if useNVHEVC {
|
||||
codecName = "NVHEVC"
|
||||
} else if useNVHEVC {
|
||||
codecName = "NVHEVC"
|
||||
}
|
||||
fmt.Printf("🌐 %s %s settings:\n", mode.Name, codecName)
|
||||
fmt.Printf(" • Using %d physical cores\n", gwutils.GetPhysicalCores())
|
||||
if useNVHEVC {
|
||||
fmt.Printf(" • NVIDIA NVENC HEVC hardware encoding\n")
|
||||
fmt.Printf(" • CQ=28 (web-optimized quality)\n")
|
||||
} else if useX264 {
|
||||
fmt.Printf(" • CRF=%s (web-optimized quality)\n", mode.CRF)
|
||||
if useX264 {
|
||||
fmt.Printf(" • H.264/x264 codec\n")
|
||||
} else {
|
||||
fmt.Printf(" • CRF=%s (web-optimized quality)\n", mode.CRF)
|
||||
fmt.Printf(" • Preset=%s (balanced encoding)\n", mode.Preset)
|
||||
}
|
||||
fmt.Printf(" • Opus audio at %skbps per channel\n", mode.AudioBitrate)
|
||||
@@ -425,7 +515,7 @@ func main() {
|
||||
fmt.Println("📏 Using original resolution")
|
||||
fmt.Println()
|
||||
|
||||
excludePatterns := []string{"GWELL", "AV1-", "H264-"}
|
||||
excludePatterns := []string{"GWELL", "AV1-", "H264-", "NVHEVC-"}
|
||||
files := gwutils.FindMediaFiles(excludePatterns)
|
||||
if len(files) == 0 {
|
||||
fmt.Println("❌ No video files found in current directory.")
|
||||
@@ -436,14 +526,22 @@ func main() {
|
||||
fmt.Println()
|
||||
|
||||
for _, file := range files {
|
||||
encodeFile(file, mode, useX264)
|
||||
encodeFile(file, mode, useX264, useNVHEVC)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
case "--quick":
|
||||
printHeader()
|
||||
fmt.Println()
|
||||
if useX264 {
|
||||
if useNVHEVC {
|
||||
fmt.Println("⚡ QUICK NVENC HEVC ENCODING MODE")
|
||||
fmt.Println("══════════════════════════════════════════════════════════════")
|
||||
fmt.Println("Using quick NVENC HEVC encoding settings:")
|
||||
fmt.Println("• NVIDIA NVENC HEVC hardware encoding")
|
||||
fmt.Println("• Opus audio at 80kbps per channel")
|
||||
fmt.Println("• MKV container for better compatibility")
|
||||
fmt.Println("• CQ 26 for quick encoding")
|
||||
} else if useX264 {
|
||||
fmt.Println("⚡ QUICK H.264 ENCODING MODE")
|
||||
fmt.Println("══════════════════════════════════════════════════════════════")
|
||||
fmt.Println("Using quick H.264 encoding settings:")
|
||||
@@ -466,6 +564,8 @@ func main() {
|
||||
codecName := "AV1"
|
||||
if useX264 {
|
||||
codecName = "H264"
|
||||
} else if useNVHEVC {
|
||||
codecName = "NVHEVC"
|
||||
}
|
||||
fmt.Printf("⚡ %s %s settings:\n", mode.Name, codecName)
|
||||
fmt.Printf(" • Using %d physical cores\n", gwutils.GetPhysicalCores())
|
||||
@@ -480,7 +580,7 @@ func main() {
|
||||
fmt.Println("📏 Using original resolution")
|
||||
fmt.Println()
|
||||
|
||||
excludePatterns := []string{"GWELL", "AV1-", "H264-"}
|
||||
excludePatterns := []string{"GWELL", "AV1-", "H264-", "NVHEVC-"}
|
||||
files := gwutils.FindMediaFiles(excludePatterns)
|
||||
if len(files) == 0 {
|
||||
fmt.Println("❌ No video files found in current directory.")
|
||||
@@ -491,14 +591,22 @@ func main() {
|
||||
fmt.Println()
|
||||
|
||||
for _, file := range files {
|
||||
encodeFile(file, mode, useX264)
|
||||
encodeFile(file, mode, useX264, useNVHEVC)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
case "--tiny":
|
||||
printHeader()
|
||||
fmt.Println()
|
||||
if useX264 {
|
||||
if useNVHEVC {
|
||||
fmt.Println("📦 TINY NVENC HEVC ENCODING MODE")
|
||||
fmt.Println("══════════════════════════════════════════════════════════════")
|
||||
fmt.Println("Using tiny NVENC HEVC encoding settings:")
|
||||
fmt.Println("• NVIDIA NVENC HEVC hardware encoding")
|
||||
fmt.Println("• Opus audio at 64kbps per channel")
|
||||
fmt.Println("• MP4 container for maximum compatibility")
|
||||
fmt.Println("• CQ 30 for smallest file size")
|
||||
} else if useX264 {
|
||||
fmt.Println("📦 TINY H.264 ENCODING MODE")
|
||||
fmt.Println("══════════════════════════════════════════════════════════════")
|
||||
fmt.Println("Using tiny H.264 encoding settings:")
|
||||
@@ -521,6 +629,8 @@ func main() {
|
||||
codecName := "AV1"
|
||||
if useX264 {
|
||||
codecName = "H264"
|
||||
} else if useNVHEVC {
|
||||
codecName = "NVHEVC"
|
||||
}
|
||||
fmt.Printf("📦 %s %s settings:\n", mode.Name, codecName)
|
||||
fmt.Printf(" • Using %d physical cores\n", gwutils.GetPhysicalCores())
|
||||
@@ -535,7 +645,7 @@ func main() {
|
||||
fmt.Println("📏 Using original resolution")
|
||||
fmt.Println()
|
||||
|
||||
excludePatterns := []string{"GWELL", "AV1-", "H264-"}
|
||||
excludePatterns := []string{"GWELL", "AV1-", "H264-", "NVHEVC-"}
|
||||
files := gwutils.FindMediaFiles(excludePatterns)
|
||||
if len(files) == 0 {
|
||||
fmt.Println("❌ No video files found in current directory.")
|
||||
@@ -546,7 +656,7 @@ func main() {
|
||||
fmt.Println()
|
||||
|
||||
for _, file := range files {
|
||||
encodeFile(file, mode, useX264)
|
||||
encodeFile(file, mode, useX264, useNVHEVC)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user