Compare commits

..

2 Commits

Author SHA1 Message Date
Alex db620b70d7
Merge f51c372183 into 31ea366e51 2025-11-10 17:09:31 +00:00
Alex P f51c372183 Enhancement: Make the code more DRY 2025-11-10 19:09:22 +02:00
1 changed files with 35 additions and 45 deletions

View File

@ -14,7 +14,7 @@ import (
var ( var (
audioMutex sync.Mutex audioMutex sync.Mutex
outputSource audio.AudioSource outputSource audio.AudioSource
inputSource audio.AudioSource inputSource atomic.Pointer[audio.AudioSource]
outputRelay *audio.OutputRelay outputRelay *audio.OutputRelay
inputRelay *audio.InputRelay inputRelay *audio.InputRelay
audioInitialized bool audioInitialized bool
@ -63,13 +63,14 @@ func startAudio() error {
// Start input audio if not running, USB audio enabled, and input enabled // Start input audio if not running, USB audio enabled, and input enabled
ensureConfigLoaded() ensureConfigLoaded()
if inputSource == nil && audioInputEnabled.Load() && config.UsbDevices != nil && config.UsbDevices.Audio { if inputSource.Load() == nil && audioInputEnabled.Load() && config.UsbDevices != nil && config.UsbDevices.Audio {
alsaPlaybackDevice := "hw:1,0" // USB speakers alsaPlaybackDevice := "hw:1,0" // USB speakers
// Create CGO audio source // Create CGO audio source
inputSource = audio.NewCgoInputSource(alsaPlaybackDevice) source := audio.NewCgoInputSource(alsaPlaybackDevice)
inputSource.Store(&source)
inputRelay = audio.NewInputRelay(inputSource) inputRelay = audio.NewInputRelay(source)
if err := inputRelay.Start(); err != nil { if err := inputRelay.Start(); err != nil {
audioLogger.Error().Err(err).Msg("Failed to start input relay") audioLogger.Error().Err(err).Msg("Failed to start input relay")
} }
@ -78,33 +79,48 @@ func startAudio() error {
return nil return nil
} }
func stopAudio() { // stopOutputAudio stops output audio (device → browser)
func stopOutputAudio() {
audioMutex.Lock() audioMutex.Lock()
outRelay := outputRelay outRelay := outputRelay
outSource := outputSource outSource := outputSource
inRelay := inputRelay
inSource := inputSource
outputRelay = nil outputRelay = nil
outputSource = nil outputSource = nil
inputRelay = nil
inputSource = nil
audioMutex.Unlock() audioMutex.Unlock()
// Disconnect outside mutex to avoid blocking new sessions during CGO calls // Disconnect outside mutex to avoid blocking during CGO calls
if outRelay != nil { if outRelay != nil {
outRelay.Stop() outRelay.Stop()
} }
if outSource != nil { if outSource != nil {
outSource.Disconnect() outSource.Disconnect()
} }
}
// stopInputAudio stops input audio (browser → device)
func stopInputAudio() {
audioMutex.Lock()
inRelay := inputRelay
inputRelay = nil
audioMutex.Unlock()
// Atomically swap and disconnect outside mutex
inSource := inputSource.Swap(nil)
// Disconnect outside mutex to avoid blocking during CGO calls
if inRelay != nil { if inRelay != nil {
inRelay.Stop() inRelay.Stop()
} }
if inSource != nil { if inSource != nil {
inSource.Disconnect() (*inSource).Disconnect()
} }
} }
func stopAudio() {
stopOutputAudio()
stopInputAudio()
}
func onWebRTCConnect() { func onWebRTCConnect() {
count := activeConnections.Add(1) count := activeConnections.Add(1)
if count == 1 { if count == 1 {
@ -158,19 +174,7 @@ func SetAudioOutputEnabled(enabled bool) error {
return startAudio() return startAudio()
} }
} else { } else {
audioMutex.Lock() stopOutputAudio()
outRelay := outputRelay
outSource := outputSource
outputRelay = nil
outputSource = nil
audioMutex.Unlock()
if outRelay != nil {
outRelay.Stop()
}
if outSource != nil {
outSource.Disconnect()
}
} }
return nil return nil
@ -187,19 +191,7 @@ func SetAudioInputEnabled(enabled bool) error {
return startAudio() return startAudio()
} }
} else { } else {
audioMutex.Lock() stopInputAudio()
inRelay := inputRelay
inSource := inputSource
inputRelay = nil
inputSource = nil
audioMutex.Unlock()
if inRelay != nil {
inRelay.Stop()
}
if inSource != nil {
inSource.Disconnect()
}
} }
return nil return nil
@ -248,23 +240,21 @@ func handleInputTrackForSession(track *webrtc.TrackRemote) {
continue // Drop frame but keep reading continue // Drop frame but keep reading
} }
// Get source in single mutex operation (hot path optimization) // Lock-free source access (hot path optimization)
audioMutex.Lock() source := inputSource.Load()
source := inputSource
audioMutex.Unlock()
if source == nil { if source == nil {
continue // No relay, drop frame but keep reading continue // No relay, drop frame but keep reading
} }
if !source.IsConnected() { if !(*source).IsConnected() {
if err := source.Connect(); err != nil { if err := (*source).Connect(); err != nil {
continue continue
} }
} }
if err := source.WriteMessage(0, opusData); err != nil { if err := (*source).WriteMessage(0, opusData); err != nil {
source.Disconnect() (*source).Disconnect()
} }
} }
} }