◊ A tool for creating a sequence of ascii text files from given video using FFMPEG ◊
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
5.2 KiB

package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"
"sort"
"sync"
"github.com/nsf/termbox-go"
"github.com/unbewohnte/VideoToAscii/audio"
"github.com/unbewohnte/VideoToAscii/extractor"
"github.com/unbewohnte/VideoToAscii/jsonData"
"github.com/unbewohnte/VideoToAscii/processor"
)
var (
settings, firstLaunch = jsonData.GetSettings()
WG sync.WaitGroup
// how many goroutines you want to be working on processing frames
MaxGOROUTINES uint = settings.MaxGOROUTINES
// width and height of an ascii text file
WIDTH uint = settings.WIDTH
HEIGHT uint = settings.HEIGHT
// the amount of frames/second you extract from video with ffmpeg
ExtractionFPS int = settings.ExtractionFPS
// where the audio file will go when you extract it from a video
AudioFilePath string = settings.AudioFilePath
// shows if you want to play audio or not
AudioPlayback bool = settings.AudioPlayback
// path to ffmpeg
FFMPEGbin = settings.FFMPEGbin
// this is where the frames from a video will be
VideoFramesOutputPath = settings.VideoFramesOutputPath
// path to a video
InputVideo = settings.InputVideo
// this is where the processed ascii textfiles will be
AsciiFilesPath = settings.AsciiFilesPath
// character set for "asciifying" images
asciiChars = settings.AsciiChars
)
func main() {
// 1) extract frames && audio (optional) from a video
// 2) process videoframes into Ascii
// 3) it`s ready to play
if firstLaunch {
fmt.Println("Created settings file; closing in 10 seconds...")
time.Sleep(time.Second * 10)
os.Exit(0)
}
fmt.Print("Extraction mode (0), playback mode (1), processing images mode (2), Extract audio (3) ? (0,1,2,3) : ")
var input string
fmt.Scanln(&input)
if input == "0" || input == "0 " {
t0 := time.Now()
fmt.Println("Extracting images...")
gff := extractor.GetFFMPEG(FFMPEGbin)
extractor.ExtractFrames(gff, InputVideo, VideoFramesOutputPath, ExtractionFPS)
FinishTime := time.Now().Sub(t0)
fmt.Printf("Done in %v", FinishTime)
fmt.Scanln()
} else if input == "1" || input == "1 " {
asciiFiles, err := ioutil.ReadDir(AsciiFilesPath)
if err != nil {
panic(err)
}
var Sequence []string
for _, file := range asciiFiles {
if file.Name()[len(file.Name())-3:] == "txt" {
frame, readErr := ioutil.ReadFile(filepath.Join(AsciiFilesPath, file.Name()))
if readErr != nil {
panic(readErr)
}
Sequence = append(Sequence, string(frame))
}
}
err = termbox.Init()
if err != nil {
panic(err)
}
timeForEachFrame := time.Duration(time.Second / time.Duration(ExtractionFPS))
t0 := time.Now()
if AudioPlayback == true {
go audio.PlayAudio(filepath.Join(AudioFilePath, "extractedAudio.mp3"))
}
var counter uint64 = 0
var nextFrameTime time.Time = time.Now()
for {
if counter < uint64(len(Sequence)) {
now := time.Now()
if now.After(nextFrameTime) {
nextFrameTime = now.Add(timeForEachFrame)
showFrame(Sequence[counter])
counter++
}
} else {
termbox.Close()
break
}
}
fmt.Printf("Took %v", time.Now().Sub(t0))
fmt.Scanln()
} else if input == "2" || input == "2 " {
t0 := time.Now()
fmt.Println("Processing images...")
files, err := ioutil.ReadDir(VideoFramesOutputPath)
if err != nil {
panic(err)
}
var sortedFilenames []string
for _, f := range files {
if f.Name()[len(f.Name())-3:] == extractor.ImageFileExtention {
sortedFilenames = append(sortedFilenames, f.Name())
}
}
sort.Strings(sortedFilenames)
jobs := make(chan *processor.DataForAscii, len(sortedFilenames))
for i := 0; i < int(MaxGOROUTINES); i++ {
WG.Add(1)
go Worker(jobs, &WG)
}
var counter uint64 = 0
for {
if counter == uint64(len(sortedFilenames)) {
break
}
if len(jobs) < int(MaxGOROUTINES) {
img, err := processor.GetImage(filepath.Join(VideoFramesOutputPath, sortedFilenames[counter]))
if err != nil {
panic(err)
}
jobs <- &processor.DataForAscii{
Img: img,
Width: WIDTH,
Height: HEIGHT,
Filename: fmt.Sprintf("%010d_ascii.txt", counter),
}
img = nil
counter++
}
}
close(jobs)
WG.Wait()
FinishTime := time.Now().Sub(t0)
fmt.Printf("Done in %v", FinishTime)
fmt.Scanln()
} else if input == "3" || input == "3 " {
t0 := time.Now()
fmt.Println("Extracting audio...")
gff := extractor.GetFFMPEG(FFMPEGbin)
extractor.ExtractAudio(gff, InputVideo, AudioFilePath)
FinishTime := time.Now().Sub(t0)
fmt.Printf("Done in %v", FinishTime)
fmt.Scanln()
}
}
func Worker(jobs <-chan *processor.DataForAscii, WG *sync.WaitGroup) {
defer WG.Done()
for data := range jobs {
processor.ASCIIfy(asciiChars, data.Img, data.Width, data.Height, filepath.Join(AsciiFilesPath, data.Filename))
data = nil
}
}
func showFrame(frame string) {
termbox.SetCursor(0, 0)
var x, y int = 0, 0
for _, char := range frame {
termbox.HideCursor()
if string(char) == "\n" {
y++
x = 0
} else {
termbox.SetCell(x, y, char, termbox.ColorWhite, termbox.ColorBlack)
x++
}
}
termbox.Flush()
}