Refactoring #3
+149
-19
@@ -8,6 +8,7 @@ import (
|
||||
"log"
|
||||
mxladapter "mxl-player/internal/adapter/mxl"
|
||||
"mxl-player/internal/imgui"
|
||||
"mxl-player/internal/output"
|
||||
"mxl-player/internal/playback"
|
||||
"mxl-player/internal/renderer"
|
||||
"mxl-player/internal/sdl"
|
||||
@@ -149,6 +150,7 @@ func main() {
|
||||
}
|
||||
// path selection
|
||||
useVideoSlot := args.AudioFlowId == ""
|
||||
useAudioSlot := args.AudioFlowId != "" && args.VideoFlowId == ""
|
||||
|
||||
runtime.LockOSThread()
|
||||
if err := sdl.Load(); err != nil {
|
||||
@@ -303,25 +305,7 @@ func main() {
|
||||
case args.VideoFlowId != "":
|
||||
// VideoSlot owns opening and closing the video reader.
|
||||
case args.AudioFlowId != "":
|
||||
audioSrc, err = source.OpenAudio(args.Domain, args.AudioFlowId)
|
||||
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)
|
||||
// AudioSlot owns opening and closing the audio reader.
|
||||
default:
|
||||
// No configured feeds. Renderer and GUI use the placeholder.
|
||||
}
|
||||
@@ -390,6 +374,7 @@ func main() {
|
||||
showStats bool = true
|
||||
)
|
||||
videoActive := args.VideoFlowId != ""
|
||||
audioActive := useAudioSlot
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
@@ -437,6 +422,44 @@ func main() {
|
||||
}
|
||||
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 {
|
||||
// Close current sources
|
||||
if syncSrc != nil {
|
||||
@@ -499,6 +522,17 @@ func main() {
|
||||
default:
|
||||
}
|
||||
}
|
||||
enqueueAudioConfig := func(config playback.FeedConfig) {
|
||||
select {
|
||||
case <-audioCommands:
|
||||
default:
|
||||
}
|
||||
|
||||
select {
|
||||
case audioCommands <- config:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
doReconnect := func() {
|
||||
if useVideoSlot {
|
||||
@@ -516,6 +550,22 @@ func main() {
|
||||
enqueueVideoConfig(config)
|
||||
return
|
||||
}
|
||||
if useAudioSlot {
|
||||
audioActive = audioStr != ""
|
||||
|
||||
config := playback.FeedConfig{}
|
||||
if audioStr != "" {
|
||||
config = playback.FeedConfig{
|
||||
Domain: domainStr,
|
||||
UUID: audioStr,
|
||||
Active: true,
|
||||
}
|
||||
}
|
||||
|
||||
enqueueAudioConfig(config)
|
||||
return
|
||||
}
|
||||
// legacy
|
||||
select {
|
||||
case <-control:
|
||||
default:
|
||||
@@ -542,6 +592,21 @@ func main() {
|
||||
}
|
||||
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.
|
||||
if audioSrc != nil && syncSrc == nil && videoSrc == nil {
|
||||
@@ -929,6 +994,71 @@ func main() {
|
||||
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()
|
||||
gui.EndFrame()
|
||||
lastFrame = time.Now()
|
||||
|
||||
Reference in New Issue
Block a user