diff --git a/cli/encode-server/decoder_dav1d.go b/cli/encode-server/decoder_dav1d.go new file mode 100644 index 0000000..419df62 --- /dev/null +++ b/cli/encode-server/decoder_dav1d.go @@ -0,0 +1,20 @@ +//go:build cgo && !disable_library_libdav1d + +package main + +import ( + "git.gammaspectra.live/S.O.N.G/Ignite/decoder" + "git.gammaspectra.live/S.O.N.G/Ignite/decoder/libdav1d" + "io" +) + +func init() { + Decoders = append(Decoders, DecoderEntry{ + Name: DecoderDav1d, + Version: libdav1d.Version, + MimeType: "video/x-ivf", + New: func(w io.Reader, settings map[string]any) (decoder.Decoder, error) { + return libdav1d.NewDecoder(w, settings) + }, + }) +} diff --git a/cli/encode-server/decoder_y4m.go b/cli/encode-server/decoder_y4m.go new file mode 100644 index 0000000..879915a --- /dev/null +++ b/cli/encode-server/decoder_y4m.go @@ -0,0 +1,20 @@ +package main + +import ( + "git.gammaspectra.live/S.O.N.G/Ignite/decoder" + "git.gammaspectra.live/S.O.N.G/Ignite/decoder/y4m" + "io" +) + +func init() { + Decoders = append(Decoders, DecoderEntry{ + Name: DecoderY4M, + Version: func() string { + return "1.0" + }, + MimeType: "video/x-yuv4mpeg2", + New: func(w io.Reader, settings map[string]any) (decoder.Decoder, error) { + return y4m.NewDecoder(w, settings) + }, + }) +} diff --git a/cli/encode-server/decoders.go b/cli/encode-server/decoders.go new file mode 100644 index 0000000..b49ad7b --- /dev/null +++ b/cli/encode-server/decoders.go @@ -0,0 +1,38 @@ +package main + +import ( + "git.gammaspectra.live/S.O.N.G/Ignite/decoder" + "io" + "slices" +) + +const ( + DecoderY4M = "y4m" + DecoderDav1d = "libdav1d" +) + +type DecoderEntry struct { + Name string + Version func() string + MimeType string + New func(w io.Reader, settings map[string]any) (decoder.Decoder, error) +} + +var Decoders []DecoderEntry + +func GetDecoderByName(name string) *DecoderEntry { + if i := slices.IndexFunc(Decoders, func(entry DecoderEntry) bool { + return entry.Name == name + }); i != -1 { + return &Decoders[i] + } + return nil +} +func GetDecoderByMimeType(mimeType string) *DecoderEntry { + if i := slices.IndexFunc(Decoders, func(entry DecoderEntry) bool { + return entry.MimeType == mimeType + }); i != -1 { + return &Decoders[i] + } + return nil +} diff --git a/cli/encode-server/encode.go b/cli/encode-server/encode.go index 940d6f2..d526bca 100644 --- a/cli/encode-server/encode.go +++ b/cli/encode-server/encode.go @@ -6,7 +6,6 @@ import ( "compress/gzip" "errors" "fmt" - "git.gammaspectra.live/S.O.N.G/Ignite/decoder/y4m" "git.gammaspectra.live/S.O.N.G/Ignite/frame" "github.com/ulikunitz/xz" "io" @@ -42,8 +41,15 @@ func handleDecompress(contentEncoding string, reader io.ReadCloser) (io.ReadClos return reader, nil } -func encodeFromReader(reader io.ReadCloser, job *Job, w http.ResponseWriter) { +func encodeFromReader(reader io.ReadCloser, job *Job, inputMimeType string, w http.ResponseWriter) { defer reader.Close() + + d := GetDecoderByMimeType(inputMimeType) + if d == nil { + w.WriteHeader(http.StatusBadRequest) + return + } + defer func() { log.Printf("[job %s] finished, took %s", job.Id, time.Now().Sub(time.Unix(int64(job.Status.Start.Load()), 0))) }() @@ -58,7 +64,7 @@ func encodeFromReader(reader io.ReadCloser, job *Job, w http.ResponseWriter) { settings["timecodes"] = job.Config.Timecodes } - decoder, err := y4m.NewDecoder(reader, settings) + decoder, err := d.New(reader, settings) if err != nil { w.Header().Set("x-encoder-error", "") w.Header().Set("x-decoder-error", err.Error()) diff --git a/cli/encode-server/main.go b/cli/encode-server/main.go index 743c764..1176f4f 100644 --- a/cli/encode-server/main.go +++ b/cli/encode-server/main.go @@ -194,11 +194,6 @@ func main() { } defer job.Unlock() - if r.Header.Get("Content-Type") != "video/x-yuv4mpeg2" { - w.WriteHeader(http.StatusBadRequest) - return - } - reader, err := handleDecompress(r.Header.Get("Content-Encoding"), r.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) @@ -208,7 +203,7 @@ func main() { defer r.Body.Close() log.Printf("[job %s] started POST", job.Id) - encodeFromReader(reader, job, w) + encodeFromReader(reader, job, r.Header.Get("Content-Type"), w) }) serveMux.HandleFunc("/startURL", func(w http.ResponseWriter, r *http.Request) { @@ -270,21 +265,21 @@ func main() { // Handle filenames switch strings.ToLower(path.Ext(urlVal.Path)) { - case "gz": + case ".gz": reader, err = handleDecompress("gz", reader) if err != nil { w.WriteHeader(http.StatusBadRequest) _, _ = w.Write([]byte(err.Error())) return } - case "bz2", "bzip2": + case ".bz2", ".bzip2": reader, err = handleDecompress("bz2", reader) if err != nil { w.WriteHeader(http.StatusBadRequest) _, _ = w.Write([]byte(err.Error())) return } - case "xz": + case ".xz": reader, err = handleDecompress("xz", reader) if err != nil { w.WriteHeader(http.StatusBadRequest) @@ -293,8 +288,19 @@ func main() { } } + fname := strings.TrimSuffix(urlVal.Path, path.Ext(urlVal.Path)) + inputMimeType := response.Header.Get("Content-Type") + switch strings.ToLower(path.Ext(fname)) { + case ".y4m": + inputMimeType = "video/x-yuv4mpeg2" + case ".av1": + inputMimeType = "video/x-ivf" + case ".h264", ".x264": + inputMimeType = "video/h264" + } + log.Printf("[job %s] started URL", job.Id) - encodeFromReader(reader, job, w) + encodeFromReader(reader, job, inputMimeType, w) }) serveMux.HandleFunc("/job", func(w http.ResponseWriter, r *http.Request) { diff --git a/decoder/decoder.go b/decoder/decoder.go index a05b1d7..546d732 100644 --- a/decoder/decoder.go +++ b/decoder/decoder.go @@ -5,6 +5,7 @@ import "git.gammaspectra.live/S.O.N.G/Ignite/frame" type Decoder interface { Decode() (frame.Frame, error) DecodeStream() *frame.Stream + Properties() frame.StreamProperties Close() Version() string }