diff --git a/pkg/encoder_options.go b/pkg/encoder_options.go index 16e0843..a925ff7 100644 --- a/pkg/encoder_options.go +++ b/pkg/encoder_options.go @@ -2,6 +2,7 @@ package transcode import ( "reflect" + "strings" buffer "github.com/harshabose/tools/buffer/pkg" @@ -16,37 +17,80 @@ type codecSettings interface { ForEach(func(string, string) error) error } -type X264Settings struct { - Preset string `x264:"preset"` - Tune string `x264:"tune"` - Refs string `x264:"refs"` - Profile string `x264:"profile"` - Level string `x264:"level"` - Qmin string `x264:"qmin"` - Qmax string `x264:"qmax"` - BFrames string `x264:"bframes"` - BAdapt string `x264:"b-adapt"` - NGOP string `x264:"keyint"` - NGOPMin string `x264:"min-keyint"` - Scenecut string `x264:"scenecut"` - InfraRefresh string `x264:"intra-refresh"` - LookAhead string `x264:"rc-lookahead"` - SlicedThreads string `x264:"sliced-threads"` - SyncLookAhead string `x264:"sync-lookahead"` - ForceIDR string `x264:"force-idr"` - AQMode string `x264:"aq-mode"` - AQStrength string `x264:"aq-strength"` - MBTree string `x264:"mbtree"` - Bitrate string `x264:"bitrate"` - VBVMaxBitrate string `x264:"vbv-maxrate"` - VBVBuffer string `x264:"vbv-bufsize"` - RateTol string `x264:"ratetol"` - Threads string `x264:"threads"` - AnnexB string `x264:"annexb"` - Aud string `x264:"aud"` +type X264Opts struct { + // RateControl string `x264-opts:"rate-control"` + Bitrate string `x264-opts:"bitrate"` + VBVMaxBitrate string `x264-opts:"vbv-maxrate"` + VBVBuffer string `x264-opts:"vbv-bufsize"` + RateTol string `x264-opts:"ratetol"` + SyncLookAhead string `x264-opts:"sync-lookahead"` + AnnexB string `x264-opts:"annexb"` } -func (s X264Settings) ForEach(fn func(key, value string) error) error { +func (x264 X264Opts) ForEach(fn func(string, string) error) error { + t := reflect.TypeOf(x264) + v := reflect.ValueOf(x264) + + // Build a single x264opts string + var optParts []string + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + tag := field.Tag.Get("x264-opts") + if tag != "" && v.Field(i).String() != "" { + optParts = append(optParts, tag+"="+v.Field(i).String()) + } + } + + // Join all options with colons + if len(optParts) > 0 { + x264optsValue := strings.Join(optParts, ":") + // Set as a single parameter + if err := fn("x264opts", x264optsValue); err != nil { + return err + } + } + + // Also apply any regular x264 parameters + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + tag := field.Tag.Get("x264") + if tag != "" { + if err := fn(tag, v.Field(i).String()); err != nil { + return err + } + } + } + + return nil +} + +type X264OpenSettings struct { + X264Opts + Preset string `x264:"preset"` // exists + Tune string `x264:"tune"` // exists + Refs string `x264:"refs"` // exists + Profile string `x264:"profile"` // exists + Level string `x264:"level"` // exists + Qmin string `x264:"qmin"` // exists + Qmax string `x264:"qmax"` // exists + BFrames string `x264:"bf"` // exists + BAdapt string `x264:"b_strategy"` // exists + NGOP string `x264:"g"` // exists + NGOPMin string `x264:"keyint_min"` // exists + Scenecut string `x264:"sc_threshold"` // exists + InfraRefresh string `x264:"intra-refresh"` // exists + LookAhead string `x264:"rc-lookahead"` // exists + SlicedThreads string `x264:"slice"` // exists + ForceIDR string `x264:"force-idr"` // exists + AQMode string `x264:"aq-mode"` // exists + AQStrength string `x264:"aq-strength"` // exists + MBTree string `x264:"mbtree"` // exists + Threads string `x264:"threads"` // exists + Aud string `x264:"aud"` // exists +} + +func (s X264OpenSettings) ForEach(fn func(key, value string) error) error { t := reflect.TypeOf(s) v := reflect.ValueOf(s) @@ -59,10 +103,20 @@ func (s X264Settings) ForEach(fn func(key, value string) error) error { } } } - return nil + + return s.X264Opts.ForEach(fn) } -var DefaultX264Settings = X264Settings{ +var DefaultX264Settings = X264OpenSettings{ + X264Opts: X264Opts{ + // RateControl: "abr", + Bitrate: "4000", + VBVMaxBitrate: "5000", + VBVBuffer: "8000", + RateTol: "1", + SyncLookAhead: "1", + AnnexB: "1", + }, Preset: "medium", Tune: "film", Refs: "6", @@ -78,21 +132,24 @@ var DefaultX264Settings = X264Settings{ InfraRefresh: "0", LookAhead: "40", SlicedThreads: "0", - SyncLookAhead: "1", ForceIDR: "0", AQMode: "1", AQStrength: "1.0", MBTree: "1", - Bitrate: "4000", - VBVMaxBitrate: "5000", - VBVBuffer: "8000", - RateTol: "1", Threads: "0", - AnnexB: "1", Aud: "0", } -var LowBandwidthX264Settings = X264Settings{ +var LowBandwidthX264Settings = X264OpenSettings{ + X264Opts: X264Opts{ + // RateControl: "abr", + Bitrate: "1500", + VBVMaxBitrate: "1800", + VBVBuffer: "3000", + RateTol: "0.25", + SyncLookAhead: "0", + AnnexB: "1", + }, Preset: "veryfast", Tune: "fastdecode", Refs: "2", @@ -108,21 +165,24 @@ var LowBandwidthX264Settings = X264Settings{ InfraRefresh: "0", LookAhead: "20", SlicedThreads: "1", - SyncLookAhead: "0", ForceIDR: "0", AQMode: "0", AQStrength: "1.2", MBTree: "0", - Bitrate: "1500", - VBVMaxBitrate: "1800", - VBVBuffer: "3000", - RateTol: "0.25", Threads: "0", - AnnexB: "1", Aud: "0", } -var LowLatencyX264Settings = X264Settings{ +var LowLatencyX264Settings = X264OpenSettings{ + X264Opts: X264Opts{ + // RateControl: "abr", + Bitrate: "5000", + VBVMaxBitrate: "12000", + VBVBuffer: "20000", + RateTol: "0.5", + SyncLookAhead: "0", + AnnexB: "1", + }, Preset: "ultrafast", Tune: "zerolatency", Refs: "1", @@ -138,21 +198,25 @@ var LowLatencyX264Settings = X264Settings{ InfraRefresh: "1", LookAhead: "10", SlicedThreads: "1", - SyncLookAhead: "0", ForceIDR: "1", AQMode: "0", AQStrength: "0", MBTree: "0", - Bitrate: "2500", - VBVMaxBitrate: "3000", - VBVBuffer: "5000", - RateTol: "0.5", - Threads: "0", - AnnexB: "1", - Aud: "1", + + Threads: "0", + Aud: "1", } -var HighQualityX264Settings = X264Settings{ +var HighQualityX264Settings = X264OpenSettings{ + X264Opts: X264Opts{ + // RateControl: "abr", + Bitrate: "15000", + VBVMaxBitrate: "20000", + VBVBuffer: "30000", + RateTol: "2.0", + SyncLookAhead: "1", + AnnexB: "1", + }, Preset: "slow", Tune: "film", Refs: "8", @@ -168,18 +232,13 @@ var HighQualityX264Settings = X264Settings{ InfraRefresh: "0", LookAhead: "60", SlicedThreads: "0", - SyncLookAhead: "1", ForceIDR: "0", AQMode: "0", AQStrength: "1.3", MBTree: "1", - Bitrate: "15000", - VBVMaxBitrate: "20000", - VBVBuffer: "30000", - RateTol: "2.0", - Threads: "0", - AnnexB: "1", - Aud: "0", + + Threads: "0", + Aud: "0", } func WithX264DefaultOptions(encoder *Encoder) error {