Can now use variants to produce things.

master
Zed A. Shaw 4 weeks ago
parent 5d58dae41f
commit cc1432889a
  1. 3
      Makefile
  2. 98
      config.json
  3. 14
      config/settings.go
  4. 113
      main.go

@ -3,3 +3,6 @@ build:
install: build install: build
sudo cp vidcrunch /usr/local/bin/ sudo cp vidcrunch /usr/local/bin/
clean:
rm -f dash_test/*.webm dash_test/*.mpd

@ -1,12 +1,34 @@
[ [
{ {
"Format": "webm", "Format": "webm",
"Scale": "1920:1080", "Variants": [
"Resize": false, {
"VideoBitrate": 900, "Scale": "1920:1080",
"AudioCodec": "none", "Resize": false,
"VideoCodec": "libvpx-vp9", "VideoBitrate": 900,
"CleanFilename": false, "AudioCodec": "none",
"VideoCodec": "libvpx-vp9"
},
{
"Scale": "1280:720",
"Resize": true,
"VideoBitrate": 600,
"AudioCodec": "none",
"VideoCodec": "libvpx-vp9"
},
{
"Scale": "640:360",
"Resize": true,
"VideoBitrate": 300,
"AudioCodec": "none",
"VideoCodec": "libvpx-vp9"
},
{
"AudioCodec": "libopus",
"AudioBitrate": 192,
"VideoCodec": "none"
}
],
"CRF": 30, "CRF": 30,
"FPS": 30, "FPS": 30,
"Input": "test_video.mp4", "Input": "test_video.mp4",
@ -24,69 +46,5 @@
"-frame-parallel", "1", "-frame-parallel", "1",
"-tune-content", "1" "-tune-content", "1"
] ]
},
{
"Format": "webm",
"Scale": "1280:720",
"Resize": false,
"VideoBitrate": 600,
"AudioCodec": "none",
"VideoCodec": "libvpx-vp9",
"CleanFilename": false,
"CRF": 30,
"FPS": 30,
"Input": "test_video.mp4",
"OutDir": "dash_test",
"Passes": 1,
"Dash": true,
"Extras": [
"-deadline", "realtime",
"-row-mt", "1",
"-cpu-used", "8",
"-g", "150",
"-keyint_min", "150",
"-tile-columns", "4",
"-tile-rows", "2",
"-frame-parallel", "1",
"-tune-content", "1"
]
},
{
"Format": "webm",
"Scale": "640:360",
"Resize": false,
"VideoBitrate": 300,
"AudioCodec": "none",
"VideoCodec": "libvpx-vp9",
"CleanFilename": false,
"CRF": 30,
"FPS": 30,
"Input": "test_video.mp4",
"OutDir": "dash_test",
"Passes": 1,
"Dash": true,
"Extras": [
"-deadline", "realtime",
"-row-mt", "1",
"-cpu-used", "8",
"-g", "150",
"-keyint_min", "150",
"-tile-columns", "4",
"-tile-rows", "2",
"-frame-parallel", "1",
"-tune-content", "1"
]
},
{
"Format": "webm",
"AudioBitrate": 192,
"AudioCodec": "libopus",
"VideoCodec": "none",
"CleanFilename": false,
"CRF": 30,
"FPS": 30,
"Input": "test_video.mp4",
"OutDir": "dash_test",
"Passes": 1
} }
] ]

@ -7,23 +7,27 @@ import (
"encoding/json" "encoding/json"
) )
type VideoOpts struct { type CodecOpts struct {
Format string
Scale string Scale string
Resize bool Resize bool
VideoBitrate int VideoBitrate int
VideoCodec string VideoCodec string
AudioBitrate int AudioBitrate int
AudioCodec string AudioCodec string
// used internally, shouldn't set int json
Target string
}
type EncodeOpts struct {
Format string
Variants []CodecOpts
Preset string Preset string
CleanFilename bool
Dash bool Dash bool
CRF int CRF int
FPS int FPS int
Tune string Tune string
Input string Input string
OutDir string OutDir string
FakeResult string
Test int Test int
TestStart int TestStart int
Passes int Passes int
@ -36,7 +40,7 @@ type Settings struct {
Serve string Serve string
Port string Port string
ConfigPath string ConfigPath string
Encodings []VideoOpts Encodings []EncodeOpts
} }
func ParseFlags(c *Settings) { func ParseFlags(c *Settings) {

@ -17,33 +17,36 @@ import (
"iter" "iter"
) )
func AudioOnly(encoding config.VideoOpts) bool { func AudioOnly(codec config.CodecOpts) bool {
return encoding.Scale == "" && encoding.VideoCodec == "none" return codec.Scale == "" && codec.VideoCodec == "none"
} }
func VideoOnly(encoding config.VideoOpts) bool { func VideoOnly(codec config.CodecOpts) bool {
return encoding.AudioCodec == "none" return codec.AudioCodec == "none"
} }
func ModFile(fname string, encoding config.VideoOpts) string { func SetCodecTarget(codec *config.CodecOpts, path string, encoding config.EncodeOpts) {
cleaned := filepath.Clean(fname) base := filepath.Base(path)
target := filepath.Join(encoding.OutDir, base)
cleaned := filepath.Clean(target)
dir, file := filepath.Split(cleaned) dir, file := filepath.Split(cleaned)
ext := filepath.Ext(file) ext := filepath.Ext(file)
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 AudioOnly(encoding) { if AudioOnly(*codec) {
renamed := fmt.Sprint(base, ".audio.", encoding.Format) renamed := fmt.Sprint(base, ".audio.", encoding.Format)
return filepath.Join(dir, renamed) codec.Target = filepath.Join(dir, renamed)
} else { } else {
dim := strings.Replace(encoding.Scale, ":", ".", 1) dim := strings.Replace(codec.Scale, ":", ".", 1)
renamed := fmt.Sprint(base, ".", dim, ".", encoding.Format) renamed := fmt.Sprint(base, ".", dim, ".", encoding.Format)
return filepath.Join(dir, renamed) codec.Target = filepath.Join(dir, renamed)
} }
} }
func Run(encoding config.VideoOpts, pass int, pid int, input string, output string) { func Run(encoding config.EncodeOpts, codec config.CodecOpts, pass int, pid int, input string, output string) {
encode := fluentffmpeg.NewCommand("") encode := fluentffmpeg.NewCommand("")
extras := []string{ extras := []string{
@ -60,18 +63,18 @@ func Run(encoding config.VideoOpts, pass int, pid int, input string, output stri
extras = append(extras, "-preset", encoding.Preset) extras = append(extras, "-preset", encoding.Preset)
} }
if encoding.Resize { if codec.Resize {
extras = append(extras, extras = append(extras,
"-vf", fmt.Sprintf("scale=%s:flags=lanczos", encoding.Scale), "-vf", fmt.Sprintf("scale=%s:flags=lanczos", codec.Scale),
"-aspect", encoding.Scale) "-aspect", codec.Scale)
} }
if pass != encoding.Passes || VideoOnly(encoding) { if pass != encoding.Passes || VideoOnly(codec) {
extras = append(extras, "-an") extras = append(extras, "-an")
} else { } else {
encode.AudioCodec(encoding.AudioCodec) encode.AudioCodec(codec.AudioCodec)
extras = append(extras, extras = append(extras,
"-b:a", fmt.Sprint(encoding.AudioBitrate * 1024)) "-b:a", fmt.Sprint(codec.AudioBitrate * 1024))
} }
@ -80,11 +83,11 @@ func Run(encoding config.VideoOpts, pass int, pid int, input string, output stri
extras = append(extras, "-t", fmt.Sprint(encoding.Test)) extras = append(extras, "-t", fmt.Sprint(encoding.Test))
} }
if encoding.VideoCodec == "none" { if codec.VideoCodec == "none" {
extras = append(extras, "-vn") extras = append(extras, "-vn")
} else { } else {
encode.VideoCodec(encoding.VideoCodec). encode.VideoCodec(codec.VideoCodec).
VideoBitRate(encoding.VideoBitrate * 1024). VideoBitRate(codec.VideoBitrate * 1024).
FrameRate(encoding.FPS). FrameRate(encoding.FPS).
ConstantRateFactor(encoding.CRF) ConstantRateFactor(encoding.CRF)
} }
@ -118,12 +121,28 @@ func DevNull() string {
} }
} }
func RenderFile(encoding config.VideoOpts, pid int, path string, target string) { func RenderFile(encoding config.EncodeOpts, pid int, path string, force bool) {
for i := 1; i < encoding.Passes; i++ { for i := 0; i < len(encoding.Variants); i++ {
Run(encoding, i, pid, path, DevNull()) codec := &encoding.Variants[i]
SetCodecTarget(codec, path, encoding)
_, err := os.Stat(codec.Target)
if err == nil && !force {
fmt.Println("^^^ SKIP", path, "->", codec.Target)
return
}
fmt.Println("--- PATH", path, "->", codec.Target)
for i := 1; i < encoding.Passes; i++ {
Run(encoding, *codec, i, pid, path, DevNull())
}
Run(encoding, *codec, encoding.Passes, pid, path, codec.Target)
} }
Run(encoding, encoding.Passes, pid, path, target) SaveMPD(encoding)
} }
func StrSeq(up_to int) iter.Seq[string] { func StrSeq(up_to int) iter.Seq[string] {
@ -134,18 +153,18 @@ func StrSeq(up_to int) iter.Seq[string] {
} }
} }
func SaveMPD(settings config.Settings) { func SaveMPD(encoding config.EncodeOpts) {
args := make([]string, 0, 10) args := make([]string, 0, 10)
var the_audio string var the_audio string
// force it to overwrite // force it to overwrite
args = append(args, "-y") args = append(args, "-y")
for _, encoding := range settings.Encodings { for _, codec := range encoding.Variants {
if VideoOnly(encoding) { if VideoOnly(codec) {
args = append(args, "-f", "webm_dash_manifest", "-i", encoding.FakeResult) args = append(args, "-f", "webm_dash_manifest", "-i", codec.Target)
} else if AudioOnly(encoding) { } else if AudioOnly(codec) {
the_audio = encoding.FakeResult the_audio = codec.Target
} }
} }
@ -154,7 +173,7 @@ func SaveMPD(settings config.Settings) {
// create map for each // create map for each
args = append(args, "-c", "copy") args = append(args, "-c", "copy")
for i := 0; i < len(settings.Encodings); i++ { for i := 0; i < len(encoding.Variants); i++ {
args = append(args, "-map", strconv.Itoa(i)) args = append(args, "-map", strconv.Itoa(i))
} }
@ -162,15 +181,15 @@ func SaveMPD(settings config.Settings) {
// id=0 is videos, id=1 is audio // id=0 is videos, id=1 is audio
args = append(args, "-f", "webm_dash_manifest", "-adaptation_sets") args = append(args, "-f", "webm_dash_manifest", "-adaptation_sets")
da_ints := slices.Collect(StrSeq(len(settings.Encodings) - 1)) da_ints := slices.Collect(StrSeq(len(encoding.Variants) - 1))
video_set := strings.Join(da_ints, ",") video_set := strings.Join(da_ints, ",")
adapt_set := fmt.Sprintf("id=0,streams=%s id=1,streams=%d", adapt_set := fmt.Sprintf("id=0,streams=%s id=1,streams=%d",
video_set, len(settings.Encodings) - 1) video_set, len(encoding.Variants) - 1)
args = append(args, adapt_set) args = append(args, adapt_set)
args = append(args, filepath.Join(settings.Encodings[0].OutDir, "manifest.mpd")) args = append(args, filepath.Join(encoding.OutDir, "manifest.mpd"))
fmt.Println("\n\n\n\nARGS", args) fmt.Println("\n\n\n\nARGS", args)
@ -181,29 +200,14 @@ func SaveMPD(settings config.Settings) {
log.Fatal(err) log.Fatal(err)
} }
func RenderToDir(encoding config.VideoOpts, force bool) string { func RenderToDir(encoding config.EncodeOpts, force bool) {
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) RenderFile(encoding, rand.Int(), path, force)
target = filepath.Join(encoding.OutDir, base)
target = ModFile(target, encoding)
_, err := os.Stat(target)
if err != nil || force {
fmt.Println("--- PATH", path, "->", target)
RenderFile(encoding, rand.Int(), path, target)
} else {
fmt.Println("^^^ SKIP", path, "->", target)
}
} }
if target == "" { log.Fatal("fuck!") }
return target
} }
func main() { func main() {
@ -224,13 +228,10 @@ func main() {
encoding := &settings.Encodings[i]; encoding := &settings.Encodings[i];
if encoding.OutDir != "" { if encoding.OutDir != "" {
encoding.FakeResult = RenderToDir(*encoding, settings.Force) 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)
} }
} }

Loading…
Cancel
Save