IDK man, changes I guess
This commit is contained in:
42
export.go
42
export.go
@@ -41,14 +41,20 @@ type ExportRequest struct {
|
||||
// animated GIF (also uses Height and FPS)
|
||||
Dither string `json:"dither"` // one of gifDithers
|
||||
Loop bool `json:"loop"`
|
||||
// captions burned into the picture (re-encode and GIF only)
|
||||
Captions map[string]string `json:"captions"` // source id -> .srt path
|
||||
CaptionSize string `json:"captionSize"` // key of captionSizes
|
||||
CaptionStyle string `json:"captionStyle"` // key of captionStyles
|
||||
}
|
||||
|
||||
// gifDithers are the paletteuse dither modes offered for GIF export.
|
||||
var gifDithers = []string{"sierra2_4a", "bayer", "none"}
|
||||
|
||||
type clipJob struct {
|
||||
m *Media
|
||||
in, out float64
|
||||
m *Media
|
||||
in, out float64
|
||||
cues []Cue // captions to burn in, on the source's timeline
|
||||
captions string // .srt file with the clip's cues, timed from In (see writeClipCaptions)
|
||||
}
|
||||
|
||||
func (c clipJob) dur() float64 { return c.out - c.in }
|
||||
@@ -109,6 +115,10 @@ func (a *App) handleExport(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "unknown export mode")
|
||||
return
|
||||
}
|
||||
if err := a.attachCaptions(&req, clips); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
outputs := []string{out}
|
||||
if !req.Join {
|
||||
@@ -334,6 +344,14 @@ func (a *App) runExport(ctx context.Context, c *JobCtl, req *ExportRequest, clip
|
||||
}
|
||||
}
|
||||
|
||||
captionDir, err := writeClipCaptions(clips)
|
||||
if captionDir != "" {
|
||||
defer os.RemoveAll(captionDir)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not prepare the captions: %w", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case copyJoin:
|
||||
out := outputs[0]
|
||||
@@ -527,6 +545,7 @@ func encodeArgs(clips []clipJob, req *ExportRequest, ext string) ([]string, erro
|
||||
}
|
||||
anyAudio = anyAudio && !req.NoAudio
|
||||
|
||||
f.CaptionSize, f.CaptionStyle = req.CaptionSize, req.CaptionStyle
|
||||
vc, _ := codecByName(req.VCodec)
|
||||
var venc []string
|
||||
venc, f.PixFmt = videoEncoderArgs(vc, req.Quality, req.Speed)
|
||||
@@ -567,6 +586,7 @@ func gifArgs(clips []clipJob, req *ExportRequest, palette string) (paletteRun, g
|
||||
return nil, nil, err
|
||||
}
|
||||
f.PixFmt = "bgra" // what palettegen and paletteuse take
|
||||
f.CaptionSize, f.CaptionStyle = req.CaptionSize, req.CaptionStyle
|
||||
inputs, graph, n := joinGraph(clips, f)
|
||||
|
||||
paletteRun = append(ffmpegBaseArgs(), inputs...)
|
||||
@@ -593,6 +613,8 @@ type outFormat struct {
|
||||
PixFmt string
|
||||
SampleRate int
|
||||
Layout string
|
||||
// how burned-in captions look (keys of captionSizes and captionStyles)
|
||||
CaptionSize, CaptionStyle string
|
||||
}
|
||||
|
||||
// frameFormat picks the output frame size and rate: the first video clip's
|
||||
@@ -625,7 +647,8 @@ func frameFormat(clips []clipJob, height int, fps string) (outFormat, error) {
|
||||
// joinGraph returns the input arguments (one seeked input per clip, plus
|
||||
// generated black video or silence for clips that lack it), how many inputs
|
||||
// that is, and a filter graph that converts every clip to f, letterboxing as
|
||||
// needed, and joins them with the concat filter into [outv] (and [outa]).
|
||||
// needed and drawing its captions, and joins them with the concat filter into
|
||||
// [outv] (and [outa]).
|
||||
func joinGraph(clips []clipJob, f outFormat) (args []string, graph string, inputs int) {
|
||||
var filters []string
|
||||
var concatIn strings.Builder
|
||||
@@ -637,17 +660,24 @@ func joinGraph(clips []clipJob, f outFormat) (args []string, graph string, input
|
||||
src := inputs
|
||||
inputs++
|
||||
|
||||
// Captions are drawn on the output-sized frame, so they get the same
|
||||
// size in every clip. After fps the frames are timed from the In point
|
||||
// on the output frame grid, which is how writeClipCaptions times them.
|
||||
captions := ""
|
||||
if cl.captions != "" {
|
||||
captions = "," + captionFilter(cl.captions, f)
|
||||
}
|
||||
if v := cl.m.Video; v != nil {
|
||||
chain := fmt.Sprintf("[%d:%d]", src, v.Index)
|
||||
if !v.SquarePixels {
|
||||
chain += "scale='trunc(iw*sar/2)*2':ih,setsar=1,"
|
||||
}
|
||||
chain += fmt.Sprintf("scale=%d:%d:force_original_aspect_ratio=decrease:force_divisible_by=2,pad=%d:%d:(ow-iw)/2:(oh-ih)/2:color=black,setsar=1,fps=%s,format=%s[v%d]",
|
||||
f.W, f.H, f.W, f.H, f.FPS, f.PixFmt, i)
|
||||
chain += fmt.Sprintf("scale=%d:%d:force_original_aspect_ratio=decrease:force_divisible_by=2,pad=%d:%d:(ow-iw)/2:(oh-ih)/2:color=black,setsar=1,fps=%s%s,format=%s[v%d]",
|
||||
f.W, f.H, f.W, f.H, f.FPS, captions, f.PixFmt, i)
|
||||
filters = append(filters, chain)
|
||||
} else {
|
||||
args = append(args, "-f", "lavfi", "-t", d, "-i", fmt.Sprintf("color=c=black:s=%dx%d:r=%s", f.W, f.H, f.FPS))
|
||||
filters = append(filters, fmt.Sprintf("[%d:v]setsar=1,format=%s[v%d]", inputs, f.PixFmt, i))
|
||||
filters = append(filters, fmt.Sprintf("[%d:v]setsar=1%s,format=%s[v%d]", inputs, captions, f.PixFmt, i))
|
||||
inputs++
|
||||
}
|
||||
fmt.Fprintf(&concatIn, "[v%d]", i)
|
||||
|
||||
Reference in New Issue
Block a user