Refactoring #3

Merged
itten merged 87 commits from refactoring into main 2026-09-01 23:52:36 +03:00
2 changed files with 151 additions and 21 deletions
Showing only changes of commit d49f4ff15f - Show all commits
+149 -19
View File
@@ -8,6 +8,7 @@ import (
"log" "log"
mxladapter "mxl-player/internal/adapter/mxl" mxladapter "mxl-player/internal/adapter/mxl"
"mxl-player/internal/imgui" "mxl-player/internal/imgui"
"mxl-player/internal/output"
"mxl-player/internal/playback" "mxl-player/internal/playback"
"mxl-player/internal/renderer" "mxl-player/internal/renderer"
"mxl-player/internal/sdl" "mxl-player/internal/sdl"
@@ -149,6 +150,7 @@ func main() {
} }
// path selection // path selection
useVideoSlot := args.AudioFlowId == "" useVideoSlot := args.AudioFlowId == ""
useAudioSlot := args.AudioFlowId != "" && args.VideoFlowId == ""
runtime.LockOSThread() runtime.LockOSThread()
if err := sdl.Load(); err != nil { if err := sdl.Load(); err != nil {
@@ -303,25 +305,7 @@ func main() {
case args.VideoFlowId != "": case args.VideoFlowId != "":
// VideoSlot owns opening and closing the video reader. // VideoSlot owns opening and closing the video reader.
case args.AudioFlowId != "": case args.AudioFlowId != "":
audioSrc, err = source.OpenAudio(args.Domain, args.AudioFlowId) // AudioSlot owns opening and closing the audio reader.
if err != nil {
log.Fatalf("audio source: %v", err)
}
aChans = audioSrc.Channels()
audioBatch = uint64(audioSrc.Rate().Num) / (100 * uint64(audioSrc.Rate().Den))
if audioBatch == 0 {
audioBatch = 1
}
audioStream = sdl.OpenAudioDeviceStream(sdlAudioDevice, sdl.AudioSpec{
Format: sdl.AudioF32,
Channels: int32(aChans),
Freq: int32(audioSrc.Rate().Num / audioSrc.Rate().Den),
})
if audioStream == 0 {
log.Fatalf("audio: %s", sdl.GetError())
}
sdl.ResumeAudioStreamDevice(audioStream)
fmt.Printf("audio: %dch %d/%d Hz\n", aChans, audioSrc.Rate().Num, audioSrc.Rate().Den)
default: default:
// No configured feeds. Renderer and GUI use the placeholder. // No configured feeds. Renderer and GUI use the placeholder.
} }
@@ -390,6 +374,7 @@ func main() {
showStats bool = true showStats bool = true
) )
videoActive := args.VideoFlowId != "" videoActive := args.VideoFlowId != ""
audioActive := useAudioSlot
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@@ -437,6 +422,44 @@ func main() {
} }
videoCommands := make(chan playback.FeedConfig, 1) videoCommands := make(chan playback.FeedConfig, 1)
audioOutput := output.NewSDLAudioSink(sdlAudioDevice)
defer audioOutput.Close()
audioWorker, err := playback.NewAudioWorker(
mxladapter.AudioFactory{},
audioOutput,
retryPolicy,
mxladapter.ShouldRetry,
func(status playback.Status) {
statusStore.Observe(status)
if status.Err != nil {
log.Printf(
"audio: state=%v attempt=%d failed=%d: %v",
status.State,
status.Attempt,
status.FailedAttempts,
status.Err,
)
return
}
log.Printf(
"audio: state=%v attempt=%d failed=%d",
status.State,
status.Attempt,
status.FailedAttempts,
)
},
)
if err != nil {
panic(err)
}
audioSlot, err := playback.NewAudioSlot(audioWorker)
if err != nil {
panic(err)
}
audioCommands := 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 {
@@ -499,6 +522,17 @@ func main() {
default: default:
} }
} }
enqueueAudioConfig := func(config playback.FeedConfig) {
select {
case <-audioCommands:
default:
}
select {
case audioCommands <- config:
default:
}
}
doReconnect := func() { doReconnect := func() {
if useVideoSlot { if useVideoSlot {
@@ -516,6 +550,22 @@ func main() {
enqueueVideoConfig(config) enqueueVideoConfig(config)
return return
} }
if useAudioSlot {
audioActive = audioStr != ""
config := playback.FeedConfig{}
if audioStr != "" {
config = playback.FeedConfig{
Domain: domainStr,
UUID: audioStr,
Active: true,
}
}
enqueueAudioConfig(config)
return
}
// legacy
select { select {
case <-control: case <-control:
default: default:
@@ -542,6 +592,21 @@ func main() {
} }
return 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)
}
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 {
@@ -929,6 +994,71 @@ func main() {
cimgui.Text("Video actual: not started") cimgui.Text("Video actual: not started")
} }
} }
if useAudioSlot && audioActive {
if cimgui.Button("Stop audio") {
audioActive = false
enqueueAudioConfig(playback.FeedConfig{
Domain: domainStr,
UUID: audioStr,
Active: false,
})
}
}
if useAudioSlot && !audioActive && audioStr != "" {
if cimgui.Button("Resume audio") {
audioActive = true
enqueueAudioConfig(playback.FeedConfig{
Domain: domainStr,
UUID: audioStr,
Active: true,
})
}
}
if useAudioSlot && audioStr != "" {
if cimgui.Button("Remove audio") {
audioActive = false
audioStr = ""
enqueueAudioConfig(playback.FeedConfig{})
}
}
if useAudioSlot {
if audioActive {
cimgui.Text("Audio desired: active")
} else if audioStr != "" {
cimgui.Text("Audio desired: stopped")
} else {
cimgui.Text("Audio desired: not configured")
}
if status, ok := statusStore.Snapshot(playback.UnitAudio); ok {
cimgui.Text(fmt.Sprintf(
"Audio actual: %s",
status.State,
))
cimgui.Text(fmt.Sprintf(
"Attempt: %d, failed: %d",
status.Attempt,
status.FailedAttempts,
))
if status.RetryIn > 0 {
cimgui.Text(fmt.Sprintf(
"Retry in: %s",
status.RetryIn.Round(time.Millisecond),
))
}
if status.Err != nil {
cimgui.TextWrapped(status.Err.Error())
}
} else {
cimgui.Text("Audio actual: not started")
}
}
cimgui.End() cimgui.End()
gui.EndFrame() gui.EndFrame()
lastFrame = time.Now() lastFrame = time.Now()
+2 -2
View File
@@ -14,7 +14,7 @@ Size=200,200
Collapsed=0 Collapsed=0
[Window][Connection] [Window][Connection]
Pos=979,700 Pos=174,363
Size=695,233 Size=986,237
Collapsed=0 Collapsed=0