run audio-only playback through audio slot
This commit is contained in:
+149
-19
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user