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