kvm/internal/audio/input_server_main.go

115 lines
3.1 KiB
Go

//go:build cgo
// +build cgo
package audio
/*
#cgo pkg-config: alsa
#cgo LDFLAGS: -lopus
*/
import "C"
import (
"context"
"os"
"os/signal"
"syscall"
"time"
"github.com/jetkvm/kvm/internal/logging"
"github.com/rs/zerolog"
)
// Global audio input server instance
var globalAudioInputServer *AudioInputServer
// GetGlobalAudioInputServer returns the global audio input server instance
func GetGlobalAudioInputServer() *AudioInputServer {
return globalAudioInputServer
}
// ResetGlobalAudioInputServerStats resets the global audio input server stats
func ResetGlobalAudioInputServerStats() {
if globalAudioInputServer != nil {
globalAudioInputServer.ResetServerStats()
}
}
// RecoverGlobalAudioInputServer attempts to recover from dropped frames
func RecoverGlobalAudioInputServer() {
if globalAudioInputServer != nil {
globalAudioInputServer.RecoverFromDroppedFrames()
}
}
// getEnvInt reads an integer from environment variable with a default value
// RunAudioInputServer runs the audio input server subprocess
// This should be called from main() when the subprocess is detected
func RunAudioInputServer() error {
logger := logging.GetSubsystemLogger("audio").With().Str("component", "audio-input-server").Logger()
// Parse OPUS configuration from environment variables
bitrate, complexity, vbr, signalType, bandwidth, dtx := parseOpusConfig()
applyOpusConfig(bitrate, complexity, vbr, signalType, bandwidth, dtx, "audio-input-server", false)
// Initialize validation cache for optimal performance
InitValidationCache()
// Initialize CGO audio playback (optional for input server)
// This is used for audio loopback/monitoring features
err := CGOAudioPlaybackInit()
if err != nil {
logger.Warn().Err(err).Msg("failed to initialize CGO audio playback - audio monitoring disabled")
// Continue without playback - input functionality doesn't require it
} else {
defer CGOAudioPlaybackClose()
logger.Info().Msg("CGO audio playback initialized successfully")
}
// Create and start the IPC server
server, err := NewAudioInputServer()
if err != nil {
logger.Error().Err(err).Msg("failed to create audio input server")
return err
}
defer server.Close()
// Store globally for access by other functions
globalAudioInputServer = server
err = server.Start()
if err != nil {
logger.Error().Err(err).Msg("failed to start audio input server")
return err
}
logger.Info().Msg("audio input server started, waiting for connections")
// Update C trace logging based on current audio scope log level (after environment variables are processed)
traceEnabled := logger.GetLevel() <= zerolog.TraceLevel
CGOSetTraceLogging(traceEnabled)
// Set up signal handling for graceful shutdown
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// Wait for shutdown signal
select {
case sig := <-sigChan:
logger.Info().Str("signal", sig.String()).Msg("received shutdown signal")
case <-ctx.Done():
}
// Graceful shutdown
server.Stop()
// Give some time for cleanup
time.Sleep(Config.DefaultSleepDuration)
return nil
}