1. 增加配置持久化
2. 完善细节
This commit is contained in:
+82
-22
@@ -56,7 +56,7 @@ func (e *Executor) Run(args []string, totalDuration float64) (context.CancelFunc
|
||||
}
|
||||
|
||||
progressCh := make(chan Progress, 16)
|
||||
logCh := make(chan string, 64)
|
||||
logCh := make(chan string, 512)
|
||||
errCh := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
@@ -134,6 +134,13 @@ func scanLinesOrCR(data []byte, atEOF bool) (advance int, token []byte, err erro
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
// escapeFilterPath escapes a file path for use inside ffmpeg filter arguments.
|
||||
func escapeFilterPath(path string) string {
|
||||
s := strings.ReplaceAll(path, "\\", "/")
|
||||
s = strings.ReplaceAll(s, ":", "\\:")
|
||||
return s
|
||||
}
|
||||
|
||||
func tailLines(s string, n int) string {
|
||||
lines := strings.Split(s, "\n")
|
||||
if len(lines) > n {
|
||||
@@ -159,8 +166,10 @@ type EncodeSettings struct {
|
||||
|
||||
// RemuxSettings contains remux parameters.
|
||||
type RemuxSettings struct {
|
||||
OutputFormat string `json:"outputFormat"`
|
||||
MapStreams []int `json:"mapStreams"`
|
||||
OutputFormat string `json:"outputFormat"`
|
||||
MapStreams []int `json:"mapStreams"`
|
||||
SubFiles []string `json:"subFiles"`
|
||||
AudioFiles []string `json:"audioFiles"`
|
||||
}
|
||||
|
||||
// SubtitleSettings contains subtitle burn-in parameters.
|
||||
@@ -170,10 +179,12 @@ type SubtitleSettings struct {
|
||||
|
||||
// SubTrack represents a subtitle track to burn.
|
||||
type SubTrack struct {
|
||||
Source string `json:"source"`
|
||||
Index int `json:"index"`
|
||||
FilePath string `json:"filePath"`
|
||||
Language string `json:"language"`
|
||||
Source string `json:"source"`
|
||||
Index int `json:"index"`
|
||||
FilePath string `json:"filePath"`
|
||||
Language string `json:"language"`
|
||||
Alignment int `json:"alignment"` // 2=bottom, 6=top, 10=middle; 0=unspecified
|
||||
MarginV int `json:"marginV"` // vertical margin in pixels
|
||||
}
|
||||
|
||||
// BuildEncodeArgs builds ffmpeg arguments for re-encoding.
|
||||
@@ -192,7 +203,8 @@ func BuildEncodeArgs(input string, output string, s EncodeSettings, hwAccel stri
|
||||
args = append(args, "-c:v", s.VideoCodec)
|
||||
}
|
||||
|
||||
if s.Preset != "" && strings.HasPrefix(s.VideoCodec, "libx") {
|
||||
if s.Preset != "" {
|
||||
// libx* uses named presets (fast, medium…), HW encoders use p1–p7
|
||||
args = append(args, "-preset", s.Preset)
|
||||
}
|
||||
|
||||
@@ -202,8 +214,16 @@ func BuildEncodeArgs(input string, output string, s EncodeSettings, hwAccel stri
|
||||
args = append(args, "-b:v", s.VideoBitrate)
|
||||
}
|
||||
|
||||
if s.Width > 0 && s.Height > 0 {
|
||||
args = append(args, "-vf", fmt.Sprintf("scale=%d:%d", s.Width, s.Height))
|
||||
if s.Width > 0 || s.Height > 0 {
|
||||
w := fmt.Sprintf("%d", s.Width)
|
||||
h := fmt.Sprintf("%d", s.Height)
|
||||
if s.Width <= 0 {
|
||||
w = "-1"
|
||||
}
|
||||
if s.Height <= 0 {
|
||||
h = "-1"
|
||||
}
|
||||
args = append(args, "-vf", fmt.Sprintf("scale=%s:%s", w, h))
|
||||
}
|
||||
if s.FPS > 0 {
|
||||
args = append(args, "-r", fmt.Sprintf("%.2f", s.FPS))
|
||||
@@ -228,6 +248,21 @@ func BuildRemuxArgs(input string, output string, s RemuxSettings, hwAccel string
|
||||
args = append(args, "-hwaccel", hwAccel)
|
||||
}
|
||||
args = append(args, "-i", input)
|
||||
|
||||
// Add external subtitle and audio files as additional inputs
|
||||
extIdx := 1
|
||||
for _, sf := range s.SubFiles {
|
||||
if sf != "" {
|
||||
args = append(args, "-i", sf)
|
||||
}
|
||||
}
|
||||
for _, af := range s.AudioFiles {
|
||||
if af != "" {
|
||||
args = append(args, "-i", af)
|
||||
}
|
||||
}
|
||||
|
||||
// Map streams from main input
|
||||
if len(s.MapStreams) > 0 {
|
||||
for _, idx := range s.MapStreams {
|
||||
args = append(args, "-map", fmt.Sprintf("0:%d", idx))
|
||||
@@ -235,32 +270,57 @@ func BuildRemuxArgs(input string, output string, s RemuxSettings, hwAccel string
|
||||
} else {
|
||||
args = append(args, "-map", "0")
|
||||
}
|
||||
args = append(args, "-c", "copy")
|
||||
if s.OutputFormat != "" {
|
||||
args = append(args, "-f", s.OutputFormat)
|
||||
|
||||
// Map subtitle streams from external files
|
||||
for range s.SubFiles {
|
||||
args = append(args, "-map", fmt.Sprintf("%d:s", extIdx))
|
||||
extIdx++
|
||||
}
|
||||
// (progress flags removed — ffmpeg outputs to stderr by default)
|
||||
// Map audio streams from external files
|
||||
for range s.AudioFiles {
|
||||
args = append(args, "-map", fmt.Sprintf("%d:a", extIdx))
|
||||
extIdx++
|
||||
}
|
||||
|
||||
args = append(args, "-c", "copy")
|
||||
// MP4 needs mov_text for embedded subtitles
|
||||
if len(s.SubFiles) > 0 && s.OutputFormat == "mp4" {
|
||||
args = append(args, "-c:s", "mov_text")
|
||||
}
|
||||
// Don't force -f; let ffmpeg determine format from output extension
|
||||
args = append(args, output)
|
||||
return args
|
||||
}
|
||||
|
||||
// BuildSubtitleArgs builds ffmpeg arguments for subtitle burn-in.
|
||||
// NOTE: -hwaccel is deliberately excluded — the subtitles filter (libass)
|
||||
// requires CPU frames and hangs when fed GPU surfaces.
|
||||
func BuildSubtitleArgs(input string, output string, s SubtitleSettings, encode EncodeSettings, hwAccel string) []string {
|
||||
args := []string{"-y"}
|
||||
|
||||
if hwAccel != "" {
|
||||
args = append(args, "-hwaccel", hwAccel)
|
||||
}
|
||||
|
||||
args = append(args, "-i", input)
|
||||
|
||||
var subFilters []string
|
||||
for _, sub := range s.Subtitles {
|
||||
if sub.Source == "external" && sub.FilePath != "" {
|
||||
escaped := strings.ReplaceAll(sub.FilePath, "\\", "/")
|
||||
escaped = strings.ReplaceAll(escaped, ":", "\\:")
|
||||
subFilters = append(subFilters, fmt.Sprintf("subtitles='%s'", escaped))
|
||||
// Internal subtitles are pre-extracted to temp files by runTask.
|
||||
// We only handle external file paths here.
|
||||
if sub.FilePath == "" {
|
||||
continue
|
||||
}
|
||||
escaped := escapeFilterPath(sub.FilePath)
|
||||
filter := fmt.Sprintf("subtitles='%s'", escaped)
|
||||
// Append force_style for position control (works with ASS/SSA subtitles)
|
||||
var styles []string
|
||||
if sub.Alignment > 0 {
|
||||
styles = append(styles, fmt.Sprintf("Alignment=%d", sub.Alignment))
|
||||
}
|
||||
if sub.MarginV > 0 {
|
||||
styles = append(styles, fmt.Sprintf("MarginV=%d", sub.MarginV))
|
||||
}
|
||||
if len(styles) > 0 {
|
||||
filter += fmt.Sprintf(":force_style='%s'", strings.Join(styles, ","))
|
||||
}
|
||||
subFilters = append(subFilters, filter)
|
||||
}
|
||||
|
||||
if len(subFilters) > 0 {
|
||||
|
||||
Reference in New Issue
Block a user