|
|
|
@ -11,8 +11,20 @@ import ( |
|
|
|
"strings" |
|
|
|
"strings" |
|
|
|
"lcthw.dev/go/vidcrunch/config" |
|
|
|
"lcthw.dev/go/vidcrunch/config" |
|
|
|
"github.com/modfy/fluent-ffmpeg" |
|
|
|
"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 { |
|
|
|
func ModFile(fname string, encoding config.VideoOpts) string { |
|
|
|
cleaned := filepath.Clean(fname) |
|
|
|
cleaned := filepath.Clean(fname) |
|
|
|
dir, file := filepath.Split(cleaned) |
|
|
|
dir, file := filepath.Split(cleaned) |
|
|
|
@ -21,7 +33,7 @@ func ModFile(fname string, encoding config.VideoOpts) string { |
|
|
|
base, found := strings.CutSuffix(file, ext) |
|
|
|
base, found := strings.CutSuffix(file, ext) |
|
|
|
if !found { panic("no extension found?!") } |
|
|
|
if !found { panic("no extension found?!") } |
|
|
|
|
|
|
|
|
|
|
|
if encoding.Scale == "" && encoding.VideoCodec == "none" { |
|
|
|
if AudioOnly(encoding) { |
|
|
|
renamed := fmt.Sprint(base, ".audio.", encoding.Format) |
|
|
|
renamed := fmt.Sprint(base, ".audio.", encoding.Format) |
|
|
|
return filepath.Join(dir, renamed) |
|
|
|
return filepath.Join(dir, renamed) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
@ -54,7 +66,7 @@ func Run(encoding config.VideoOpts, pass int, pid int, input string, output stri |
|
|
|
"-aspect", encoding.Scale) |
|
|
|
"-aspect", encoding.Scale) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if pass != encoding.Passes || encoding.AudioCodec == "none" { |
|
|
|
if pass != encoding.Passes || VideoOnly(encoding) { |
|
|
|
extras = append(extras, "-an") |
|
|
|
extras = append(extras, "-an") |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
encode.AudioCodec(encoding.AudioCodec) |
|
|
|
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) |
|
|
|
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) |
|
|
|
matches, err := filepath.Glob(encoding.Input) |
|
|
|
if err != nil { log.Fatalf("%v", err) } |
|
|
|
if err != nil { log.Fatalf("%v", err) } |
|
|
|
|
|
|
|
target := "" |
|
|
|
|
|
|
|
|
|
|
|
for _, path := range matches { |
|
|
|
for _, path := range matches { |
|
|
|
base := filepath.Base(path) |
|
|
|
base := filepath.Base(path) |
|
|
|
target := filepath.Join(encoding.OutDir, base) |
|
|
|
target = filepath.Join(encoding.OutDir, base) |
|
|
|
target = ModFile(target, encoding) |
|
|
|
target = ModFile(target, encoding) |
|
|
|
|
|
|
|
|
|
|
|
_, err := os.Stat(target) |
|
|
|
_, err := os.Stat(target) |
|
|
|
@ -132,6 +200,10 @@ func RenderToDir(encoding config.VideoOpts, force bool) { |
|
|
|
fmt.Println("^^^ SKIP", path, "->", target) |
|
|
|
fmt.Println("^^^ SKIP", path, "->", target) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if target == "" { log.Fatal("fuck!") } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return target |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func main() { |
|
|
|
func main() { |
|
|
|
@ -147,12 +219,18 @@ func main() { |
|
|
|
|
|
|
|
|
|
|
|
log.Fatal(http.ListenAndServe(settings.Port, nil)) |
|
|
|
log.Fatal(http.ListenAndServe(settings.Port, nil)) |
|
|
|
} else { |
|
|
|
} 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 != "" { |
|
|
|
if encoding.OutDir != "" { |
|
|
|
RenderToDir(encoding, settings.Force) |
|
|
|
encoding.FakeResult = RenderToDir(*encoding, settings.Force) |
|
|
|
|
|
|
|
fmt.Println("ENCODING result", encoding.FakeResult, "len=", len(settings.Encodings)) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
log.Fatal("config file needs either Output or OutDir") |
|
|
|
log.Fatal("config file needs either Output or OutDir") |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SaveMPD(settings) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|