package main import ( "bytes" "encoding/json" "flag" "fmt" "io" "log" "sync" ffmpeg "github.com/u2takey/ffmpeg-go" ) var ( InputFile = flag.String("in_filename", "./in1.mp4", "Input filename") OutputFile = flag.String("out_filename", "./out.mp4", "Input filename") Dream = flag.Bool("dream", false, "Use DeepDream frame processing (requires tensorflow)") ) // Buffer is a goroutine safe bytes.Buffer type Buffer struct { buffer bytes.Buffer mutex sync.Mutex } func (s *Buffer) Write(p []byte) (n int, err error) { s.mutex.Lock() defer s.mutex.Unlock() return s.buffer.Write(p) } func (s *Buffer) Read(p []byte) (n int, err error) { s.mutex.Lock() defer s.mutex.Unlock() return s.buffer.Read(p) } func getVideoSize(fileName string) (int, int) { log.Println("Getting video size for", fileName) data, err := ffmpeg.Probe(fileName) if err != nil { panic(err) } log.Println("got video info", data) type VideoInfo struct { Streams []struct { CodecType string `json:"codec_type"` Width int Height int } `json:"streams"` } vInfo := &VideoInfo{} err = json.Unmarshal([]byte(data), vInfo) if err != nil { panic(err) } for _, s := range vInfo.Streams { if s.CodecType == "video" { return s.Width, s.Height } } return 0, 0 } func startFFmpegProcess1(infileName string, writer io.WriteCloser) <-chan error { log.Println("Starting ffmpeg process1") done := make(chan error) go func() { err := ffmpeg.Input(infileName). Output("pipe:", ffmpeg.KwArgs{ "format": "rawvideo", "pix_fmt": "rgb24", }). WithOutput(writer). Run() log.Println("ffmpeg process1 done") _ = writer.Close() done <- err close(done) }() return done } func startFFmpegProcess2(outfileName string, buf io.Reader, width, height int) <-chan error { log.Println("Starting ffmpeg process2") done := make(chan error) go func() { err := ffmpeg.Input("pipe:", ffmpeg.KwArgs{"format": "rawvideo", "pix_fmt": "rgb24", "s": fmt.Sprintf("%dx%d", width, height), }). Output(outfileName, ffmpeg.KwArgs{"pix_fmt": "yuv420p"}). OverWriteOutput(). WithInput(buf). Run() log.Println("ffmpeg process2 done") done <- err close(done) }() return done } func process(reader io.ReadCloser, writer io.WriteCloser, w, h int) { go func() { frameSize := w * h * 3 buf := make([]byte, frameSize, frameSize) for { n, err := io.ReadFull(reader, buf) if n == 0 || err == io.EOF { _ = writer.Close() return } else if n != frameSize || err != nil { panic(fmt.Sprintf("read error: %d, %s", n, err)) } for i := range buf { buf[i] = buf[i] / 3 } n, err = writer.Write(buf) if n != frameSize || err != nil { panic(fmt.Sprintf("write error: %d, %s", n, err)) } } }() return } func run(inFile, outFile string) { w, h := getVideoSize(inFile) log.Println(w, h) pr1, pw1 := io.Pipe() pr2, pw2 := io.Pipe() done1 := startFFmpegProcess1(inFile, pw1) process(pr1, pw2, w, h) done2 := startFFmpegProcess2(outFile, pr2, w, h) err := <-done1 if err != nil { panic(err) } err = <-done2 if err != nil { panic(err) } log.Println("Done") } func main() { flag.Parse() if *Dream == true { fmt.Println("tensorflow mode not implemented, todo") return } run(*InputFile, *OutputFile) }