Compare commits

..

5 Commits

3 changed files with 23 additions and 13 deletions

View File

@ -22,7 +22,7 @@ var (
audioLogger zerolog.Logger audioLogger zerolog.Logger
currentAudioTrack *webrtc.TrackLocalStaticSample currentAudioTrack *webrtc.TrackLocalStaticSample
inputTrackHandling atomic.Bool inputTrackHandling atomic.Bool
useUSBForAudioOutput bool useUSBForAudioOutput atomic.Bool
audioOutputEnabled atomic.Bool audioOutputEnabled atomic.Bool
audioInputEnabled atomic.Bool audioInputEnabled atomic.Bool
) )
@ -32,7 +32,7 @@ func initAudio() {
// Load audio output source from config // Load audio output source from config
ensureConfigLoaded() ensureConfigLoaded()
useUSBForAudioOutput = config.AudioOutputSource == "usb" useUSBForAudioOutput.Store(config.AudioOutputSource == "usb")
// Enable both by default // Enable both by default
audioOutputEnabled.Store(true) audioOutputEnabled.Store(true)
@ -57,7 +57,7 @@ func startAudio() error {
// Start output audio if not running and enabled // Start output audio if not running and enabled
if outputSource == nil && audioOutputEnabled.Load() { if outputSource == nil && audioOutputEnabled.Load() {
alsaDevice := "hw:0,0" // HDMI alsaDevice := "hw:0,0" // HDMI
if useUSBForAudioOutput { if useUSBForAudioOutput.Load() {
alsaDevice = "hw:1,0" // USB alsaDevice = "hw:1,0" // USB
} }
@ -167,16 +167,17 @@ func SetAudioOutputSource(useUSB bool) error {
audioMutex.Lock() audioMutex.Lock()
defer audioMutex.Unlock() defer audioMutex.Unlock()
if useUSBForAudioOutput == useUSB { if useUSBForAudioOutput.Load() == useUSB {
return nil return nil
} }
audioLogger.Info(). audioLogger.Info().
Bool("old_usb", useUSBForAudioOutput). Bool("old_usb", useUSBForAudioOutput.Load()).
Bool("new_usb", useUSB). Bool("new_usb", useUSB).
Msg("Switching audio output source") Msg("Switching audio output source")
useUSBForAudioOutput = useUSB oldValue := useUSBForAudioOutput.Load()
useUSBForAudioOutput.Store(useUSB)
ensureConfigLoaded() ensureConfigLoaded()
if useUSB { if useUSB {
@ -186,6 +187,7 @@ func SetAudioOutputSource(useUSB bool) error {
} }
if err := SaveConfig(); err != nil { if err := SaveConfig(); err != nil {
audioLogger.Error().Err(err).Msg("Failed to save config") audioLogger.Error().Err(err).Msg("Failed to save config")
useUSBForAudioOutput.Store(oldValue)
return err return err
} }

View File

@ -20,7 +20,8 @@ type OutputRelay struct {
cancel context.CancelFunc cancel context.CancelFunc
logger zerolog.Logger logger zerolog.Logger
running atomic.Bool running atomic.Bool
sample media.Sample // Reusable sample for zero-allocation hot path sample media.Sample
stopped chan struct{}
// Stats (Uint32: overflows after 2.7 years @ 50fps, faster atomics on 32-bit ARM) // Stats (Uint32: overflows after 2.7 years @ 50fps, faster atomics on 32-bit ARM)
framesRelayed atomic.Uint32 framesRelayed atomic.Uint32
@ -38,8 +39,9 @@ func NewOutputRelay(source AudioSource, audioTrack *webrtc.TrackLocalStaticSampl
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
logger: logger, logger: logger,
stopped: make(chan struct{}),
sample: media.Sample{ sample: media.Sample{
Duration: 20 * time.Millisecond, // Constant for all Opus frames Duration: 20 * time.Millisecond,
}, },
} }
} }
@ -55,13 +57,15 @@ func (r *OutputRelay) Start() error {
return nil return nil
} }
// Stop stops the relay // Stop stops the relay and waits for goroutine to exit
func (r *OutputRelay) Stop() { func (r *OutputRelay) Stop() {
if !r.running.Swap(false) { if !r.running.Swap(false) {
return return
} }
r.cancel() r.cancel()
<-r.stopped
r.logger.Debug(). r.logger.Debug().
Uint32("frames_relayed", r.framesRelayed.Load()). Uint32("frames_relayed", r.framesRelayed.Load()).
Uint32("frames_dropped", r.framesDropped.Load()). Uint32("frames_dropped", r.framesDropped.Load()).
@ -70,6 +74,8 @@ func (r *OutputRelay) Stop() {
// relayLoop continuously reads from audio source and writes to WebRTC // relayLoop continuously reads from audio source and writes to WebRTC
func (r *OutputRelay) relayLoop() { func (r *OutputRelay) relayLoop() {
defer close(r.stopped)
const reconnectDelay = 1 * time.Second const reconnectDelay = 1 * time.Second
for r.running.Load() { for r.running.Load() {

View File

@ -899,7 +899,7 @@ func updateUsbRelatedConfig(wasAudioEnabled bool) error {
if config.UsbDevices != nil && !config.UsbDevices.Audio && config.AudioOutputSource == "usb" { if config.UsbDevices != nil && !config.UsbDevices.Audio && config.AudioOutputSource == "usb" {
audioMutex.Lock() audioMutex.Lock()
config.AudioOutputSource = "hdmi" config.AudioOutputSource = "hdmi"
useUSBForAudioOutput = false useUSBForAudioOutput.Store(false)
audioSourceChanged = true audioSourceChanged = true
audioMutex.Unlock() audioMutex.Unlock()
} }
@ -908,7 +908,7 @@ func updateUsbRelatedConfig(wasAudioEnabled bool) error {
if config.UsbDevices != nil && config.UsbDevices.Audio && !wasAudioEnabled { if config.UsbDevices != nil && config.UsbDevices.Audio && !wasAudioEnabled {
audioMutex.Lock() audioMutex.Lock()
config.AudioOutputSource = "usb" config.AudioOutputSource = "usb"
useUSBForAudioOutput = true useUSBForAudioOutput.Store(true)
audioSourceChanged = true audioSourceChanged = true
audioMutex.Unlock() audioMutex.Unlock()
} }
@ -970,8 +970,10 @@ func rpcSetUsbDeviceState(device string, enabled bool) error {
} }
func rpcGetAudioOutputSource() (string, error) { func rpcGetAudioOutputSource() (string, error) {
ensureConfigLoaded() if useUSBForAudioOutput.Load() {
return config.AudioOutputSource, nil return "usb", nil
}
return "hdmi", nil
} }
func rpcSetAudioOutputSource(source string) error { func rpcSetAudioOutputSource(source string) error {