mirror of https://github.com/jetkvm/kvm.git
feat(audio): implement audio input supervisor and opus config management
add audio input supervisor with opus configuration support update audio quality presets and configuration handling restructure audio subprocess management with environment variables
This commit is contained in:
parent
0d4176cf98
commit
9c0aff4489
|
@ -13,6 +13,7 @@ func main() {
|
|||
versionJsonPtr := flag.Bool("version-json", false, "print version as json and exit")
|
||||
audioServerPtr := flag.Bool("audio-output-server", false, "Run as audio server subprocess")
|
||||
audioInputServerPtr := flag.Bool("audio-input-server", false, "Run as audio input server subprocess")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *versionPtr || *versionJsonPtr {
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
// Global audio output supervisor instance
|
||||
globalOutputSupervisor unsafe.Pointer // *AudioOutputSupervisor
|
||||
globalInputSupervisor unsafe.Pointer // *AudioInputSupervisor
|
||||
)
|
||||
|
||||
// isAudioServerProcess detects if we're running as the audio server subprocess
|
||||
|
@ -70,3 +70,17 @@ func GetAudioOutputSupervisor() *AudioOutputSupervisor {
|
|||
}
|
||||
return (*AudioOutputSupervisor)(ptr)
|
||||
}
|
||||
|
||||
// SetAudioInputSupervisor sets the global audio input supervisor
|
||||
func SetAudioInputSupervisor(supervisor *AudioInputSupervisor) {
|
||||
atomic.StorePointer(&globalInputSupervisor, unsafe.Pointer(supervisor))
|
||||
}
|
||||
|
||||
// GetAudioInputSupervisor returns the global audio input supervisor
|
||||
func GetAudioInputSupervisor() *AudioInputSupervisor {
|
||||
ptr := atomic.LoadPointer(&globalInputSupervisor)
|
||||
if ptr == nil {
|
||||
return nil
|
||||
}
|
||||
return (*AudioInputSupervisor)(ptr)
|
||||
}
|
||||
|
|
|
@ -167,7 +167,7 @@ func SetAudioQuality(quality AudioQuality) {
|
|||
if config, exists := presets[quality]; exists {
|
||||
currentConfig = config
|
||||
|
||||
// Update CGO OPUS encoder parameters based on quality
|
||||
// Get OPUS encoder parameters based on quality
|
||||
var complexity, vbr, signalType, bandwidth, dtx int
|
||||
switch quality {
|
||||
case AudioQualityLow:
|
||||
|
@ -203,11 +203,27 @@ func SetAudioQuality(quality AudioQuality) {
|
|||
dtx = GetConfig().AudioQualityMediumOpusDTX
|
||||
}
|
||||
|
||||
// Dynamically update CGO OPUS encoder parameters
|
||||
// Use current VBR constraint setting from config
|
||||
vbrConstraint := GetConfig().CGOOpusVBRConstraint
|
||||
if err := updateOpusEncoderParams(config.Bitrate*1000, complexity, vbr, vbrConstraint, signalType, bandwidth, dtx); err != nil {
|
||||
logging.GetDefaultLogger().Error().Err(err).Msg("Failed to update OPUS encoder parameters")
|
||||
// Restart audio output subprocess with new OPUS configuration
|
||||
if supervisor := GetAudioOutputSupervisor(); supervisor != nil {
|
||||
logger := logging.GetDefaultLogger().With().Str("component", "audio").Logger()
|
||||
logger.Info().Int("quality", int(quality)).Msg("restarting audio output subprocess with new quality settings")
|
||||
|
||||
// Set new OPUS configuration
|
||||
supervisor.SetOpusConfig(config.Bitrate*1000, complexity, vbr, signalType, bandwidth, dtx)
|
||||
|
||||
// Stop current subprocess
|
||||
supervisor.Stop()
|
||||
|
||||
// Start subprocess with new configuration
|
||||
if err := supervisor.Start(); err != nil {
|
||||
logger.Error().Err(err).Msg("failed to restart audio output subprocess")
|
||||
}
|
||||
} else {
|
||||
// Fallback to dynamic update if supervisor is not available
|
||||
vbrConstraint := GetConfig().CGOOpusVBRConstraint
|
||||
if err := updateOpusEncoderParams(config.Bitrate*1000, complexity, vbr, vbrConstraint, signalType, bandwidth, dtx); err != nil {
|
||||
logging.GetDefaultLogger().Error().Err(err).Msg("Failed to update OPUS encoder parameters")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,6 +246,59 @@ func SetMicrophoneQuality(quality AudioQuality) {
|
|||
presets := GetMicrophoneQualityPresets()
|
||||
if config, exists := presets[quality]; exists {
|
||||
currentMicrophoneConfig = config
|
||||
|
||||
// Get OPUS parameters for the selected quality
|
||||
var complexity, vbr, signalType, bandwidth, dtx int
|
||||
switch quality {
|
||||
case AudioQualityLow:
|
||||
complexity = GetConfig().AudioQualityLowOpusComplexity
|
||||
vbr = GetConfig().AudioQualityLowOpusVBR
|
||||
signalType = GetConfig().AudioQualityLowOpusSignalType
|
||||
bandwidth = GetConfig().AudioQualityLowOpusBandwidth
|
||||
dtx = GetConfig().AudioQualityLowOpusDTX
|
||||
case AudioQualityMedium:
|
||||
complexity = GetConfig().AudioQualityMediumOpusComplexity
|
||||
vbr = GetConfig().AudioQualityMediumOpusVBR
|
||||
signalType = GetConfig().AudioQualityMediumOpusSignalType
|
||||
bandwidth = GetConfig().AudioQualityMediumOpusBandwidth
|
||||
dtx = GetConfig().AudioQualityMediumOpusDTX
|
||||
case AudioQualityHigh:
|
||||
complexity = GetConfig().AudioQualityHighOpusComplexity
|
||||
vbr = GetConfig().AudioQualityHighOpusVBR
|
||||
signalType = GetConfig().AudioQualityHighOpusSignalType
|
||||
bandwidth = GetConfig().AudioQualityHighOpusBandwidth
|
||||
dtx = GetConfig().AudioQualityHighOpusDTX
|
||||
case AudioQualityUltra:
|
||||
complexity = GetConfig().AudioQualityUltraOpusComplexity
|
||||
vbr = GetConfig().AudioQualityUltraOpusVBR
|
||||
signalType = GetConfig().AudioQualityUltraOpusSignalType
|
||||
bandwidth = GetConfig().AudioQualityUltraOpusBandwidth
|
||||
dtx = GetConfig().AudioQualityUltraOpusDTX
|
||||
default:
|
||||
// Use medium quality as fallback
|
||||
complexity = GetConfig().AudioQualityMediumOpusComplexity
|
||||
vbr = GetConfig().AudioQualityMediumOpusVBR
|
||||
signalType = GetConfig().AudioQualityMediumOpusSignalType
|
||||
bandwidth = GetConfig().AudioQualityMediumOpusBandwidth
|
||||
dtx = GetConfig().AudioQualityMediumOpusDTX
|
||||
}
|
||||
|
||||
// Restart audio input subprocess with new OPUS configuration
|
||||
if supervisor := GetAudioInputSupervisor(); supervisor != nil {
|
||||
logger := logging.GetDefaultLogger().With().Str("component", "audio").Logger()
|
||||
logger.Info().Int("quality", int(quality)).Msg("restarting audio input subprocess with new quality settings")
|
||||
|
||||
// Set new OPUS configuration
|
||||
supervisor.SetOpusConfig(config.Bitrate*1000, complexity, vbr, signalType, bandwidth, dtx)
|
||||
|
||||
// Stop current subprocess
|
||||
supervisor.Stop()
|
||||
|
||||
// Start subprocess with new configuration
|
||||
if err := supervisor.Start(); err != nil {
|
||||
logger.Error().Err(err).Msg("failed to restart audio input subprocess")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1555,10 +1555,10 @@ func DefaultAudioConfig() *AudioConfigConstants {
|
|||
MaxPacketSize: 4000,
|
||||
|
||||
// Audio Quality Bitrates
|
||||
AudioQualityLowOutputBitrate: 32,
|
||||
AudioQualityLowInputBitrate: 16,
|
||||
AudioQualityMediumOutputBitrate: 64,
|
||||
AudioQualityMediumInputBitrate: 32,
|
||||
AudioQualityLowOutputBitrate: 48,
|
||||
AudioQualityLowInputBitrate: 24,
|
||||
AudioQualityMediumOutputBitrate: 80,
|
||||
AudioQualityMediumInputBitrate: 40,
|
||||
|
||||
// AudioQualityHighOutputBitrate defines bitrate for high-quality output.
|
||||
// Used in: Professional applications requiring excellent audio fidelity
|
||||
|
@ -1573,16 +1573,16 @@ func DefaultAudioConfig() *AudioConfigConstants {
|
|||
AudioQualityHighInputBitrate: 64,
|
||||
|
||||
// AudioQualityUltraOutputBitrate defines bitrate for ultra-quality output.
|
||||
// Used in: Audiophile-grade reproduction and high-bandwidth connections
|
||||
// Impact: Maximum quality but requires significant bandwidth.
|
||||
// Default 192kbps suitable for high-bandwidth, quality-critical scenarios.
|
||||
AudioQualityUltraOutputBitrate: 192,
|
||||
// Used in: High-quality reproduction with optimized resource usage
|
||||
// Impact: Excellent quality while maintaining system stability.
|
||||
// Default 160kbps provides excellent audio quality with reduced CPU load.
|
||||
AudioQualityUltraOutputBitrate: 160,
|
||||
|
||||
// AudioQualityUltraInputBitrate defines bitrate for ultra-quality input.
|
||||
// Used in: Professional microphone input requiring maximum quality
|
||||
// Impact: Provides audiophile-grade voice quality with high bandwidth.
|
||||
// Default 96kbps ensures maximum voice reproduction quality.
|
||||
AudioQualityUltraInputBitrate: 96,
|
||||
// Used in: Professional microphone input with balanced resource usage
|
||||
// Impact: Provides excellent voice quality while maintaining stability.
|
||||
// Default 80kbps ensures excellent voice reproduction with reduced CPU load.
|
||||
AudioQualityUltraInputBitrate: 80,
|
||||
|
||||
// Audio Quality Sample Rates - Sampling frequencies for different quality levels
|
||||
// Used in: Audio capture, processing, and format negotiation
|
||||
|
@ -1590,15 +1590,15 @@ func DefaultAudioConfig() *AudioConfigConstants {
|
|||
|
||||
// AudioQualityLowSampleRate defines sampling frequency for low-quality audio.
|
||||
// Used in: Bandwidth-constrained scenarios and basic audio requirements
|
||||
// Impact: Captures frequencies up to 11kHz while minimizing processing load.
|
||||
// Default 22.05kHz sufficient for speech and basic audio.
|
||||
AudioQualityLowSampleRate: 22050,
|
||||
// Impact: Captures frequencies up to 24kHz while maintaining efficiency.
|
||||
// Default 48kHz provides better quality while maintaining compatibility.
|
||||
AudioQualityLowSampleRate: 48000,
|
||||
|
||||
// AudioQualityMediumSampleRate defines sampling frequency for medium-quality audio.
|
||||
// Used in: Standard audio scenarios requiring CD-quality reproduction
|
||||
// Impact: Captures full audible range up to 22kHz with balanced processing.
|
||||
// Default 44.1kHz provides CD-quality standard with excellent balance.
|
||||
AudioQualityMediumSampleRate: 44100,
|
||||
// Used in: Standard audio scenarios requiring high-quality reproduction
|
||||
// Impact: Captures full audible range up to 24kHz with excellent processing.
|
||||
// Default 48kHz provides professional standard with optimal balance.
|
||||
AudioQualityMediumSampleRate: 48000,
|
||||
|
||||
// AudioQualityMicLowSampleRate defines sampling frequency for low-quality microphone.
|
||||
// Used in: Voice/microphone input in bandwidth-constrained scenarios
|
||||
|
@ -1612,9 +1612,9 @@ func DefaultAudioConfig() *AudioConfigConstants {
|
|||
|
||||
// AudioQualityLowFrameSize defines frame duration for low-quality audio.
|
||||
// Used in: Bandwidth-constrained scenarios prioritizing efficiency
|
||||
// Impact: Reduces processing overhead with acceptable latency increase.
|
||||
// Default 40ms provides efficiency for constrained scenarios.
|
||||
AudioQualityLowFrameSize: 40 * time.Millisecond,
|
||||
// Impact: Balances processing overhead with acceptable latency.
|
||||
// Default 20ms provides better responsiveness for low-quality scenarios.
|
||||
AudioQualityLowFrameSize: 20 * time.Millisecond,
|
||||
|
||||
// AudioQualityMediumFrameSize defines frame duration for medium-quality audio.
|
||||
// Used in: Standard real-time audio applications
|
||||
|
@ -1629,14 +1629,14 @@ func DefaultAudioConfig() *AudioConfigConstants {
|
|||
AudioQualityHighFrameSize: 20 * time.Millisecond,
|
||||
|
||||
// AudioQualityUltraFrameSize defines frame duration for ultra-quality audio.
|
||||
// Used in: Applications requiring immediate audio feedback
|
||||
// Impact: Minimizes latency for ultra-responsive audio processing.
|
||||
// Default 10ms ensures minimal latency for immediate feedback.
|
||||
AudioQualityUltraFrameSize: 10 * time.Millisecond,
|
||||
// Used in: Applications requiring excellent quality with balanced performance
|
||||
// Impact: Balances latency and processing efficiency for stable operation.
|
||||
// Default 20ms provides excellent quality while reducing CPU load.
|
||||
AudioQualityUltraFrameSize: 20 * time.Millisecond,
|
||||
|
||||
// Audio Quality Channels - Channel configuration for different quality levels
|
||||
// Used in: Audio processing pipeline for channel handling and bandwidth control
|
||||
AudioQualityLowChannels: 1,
|
||||
AudioQualityLowChannels: 2,
|
||||
AudioQualityMediumChannels: 2,
|
||||
AudioQualityHighChannels: 2,
|
||||
AudioQualityUltraChannels: 2,
|
||||
|
@ -1645,32 +1645,32 @@ func DefaultAudioConfig() *AudioConfigConstants {
|
|||
// Used in: Dynamic OPUS encoder configuration based on quality presets
|
||||
// Impact: Controls encoding complexity, VBR, signal type, bandwidth, and DTX
|
||||
|
||||
// Low Quality OPUS Parameters - Optimized for bandwidth conservation
|
||||
AudioQualityLowOpusComplexity: 1, // Low complexity for minimal CPU usage
|
||||
AudioQualityLowOpusVBR: 0, // CBR for predictable bandwidth
|
||||
AudioQualityLowOpusSignalType: 3001, // OPUS_SIGNAL_VOICE
|
||||
AudioQualityLowOpusBandwidth: 1101, // OPUS_BANDWIDTH_NARROWBAND
|
||||
AudioQualityLowOpusDTX: 1, // Enable DTX for silence suppression
|
||||
// Low Quality OPUS Parameters - Optimized for bandwidth conservation with better quality
|
||||
AudioQualityLowOpusComplexity: 3, // Balanced complexity for better quality
|
||||
AudioQualityLowOpusVBR: 1, // VBR for better quality at same bitrate
|
||||
AudioQualityLowOpusSignalType: 3002, // OPUS_SIGNAL_MUSIC for better general audio
|
||||
AudioQualityLowOpusBandwidth: 1103, // OPUS_BANDWIDTH_WIDEBAND for better frequency range
|
||||
AudioQualityLowOpusDTX: 0, // Disable DTX for consistent quality
|
||||
|
||||
// Medium Quality OPUS Parameters - Balanced performance and quality
|
||||
AudioQualityMediumOpusComplexity: 5, // Medium complexity for balanced performance
|
||||
AudioQualityMediumOpusVBR: 1, // VBR for better quality
|
||||
// Medium Quality OPUS Parameters - Enhanced performance and quality
|
||||
AudioQualityMediumOpusComplexity: 6, // Higher complexity for better quality
|
||||
AudioQualityMediumOpusVBR: 1, // VBR for optimal quality
|
||||
AudioQualityMediumOpusSignalType: 3002, // OPUS_SIGNAL_MUSIC
|
||||
AudioQualityMediumOpusBandwidth: 1103, // OPUS_BANDWIDTH_WIDEBAND
|
||||
AudioQualityMediumOpusBandwidth: 1104, // OPUS_BANDWIDTH_SUPERWIDEBAND for better range
|
||||
AudioQualityMediumOpusDTX: 0, // Disable DTX for consistent quality
|
||||
|
||||
// High Quality OPUS Parameters - High quality with good performance
|
||||
AudioQualityHighOpusComplexity: 8, // High complexity for better quality
|
||||
// High Quality OPUS Parameters - Premium quality with optimized performance
|
||||
AudioQualityHighOpusComplexity: 9, // Near-maximum complexity for excellent quality
|
||||
AudioQualityHighOpusVBR: 1, // VBR for optimal quality
|
||||
AudioQualityHighOpusSignalType: 3002, // OPUS_SIGNAL_MUSIC
|
||||
AudioQualityHighOpusBandwidth: 1104, // OPUS_BANDWIDTH_SUPERWIDEBAND
|
||||
AudioQualityHighOpusBandwidth: 1105, // OPUS_BANDWIDTH_FULLBAND for full frequency range
|
||||
AudioQualityHighOpusDTX: 0, // Disable DTX for consistent quality
|
||||
|
||||
// Ultra Quality OPUS Parameters - Maximum quality settings
|
||||
AudioQualityUltraOpusComplexity: 10, // Maximum complexity for best quality
|
||||
// Ultra Quality OPUS Parameters - Optimized for high quality with reasonable resource usage
|
||||
AudioQualityUltraOpusComplexity: 8, // Reduced complexity to prevent CPU overload
|
||||
AudioQualityUltraOpusVBR: 1, // VBR for optimal quality
|
||||
AudioQualityUltraOpusSignalType: 3002, // OPUS_SIGNAL_MUSIC
|
||||
AudioQualityUltraOpusBandwidth: 1105, // OPUS_BANDWIDTH_FULLBAND
|
||||
AudioQualityUltraOpusBandwidth: 1104, // OPUS_BANDWIDTH_SUPERWIDEBAND for better stability
|
||||
AudioQualityUltraOpusDTX: 0, // Disable DTX for maximum quality
|
||||
|
||||
// CGO Audio Constants
|
||||
|
|
|
@ -4,18 +4,58 @@ import (
|
|||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/logging"
|
||||
)
|
||||
|
||||
// getEnvInt reads an integer from environment variable with a default value
|
||||
func getEnvIntInput(key string, defaultValue int) int {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
if intValue, err := strconv.Atoi(value); err == nil {
|
||||
return intValue
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// parseOpusConfigInput reads OPUS configuration from environment variables
|
||||
// with fallback to default config values for input server
|
||||
func parseOpusConfigInput() (bitrate, complexity, vbr, signalType, bandwidth, dtx int) {
|
||||
// Read configuration from environment variables with config defaults
|
||||
bitrate = getEnvIntInput("JETKVM_OPUS_BITRATE", GetConfig().CGOOpusBitrate)
|
||||
complexity = getEnvIntInput("JETKVM_OPUS_COMPLEXITY", GetConfig().CGOOpusComplexity)
|
||||
vbr = getEnvIntInput("JETKVM_OPUS_VBR", GetConfig().CGOOpusVBR)
|
||||
signalType = getEnvIntInput("JETKVM_OPUS_SIGNAL_TYPE", GetConfig().CGOOpusSignalType)
|
||||
bandwidth = getEnvIntInput("JETKVM_OPUS_BANDWIDTH", GetConfig().CGOOpusBandwidth)
|
||||
dtx = getEnvIntInput("JETKVM_OPUS_DTX", GetConfig().CGOOpusDTX)
|
||||
|
||||
return bitrate, complexity, vbr, signalType, bandwidth, dtx
|
||||
}
|
||||
|
||||
// applyOpusConfigInput applies OPUS configuration to the global config for input server
|
||||
func applyOpusConfigInput(bitrate, complexity, vbr, signalType, bandwidth, dtx int) {
|
||||
config := GetConfig()
|
||||
config.CGOOpusBitrate = bitrate
|
||||
config.CGOOpusComplexity = complexity
|
||||
config.CGOOpusVBR = vbr
|
||||
config.CGOOpusSignalType = signalType
|
||||
config.CGOOpusBandwidth = bandwidth
|
||||
config.CGOOpusDTX = dtx
|
||||
}
|
||||
|
||||
// RunAudioInputServer runs the audio input server subprocess
|
||||
// This should be called from main() when the subprocess is detected
|
||||
func RunAudioInputServer() error {
|
||||
logger := logging.GetDefaultLogger().With().Str("component", "audio-input-server").Logger()
|
||||
logger.Debug().Msg("audio input server subprocess starting")
|
||||
|
||||
// Parse OPUS configuration from environment variables
|
||||
bitrate, complexity, vbr, signalType, bandwidth, dtx := parseOpusConfigInput()
|
||||
applyOpusConfigInput(bitrate, complexity, vbr, signalType, bandwidth, dtx)
|
||||
|
||||
// Initialize validation cache for optimal performance
|
||||
InitValidationCache()
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
@ -12,6 +13,9 @@ import (
|
|||
type AudioInputSupervisor struct {
|
||||
*BaseSupervisor
|
||||
client *AudioInputClient
|
||||
|
||||
// Environment variables for OPUS configuration
|
||||
opusEnv []string
|
||||
}
|
||||
|
||||
// NewAudioInputSupervisor creates a new audio input supervisor
|
||||
|
@ -22,6 +26,23 @@ func NewAudioInputSupervisor() *AudioInputSupervisor {
|
|||
}
|
||||
}
|
||||
|
||||
// SetOpusConfig sets OPUS configuration parameters as environment variables
|
||||
// for the audio input subprocess
|
||||
func (ais *AudioInputSupervisor) SetOpusConfig(bitrate, complexity, vbr, signalType, bandwidth, dtx int) {
|
||||
ais.mutex.Lock()
|
||||
defer ais.mutex.Unlock()
|
||||
|
||||
// Store OPUS parameters as environment variables
|
||||
ais.opusEnv = []string{
|
||||
"JETKVM_OPUS_BITRATE=" + strconv.Itoa(bitrate),
|
||||
"JETKVM_OPUS_COMPLEXITY=" + strconv.Itoa(complexity),
|
||||
"JETKVM_OPUS_VBR=" + strconv.Itoa(vbr),
|
||||
"JETKVM_OPUS_SIGNAL_TYPE=" + strconv.Itoa(signalType),
|
||||
"JETKVM_OPUS_BANDWIDTH=" + strconv.Itoa(bandwidth),
|
||||
"JETKVM_OPUS_DTX=" + strconv.Itoa(dtx),
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the audio input server subprocess
|
||||
func (ais *AudioInputSupervisor) Start() error {
|
||||
ais.mutex.Lock()
|
||||
|
@ -40,11 +61,16 @@ func (ais *AudioInputSupervisor) Start() error {
|
|||
return fmt.Errorf("failed to get executable path: %w", err)
|
||||
}
|
||||
|
||||
// Build command arguments (only subprocess flag)
|
||||
args := []string{"--audio-input-server"}
|
||||
|
||||
// Create command for audio input server subprocess
|
||||
cmd := exec.CommandContext(ais.ctx, execPath, "--audio-input-server")
|
||||
cmd.Env = append(os.Environ(),
|
||||
"JETKVM_AUDIO_INPUT_IPC=true", // Enable IPC mode
|
||||
)
|
||||
cmd := exec.CommandContext(ais.ctx, execPath, args...)
|
||||
|
||||
// Set environment variables for IPC and OPUS configuration
|
||||
env := append(os.Environ(), "JETKVM_AUDIO_INPUT_IPC=true") // Enable IPC mode
|
||||
env = append(env, ais.opusEnv...) // Add OPUS configuration
|
||||
cmd.Env = env
|
||||
|
||||
// Set process group to allow clean termination
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
|
@ -62,7 +88,7 @@ func (ais *AudioInputSupervisor) Start() error {
|
|||
return fmt.Errorf("failed to start audio input server process: %w", err)
|
||||
}
|
||||
|
||||
ais.logger.Info().Int("pid", cmd.Process.Pid).Msg("Audio input server subprocess started")
|
||||
ais.logger.Info().Int("pid", cmd.Process.Pid).Strs("args", args).Strs("opus_env", ais.opusEnv).Msg("Audio input server subprocess started")
|
||||
|
||||
// Add process to monitoring
|
||||
ais.processMonitor.AddProcess(cmd.Process.Pid, "audio-input-server")
|
||||
|
|
|
@ -4,18 +4,69 @@ import (
|
|||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/logging"
|
||||
)
|
||||
|
||||
// getEnvInt reads an integer from environment variable with a default value
|
||||
func getEnvInt(key string, defaultValue int) int {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
if intValue, err := strconv.Atoi(value); err == nil {
|
||||
return intValue
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// parseOpusConfig reads OPUS configuration from environment variables
|
||||
// with fallback to default config values
|
||||
func parseOpusConfig() (bitrate, complexity, vbr, signalType, bandwidth, dtx int) {
|
||||
// Read configuration from environment variables with config defaults
|
||||
bitrate = getEnvInt("JETKVM_OPUS_BITRATE", GetConfig().CGOOpusBitrate)
|
||||
complexity = getEnvInt("JETKVM_OPUS_COMPLEXITY", GetConfig().CGOOpusComplexity)
|
||||
vbr = getEnvInt("JETKVM_OPUS_VBR", GetConfig().CGOOpusVBR)
|
||||
signalType = getEnvInt("JETKVM_OPUS_SIGNAL_TYPE", GetConfig().CGOOpusSignalType)
|
||||
bandwidth = getEnvInt("JETKVM_OPUS_BANDWIDTH", GetConfig().CGOOpusBandwidth)
|
||||
dtx = getEnvInt("JETKVM_OPUS_DTX", GetConfig().CGOOpusDTX)
|
||||
|
||||
return bitrate, complexity, vbr, signalType, bandwidth, dtx
|
||||
}
|
||||
|
||||
// applyOpusConfig applies OPUS configuration to the global config
|
||||
func applyOpusConfig(bitrate, complexity, vbr, signalType, bandwidth, dtx int) {
|
||||
logger := logging.GetDefaultLogger().With().Str("component", "audio-output-server").Logger()
|
||||
|
||||
config := GetConfig()
|
||||
config.CGOOpusBitrate = bitrate
|
||||
config.CGOOpusComplexity = complexity
|
||||
config.CGOOpusVBR = vbr
|
||||
config.CGOOpusSignalType = signalType
|
||||
config.CGOOpusBandwidth = bandwidth
|
||||
config.CGOOpusDTX = dtx
|
||||
|
||||
logger.Info().
|
||||
Int("bitrate", bitrate).
|
||||
Int("complexity", complexity).
|
||||
Int("vbr", vbr).
|
||||
Int("signal_type", signalType).
|
||||
Int("bandwidth", bandwidth).
|
||||
Int("dtx", dtx).
|
||||
Msg("applied OPUS configuration")
|
||||
}
|
||||
|
||||
// RunAudioOutputServer runs the audio output server subprocess
|
||||
// This should be called from main() when the subprocess is detected
|
||||
func RunAudioOutputServer() error {
|
||||
logger := logging.GetDefaultLogger().With().Str("component", "audio-output-server").Logger()
|
||||
logger.Debug().Msg("audio output server subprocess starting")
|
||||
|
||||
// Parse OPUS configuration from environment variables
|
||||
bitrate, complexity, vbr, signalType, bandwidth, dtx := parseOpusConfig()
|
||||
applyOpusConfig(bitrate, complexity, vbr, signalType, bandwidth, dtx)
|
||||
|
||||
// Initialize validation cache for optimal performance
|
||||
InitValidationCache()
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
@ -42,6 +43,9 @@ type AudioOutputSupervisor struct {
|
|||
stopChanClosed bool // Track if stopChan is closed
|
||||
processDoneClosed bool // Track if processDone is closed
|
||||
|
||||
// Environment variables for OPUS configuration
|
||||
opusEnv []string
|
||||
|
||||
// Callbacks
|
||||
onProcessStart func(pid int)
|
||||
onProcessExit func(pid int, exitCode int, crashed bool)
|
||||
|
@ -72,6 +76,23 @@ func (s *AudioOutputSupervisor) SetCallbacks(
|
|||
s.onRestart = onRestart
|
||||
}
|
||||
|
||||
// SetOpusConfig sets OPUS configuration parameters as environment variables
|
||||
// for the audio output subprocess
|
||||
func (s *AudioOutputSupervisor) SetOpusConfig(bitrate, complexity, vbr, signalType, bandwidth, dtx int) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
// Store OPUS parameters as environment variables
|
||||
s.opusEnv = []string{
|
||||
"JETKVM_OPUS_BITRATE=" + strconv.Itoa(bitrate),
|
||||
"JETKVM_OPUS_COMPLEXITY=" + strconv.Itoa(complexity),
|
||||
"JETKVM_OPUS_VBR=" + strconv.Itoa(vbr),
|
||||
"JETKVM_OPUS_SIGNAL_TYPE=" + strconv.Itoa(signalType),
|
||||
"JETKVM_OPUS_BANDWIDTH=" + strconv.Itoa(bandwidth),
|
||||
"JETKVM_OPUS_DTX=" + strconv.Itoa(dtx),
|
||||
}
|
||||
}
|
||||
|
||||
// Start begins supervising the audio output server process
|
||||
func (s *AudioOutputSupervisor) Start() error {
|
||||
if !atomic.CompareAndSwapInt32(&s.running, 0, 1) {
|
||||
|
@ -223,18 +244,24 @@ func (s *AudioOutputSupervisor) startProcess() error {
|
|||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
// Build command arguments (only subprocess flag)
|
||||
args := []string{"--audio-output-server"}
|
||||
|
||||
// Create new command
|
||||
s.cmd = exec.CommandContext(s.ctx, execPath, "--audio-output-server")
|
||||
s.cmd = exec.CommandContext(s.ctx, execPath, args...)
|
||||
s.cmd.Stdout = os.Stdout
|
||||
s.cmd.Stderr = os.Stderr
|
||||
|
||||
// Set environment variables for OPUS configuration
|
||||
s.cmd.Env = append(os.Environ(), s.opusEnv...)
|
||||
|
||||
// Start the process
|
||||
if err := s.cmd.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start audio output server process: %w", err)
|
||||
}
|
||||
|
||||
s.processPID = s.cmd.Process.Pid
|
||||
s.logger.Info().Int("pid", s.processPID).Msg("audio server process started")
|
||||
s.logger.Info().Int("pid", s.processPID).Strs("args", args).Strs("opus_env", s.opusEnv).Msg("audio server process started")
|
||||
|
||||
// Add process to monitoring
|
||||
s.processMonitor.AddProcess(s.processPID, "audio-output-server")
|
||||
|
|
21
main.go
21
main.go
|
@ -44,6 +44,27 @@ func startAudioSubprocess() error {
|
|||
// Set the global supervisor for access from audio package
|
||||
audio.SetAudioOutputSupervisor(audioSupervisor)
|
||||
|
||||
// Create and register audio input supervisor
|
||||
audioInputSupervisor := audio.NewAudioInputSupervisor()
|
||||
audio.SetAudioInputSupervisor(audioInputSupervisor)
|
||||
|
||||
// Set default OPUS configuration for audio input supervisor (low quality for single-core RV1106)
|
||||
config := audio.GetConfig()
|
||||
audioInputSupervisor.SetOpusConfig(
|
||||
config.AudioQualityLowInputBitrate*1000, // Convert kbps to bps
|
||||
config.AudioQualityLowOpusComplexity,
|
||||
config.AudioQualityLowOpusVBR,
|
||||
config.AudioQualityLowOpusSignalType,
|
||||
config.AudioQualityLowOpusBandwidth,
|
||||
config.AudioQualityLowOpusDTX,
|
||||
)
|
||||
|
||||
// Start audio input supervisor
|
||||
if err := audioInputSupervisor.Start(); err != nil {
|
||||
logger.Error().Err(err).Msg("failed to start audio input supervisor")
|
||||
// Continue execution as audio input is not critical for basic functionality
|
||||
}
|
||||
|
||||
// Set up callbacks for process lifecycle events
|
||||
audioSupervisor.SetCallbacks(
|
||||
// onProcessStart
|
||||
|
|
Loading…
Reference in New Issue