Compare commits

..

2 Commits

Author SHA1 Message Date
Dmitry Sergeev 27dcbd1b40 minor UI fixes 2026-08-28 22:53:50 +03:00
Dmitry Sergeev 8d400dab7a run audio and video slots independently 2026-08-28 22:50:19 +03:00
2 changed files with 80 additions and 62 deletions
+53 -35
View File
@@ -15,6 +15,7 @@ import (
"mxl-player/internal/source" "mxl-player/internal/source"
"os" "os"
"runtime" "runtime"
"sync"
"time" "time"
"unsafe" "unsafe"
@@ -109,6 +110,12 @@ func main() {
flagSet.StringVarP(&args.Domain, "domain", "d", "", "MXL domain directory") flagSet.StringVarP(&args.Domain, "domain", "d", "", "MXL domain directory")
flagSet.StringVarP(&args.VideoFlowId, "video", "v", "", "Video flow UUID") flagSet.StringVarP(&args.VideoFlowId, "video", "v", "", "Video flow UUID")
flagSet.StringVarP(&args.AudioFlowId, "audio", "a", "", "Audio flow UUID") flagSet.StringVarP(&args.AudioFlowId, "audio", "a", "", "Audio flow UUID")
flagSet.BoolVarP(
&args.SyncRequested,
"sync", "s",
false,
"Start configured audio and video as a synchronized group",
)
flagSet.IntVar( flagSet.IntVar(
&args.MaxAttempts, &args.MaxAttempts,
"max-attempts", "max-attempts",
@@ -149,8 +156,10 @@ func main() {
checkMXLargs(args) checkMXLargs(args)
} }
// path selection // path selection
useVideoSlot := args.AudioFlowId == "" useLegacySync := args.SyncRequested &&
useAudioSlot := args.AudioFlowId != "" && args.VideoFlowId == "" args.VideoFlowId != "" &&
args.AudioFlowId != ""
useIndependentSlots := !useLegacySync
runtime.LockOSThread() runtime.LockOSThread()
if err := sdl.Load(); err != nil { if err := sdl.Load(); err != nil {
@@ -281,8 +290,12 @@ func main() {
} }
switch { switch {
case args.VideoFlowId != "" && args.AudioFlowId != "": case useLegacySync:
syncSrc, err = source.OpenSameDomainSync(args.Domain, args.VideoFlowId, args.AudioFlowId) syncSrc, err = source.OpenSameDomainSync(
args.Domain,
args.VideoFlowId,
args.AudioFlowId,
)
if err != nil { if err != nil {
log.Fatalf("sync source: %v", err) log.Fatalf("sync source: %v", err)
} }
@@ -302,12 +315,9 @@ func main() {
sdl.ResumeAudioStreamDevice(audioStream) sdl.ResumeAudioStreamDevice(audioStream)
fmt.Printf("sync: video %dx%d audio %dch batch=%d\n", fmt.Printf("sync: video %dx%d audio %dch batch=%d\n",
syncSrc.Width(), syncSrc.Height(), aChans, audioBatch) syncSrc.Width(), syncSrc.Height(), aChans, audioBatch)
case args.VideoFlowId != "":
// VideoSlot owns opening and closing the video reader.
case args.AudioFlowId != "":
// AudioSlot owns opening and closing the audio reader.
default: default:
// No configured feeds. Renderer and GUI use the placeholder. // Independent slots own their readers.
// Empty startup opens nothing.
} }
defer func() { defer func() {
@@ -374,7 +384,7 @@ func main() {
showStats bool = true showStats bool = true
) )
videoActive := args.VideoFlowId != "" videoActive := args.VideoFlowId != ""
audioActive := useAudioSlot audioActive := args.AudioFlowId != ""
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@@ -535,34 +545,30 @@ func main() {
} }
doReconnect := func() { doReconnect := func() {
if useVideoSlot { if useIndependentSlots {
videoActive = videoStr != "" videoActive = videoStr != ""
audioActive = audioStr != ""
config := playback.FeedConfig{} videoConfig := playback.FeedConfig{}
if videoStr != "" { if videoStr != "" {
config = playback.FeedConfig{ videoConfig = playback.FeedConfig{
Domain: domainStr, Domain: domainStr,
UUID: videoStr, UUID: videoStr,
Active: true, Active: true,
} }
} }
enqueueVideoConfig(config) audioConfig := playback.FeedConfig{}
return
}
if useAudioSlot {
audioActive = audioStr != ""
config := playback.FeedConfig{}
if audioStr != "" { if audioStr != "" {
config = playback.FeedConfig{ audioConfig = playback.FeedConfig{
Domain: domainStr, Domain: domainStr,
UUID: audioStr, UUID: audioStr,
Active: true, Active: true,
} }
} }
enqueueAudioConfig(config) enqueueVideoConfig(videoConfig)
enqueueAudioConfig(audioConfig)
return return
} }
// legacy // legacy
@@ -577,7 +583,13 @@ func main() {
go func() { go func() {
defer close(playbackDone) defer close(playbackDone)
if useVideoSlot { if useIndependentSlots {
var slots sync.WaitGroup
slots.Add(2)
go func() {
defer slots.Done()
err := videoSlot.Run( err := videoSlot.Run(
ctx, ctx,
playback.FeedConfig{ playback.FeedConfig{
@@ -590,9 +602,11 @@ func main() {
if err != nil && !errors.Is(err, context.Canceled) { if err != nil && !errors.Is(err, context.Canceled) {
log.Printf("video slot: %v", err) log.Printf("video slot: %v", err)
} }
return }()
}
if useAudioSlot { go func() {
defer slots.Done()
err := audioSlot.Run( err := audioSlot.Run(
ctx, ctx,
playback.FeedConfig{ playback.FeedConfig{
@@ -605,6 +619,10 @@ func main() {
if err != nil && !errors.Is(err, context.Canceled) { if err != nil && !errors.Is(err, context.Canceled) {
log.Printf("audio slot: %v", err) log.Printf("audio slot: %v", err)
} }
}()
<-ctx.Done()
slots.Wait()
return return
} }
@@ -926,12 +944,12 @@ func main() {
cimgui.InputTextWithHint("Domain", "/dev/shm/mxl", &domainStr, 0, nil) cimgui.InputTextWithHint("Domain", "/dev/shm/mxl", &domainStr, 0, nil)
cimgui.InputTextWithHint("Video UUID", "", &videoStr, 0, nil) cimgui.InputTextWithHint("Video UUID", "", &videoStr, 0, nil)
cimgui.InputTextWithHint("Audio UUID", "", &audioStr, 0, nil) cimgui.InputTextWithHint("Audio UUID", "", &audioStr, 0, nil)
cimgui.Checkbox("Show stats", &showStats)
if cimgui.Button("Connect") { if cimgui.Button("Connect") {
doReconnect() doReconnect()
} }
if useVideoSlot && videoActive {
cimgui.SameLine() cimgui.SameLine()
cimgui.Checkbox("Show stats", &showStats)
if useIndependentSlots && videoActive {
if cimgui.Button("Stop video") { if cimgui.Button("Stop video") {
videoActive = false videoActive = false
enqueueVideoConfig( enqueueVideoConfig(
@@ -942,7 +960,7 @@ func main() {
}) })
} }
} }
if useVideoSlot && !videoActive && videoStr != "" { if useIndependentSlots && !videoActive && videoStr != "" {
cimgui.SameLine() cimgui.SameLine()
if cimgui.Button("Resume video") { if cimgui.Button("Resume video") {
videoActive = true videoActive = true
@@ -953,14 +971,14 @@ func main() {
}) })
} }
} }
if useVideoSlot && videoStr != "" { if useIndependentSlots && videoStr != "" {
if cimgui.Button("Remove video") { if cimgui.Button("Remove video") {
videoActive = false videoActive = false
videoStr = "" videoStr = ""
enqueueVideoConfig(playback.FeedConfig{}) enqueueVideoConfig(playback.FeedConfig{})
} }
} }
if useVideoSlot { if useIndependentSlots {
if videoActive { if videoActive {
cimgui.Text("Video desired: active") cimgui.Text("Video desired: active")
} else if videoStr != "" { } else if videoStr != "" {
@@ -994,7 +1012,7 @@ func main() {
cimgui.Text("Video actual: not started") cimgui.Text("Video actual: not started")
} }
} }
if useAudioSlot && audioActive { if useIndependentSlots && audioActive {
if cimgui.Button("Stop audio") { if cimgui.Button("Stop audio") {
audioActive = false audioActive = false
enqueueAudioConfig(playback.FeedConfig{ enqueueAudioConfig(playback.FeedConfig{
@@ -1005,7 +1023,7 @@ func main() {
} }
} }
if useAudioSlot && !audioActive && audioStr != "" { if useIndependentSlots && !audioActive && audioStr != "" {
if cimgui.Button("Resume audio") { if cimgui.Button("Resume audio") {
audioActive = true audioActive = true
enqueueAudioConfig(playback.FeedConfig{ enqueueAudioConfig(playback.FeedConfig{
@@ -1016,7 +1034,7 @@ func main() {
} }
} }
if useAudioSlot && audioStr != "" { if useIndependentSlots && audioStr != "" {
if cimgui.Button("Remove audio") { if cimgui.Button("Remove audio") {
audioActive = false audioActive = false
audioStr = "" audioStr = ""
@@ -1024,7 +1042,7 @@ func main() {
} }
} }
if useAudioSlot { if useIndependentSlots {
if audioActive { if audioActive {
cimgui.Text("Audio desired: active") cimgui.Text("Audio desired: active")
} else if audioStr != "" { } else if audioStr != "" {
+2 -2
View File
@@ -14,7 +14,7 @@ Size=200,200
Collapsed=0 Collapsed=0
[Window][Connection] [Window][Connection]
Pos=174,363 Pos=185,295
Size=986,237 Size=640,354
Collapsed=0 Collapsed=0