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.1 KiB
209 lines
5.1 KiB
package main |
|
|
|
import ( |
|
"fmt" |
|
"io/ioutil" |
|
"os" |
|
"path/filepath" |
|
"time" |
|
|
|
"sort" |
|
"sync" |
|
|
|
"github.com/nsf/termbox-go" |
|
|
|
"./audio" |
|
"./extractor" |
|
"./jsonData" |
|
"./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() |
|
}
|
|
|