CLI args parse basic

This commit is contained in:
Dmitry Sergeev
2026-08-24 23:07:23 +03:00
parent 819a8a9c83
commit a7874dddd8
+84 -47
View File
@@ -3,12 +3,13 @@ package main
import (
"context"
"errors"
"flag"
"fmt"
"io"
"log"
"mxl-player/internal/renderer"
"mxl-player/internal/sdl"
"mxl-player/internal/source"
"os"
"runtime"
"time"
"unsafe"
@@ -25,7 +26,7 @@ const (
WIN_HEIGHT int32 = 720
)
type CliArgs struct {
type appArgs struct {
ShowHelp bool
Domain string
VideoFlowId string
@@ -40,7 +41,7 @@ type CliArgs struct {
func printCliHelp(fs *pflag.FlagSet) {
fmt.Printf("%s %s\n", APP_NAME, APP_VER)
fmt.Println("Usage: mxl-player -d <domain> (-v <uuid> | -a <uuid>) [-f] [-h]...")
fmt.Println("Usage: mxl-player -d <domain> (-v <uuid> | -a <uuid>) [-f] [-h]")
fmt.Println(" [-g <gpu-id>] [-p <playback-id>] [--verbose]")
fmt.Println(" or: mxl-player [--list-playback] [--list-gpu]")
fmt.Println(" or: mxl-player (and set everything in GUI)")
@@ -48,50 +49,62 @@ func printCliHelp(fs *pflag.FlagSet) {
fs.PrintDefaults()
}
func printUsage(w io.Writer) {
fmt.Fprintln(w, "Usage: mxl-player -d <domain> (-v <uuid> | -a <uuid>) [-f] [-h]")
fmt.Fprintln(w, "Try 'mxl-player -h' for more information.")
}
func checkMXLargs(args appArgs) {
if args.Domain == "" {
fmt.Fprintln(os.Stderr, "You should provide valid MXL domain and UUID of at least one flow")
printUsage(os.Stderr)
os.Exit(2)
}
fi, err := os.Stat(args.Domain)
if err != nil || !fi.IsDir() {
fmt.Fprintln(os.Stderr, "Invalid MXL domain:", args.Domain)
fmt.Fprintln(os.Stderr, "Domain must be a directory in tmpfs")
printUsage(os.Stderr)
os.Exit(2)
}
if args.VideoFlowId == "" && args.AudioFlowId == "" {
fmt.Fprintln(os.Stderr, "You must provide at least 1 MXL flow UUID")
printUsage(os.Stderr)
os.Exit(2)
}
}
func main() {
// CLI args TODO:
// help + sample of good usage
// domain (-d, --domain)
// video flow UUID (-v, --video)
// audio flow UUID (-a, --audio)
// list-audio for list audio playback devices (-la, --list-playback)
// list-video fpr list video playback devices (-lg, --list-gpu)
// gpu-id for GPU selection (-p, --playback-id)
// playback-id for playback id selection (-g, --gpu-id)
// verbose flag, which turn on logs (-vb,--verbose)
//
// video: 5fbec3b1-1b0f-417d-9059-8b94a47197ed
// audio: 5fbec3b1-1b0f-417d-9059-8b94a47197ec
var args CliArgs
flagSet := pflag.NewFlagSet(APP_NAME, pflag.ExitOnError)
var args appArgs
flagSet := pflag.NewFlagSet(APP_NAME, pflag.ContinueOnError)
flagSet.Usage = func() { printUsage(os.Stderr) }
flagSet.BoolVarP(&args.ShowHelp, "help", "h", false, "Show help message and exit")
flagSet.StringVarP(&args.Domain, "domain", "d", "", "MXL domain directory")
flagSet.StringVarP(&args.VideoFlowId, "video", "v", "", "Video flow UUID")
flagSet.StringVarP(&args.AudioFlowId, "audio", "a", "", "Audio flow UUID")
flagSet.BoolVarP(&args.IsFullscreen, "fullscreen", "f", false, "Run app in fullscreen mode")
flagSet.Uint32VarP(&args.GpuId, "gpu-id", "g", 0, "GPU id")
flagSet.Uint32VarP(&args.GpuId, "gpu-id", "g", 0, "GPU id [TODO]")
flagSet.Uint32VarP(&args.PlaybackId, "playback-id", "p", 0, "Playback audio device id")
flagSet.BoolVar(&args.IsVerbose, "verbose", false, "Verbose output")
flagSet.BoolVar(&args.IsVerbose, "verbose", false, "Verbose output [TODO]")
flagSet.BoolVar(&args.ListAudio, "list-playback", false, "List audio playback devices and exit")
flagSet.BoolVar(&args.ListGPU, "list-gpu", false, "List GPU's and exit")
pflag.Parse()
flagSet.BoolVar(&args.ListGPU, "list-gpu", false, "List GPUs and exit")
if args.Domain == "" || args.ShowHelp {
// TODO. Fix after polishing GUI
// Cause required/optional will be changed
if err := flagSet.Parse(os.Args[1:]); err != nil {
fmt.Fprintln(os.Stderr, err)
printUsage(os.Stderr)
os.Exit(2)
}
if args.ShowHelp {
printCliHelp(flagSet)
}
return
var videoFlowId, audioFlowId string
mxlDomain := flag.String("d", "/dev/shm/mxl", "MXL domain")
mxlVideoFlowID := flag.String("v", videoFlowId, "MXL video flow UUID")
mxlAudioFlowID := flag.String("a", audioFlowId, "MXL audio flow UUID")
flag.Parse()
if *mxlVideoFlowID == "" && *mxlAudioFlowID == "" {
log.Fatal("need at least -v <video-flow> or -a <audio-flow>")
}
if !args.ListAudio && !args.ListGPU {
checkMXLargs(args)
}
// end of cli args parse
runtime.LockOSThread()
if err := sdl.Load(); err != nil {
@@ -102,6 +115,21 @@ func main() {
return
}
sdlAudioDevice := sdl.AudioDeviceDefaultPlayback
if args.PlaybackId != 0 {
sdlAudioDevice = args.PlaybackId
}
// List audio playback devices and exit
if args.ListAudio {
devs := sdl.GetAudioPlaybackDevices()
fmt.Println("Available playback audio devices")
fmt.Println("id name")
for _, d := range devs {
fmt.Println(d.ID, d.Name)
}
return
}
windowHandler := sdl.CreateWindow(fmt.Sprintf("%s %s", APP_NAME, APP_VER), WIN_WIDTH, WIN_HEIGHT, sdl.WindowVulkan|sdl.WindowResizable)
if windowHandler == 0 {
sdl.Quit()
@@ -144,9 +172,15 @@ func main() {
if err != nil || len(devices) == 0 {
panic("No Vulkan devices")
}
for _, pd := range devices {
// List GPU's and exit
if args.ListGPU {
fmt.Println("Available Vulkan physical devices:")
fmt.Println("id name (type)")
for i, pd := range devices {
info := pd.Info()
fmt.Printf("%s (%s)\n", info.Name, info.Type)
fmt.Printf("%2d %s (%s)\n", i, info.Name, info.Type)
}
return
}
vkPhysDevice := devices[0]
@@ -194,8 +228,8 @@ func main() {
}
switch {
case *mxlVideoFlowID != "" && *mxlAudioFlowID != "":
syncSrc, err = source.OpenSync(*mxlDomain, *mxlVideoFlowID, *mxlAudioFlowID)
case args.VideoFlowId != "" && args.AudioFlowId != "":
syncSrc, err = source.OpenSync(args.Domain, args.VideoFlowId, args.AudioFlowId)
if err != nil {
log.Fatalf("sync source: %v", err)
}
@@ -204,7 +238,7 @@ func main() {
if audioBatch == 0 {
audioBatch = 1
}
audioStream = sdl.OpenAudioDeviceStream(sdl.AudioDeviceDefaultPlayback, sdl.AudioSpec{
audioStream = sdl.OpenAudioDeviceStream(sdlAudioDevice, sdl.AudioSpec{
Format: sdl.AudioF32,
Channels: int32(aChans),
Freq: int32(syncSrc.AudioRate().Num / syncSrc.AudioRate().Den),
@@ -215,16 +249,16 @@ func main() {
sdl.ResumeAudioStreamDevice(audioStream)
fmt.Printf("sync: video %dx%d audio %dch batch=%d\n",
syncSrc.Width(), syncSrc.Height(), aChans, audioBatch)
case args.VideoFlowId != "":
case *mxlVideoFlowID != "":
videoSrc, err = source.Open(*mxlDomain, *mxlVideoFlowID)
videoSrc, err = source.Open(args.Domain, args.VideoFlowId)
if err != nil {
log.Fatalf("source: %v", err)
}
fmt.Printf("video: %dx%d stride=%d\n", videoSrc.Width(), videoSrc.Height(), videoSrc.Stride())
default:
audioSrc, err = source.OpenAudio(*mxlDomain, *mxlAudioFlowID)
audioSrc, err = source.OpenAudio(args.Domain, args.AudioFlowId)
if err != nil {
log.Fatalf("audio source: %v", err)
}
@@ -233,7 +267,7 @@ func main() {
if audioBatch == 0 {
audioBatch = 1
}
audioStream = sdl.OpenAudioDeviceStream(sdl.AudioDeviceDefaultPlayback, sdl.AudioSpec{
audioStream = sdl.OpenAudioDeviceStream(sdlAudioDevice, sdl.AudioSpec{
Format: sdl.AudioF32,
Channels: int32(aChans),
Freq: int32(audioSrc.Rate().Num / audioSrc.Rate().Den),
@@ -261,7 +295,7 @@ func main() {
}
var r *renderer.Renderer
if *mxlVideoFlowID != "" {
if args.VideoFlowId != "" {
var w, h, stride uint32
if syncSrc != nil {
w, h, stride = syncSrc.Width(), syncSrc.Height(), syncSrc.Stride()
@@ -309,7 +343,7 @@ func main() {
default:
}
if syncSrc != nil {
s, e := source.OpenSync(*mxlDomain, *mxlVideoFlowID, *mxlAudioFlowID)
s, e := source.OpenSync(args.Domain, args.VideoFlowId, args.AudioFlowId)
if e == nil {
if r != nil {
newSize := vk.DeviceSize(s.Stride()) * vk.DeviceSize(s.Height())
@@ -324,7 +358,7 @@ func main() {
}
log.Printf("source: reopen retry: %v", e)
} else if videoSrc != nil {
s, e := source.Open(*mxlDomain, *mxlVideoFlowID)
s, e := source.Open(args.Domain, args.VideoFlowId)
if e == nil {
if r != nil {
newSize := vk.DeviceSize(s.Stride()) * vk.DeviceSize(s.Height())
@@ -339,7 +373,7 @@ func main() {
}
log.Printf("source: reopen retry: %v", e)
} else if audioSrc != nil {
s, e := source.OpenAudio(*mxlDomain, *mxlAudioFlowID)
s, e := source.OpenAudio(args.Domain, args.AudioFlowId)
if e == nil {
audioSrc = s
return nil
@@ -477,7 +511,10 @@ func main() {
running := true
resized := false
granted := false
fullscreen := false
fullscreen := args.IsFullscreen
if fullscreen {
sdl.SetWindowFullscreen(windowHandler, true)
}
var (
lastIndex uint64
dropped uint64