Added a -serve and -port option to do a quick webserver for testing.

master
Zed A. Shaw 1 month ago
parent ff20dd1137
commit 9d87143a95
  1. 9
      Makefile
  2. 31
      config.json
  3. 6
      config/settings.go
  4. 209
      main.go

@ -1,8 +1,5 @@
GO_IS_STUPID_EXE=
ifeq '$(OS)' 'Windows_NT'
GO_IS_STUPID_EXE=.exe
endif
build: build:
go build . go build .
install: build
sudo cp vidcrunch /usr/local/bin/

@ -6,9 +6,8 @@
"VideoBitrate": 900, "VideoBitrate": 900,
"AudioBitrate": 192, "AudioBitrate": 192,
"VideoCodec": "libvpx-vp9", "VideoCodec": "libvpx-vp9",
"Preset": "veryfast",
"CleanFilename": false, "CleanFilename": false,
"CRF": 30, "CRF": 20,
"FPS": 30, "FPS": 30,
"Tune": "animation", "Tune": "animation",
"Input": "LGo*.mp4", "Input": "LGo*.mp4",
@ -17,27 +16,13 @@
"Extras": [ "Extras": [
"-deadline", "realtime", "-deadline", "realtime",
"-row-mt", "1", "-row-mt", "1",
"-cpu-used", "8" "-cpu-used", "8",
"-g", "150",
"-keyint_min", "150",
"-tile-columns", "4",
"-tile-rows", "2",
"-frame-parallel", "1",
"-tune-content", "1"
] ]
},
{
"Format": "webm",
"VideoCodec": "libvpx-vp9",
"Scale": "1280:720",
"Resize": false,
"VideoBitrate": 600,
"AudioBitrate": 192,
"CleanFilename": false,
"CRF": 30,
"FPS": 30,
"Tune": "animation",
"Input": "LGo*.mp4",
"OutDir": "dash_test",
"Passes": 1,
"Extras": [
"-deadline", "realtime",
"-row-mt", "1",
"-cpu-used", "8"
]
} }
] ]

@ -31,7 +31,8 @@ type VideoOpts struct {
type Settings struct { type Settings struct {
Debug int Debug int
Force bool Force bool
Server string Serve string
Port string
ConfigPath string ConfigPath string
Encodings []VideoOpts Encodings []VideoOpts
} }
@ -40,7 +41,8 @@ func ParseFlags(c *Settings) {
flag.IntVar(&c.Debug, "debug", 0, "1=print the ffmpeg command, 2=and its stderr output") flag.IntVar(&c.Debug, "debug", 0, "1=print the ffmpeg command, 2=and its stderr output")
flag.BoolVar(&c.Force, "force", false, "Force the render even if the file exists.") flag.BoolVar(&c.Force, "force", false, "Force the render even if the file exists.")
flag.StringVar(&c.ConfigPath, "config", "config.json", "config.json to load") flag.StringVar(&c.ConfigPath, "config", "config.json", "config.json to load")
flag.StringVar(&c.Server, "server", ":8099", "start a simple HTTP server for testing") flag.StringVar(&c.Serve, "serve", "", "serve a directory webserver")
flag.StringVar(&c.Port, "port", ":8099", "start a simple HTTP server for testing")
flag.Parse() flag.Parse()
} }

@ -1,128 +1,139 @@
package main package main
import ( import (
"fmt" "fmt"
"path/filepath" "path/filepath"
"runtime" "runtime"
"log" "log"
"os" "os"
"math/rand" "math/rand"
"strings" "net/http"
"lcthw.dev/go/vidcrunch/config" "strings"
"github.com/modfy/fluent-ffmpeg" "lcthw.dev/go/vidcrunch/config"
"github.com/modfy/fluent-ffmpeg"
) )
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)
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?!") }
dim := strings.Replace(encoding.Scale, ":", ".", 1) dim := strings.Replace(encoding.Scale, ":", ".", 1)
renamed := fmt.Sprint(base, ".", dim, ".", encoding.Format) renamed := fmt.Sprint(base, ".", dim, ".", encoding.Format)
return filepath.Join(dir, renamed) return filepath.Join(dir, renamed)
} }
func Run(encoding config.VideoOpts, pass int, pid int, input string, output string) { func Run(encoding config.VideoOpts, pass int, pid int, input string, output string) {
encode := fluentffmpeg.NewCommand("") encode := fluentffmpeg.NewCommand("")
extras := []string{ extras := []string{
"-pix_fmt", "yuv420p", "-pix_fmt", "yuv420p",
"-pass", fmt.Sprint(pass), "-pass", fmt.Sprint(pass),
"-passlogfile", fmt.Sprintf("ffmpegpass-%x.log", pid), "-passlogfile", fmt.Sprintf("ffmpegpass-%x.log", pid),
} }
if encoding.Preset != "" { if encoding.Preset != "" {
extras = append(extras, "-preset", encoding.Preset) extras = append(extras, "-preset", encoding.Preset)
} }
if encoding.Resize { if encoding.Resize {
extras = append(extras, extras = append(extras,
"-vf", fmt.Sprintf("scale=%s:flags=lanczos", encoding.Scale), "-vf", fmt.Sprintf("scale=%s:flags=lanczos", encoding.Scale),
"-aspect", encoding.Scale) "-aspect", encoding.Scale)
} }
if pass != encoding.Passes { if pass != encoding.Passes {
extras = append(extras, "-an") extras = append(extras, "-an")
} else { } else {
encode.AudioCodec(encoding.AudioCodec) encode.AudioCodec(encoding.AudioCodec)
extras = append(extras, extras = append(extras,
"-b:a", fmt.Sprint(encoding.AudioBitrate * 1024)) "-b:a", fmt.Sprint(encoding.AudioBitrate * 1024))
} }
if encoding.Test > 0 { if encoding.Test > 0 {
encode.InputOptions( encode.InputOptions("-ss", fmt.Sprintf("00:%d", encoding.TestStart))
"-ss", fmt.Sprintf("00:%d", encoding.TestStart)) extras = append(extras, "-t", fmt.Sprint(encoding.Test))
extras = append(extras, "-t", fmt.Sprint(encoding.Test)) }
}
encode.VideoCodec(encoding.VideoCodec).
encode.VideoCodec(encoding.VideoCodec). VideoBitRate(encoding.VideoBitrate * 1024).
VideoBitRate(encoding.VideoBitrate * 1024). FrameRate(encoding.FPS).
FrameRate(encoding.FPS). ConstantRateFactor(encoding.CRF)
ConstantRateFactor(encoding.CRF)
extras = append(extras, encoding.Extras...)
extras = append(extras, encoding.Extras...) encode.OutputOptions(extras...)
encode.OutputOptions(extras...)
cmd := encode.InputPath(input).
cmd := encode.InputPath(input). OutputFormat(encoding.Format).
OutputFormat(encoding.Format). OutputPath(output).
OutputPath(output).
OutputLogs(os.Stdout). OutputLogs(os.Stdout).
Overwrite(true). Overwrite(true).
Build() Build()
fmt.Println(">", cmd.String()) fmt.Println(">", cmd.String())
err := cmd.Run() err := cmd.Run()
if err != nil { log.Fatalf("%v", err) } if err != nil { log.Fatalf("%v", err) }
} }
func DevNull() string { func DevNull() string {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
return "NUL" return "NUL"
} else { } else {
return "/dev/null" return "/dev/null"
} }
} }
func RenderFile(encoding config.VideoOpts, pid int, path string, target string) { func RenderFile(encoding config.VideoOpts, pid int, path string, target string) {
for i := 1; i < encoding.Passes; i++ { for i := 1; i < encoding.Passes; i++ {
Run(encoding, i, pid, path, DevNull()) Run(encoding, i, pid, path, DevNull())
} }
Run(encoding, encoding.Passes, pid, path, target) Run(encoding, encoding.Passes, pid, path, target)
} }
func RenderToDir(encoding config.VideoOpts, force bool) { func RenderToDir(encoding config.VideoOpts, 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) }
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)
if err != nil || force { if err != nil || force {
fmt.Println("--- PATH", path, "->", target) fmt.Println("--- PATH", path, "->", target)
RenderFile(encoding, rand.Int(), path, target) RenderFile(encoding, rand.Int(), path, target)
} else { } else {
fmt.Println("^^^ SKIP", path, "->", target) fmt.Println("^^^ SKIP", path, "->", target)
} }
} }
} }
func main() { func main() {
settings := config.Load() settings := config.Load()
for _, encoding := range settings.Encodings { if settings.Serve != "" {
if encoding.OutDir != "" { dir_handler := http.FileServer(http.Dir(settings.Serve))
RenderToDir(encoding, settings.Force)
} else { http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) {
log.Fatal("config file needs either Output or OutDir") fmt.Println(r.Method, r.URL)
} dir_handler.ServeHTTP(w, r)
} })
log.Fatal(http.ListenAndServe(settings.Port, nil))
} else {
for _, encoding := range settings.Encodings {
if encoding.OutDir != "" {
RenderToDir(encoding, settings.Force)
} else {
log.Fatal("config file needs either Output or OutDir")
}
}
}
} }

Loading…
Cancel
Save