run audio and video slots independently

This commit is contained in:
Dmitry Sergeev
2026-08-28 22:50:19 +03:00
parent d49f4ff15f
commit 8d400dab7a
2 changed files with 78 additions and 60 deletions
+76 -58
View File
@@ -15,6 +15,7 @@ import (
"mxl-player/internal/source"
"os"
"runtime"
"sync"
"time"
"unsafe"
@@ -109,6 +110,12 @@ func main() {
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.SyncRequested,
"sync", "s",
false,
"Start configured audio and video as a synchronized group",
)
flagSet.IntVar(
&args.MaxAttempts,
"max-attempts",
@@ -149,8 +156,10 @@ func main() {
checkMXLargs(args)
}
// path selection
useVideoSlot := args.AudioFlowId == ""
useAudioSlot := args.AudioFlowId != "" && args.VideoFlowId == ""
useLegacySync := args.SyncRequested &&
args.VideoFlowId != "" &&
args.AudioFlowId != ""
useIndependentSlots := !useLegacySync
runtime.LockOSThread()
if err := sdl.Load(); err != nil {
@@ -281,8 +290,12 @@ func main() {
}
switch {
case args.VideoFlowId != "" && args.AudioFlowId != "":
syncSrc, err = source.OpenSameDomainSync(args.Domain, args.VideoFlowId, args.AudioFlowId)
case useLegacySync:
syncSrc, err = source.OpenSameDomainSync(
args.Domain,
args.VideoFlowId,
args.AudioFlowId,
)
if err != nil {
log.Fatalf("sync source: %v", err)
}
@@ -302,12 +315,9 @@ 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 != "":
// VideoSlot owns opening and closing the video reader.
case args.AudioFlowId != "":
// AudioSlot owns opening and closing the audio reader.
default:
// No configured feeds. Renderer and GUI use the placeholder.
// Independent slots own their readers.
// Empty startup opens nothing.
}
defer func() {
@@ -374,7 +384,7 @@ func main() {
showStats bool = true
)
videoActive := args.VideoFlowId != ""
audioActive := useAudioSlot
audioActive := args.AudioFlowId != ""
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@@ -535,34 +545,30 @@ func main() {
}
doReconnect := func() {
if useVideoSlot {
if useIndependentSlots {
videoActive = videoStr != ""
audioActive = audioStr != ""
config := playback.FeedConfig{}
videoConfig := playback.FeedConfig{}
if videoStr != "" {
config = playback.FeedConfig{
videoConfig = playback.FeedConfig{
Domain: domainStr,
UUID: videoStr,
Active: true,
}
}
enqueueVideoConfig(config)
return
}
if useAudioSlot {
audioActive = audioStr != ""
config := playback.FeedConfig{}
audioConfig := playback.FeedConfig{}
if audioStr != "" {
config = playback.FeedConfig{
audioConfig = playback.FeedConfig{
Domain: domainStr,
UUID: audioStr,
Active: true,
}
}
enqueueAudioConfig(config)
enqueueVideoConfig(videoConfig)
enqueueAudioConfig(audioConfig)
return
}
// legacy
@@ -577,34 +583,46 @@ func main() {
go func() {
defer close(playbackDone)
if useVideoSlot {
err := videoSlot.Run(
ctx,
playback.FeedConfig{
Domain: args.Domain,
UUID: args.VideoFlowId,
Active: args.VideoFlowId != "",
},
videoCommands,
)
if err != nil && !errors.Is(err, context.Canceled) {
log.Printf("video slot: %v", err)
}
return
}
if useAudioSlot {
err := audioSlot.Run(
ctx,
playback.FeedConfig{
Domain: args.Domain,
UUID: args.AudioFlowId,
Active: args.AudioFlowId != "",
},
audioCommands,
)
if err != nil && !errors.Is(err, context.Canceled) {
log.Printf("audio slot: %v", err)
}
if useIndependentSlots {
var slots sync.WaitGroup
slots.Add(2)
go func() {
defer slots.Done()
err := videoSlot.Run(
ctx,
playback.FeedConfig{
Domain: args.Domain,
UUID: args.VideoFlowId,
Active: args.VideoFlowId != "",
},
videoCommands,
)
if err != nil && !errors.Is(err, context.Canceled) {
log.Printf("video slot: %v", err)
}
}()
go func() {
defer slots.Done()
err := audioSlot.Run(
ctx,
playback.FeedConfig{
Domain: args.Domain,
UUID: args.AudioFlowId,
Active: args.AudioFlowId != "",
},
audioCommands,
)
if err != nil && !errors.Is(err, context.Canceled) {
log.Printf("audio slot: %v", err)
}
}()
<-ctx.Done()
slots.Wait()
return
}
@@ -930,7 +948,7 @@ func main() {
if cimgui.Button("Connect") {
doReconnect()
}
if useVideoSlot && videoActive {
if useIndependentSlots && videoActive {
cimgui.SameLine()
if cimgui.Button("Stop video") {
videoActive = false
@@ -942,7 +960,7 @@ func main() {
})
}
}
if useVideoSlot && !videoActive && videoStr != "" {
if useIndependentSlots && !videoActive && videoStr != "" {
cimgui.SameLine()
if cimgui.Button("Resume video") {
videoActive = true
@@ -953,14 +971,14 @@ func main() {
})
}
}
if useVideoSlot && videoStr != "" {
if useIndependentSlots && videoStr != "" {
if cimgui.Button("Remove video") {
videoActive = false
videoStr = ""
enqueueVideoConfig(playback.FeedConfig{})
}
}
if useVideoSlot {
if useIndependentSlots {
if videoActive {
cimgui.Text("Video desired: active")
} else if videoStr != "" {
@@ -994,7 +1012,7 @@ func main() {
cimgui.Text("Video actual: not started")
}
}
if useAudioSlot && audioActive {
if useIndependentSlots && audioActive {
if cimgui.Button("Stop audio") {
audioActive = false
enqueueAudioConfig(playback.FeedConfig{
@@ -1005,7 +1023,7 @@ func main() {
}
}
if useAudioSlot && !audioActive && audioStr != "" {
if useIndependentSlots && !audioActive && audioStr != "" {
if cimgui.Button("Resume audio") {
audioActive = true
enqueueAudioConfig(playback.FeedConfig{
@@ -1016,7 +1034,7 @@ func main() {
}
}
if useAudioSlot && audioStr != "" {
if useIndependentSlots && audioStr != "" {
if cimgui.Button("Remove audio") {
audioActive = false
audioStr = ""
@@ -1024,7 +1042,7 @@ func main() {
}
}
if useAudioSlot {
if useIndependentSlots {
if audioActive {
cimgui.Text("Audio desired: active")
} else if audioStr != "" {