summaryrefslogtreecommitdiff
path: root/autoart/autovideos.go
diff options
context:
space:
mode:
Diffstat (limited to 'autoart/autovideos.go')
-rw-r--r--autoart/autovideos.go171
1 files changed, 171 insertions, 0 deletions
diff --git a/autoart/autovideos.go b/autoart/autovideos.go
new file mode 100644
index 0000000..4a51d1f
--- /dev/null
+++ b/autoart/autovideos.go
@@ -0,0 +1,171 @@
+/*
+Copyright (C) 2019 Leo Tenenbaum
+
+This file is part of AutoArt.
+
+AutoArt is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+AutoArt is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with AutoArt. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+/*
+NOTE: AutoVideos requires Go 1.11 or newer (for temp file patterns).
+*/
+
+package autoart
+
+import (
+ "github.com/pommicket/autoart/autoutils"
+ "io"
+ "io/ioutil"
+ "image"
+ "image/png"
+ "image/color"
+ "math/rand"
+ "fmt"
+ "os/exec"
+ "os"
+)
+
+func generateFrame(width int, height int, paletted bool, config Config,
+ pconfig PaletteConfig, palette []color.RGBA,
+ functions []autoutils.Function, time float64,
+ frameNumber int64, file *os.File) error { // NOTE: file is closed by this function
+ vars := []float64{0, 0, time}
+ var img image.Image
+ if paletted {
+ img = GenerateImagePaletteFrom(width, height, pconfig, functions, vars, palette)
+ } else {
+ img = GenerateImageFromFunctions(width, height, config, functions, vars)
+ }
+ if err := png.Encode(file, img); err != nil {
+ file.Close()
+ return err
+ }
+ return file.Close()
+}
+
+func generateVideo(width int, height int, paletted bool, config Config,
+ pconfig PaletteConfig, time float64,
+ framerate int, filename string, verbose bool) error {
+
+ var palette []color.RGBA
+ if paletted {
+ // Generate palette
+ palette = make([]color.RGBA, pconfig.NColors)
+ for i := range palette {
+ r := uint8(rand.Intn(256))
+ g := uint8(rand.Intn(256))
+ b := uint8(rand.Intn(256))
+ a := uint8(255)
+ if pconfig.Alpha {
+ a = uint8(rand.Intn(256))
+ }
+ palette[i] = color.RGBA{r, g, b, a}
+ }
+ }
+
+ if config.FunctionLength == 0 {
+ // 0 value of config shouldn't have empty functions
+ config.FunctionLength = defaultFunctionLength
+ }
+
+ var functionLength int
+ if paletted {
+ functionLength = pconfig.FunctionLength
+ } else {
+ functionLength = config.FunctionLength
+ }
+
+ var nfunctions int
+ if paletted {
+ nfunctions = pconfig.NColors
+ } else {
+ nfunctions = config.nFunctions()
+ }
+ functions := make([]autoutils.Function, nfunctions)
+ for i := range functions {
+ functions[i].Generate(3, functionLength)
+ }
+
+ frames := int64(time * float64(framerate))
+
+ files := make([]*os.File, frames)
+ defer func() {
+ // Delete all temporary files
+ for _, file := range files {
+ if file != nil {
+ os.Remove(file.Name())
+ }
+ }
+ }()
+ // Create temporary frame files
+ for i := range files {
+ var err error
+ files[i], err = ioutil.TempFile("", "frame*.png")
+ if err != nil {
+ return err
+ }
+ }
+
+ autoutils.RunInBatches(frames, "Generating video...", func (n int64, errs chan<- error) {
+ t := float64(n)/float64(framerate)
+ errs <- generateFrame(width, height, paletted, config, pconfig, palette, functions, t, n, files[n])
+ })
+
+ ffmpegInputFile, err := ioutil.TempFile("", "input*.txt")
+ if err != nil {
+ return err
+ }
+ defer os.Remove(ffmpegInputFile.Name())
+
+ for _, file := range files {
+ name := file.Name()
+ info := fmt.Sprintf("file '%v'\nduration %v\n", name, 1/float64(framerate))
+ if _, err = io.WriteString(ffmpegInputFile, info); err != nil {
+ return err
+ }
+ }
+
+ fmt.Println(ffmpegInputFile.Name())
+
+ if err = ffmpegInputFile.Close(); err != nil {
+ return err
+ }
+
+ if verbose {
+ fmt.Println("ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", ffmpegInputFile.Name(), filename)
+ }
+
+ cmd := exec.Command("ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", ffmpegInputFile.Name(), filename)
+ if verbose {
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ }
+ if err = cmd.Run(); err != nil {
+ return fmt.Errorf("ffmpeg failed: %v", err)
+ }
+
+ return nil
+}
+
+func GenerateVideo(width int, height int, config Config, time float64,
+ framerate int, filename string, verbose bool) error {
+ var pconfig PaletteConfig
+ return generateVideo(width, height, false, config, pconfig, time, framerate, filename, verbose)
+}
+
+func GenerateVideoPalette(width int, height int, pconfig PaletteConfig,
+ time float64, framerate int, filename string, verbose bool) error {
+ var config Config
+ return generateVideo(width, height, true, config, pconfig, time, framerate, filename, verbose)
+}