diff --git a/.gitignore b/.gitignore index 5d982c5..efb550a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,5 @@ public *.mp4 ffmpeg* renders +vidcrunch +vidcrunch.exe diff --git a/config/settings.go b/config/settings.go index b62e1f9..499330b 100644 --- a/config/settings.go +++ b/config/settings.go @@ -23,6 +23,7 @@ type VideoOpts struct { Tune string Input string OutDir string + FakeResult string Test int TestStart int Passes int diff --git a/go.mod b/go.mod index 7e1f2c3..8b735ce 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,10 @@ module lcthw.dev/go/vidcrunch go 1.24.2 -require github.com/modfy/fluent-ffmpeg v0.1.0 +require ( + github.com/modfy/fluent-ffmpeg v0.1.0 + github.com/zencoder/go-dash v0.0.0-20201006100653-2f93b14912b2 +) require ( github.com/fatih/structs v1.1.0 // indirect diff --git a/go.sum b/go.sum index 891cde9..fcf9b72 100644 --- a/go.sum +++ b/go.sum @@ -4,3 +4,5 @@ github.com/modfy/fluent-ffmpeg v0.1.0 h1:9T191rhSK6KfoDo9Y/+0Tph3khrudvLQEEi05O+ github.com/modfy/fluent-ffmpeg v0.1.0/go.mod h1:GauXGqGYAmYFupCWG8n1eyuLZMKmLxGTGvszYkJ0Oyo= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/zencoder/go-dash v0.0.0-20201006100653-2f93b14912b2 h1:0iAY2pL6yYhNYpdc1DbFq0p7ocyu5MlgKmkealhz3nk= +github.com/zencoder/go-dash v0.0.0-20201006100653-2f93b14912b2/go.mod h1:c8Gxxfmh0jmZ6G+ISlpa315WBVkzd8mEhu6gN9mn5Qg= diff --git a/main.go b/main.go index 2c3687a..a015466 100644 --- a/main.go +++ b/main.go @@ -11,8 +11,20 @@ import ( "strings" "lcthw.dev/go/vidcrunch/config" "github.com/modfy/fluent-ffmpeg" + "strconv" + "os/exec" + "slices" + "iter" ) +func AudioOnly(encoding config.VideoOpts) bool { + return encoding.Scale == "" && encoding.VideoCodec == "none" +} + +func VideoOnly(encoding config.VideoOpts) bool { + return encoding.AudioCodec == "none" +} + func ModFile(fname string, encoding config.VideoOpts) string { cleaned := filepath.Clean(fname) dir, file := filepath.Split(cleaned) @@ -21,7 +33,7 @@ func ModFile(fname string, encoding config.VideoOpts) string { base, found := strings.CutSuffix(file, ext) if !found { panic("no extension found?!") } - if encoding.Scale == "" && encoding.VideoCodec == "none" { + if AudioOnly(encoding) { renamed := fmt.Sprint(base, ".audio.", encoding.Format) return filepath.Join(dir, renamed) } else { @@ -54,7 +66,7 @@ func Run(encoding config.VideoOpts, pass int, pid int, input string, output stri "-aspect", encoding.Scale) } - if pass != encoding.Passes || encoding.AudioCodec == "none" { + if pass != encoding.Passes || VideoOnly(encoding) { extras = append(extras, "-an") } else { encode.AudioCodec(encoding.AudioCodec) @@ -114,13 +126,69 @@ func RenderFile(encoding config.VideoOpts, pid int, path string, target string) Run(encoding, encoding.Passes, pid, path, target) } -func RenderToDir(encoding config.VideoOpts, force bool) { +func StrSeq(up_to int) iter.Seq[string] { + return func (yield func(x string) bool) { + for i := range up_to { + if !yield(strconv.Itoa(i)) { return } + } + } +} + +func SaveMPD(settings config.Settings) { + args := make([]string, 0, 10) + var the_audio string + + // force it to overwrite + args = append(args, "-y") + + for _, encoding := range settings.Encodings { + if VideoOnly(encoding) { + args = append(args, "-f", "webm_dash_manifest", "-i", encoding.FakeResult) + } else if AudioOnly(encoding) { + the_audio = encoding.FakeResult + } + } + + args = append(args, "-f", "webm_dash_manifest", "-i", the_audio) + + // create map for each + args = append(args, "-c", "copy") + + for i := 0; i < len(settings.Encodings); i++ { + args = append(args, "-map", strconv.Itoa(i)) + } + + // generate adaptation sets, separating the audio + // id=0 is videos, id=1 is audio + args = append(args, "-f", "webm_dash_manifest", "-adaptation_sets") + + da_ints := slices.Collect(StrSeq(len(settings.Encodings) - 1)) + + video_set := strings.Join(da_ints, ",") + + adapt_set := fmt.Sprintf("id=0,streams=%s id=1,streams=%d", + video_set, len(settings.Encodings) - 1) + + args = append(args, adapt_set) + args = append(args, filepath.Join(settings.Encodings[0].OutDir, "manifest.mpd")) + + fmt.Println("\n\n\n\nARGS", args) + + ffmpeg := exec.Command("ffmpeg", args...) + out, err := ffmpeg.CombinedOutput() + + os.Stdout.Write(out) + log.Fatal(err) +} + +func RenderToDir(encoding config.VideoOpts, force bool) string { matches, err := filepath.Glob(encoding.Input) if err != nil { log.Fatalf("%v", err) } + target := "" for _, path := range matches { base := filepath.Base(path) - target := filepath.Join(encoding.OutDir, base) + target = filepath.Join(encoding.OutDir, base) target = ModFile(target, encoding) _, err := os.Stat(target) @@ -132,6 +200,10 @@ func RenderToDir(encoding config.VideoOpts, force bool) { fmt.Println("^^^ SKIP", path, "->", target) } } + + if target == "" { log.Fatal("fuck!") } + + return target } func main() { @@ -147,12 +219,18 @@ func main() { log.Fatal(http.ListenAndServe(settings.Port, nil)) } else { - for _, encoding := range settings.Encodings { + for i := 0; i < len(settings.Encodings); i++ { + fmt.Println("LOOP", i) + encoding := &settings.Encodings[i]; + if encoding.OutDir != "" { - RenderToDir(encoding, settings.Force) + encoding.FakeResult = RenderToDir(*encoding, settings.Force) + fmt.Println("ENCODING result", encoding.FakeResult, "len=", len(settings.Encodings)) } else { log.Fatal("config file needs either Output or OutDir") } } + + SaveMPD(settings) } }