Ignite/decoder/ffmpeg/ffmpeg.go

118 lines
2 KiB
Go

package ffmpeg
import (
"fmt"
"git.gammaspectra.live/S.O.N.G/Ignite/decoder/y4m"
"git.gammaspectra.live/S.O.N.G/Ignite/frame"
"git.gammaspectra.live/S.O.N.G/Ignite/utilities"
"io"
"os"
"os/exec"
"strconv"
)
type Decoder struct {
cmd *exec.Cmd
y4m *y4m.Decoder
}
func NewDecoder(r io.Reader, settings map[string]any) (d *Decoder, err error) {
videoIndex, err := strconv.ParseUint(utilities.GetSettingString(settings, "input-video-index", "0"), 10, 0)
if err != nil {
return nil, err
}
pixelFormat := utilities.GetSettingString(settings, "pix_fmt", "")
ffmpegBinary := "ffmpeg"
if p := os.Getenv("FFMPEG_PATH"); p != "" {
ffmpegBinary = p
}
args := []string{
"-v", "quiet",
"-probesize", "8192",
"-strict", "experimental",
"-i", "pipe:",
"-map_metadata", "-1",
"-map", fmt.Sprintf("0:v:%d", videoIndex),
}
if pixelFormat != "" {
args = append(args, "-pix_fmt:v", pixelFormat)
}
args = append(args, []string{
"-f", "yuv4mpegpipe",
"-strict", "experimental",
"pipe:",
}...)
cmd := exec.Command(ffmpegBinary,
args...,
)
cmdIn, err := cmd.StdinPipe()
if err != nil {
return nil, err
}
cmdOut, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
cmd.Stderr = os.Stderr
err = cmd.Start()
if err != nil {
defer cmd.Process.Release()
return nil, err
}
d = &Decoder{
cmd: cmd,
}
go func() {
defer cmdIn.Close()
_, _ = io.Copy(cmdIn, r)
}()
d.y4m, err = y4m.NewDecoder(cmdOut, settings)
if err != nil {
defer d.Close()
return nil, err
}
return d, nil
}
func (d *Decoder) Decode() (frame.Frame, error) {
return d.y4m.Decode()
}
func (d *Decoder) DecodeStream() *frame.Stream {
return d.y4m.DecodeStream()
}
func (d *Decoder) Properties() frame.StreamProperties {
return d.y4m.Properties()
}
func (d *Decoder) Version() string {
return d.y4m.Version()
}
func (d *Decoder) Close() {
if d.y4m != nil {
d.y4m.Close()
d.y4m = nil
}
if d.cmd != nil {
d.cmd.Process.Kill()
d.cmd.Process.Release()
d.cmd = nil
}
}