run video-only playback through video slot

This commit is contained in:
Dmitry Sergeev
2026-08-27 21:47:24 +03:00
parent 5284919d47
commit 461793b181
+76 -7
View File
@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"io" "io"
"log" "log"
mxladapter "mxl-player/internal/adapter/mxl"
"mxl-player/internal/imgui" "mxl-player/internal/imgui"
"mxl-player/internal/playback" "mxl-player/internal/playback"
"mxl-player/internal/renderer" "mxl-player/internal/renderer"
@@ -144,7 +145,8 @@ func main() {
if !args.ListAudio && !args.ListGPU { if !args.ListAudio && !args.ListGPU {
checkMXLargs(args) checkMXLargs(args)
} }
// end of cli args parse // path selection
useVideoSlot := args.VideoFlowId != "" && args.AudioFlowId == ""
runtime.LockOSThread() runtime.LockOSThread()
if err := sdl.Load(); err != nil { if err := sdl.Load(); err != nil {
@@ -297,12 +299,7 @@ func main() {
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 != "": case args.VideoFlowId != "":
videoSrc, err = source.Open(args.Domain, args.VideoFlowId) // VideoSlot owns opening and closing the video reader.
if err != nil {
log.Fatalf("source: %v", err)
}
fmt.Printf("video: %dx%d stride=%d\n", videoSrc.Width(), videoSrc.Height(), videoSrc.Stride())
default: default:
audioSrc, err = source.OpenAudio(args.Domain, args.AudioFlowId) audioSrc, err = source.OpenAudio(args.Domain, args.AudioFlowId)
if err != nil { if err != nil {
@@ -400,6 +397,39 @@ func main() {
control := make(chan reconnectParams, 1) control := make(chan reconnectParams, 1)
videoBridge := playback.NewVideoBridge() videoBridge := playback.NewVideoBridge()
videoWorker, err := playback.NewVideoWorker(
mxladapter.VideoFactory{},
videoBridge,
retryPolicy,
mxladapter.ShouldRetry,
func(status playback.Status) {
if status.Err != nil {
log.Printf(
"video: state=%v attempt=%d failed=%d: %v",
status.State,
status.Attempt,
status.FailedAttempts,
status.Err,
)
return
}
log.Printf(
"video: state=%v attempt=%d failed=%d",
status.State,
status.Attempt,
status.FailedAttempts,
)
},
)
if err != nil {
panic(err)
}
videoSlot, err := playback.NewVideoSlot(videoWorker)
if err != nil {
panic(err)
}
videoCommands := make(chan playback.FeedConfig, 1)
reopen := func(params reconnectParams) error { reopen := func(params reconnectParams) error {
// Close current sources // Close current sources
if syncSrc != nil { if syncSrc != nil {
@@ -452,6 +482,23 @@ func main() {
} }
doReconnect := func() { doReconnect := func() {
if useVideoSlot {
config := playback.FeedConfig{
Domain: domainStr,
UUID: videoStr,
Active: videoStr != "",
}
// GUI is the only sender. Replace an older pending command
select {
case <-videoCommands:
default:
}
select {
case videoCommands <- config:
default:
}
return
}
select { select {
case <-control: case <-control:
default: default:
@@ -459,7 +506,26 @@ func main() {
control <- reconnectParams{domain: domainStr, video: videoStr, audio: audioStr} control <- reconnectParams{domain: domainStr, video: videoStr, audio: audioStr}
} }
playbackDone := make(chan struct{})
go func() { go func() {
defer close(playbackDone)
if useVideoSlot {
err := videoSlot.Run(
ctx,
playback.FeedConfig{
Domain: args.Domain,
UUID: args.VideoFlowId,
Active: true,
},
videoCommands,
)
if err != nil && !errors.Is(err, context.Canceled) {
log.Printf("video slot: %v", err)
}
return
}
// Audio-only mode: independent loop. // Audio-only mode: independent loop.
if audioSrc != nil && syncSrc == nil && videoSrc == nil { if audioSrc != nil && syncSrc == nil && videoSrc == nil {
for { for {
@@ -809,4 +875,7 @@ func main() {
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
} }
} }
cancel()
<-playbackDone
} }