-
Notifications
You must be signed in to change notification settings - Fork 8
/
ffmpeg.go
142 lines (114 loc) · 4.05 KB
/
ffmpeg.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package main
import (
"bufio"
"fmt"
"io"
"slices"
"strconv"
"strings"
g "github.com/AllenDang/giu"
)
func handleUpscalingLogs(stderr io.ReadCloser, anime Anime) string {
scanner := bufio.NewScanner(stderr)
scanner.Split(bufio.ScanRunes)
progressLine := "frame= 0 fps=0.0 q=0.0 size= 0kB time=N/A bitrate=N/A speed=N/A"
ffmpegLogs := ""
line := ""
for scanner.Scan() {
char := scanner.Text()
if char != "\r" {
// It's still the same line
line += char
continue
}
if !strings.HasPrefix(line, "frame=") {
trim := strings.ReplaceAll(line, "\r", "")
trim = strings.ReplaceAll(trim, "\n", "")
logDebug(trim, false)
ffmpegLogs += line
line = ""
continue
}
// It's line with speed and time
time := strings.Split(readOutputParameter(line, "time", "bitrate"), ".")[0]
millis := durationToMillis(time)
gui.Progress = float32(millis) / float32(anime.Length)
// FFMPEG may not report time, we must calculate it manually
if time == "N/A" && len(strings.Split(line, "frame=")) > 1 {
frame, _ := strconv.ParseFloat(readOutputParameter(line, "frame", "fps"), 32)
gui.Progress = float32(frame) / float32(anime.TotalFrames)
fps, _ := strconv.ParseFloat(readOutputParameter(line, "fps", "q"), 32)
gui.CurrentSpeed = fmt.Sprintf("Speed: %.2fx", fps/anime.FrameRate)
leftFrames := anime.TotalFrames - int(frame)
leftMillis := int64((float32(leftFrames) / float32(fps)) * 1000)
gui.Eta = fmt.Sprintf("ETA: %s", formatMillis(leftMillis))
}
// Speed
speedRaw := strings.ReplaceAll(readOutputParameter(line, "speed", ""), "x", "")
if strings.Contains(speedRaw, ".") {
gui.CurrentSpeed = fmt.Sprintf("Speed: %s", speedRaw)
speedValue, _ := strconv.ParseFloat(speedRaw, 32)
// Just for safety
if speedValue != 0 {
etaMillis := float64(anime.Length-millis) / speedValue
gui.Eta = fmt.Sprintf("ETA: %s", formatMillis(int64(etaMillis)))
}
}
// Progress bar
rounded := int(gui.Progress * 100)
if rounded == 99 {
gui.Progress = 1
gui.ProgressLabel = "100%"
} else {
gui.ProgressLabel = fmt.Sprintf("%d%%", rounded)
}
ffmpegLogs = strings.ReplaceAll(ffmpegLogs, progressLine, line)
gui.Logs = strings.ReplaceAll(gui.Logs, progressLine, line)
progressLine = line
line = ""
g.Update()
}
return ffmpegLogs
}
func buildUpscalingParams(anime Anime, resolution Resolution, shader Shader, outputPath string) []string {
videoCodec = availableEncoders[settings.Encoder].FfmpegValue
params := []string{
"-hide_banner", // Hide banner with FFMPEG version
"-y", // Override output file
}
if !settings.CompatibilityMode {
params = append(params, hwaccelParams...) // Apply selected video encoder and hardware acceleration parameters
}
params = append(params,
"-i", fmt.Sprintf("%s", anime.Path), // Path to input file
"-init_hw_device", "vulkan",
"-vf", fmt.Sprintf("format=%s,hwupload,libplacebo=w=%d:h=%d:upscaler=ewa_lanczos:custom_shader_path=%s,format=%s",
anime.PixelFormat, resolution.Width, resolution.Height, shader.Path, anime.PixelFormat),
"-c:a", "copy", // Copy all audio streams
"-c:s", "copy", // Copy all subtitles streams
"-c:d", "copy", // Copy all data streams
)
for _, stream := range anime.Streams {
if !slices.Contains(codecsBlacklist, stream.CodecName) {
params = append(params, "-map", fmt.Sprintf("0:%d", stream.Index))
}
}
params = append(params,
"-crf", fmt.Sprintf("%d", settings.Crf), // Constant Rate Factor (CRF) for encoder)
)
if !settings.CompatibilityMode {
params = append(params, "-c:v", videoCodec) // Apply selected video codec
// Preset for encoder, supported only by H264/H265
if videoCodec != "libsvtav1" {
params = append(params, "-preset", "slow")
}
}
params = append(params, outputPath)
return params
}
func readOutputParameter(line string, parameter string, nextParameter string) string {
if nextParameter == "" {
return strings.ReplaceAll(strings.Split(line, parameter+"=")[1], " ", "")
}
return strings.ReplaceAll(strings.Split(strings.Split(line, parameter+"=")[1], nextParameter)[0], " ", "")
}