func parseOptions(input, output, data string) []string {
	args := []string{
		"-hide_banner",
		"-loglevel", "error",  
		"-progress", "pipe:1",
		"-i", input,
	}
	options := &ffmpegOptions{}
	if err := json.Unmarshal([]byte(data), &options); err != nil {
		panic(err)
	}
	if len(options.Raw) > 0 {
		for _, v := range options.Raw {
			args = append(args, strings.Split(v, " ")...)
		}
		args = append(args, output)
		return args
	}
	args = append(args, transformOptions(options)...)
	if options.Video.Pass == "2" {
		args = append(args, set2Pass(&args)...)
	}
	args = append(args, output)
	return args
}
Utilities for parsing ffmpeg options.
package encoder
import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os/exec"
	"strconv"
	"strings"
	"time"
)
const (
	ffmpegCmd      = "ffmpeg"
	updateInterval = time.Second * 5
)
type FFmpeg struct {
	Progress    progress
	cmd         *exec.Cmd
	isCancelled bool
}
type progress struct {
	quit chan struct{}
	Frame      int
	FPS        float64
	Bitrate    float64
	TotalSize  int
	OutTimeMS  int
	OutTime    string
	DupFrames  int
	DropFrames int
	Speed      string
	Progress   float64
}
type ffmpegOptions struct {
	Input  string
	Output string
	Format formatOptions `json:"format"`
	Video  videoOptions  `json:"video"`
	Audio  audioOptions  `json:"audio"`
	Filter filterOptions `json:"filter"`
	Raw []string `json:"raw"`  
}
type formatOptions struct {
	Container string `json:"container"`
	Clip      bool   `json:"clip"`
	StartTime string `json:"startTime"`
	StopTime  string `json:"stopTime"`
}
type videoOptions struct {
	Codec        string `json:"codec"`
	Preset       string `json:"preset"`
	Pass         string `json:"pass"`
	Crf          int    `json:"crf"`
	Bitrate      string `json:"bitrate"`
	MinRate      string `json:"minrate"`
	MaxRate      string `json:"maxrate"`
	BufSize      string `json:"bufsize"`
	PixelFormat  string `json:"pixel_format"`
	FrameRate    string `json:"frame_rate"`
	Speed        string `json:"speed"`
	Tune         string `json:"tune"`
	Profile      string `json:"profile"`
	Level        string `json:"level"`
	FastStart    bool   `json:"faststart"`
	Size         string `json:"size"`
	Width        string `json:"width"`
	Height       string `json:"height"`
	Format       string `json:"format"`
	Aspect       string `json:"aspect"`
	Scaling      string `json:"scaling"`
	CodecOptions string `json:"codec_options"`
}
type audioOptions struct {
	Codec      string `json:"codec"`
	Channel    string `json:"channel"`
	Quality    string `json:"quality"`
	SampleRate string `json:"sample_rate"`
	Volume     string `json:"volume"`
}
type filterOptions struct {
	Deband      bool   `json:"deband"`
	Deshake     bool   `json:"deshake"`
	Deflicker   bool   `json:"deflicker"`
	Dejudder    bool   `json:"dejudder"`
	Denoise     string `json:"denoise"`
	Deinterlace string `json:"deinterlace"`
	Brightness  string `json:"brightness"`
	Contrast    string `json:"contrast"`
	Saturation  string `json:"saturation"`
	Gamma       string `json:"gamma"`
	Acontrast   string `json:"acontrast"`
}
func (f *FFmpeg) Run(input, output, data string) error {
	args := parseOptions(input, output, data)
	log.Info("running FFmpeg with options: ", args)
	f.cmd = exec.Command(ffmpegCmd, args...)
	stdout, _ := f.cmd.StdoutPipe()
	var stderr bytes.Buffer
	f.cmd.Stderr = &stderr
	err := f.cmd.Start()
	if err != nil {
		return err
	}
	go f.trackProgress()
	f.updateProgress(stdout)
	err = f.cmd.Wait()
	if err != nil {
		if f.isCancelled {
			return errors.New("cancelled")
		}
		f.finish()
		return err
	}
	f.finish()
	return nil
}
func (f *FFmpeg) Cancel() {
	log.Warn("killing ffmpeg process")
	f.isCancelled = true
	if err := f.cmd.Process.Kill(); err != nil {
		log.Warn("failed to kill process: ", err)
	}
	log.Warn("killed ffmpeg process")
}
func (f *FFmpeg) Version() string {
	out, _ := exec.Command(ffmpegCmd, "-version").Output()
	return string(out)
}
func (f *FFmpeg) updateProgress(stdout io.ReadCloser) {
	scanner := bufio.NewScanner(stdout)
	for scanner.Scan() {
		line := scanner.Text()
		str := strings.Replace(line, " ", "", -1)
		parts := strings.Split(str, " ")
		f.setProgressParts(parts)
	}
}
func (f *FFmpeg) setProgressParts(parts []string) {
	for i := 0; i < len(parts); i++ {
		progressSplit := strings.Split(parts[i], "=")
		k := progressSplit[0]
		v := progressSplit[1]
		switch k {
		case "frame":
			frame, _ := strconv.Atoi(v)
			f.Progress.Frame = frame
		case "fps":
			fps, _ := strconv.ParseFloat(v, 64)
			f.Progress.FPS = fps
		case "bitrate":
			v = strings.Replace(v, "kbits/s", "", -1)
			bitrate, _ := strconv.ParseFloat(v, 64)
			f.Progress.Bitrate = bitrate
		case "total_size":
			size, _ := strconv.Atoi(v)
			f.Progress.TotalSize = size
		case "out_time_ms":
			outTimeMS, _ := strconv.Atoi(v)
			f.Progress.OutTimeMS = outTimeMS
		case "out_time":
			f.Progress.OutTime = v
		case "dup_frames":
			frames, _ := strconv.Atoi(v)
			f.Progress.DupFrames = frames
		case "drop_frames":
			frames, _ := strconv.Atoi(v)
			f.Progress.DropFrames = frames
		case "speed":
			f.Progress.Speed = v
		case "progress":
			progress, _ := strconv.ParseFloat(v, 64)
			f.Progress.Progress = progress
		}
	}
}
func (f *FFmpeg) trackProgress() {
	f.Progress.quit = make(chan struct{})
	ticker := time.NewTicker(updateInterval)
	for {
		select {
		case <-f.Progress.quit:
			ticker.Stop()
			return
		case <-ticker.C:
		}
	}
}
func (f *FFmpeg) finish() {
	close(f.Progress.quit)
}