package main import ( "flag" "fmt" "os" "os/exec" "path/filepath" "strconv" "strings" "time" ) const DEFAULT_PRESET = "slow" const DEFAULT_CRF = "20" func scanArgFromConsole(text string, v *string) error { fmt.Print(text) if _, err := fmt.Scanln(v); err != nil && err.Error() == "EOF" { return err } return nil } func pressEnterToExit(code int) { fmt.Print("\npress enter to exit... ") _, _ = fmt.Scanln() os.Exit(code) } func main() { var err error var debug bool var vfArgs []string var audioFilters []string var videoFilters []string var inputVideo, ss, to, an, crf, preset, mergeAudioStreams, speedUp string ffmpegExecutable := "ffmpeg" ffmpegArgs := append(make([]string, 0, 30), "-hide_banner") inputArgs := []string{ "-hwaccel", "nvdec", "-threads", "1", } postArgs := []string{ "-c:v", "h264", "-profile:v", "high", "-pix_fmt", "yuv420p", "-tune", "film", "-movflags", "+faststart", "-r", "60", } flag.StringVar(&inputVideo, "i", "", "input video path") flag.BoolVar(&debug, "debug", false, "debug mode (default params)") flag.Parse() // check video exists if _, err := os.Stat(inputVideo); err != nil { fmt.Println(err) pressEnterToExit(1) } stat, err := ffprobe(inputVideo) if err != nil { fmt.Println(err) return } if !debug { err = func() error { if err = scanArgFromConsole("ss > ", &ss); err != nil { return err } if err = scanArgFromConsole("to > ", &to); err != nil { return err } if err = scanArgFromConsole("an > ", &an); err != nil { return err } if err = scanArgFromConsole(fmt.Sprintf("crf [%v] > ", DEFAULT_CRF), &crf); err != nil { return err } if err = scanArgFromConsole("speed up > ", &speedUp); err != nil { return err } if err = scanArgFromConsole(fmt.Sprintf("preset [%v] > ", DEFAULT_PRESET), &preset); err != nil { return err } if err = scanArgFromConsole("merge audio streams > ", &mergeAudioStreams); err != nil { return err } return nil }() if err != nil { os.Exit(0) } } for _, v := range []*string{&ss, &to, &an, &crf, &preset, &mergeAudioStreams, &speedUp} { *v = strings.ToLower(strings.TrimSpace(*v)) } if len(ss) != 0 { inputArgs = append(inputArgs, "-ss", ss) } if len(to) != 0 { inputArgs = append(inputArgs, "-to", to) } if len(an) != 0 { postArgs = append(postArgs, "-an") } else { audioFilters = append(audioFilters, "amix=inputs=2:normalize=0") } if len(crf) == 0 { crf = DEFAULT_CRF } if len(preset) == 0 { preset = DEFAULT_PRESET } postArgs = append(postArgs, "-preset", preset) postArgs = append(postArgs, "-crf", crf) // speed up video and audio(?) if len(speedUp) != 0 { videoFilters = append(videoFilters, fmt.Sprintf("setpts=1/%s*PTS", speedUp)) if len(an) == 0 { audioFilters = append(audioFilters, fmt.Sprintf("atempo=%s", speedUp)) } } if len(videoFilters) != 0 { vfArgs = append(vfArgs, "[0:v:0]"+strings.Join(videoFilters, ",")+"[v]") postArgs = append(postArgs, "-map", "[v]") } else { postArgs = append(postArgs, "-map", "0:v:0") } if len(audioFilters) != 0 { vfArgs = append(vfArgs, "[0:a]"+strings.Join(audioFilters, ",")+"[a]") postArgs = append(postArgs, "-map", "[a]") } else { postArgs = append(postArgs, "-map", "0:a") } // merge filter complex args if len(vfArgs) != 0 { postArgs = append(postArgs, "-filter_complex", strings.Join(vfArgs, ";")) } // out file name outDir, _ := filepath.Split(inputVideo) outFile := filepath.Join(outDir, strconv.FormatInt(time.Now().Unix(), 10)+".mp4") // merge args to one slice ffmpegArgs = append(ffmpegArgs, inputArgs...) ffmpegArgs = append(ffmpegArgs, "-i", inputVideo) ffmpegArgs = append(ffmpegArgs, postArgs...) ffmpegArgs = append(ffmpegArgs, outFile) fmt.Printf(" running %v: %v ", ffmpegExecutable, strings.Join(ffmpegArgs, " ")) proc := exec.Command(ffmpegExecutable, ffmpegArgs...) proc.Stderr = os.Stderr err = proc.Start() if err != nil { fmt.Println(err) pressEnterToExit(1) } err = proc.Wait() if err != nil { fmt.Println(err) pressEnterToExit(1) } if proc.ProcessState.ExitCode() != 0 { fmt.Println(err) pressEnterToExit(1) } }