功能完善
UI改进
This commit is contained in:
+29
-10
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
@@ -141,6 +142,24 @@ func escapeFilterPath(path string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
// qualityArg maps a CRF-like quality value to encoder-specific arguments.
|
||||
// Software encoders use -crf, NVENC uses -cq, QSV uses -global_quality,
|
||||
// and AMF uses -qp_i/-qp_p for constant-quality encoding.
|
||||
func qualityArg(codec string, crf int) []string {
|
||||
v := strconv.Itoa(crf)
|
||||
lower := strings.ToLower(codec)
|
||||
switch {
|
||||
case strings.Contains(lower, "nvenc"):
|
||||
return []string{"-cq", v}
|
||||
case strings.Contains(lower, "qsv"):
|
||||
return []string{"-global_quality", v}
|
||||
case strings.Contains(lower, "amf"):
|
||||
return []string{"-qp_i", v, "-qp_p", v}
|
||||
default:
|
||||
return []string{"-crf", v}
|
||||
}
|
||||
}
|
||||
|
||||
func tailLines(s string, n int) string {
|
||||
lines := strings.Split(s, "\n")
|
||||
if len(lines) > n {
|
||||
@@ -197,11 +216,11 @@ func BuildEncodeArgs(input string, output string, s EncodeSettings, hwAccel stri
|
||||
|
||||
args = append(args, "-i", input)
|
||||
|
||||
if s.HWEncoder != "" {
|
||||
args = append(args, "-c:v", s.HWEncoder)
|
||||
} else {
|
||||
args = append(args, "-c:v", s.VideoCodec)
|
||||
codec := s.HWEncoder
|
||||
if codec == "" {
|
||||
codec = s.VideoCodec
|
||||
}
|
||||
args = append(args, "-c:v", codec)
|
||||
|
||||
if s.Preset != "" {
|
||||
// libx* uses named presets (fast, medium…), HW encoders use p1–p7
|
||||
@@ -209,7 +228,7 @@ func BuildEncodeArgs(input string, output string, s EncodeSettings, hwAccel stri
|
||||
}
|
||||
|
||||
if s.CRF > 0 {
|
||||
args = append(args, "-crf", fmt.Sprintf("%d", s.CRF))
|
||||
args = append(args, qualityArg(codec, s.CRF)...)
|
||||
} else if s.VideoBitrate != "" {
|
||||
args = append(args, "-b:v", s.VideoBitrate)
|
||||
}
|
||||
@@ -327,16 +346,16 @@ func BuildSubtitleArgs(input string, output string, s SubtitleSettings, encode E
|
||||
args = append(args, "-vf", strings.Join(subFilters, ","))
|
||||
}
|
||||
|
||||
if encode.HWEncoder != "" {
|
||||
args = append(args, "-c:v", encode.HWEncoder)
|
||||
} else {
|
||||
args = append(args, "-c:v", encode.VideoCodec)
|
||||
codec := encode.HWEncoder
|
||||
if codec == "" {
|
||||
codec = encode.VideoCodec
|
||||
}
|
||||
args = append(args, "-c:v", codec)
|
||||
if encode.Preset != "" {
|
||||
args = append(args, "-preset", encode.Preset)
|
||||
}
|
||||
if encode.CRF > 0 {
|
||||
args = append(args, "-crf", fmt.Sprintf("%d", encode.CRF))
|
||||
args = append(args, qualityArg(codec, encode.CRF)...)
|
||||
} else if encode.VideoBitrate != "" {
|
||||
args = append(args, "-b:v", encode.VideoBitrate)
|
||||
}
|
||||
|
||||
+43
-12
@@ -19,28 +19,59 @@ type Progress struct {
|
||||
Percent float64 `json:"percent"`
|
||||
}
|
||||
|
||||
var progressRe = regexp.MustCompile(
|
||||
`frame=\s*(\d+)\s+fps=\s*([\d.]+)\s+q=\s*([\d.-]+)\s+(?:size=\s*(\S+)\s+)?time=\s*([\d:.]+)\s+bitrate=\s*(\S+)\s+speed=\s*(\S+)`,
|
||||
)
|
||||
// kvPairRe matches a single key=value token from an ffmpeg progress line.
|
||||
// Keys are alphanumeric (e.g. frame, dup, Lsize). Values are the contiguous
|
||||
// non-whitespace characters after the '=' — this handles N/A, kbits/s, times,
|
||||
// and numeric values equally.
|
||||
var kvPairRe = regexp.MustCompile(`(\w+)=\s*(\S+)`)
|
||||
|
||||
// parseProgressLine parses an ffmpeg stderr progress line into a Progress struct.
|
||||
// Instead of relying on a fixed field order, it extracts every key=value pair
|
||||
// and picks the ones we care about. This tolerates extra fields (dup, drop,
|
||||
// Lsize vs size, etc.) that appear between standard ffmpeg releases.
|
||||
func parseProgressLine(line string) (Progress, bool) {
|
||||
m := progressRe.FindStringSubmatch(line)
|
||||
if m == nil {
|
||||
pairs := kvPairRe.FindAllStringSubmatch(line, -1)
|
||||
if len(pairs) == 0 {
|
||||
return Progress{}, false
|
||||
}
|
||||
|
||||
frame, _ := strconv.ParseInt(m[1], 10, 64)
|
||||
fps, _ := strconv.ParseFloat(m[2], 64)
|
||||
q, _ := strconv.ParseFloat(m[3], 64)
|
||||
kv := make(map[string]string, len(pairs))
|
||||
for _, m := range pairs {
|
||||
kv[m[1]] = m[2]
|
||||
}
|
||||
|
||||
// Frame is required — without it we don't have a real progress line.
|
||||
frameStr, ok := kv["frame"]
|
||||
if !ok {
|
||||
return Progress{}, false
|
||||
}
|
||||
|
||||
frame, _ := strconv.ParseInt(frameStr, 10, 64)
|
||||
fps, _ := strconv.ParseFloat(kv["fps"], 64)
|
||||
q, _ := strconv.ParseFloat(kv["q"], 64)
|
||||
|
||||
// ffmpeg emits either "size" or "Lsize" depending on context.
|
||||
size := kv["size"]
|
||||
if size == "" {
|
||||
size = kv["Lsize"]
|
||||
}
|
||||
if size == "N/A" {
|
||||
size = ""
|
||||
}
|
||||
|
||||
bitrate := kv["bitrate"]
|
||||
if bitrate == "N/A" {
|
||||
bitrate = ""
|
||||
}
|
||||
|
||||
return Progress{
|
||||
Frame: frame,
|
||||
FPS: fps,
|
||||
Q: q,
|
||||
Size: m[4],
|
||||
Time: m[5],
|
||||
Bitrate: m[6],
|
||||
Speed: m[7],
|
||||
Size: size,
|
||||
Time: kv["time"],
|
||||
Bitrate: bitrate,
|
||||
Speed: kv["speed"],
|
||||
}, true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user