video backend CLI flag

This commit is contained in:
Dmitry Sergeev
2026-09-17 23:16:59 +03:00
parent f5df9506ce
commit fa787b5ef1
3 changed files with 106 additions and 5 deletions
+32 -5
View File
@@ -43,11 +43,12 @@ type appArgs struct {
overlayY int overlayY int
overlayPos string overlayPos string
videoWidth uint videoWidth uint
videoHeight uint videoHeight uint
videoFPS string videoFPS string
videoUUID string videoUUID string
noVideo bool noVideo bool
videoBackend string
audioChannels uint8 audioChannels uint8
audioSamplingFreq string audioSamplingFreq string
@@ -136,6 +137,9 @@ func validateVideoArgs(args *appArgs) error {
if args.noVideo { if args.noVideo {
return nil return nil
} }
if _, err := parseVideoBackend(args.videoBackend); err != nil {
return err
}
if !video.HasPattern(args.pattern) { if !video.HasPattern(args.pattern) {
return fmt.Errorf("unknown video pattern %q (use --list-patterns to see available patterns)", args.pattern) return fmt.Errorf("unknown video pattern %q (use --list-patterns to see available patterns)", args.pattern)
} }
@@ -284,6 +288,7 @@ func addFlags(fs *pflag.FlagSet, args *appArgs) {
fs.StringVar(&args.videoFPS, "fps", "25", "Video pattern FPS") fs.StringVar(&args.videoFPS, "fps", "25", "Video pattern FPS")
fs.StringVar(&args.videoUUID, "video-id", "", "Video UUID. Will be created, if not provided") fs.StringVar(&args.videoUUID, "video-id", "", "Video UUID. Will be created, if not provided")
fs.BoolVar(&args.noVideo, "no-video", false, "Disable video generation; audio must be enabled") fs.BoolVar(&args.noVideo, "no-video", false, "Disable video generation; audio must be enabled")
fs.StringVar(&args.videoBackend, "backend", string(video.BackendAuto), "Video generator backend: auto, gpu or cpu")
// Audio pattern flags // Audio pattern flags
fs.Uint8VarP(&args.audioChannels, "channel", "c", 0, "Amount of audio channels. Each channel: num * 1kHz") fs.Uint8VarP(&args.audioChannels, "channel", "c", 0, "Amount of audio channels. Each channel: num * 1kHz")
fs.StringVarP(&args.audioSamplingFreq, "freq", "f", "48", "Sampling frequency of test audio feed in kHz") fs.StringVarP(&args.audioSamplingFreq, "freq", "f", "48", "Sampling frequency of test audio feed in kHz")
@@ -319,6 +324,22 @@ func parseArgs(argv []string, stdout, stderr io.Writer) (parseResult, error) {
return parseResult{args: args, shouldRun: true}, nil return parseResult{args: args, shouldRun: true}, nil
} }
func parseVideoBackend(value string) (video.Backend, error) {
if value == "" {
return video.BackendAuto, nil
}
backend := video.Backend(value)
switch backend {
case video.BackendAuto, video.BackendGPU, video.BackendCPU:
return backend, nil
default:
return "", fmt.Errorf(
"unsupported video backend %q (supported: auto, gpu, cpu)",
value,
)
}
}
func buildVideoConfig(args appArgs) (*video.Config, error) { func buildVideoConfig(args appArgs) (*video.Config, error) {
if args.noVideo { if args.noVideo {
return nil, nil return nil, nil
@@ -370,6 +391,11 @@ func buildVideoConfig(args appArgs) (*video.Config, error) {
return nil, fmt.Errorf("unknown video pattern %q", args.pattern) return nil, fmt.Errorf("unknown video pattern %q", args.pattern)
} }
backend, err := parseVideoBackend(args.videoBackend)
if err != nil {
return nil, err
}
return &video.Config{ return &video.Config{
Definition: definition, Definition: definition,
Pattern: args.pattern, Pattern: args.pattern,
@@ -379,6 +405,7 @@ func buildVideoConfig(args appArgs) (*video.Config, error) {
Y: args.overlayY, Y: args.overlayY,
Position: args.overlayPos, Position: args.overlayPos,
}, },
Backend: backend,
}, nil }, nil
} }
+65
View File
@@ -9,6 +9,7 @@ import (
"mxl-pattern-generator/internal/audio" "mxl-pattern-generator/internal/audio"
"mxl-pattern-generator/internal/flowdef" "mxl-pattern-generator/internal/flowdef"
"mxl-pattern-generator/internal/video"
) )
func TestParseArgsHelpStopsBeforeValidation(t *testing.T) { func TestParseArgsHelpStopsBeforeValidation(t *testing.T) {
@@ -112,6 +113,46 @@ func TestValidateVideoArgsSkipsDisabledVideo(t *testing.T) {
} }
} }
func TestParseVideoBackend(t *testing.T) {
tests := []struct {
name string
value string
want video.Backend
wantErr bool
}{
{name: "zero value defaults to auto", want: video.BackendAuto},
{name: "auto", value: "auto", want: video.BackendAuto},
{name: "gpu", value: "gpu", want: video.BackendGPU},
{name: "cpu", value: "cpu", want: video.BackendCPU},
{name: "unknown", value: "other", wantErr: true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got, err := parseVideoBackend(tc.value)
if tc.wantErr {
if err == nil || !strings.Contains(err.Error(), "unsupported video backend") {
t.Fatalf("error = %v, want unsupported backend error", err)
}
return
}
if err != nil {
t.Fatalf("parseVideoBackend: %v", err)
}
if got != tc.want {
t.Fatalf("backend = %q, want %q", got, tc.want)
}
})
}
}
func TestValidateVideoArgsRejectsUnknownBackend(t *testing.T) {
args := appArgs{videoBackend: "other"}
if err := validateVideoArgs(&args); err == nil || !strings.Contains(err.Error(), "unsupported video backend") {
t.Fatalf("error = %v, want unsupported backend error", err)
}
}
func TestValidateMediaSelection(t *testing.T) { func TestValidateMediaSelection(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
@@ -187,6 +228,30 @@ func TestBuildVideoConfigFromArgs(t *testing.T) {
if cfg.Rate().Num != 30000 || cfg.Rate().Den != 1001 { if cfg.Rate().Num != 30000 || cfg.Rate().Den != 1001 {
t.Fatalf("rate = %d/%d, want 30000/1001", cfg.Rate().Num, cfg.Rate().Den) t.Fatalf("rate = %d/%d, want 30000/1001", cfg.Rate().Num, cfg.Rate().Den)
} }
if cfg.Backend != video.BackendAuto {
t.Fatalf("backend = %q, want %q", cfg.Backend, video.BackendAuto)
}
}
func TestBuildVideoConfigBackends(t *testing.T) {
for _, backend := range []video.Backend{video.BackendGPU, video.BackendCPU} {
t.Run(string(backend), func(t *testing.T) {
cfg, err := buildVideoConfig(appArgs{
videoUUID: "5fbec3b1-1b0f-417d-9059-8b94a47197ed",
videoWidth: 1920,
videoHeight: 1080,
videoFPS: "25",
pattern: "ebu75",
videoBackend: string(backend),
})
if err != nil {
t.Fatalf("buildVideoConfig: %v", err)
}
if cfg.Backend != backend {
t.Fatalf("backend = %q, want %q", cfg.Backend, backend)
}
})
}
} }
func TestBuildVideoConfigDisabled(t *testing.T) { func TestBuildVideoConfigDisabled(t *testing.T) {
+9
View File
@@ -6,10 +6,19 @@ import (
"github.com/qvest-digital/go-mxl/mxl" "github.com/qvest-digital/go-mxl/mxl"
) )
type Backend string
const (
BackendAuto Backend = "auto"
BackendGPU Backend = "gpu"
BackendCPU Backend = "cpu"
)
type Config struct { type Config struct {
Definition flowdef.Video Definition flowdef.Video
Pattern string Pattern string
Overlay OverlayConfig Overlay OverlayConfig
Backend Backend
} }
type OverlayConfig struct { type OverlayConfig struct {