mirror of https://github.com/jetkvm/kvm.git
refactor(audio): centralize configuration constants in audio module
Replace hardcoded values with centralized config constants for better maintainability and flexibility. This includes sleep durations, buffer sizes, thresholds, and various audio processing parameters. The changes affect multiple components including buffer pools, latency monitoring, IPC, and audio processing. This refactoring makes it easier to adjust parameters without modifying individual files. Key changes: - Replace hardcoded sleep durations with config values - Centralize buffer sizes and pool configurations - Move thresholds and limits to config - Update audio quality presets to use config values
This commit is contained in:
parent
7ec583ed6a
commit
35a666ed31
|
@ -42,20 +42,20 @@ func DefaultAdaptiveBufferConfig() AdaptiveBufferConfig {
|
|||
DefaultBufferSize: 6, // Default 6 frames (increased for better stability)
|
||||
|
||||
// CPU thresholds optimized for single-core ARM Cortex A7 under load
|
||||
LowCPUThreshold: 20.0, // Below 20% CPU
|
||||
HighCPUThreshold: 60.0, // Above 60% CPU (lowered to be more responsive)
|
||||
LowCPUThreshold: GetConfig().LowCPUThreshold * 100, // Below 20% CPU
|
||||
HighCPUThreshold: GetConfig().HighCPUThreshold * 100, // Above 60% CPU (lowered to be more responsive)
|
||||
|
||||
// Memory thresholds for 256MB total RAM
|
||||
LowMemoryThreshold: 35.0, // Below 35% memory usage
|
||||
HighMemoryThreshold: 75.0, // Above 75% memory usage (lowered for earlier response)
|
||||
LowMemoryThreshold: GetConfig().LowMemoryThreshold * 100, // Below 35% memory usage
|
||||
HighMemoryThreshold: GetConfig().HighMemoryThreshold * 100, // Above 75% memory usage (lowered for earlier response)
|
||||
|
||||
// Latency targets
|
||||
TargetLatency: 20 * time.Millisecond, // Target 20ms latency
|
||||
MaxLatency: 50 * time.Millisecond, // Max acceptable 50ms
|
||||
TargetLatency: GetConfig().TargetLatency, // Target 20ms latency
|
||||
MaxLatency: GetConfig().MaxLatencyTarget, // Max acceptable latency
|
||||
|
||||
// Adaptation settings
|
||||
AdaptationInterval: 500 * time.Millisecond, // Check every 500ms
|
||||
SmoothingFactor: 0.3, // Moderate responsiveness
|
||||
AdaptationInterval: GetConfig().BufferUpdateInterval, // Check every 500ms
|
||||
SmoothingFactor: GetConfig().SmoothingFactor, // Moderate responsiveness
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,7 +133,7 @@ func (abm *AdaptiveBufferManager) UpdateLatency(latency time.Duration) {
|
|||
atomic.StoreInt64(&abm.averageLatency, newLatency)
|
||||
} else {
|
||||
// Exponential moving average: 70% historical, 30% current
|
||||
newAvg := int64(float64(currentAvg)*0.7 + float64(newLatency)*0.3)
|
||||
newAvg := int64(float64(currentAvg)*GetConfig().HistoricalWeight + float64(newLatency)*GetConfig().CurrentWeight)
|
||||
atomic.StoreInt64(&abm.averageLatency, newAvg)
|
||||
}
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ func (abm *AdaptiveBufferManager) adaptBufferSizes() {
|
|||
latencyFactor := abm.calculateLatencyFactor(currentLatency)
|
||||
|
||||
// Combine factors with weights (CPU has highest priority for KVM coexistence)
|
||||
combinedFactor := 0.5*cpuFactor + 0.3*memoryFactor + 0.2*latencyFactor
|
||||
combinedFactor := GetConfig().CPUMemoryWeight*cpuFactor + GetConfig().MemoryWeight*memoryFactor + GetConfig().LatencyWeight*latencyFactor
|
||||
|
||||
// Apply adaptation with smoothing
|
||||
currentInput := float64(atomic.LoadInt64(&abm.currentInputBufferSize))
|
||||
|
@ -306,8 +306,8 @@ func (abm *AdaptiveBufferManager) GetStats() map[string]interface{} {
|
|||
"input_buffer_size": abm.GetInputBufferSize(),
|
||||
"output_buffer_size": abm.GetOutputBufferSize(),
|
||||
"average_latency_ms": float64(atomic.LoadInt64(&abm.averageLatency)) / 1e6,
|
||||
"system_cpu_percent": float64(atomic.LoadInt64(&abm.systemCPUPercent)) / 100,
|
||||
"system_memory_percent": float64(atomic.LoadInt64(&abm.systemMemoryPercent)) / 100,
|
||||
"system_cpu_percent": float64(atomic.LoadInt64(&abm.systemCPUPercent)) / GetConfig().PercentageMultiplier,
|
||||
"system_memory_percent": float64(atomic.LoadInt64(&abm.systemMemoryPercent)) / GetConfig().PercentageMultiplier,
|
||||
"adaptation_count": atomic.LoadInt64(&abm.adaptationCount),
|
||||
"last_adaptation": lastAdaptation,
|
||||
}
|
||||
|
|
|
@ -42,9 +42,9 @@ type OptimizerConfig struct {
|
|||
func DefaultOptimizerConfig() OptimizerConfig {
|
||||
return OptimizerConfig{
|
||||
MaxOptimizationLevel: 8,
|
||||
CooldownPeriod: 30 * time.Second,
|
||||
Aggressiveness: 0.7,
|
||||
RollbackThreshold: 300 * time.Millisecond,
|
||||
CooldownPeriod: GetConfig().CooldownPeriod,
|
||||
Aggressiveness: GetConfig().OptimizerAggressiveness,
|
||||
RollbackThreshold: GetConfig().RollbackThreshold,
|
||||
StabilityPeriod: 10 * time.Second,
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ func (ao *AdaptiveOptimizer) handleLatencyOptimization(metrics LatencyMetrics) e
|
|||
// calculateTargetOptimizationLevel determines the appropriate optimization level
|
||||
func (ao *AdaptiveOptimizer) calculateTargetOptimizationLevel(metrics LatencyMetrics) int64 {
|
||||
// Base calculation on current latency vs target
|
||||
latencyRatio := float64(metrics.Current) / float64(50*time.Millisecond) // 50ms target
|
||||
latencyRatio := float64(metrics.Current) / float64(GetConfig().LatencyTarget) // 50ms target
|
||||
|
||||
// Adjust based on trend
|
||||
switch metrics.Trend {
|
||||
|
@ -125,7 +125,7 @@ func (ao *AdaptiveOptimizer) calculateTargetOptimizationLevel(metrics LatencyMet
|
|||
latencyRatio *= ao.config.Aggressiveness
|
||||
|
||||
// Convert to optimization level
|
||||
targetLevel := int64(latencyRatio * 2) // Scale to 0-10 range
|
||||
targetLevel := int64(latencyRatio * GetConfig().LatencyScalingFactor) // Scale to 0-10 range
|
||||
if targetLevel > int64(ao.config.MaxOptimizationLevel) {
|
||||
targetLevel = int64(ao.config.MaxOptimizationLevel)
|
||||
}
|
||||
|
|
|
@ -47,17 +47,17 @@ type AudioMetrics struct {
|
|||
var (
|
||||
currentConfig = AudioConfig{
|
||||
Quality: AudioQualityMedium,
|
||||
Bitrate: 64,
|
||||
Bitrate: GetConfig().AudioQualityMediumOutputBitrate,
|
||||
SampleRate: GetConfig().SampleRate,
|
||||
Channels: GetConfig().Channels,
|
||||
FrameSize: 20 * time.Millisecond,
|
||||
FrameSize: GetConfig().AudioQualityMediumFrameSize,
|
||||
}
|
||||
currentMicrophoneConfig = AudioConfig{
|
||||
Quality: AudioQualityMedium,
|
||||
Bitrate: 32,
|
||||
Bitrate: GetConfig().AudioQualityMediumInputBitrate,
|
||||
SampleRate: GetConfig().SampleRate,
|
||||
Channels: 1,
|
||||
FrameSize: 20 * time.Millisecond,
|
||||
FrameSize: GetConfig().AudioQualityMediumFrameSize,
|
||||
}
|
||||
metrics AudioMetrics
|
||||
)
|
||||
|
@ -69,24 +69,24 @@ var qualityPresets = map[AudioQuality]struct {
|
|||
frameSize time.Duration
|
||||
}{
|
||||
AudioQualityLow: {
|
||||
outputBitrate: 32, inputBitrate: 16,
|
||||
sampleRate: 22050, channels: 1,
|
||||
frameSize: 40 * time.Millisecond,
|
||||
outputBitrate: GetConfig().AudioQualityLowOutputBitrate, inputBitrate: GetConfig().AudioQualityLowInputBitrate,
|
||||
sampleRate: GetConfig().AudioQualityLowSampleRate, channels: GetConfig().AudioQualityLowChannels,
|
||||
frameSize: GetConfig().AudioQualityLowFrameSize,
|
||||
},
|
||||
AudioQualityMedium: {
|
||||
outputBitrate: 64, inputBitrate: 32,
|
||||
sampleRate: 44100, channels: 2,
|
||||
frameSize: 20 * time.Millisecond,
|
||||
outputBitrate: GetConfig().AudioQualityMediumOutputBitrate, inputBitrate: GetConfig().AudioQualityMediumInputBitrate,
|
||||
sampleRate: GetConfig().AudioQualityMediumSampleRate, channels: GetConfig().AudioQualityMediumChannels,
|
||||
frameSize: GetConfig().AudioQualityMediumFrameSize,
|
||||
},
|
||||
AudioQualityHigh: {
|
||||
outputBitrate: 128, inputBitrate: 64,
|
||||
sampleRate: GetConfig().SampleRate, channels: GetConfig().Channels,
|
||||
frameSize: 20 * time.Millisecond,
|
||||
outputBitrate: GetConfig().AudioQualityHighOutputBitrate, inputBitrate: GetConfig().AudioQualityHighInputBitrate,
|
||||
sampleRate: GetConfig().SampleRate, channels: GetConfig().AudioQualityHighChannels,
|
||||
frameSize: GetConfig().AudioQualityHighFrameSize,
|
||||
},
|
||||
AudioQualityUltra: {
|
||||
outputBitrate: 192, inputBitrate: 96,
|
||||
sampleRate: GetConfig().SampleRate, channels: GetConfig().Channels,
|
||||
frameSize: 10 * time.Millisecond,
|
||||
outputBitrate: GetConfig().AudioQualityUltraOutputBitrate, inputBitrate: GetConfig().AudioQualityUltraInputBitrate,
|
||||
sampleRate: GetConfig().SampleRate, channels: GetConfig().AudioQualityUltraChannels,
|
||||
frameSize: GetConfig().AudioQualityUltraFrameSize,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -114,7 +114,7 @@ func GetMicrophoneQualityPresets() map[AudioQuality]AudioConfig {
|
|||
Bitrate: preset.inputBitrate,
|
||||
SampleRate: func() int {
|
||||
if quality == AudioQualityLow {
|
||||
return 16000
|
||||
return GetConfig().AudioQualityMicLowSampleRate
|
||||
}
|
||||
return preset.sampleRate
|
||||
}(),
|
||||
|
|
|
@ -72,7 +72,7 @@ func NewBatchAudioProcessor(batchSize int, batchDuration time.Duration) *BatchAu
|
|||
readQueue: make(chan batchReadRequest, batchSize*2),
|
||||
readBufPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 1500) // Max audio frame size
|
||||
return make([]byte, GetConfig().AudioFramePoolSize) // Max audio frame size
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ func (bap *BatchAudioProcessor) Stop() {
|
|||
bap.cancel()
|
||||
|
||||
// Wait for processing to complete
|
||||
time.Sleep(bap.batchDuration + 10*time.Millisecond)
|
||||
time.Sleep(bap.batchDuration + GetConfig().BatchProcessingDelay)
|
||||
|
||||
bap.logger.Info().Msg("batch audio processor stopped")
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ func (bap *BatchAudioProcessor) BatchReadEncode(buffer []byte) (int, error) {
|
|||
select {
|
||||
case bap.readQueue <- request:
|
||||
// Successfully queued
|
||||
case <-time.After(5 * time.Millisecond):
|
||||
case <-time.After(GetConfig().ShortTimeout):
|
||||
// Queue is full or blocked, fallback to single operation
|
||||
atomic.AddInt64(&bap.stats.SingleReads, 1)
|
||||
atomic.AddInt64(&bap.stats.SingleFrames, 1)
|
||||
|
@ -145,7 +145,7 @@ func (bap *BatchAudioProcessor) BatchReadEncode(buffer []byte) (int, error) {
|
|||
select {
|
||||
case result := <-resultChan:
|
||||
return result.length, result.err
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
case <-time.After(GetConfig().MediumTimeout):
|
||||
// Timeout, fallback to single operation
|
||||
atomic.AddInt64(&bap.stats.SingleReads, 1)
|
||||
atomic.AddInt64(&bap.stats.SingleFrames, 1)
|
||||
|
|
|
@ -23,7 +23,7 @@ type AudioBufferPool struct {
|
|||
|
||||
func NewAudioBufferPool(bufferSize int) *AudioBufferPool {
|
||||
// Pre-allocate 20% of max pool size for immediate availability
|
||||
preallocSize := 20
|
||||
preallocSize := GetConfig().PreallocPercentage
|
||||
preallocated := make([]*[]byte, 0, preallocSize)
|
||||
|
||||
// Pre-allocate buffers to reduce initial allocation overhead
|
||||
|
@ -34,7 +34,7 @@ func NewAudioBufferPool(bufferSize int) *AudioBufferPool {
|
|||
|
||||
return &AudioBufferPool{
|
||||
bufferSize: bufferSize,
|
||||
maxPoolSize: 100, // Limit pool size to prevent excessive memory usage
|
||||
maxPoolSize: GetConfig().MaxPoolSize, // Limit pool size to prevent excessive memory usage
|
||||
preallocated: preallocated,
|
||||
preallocSize: preallocSize,
|
||||
pool: sync.Pool{
|
||||
|
@ -111,8 +111,8 @@ func (p *AudioBufferPool) Put(buf []byte) {
|
|||
}
|
||||
|
||||
var (
|
||||
audioFramePool = NewAudioBufferPool(1500)
|
||||
audioControlPool = NewAudioBufferPool(64)
|
||||
audioFramePool = NewAudioBufferPool(GetConfig().AudioFramePoolSize)
|
||||
audioControlPool = NewAudioBufferPool(GetConfig().OutputHeaderSize)
|
||||
)
|
||||
|
||||
func GetAudioFrameBuffer() []byte {
|
||||
|
@ -144,7 +144,7 @@ func (p *AudioBufferPool) GetPoolStats() AudioBufferPoolDetailedStats {
|
|||
|
||||
var hitRate float64
|
||||
if totalRequests > 0 {
|
||||
hitRate = float64(hitCount) / float64(totalRequests) * 100
|
||||
hitRate = float64(hitCount) / float64(totalRequests) * GetConfig().PercentageMultiplier
|
||||
}
|
||||
|
||||
return AudioBufferPoolDetailedStats{
|
||||
|
|
|
@ -22,18 +22,37 @@ static snd_pcm_t *pcm_handle = NULL;
|
|||
static snd_pcm_t *pcm_playback_handle = NULL;
|
||||
static OpusEncoder *encoder = NULL;
|
||||
static OpusDecoder *decoder = NULL;
|
||||
// Optimized Opus encoder settings for ARM Cortex-A7
|
||||
static int opus_bitrate = 96000; // Increased for better quality
|
||||
static int opus_complexity = 3; // Reduced for ARM performance
|
||||
static int opus_vbr = 1; // Variable bitrate enabled
|
||||
static int opus_vbr_constraint = 1; // Constrained VBR for consistent latency
|
||||
static int opus_signal_type = OPUS_SIGNAL_MUSIC; // Optimized for general audio
|
||||
static int opus_bandwidth = OPUS_BANDWIDTH_FULLBAND; // Full bandwidth
|
||||
static int opus_dtx = 0; // Disable DTX for real-time audio
|
||||
static int sample_rate = 48000;
|
||||
static int channels = 2;
|
||||
static int frame_size = 960; // 20ms for 48kHz
|
||||
static int max_packet_size = 1500;
|
||||
// Opus encoder settings - initialized from Go configuration
|
||||
static int opus_bitrate = 96000; // Will be set from GetConfig().CGOOpusBitrate
|
||||
static int opus_complexity = 3; // Will be set from GetConfig().CGOOpusComplexity
|
||||
static int opus_vbr = 1; // Will be set from GetConfig().CGOOpusVBR
|
||||
static int opus_vbr_constraint = 1; // Will be set from GetConfig().CGOOpusVBRConstraint
|
||||
static int opus_signal_type = 3; // Will be set from GetConfig().CGOOpusSignalType
|
||||
static int opus_bandwidth = 1105; // Will be set from GetConfig().CGOOpusBandwidth
|
||||
static int opus_dtx = 0; // Will be set from GetConfig().CGOOpusDTX
|
||||
static int sample_rate = 48000; // Will be set from GetConfig().CGOSampleRate
|
||||
static int channels = 2; // Will be set from GetConfig().CGOChannels
|
||||
static int frame_size = 960; // Will be set from GetConfig().CGOFrameSize
|
||||
static int max_packet_size = 1500; // Will be set from GetConfig().CGOMaxPacketSize
|
||||
static int sleep_microseconds = 50000; // Will be set from GetConfig().CGOSleepMicroseconds
|
||||
|
||||
// Function to update constants from Go configuration
|
||||
void update_audio_constants(int bitrate, int complexity, int vbr, int vbr_constraint,
|
||||
int signal_type, int bandwidth, int dtx, int sr, int ch,
|
||||
int fs, int max_pkt, int sleep_us) {
|
||||
opus_bitrate = bitrate;
|
||||
opus_complexity = complexity;
|
||||
opus_vbr = vbr;
|
||||
opus_vbr_constraint = vbr_constraint;
|
||||
opus_signal_type = signal_type;
|
||||
opus_bandwidth = bandwidth;
|
||||
opus_dtx = dtx;
|
||||
sample_rate = sr;
|
||||
channels = ch;
|
||||
frame_size = fs;
|
||||
max_packet_size = max_pkt;
|
||||
sleep_microseconds = sleep_us;
|
||||
}
|
||||
|
||||
// State tracking to prevent race conditions during rapid start/stop
|
||||
static volatile int capture_initializing = 0;
|
||||
|
@ -56,7 +75,7 @@ static int safe_alsa_open(snd_pcm_t **handle, const char *device, snd_pcm_stream
|
|||
|
||||
if (err == -EBUSY && attempts > 0) {
|
||||
// Device busy, wait and retry
|
||||
usleep(50000); // 50ms
|
||||
usleep(sleep_microseconds); // 50ms
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
@ -415,8 +434,25 @@ var (
|
|||
)
|
||||
|
||||
func cgoAudioInit() error {
|
||||
ret := C.jetkvm_audio_init()
|
||||
if ret != 0 {
|
||||
// Update C constants from Go configuration
|
||||
config := GetConfig()
|
||||
C.update_audio_constants(
|
||||
C.int(config.CGOOpusBitrate),
|
||||
C.int(config.CGOOpusComplexity),
|
||||
C.int(config.CGOOpusVBR),
|
||||
C.int(config.CGOOpusVBRConstraint),
|
||||
C.int(config.CGOOpusSignalType),
|
||||
C.int(config.CGOOpusBandwidth),
|
||||
C.int(config.CGOOpusDTX),
|
||||
C.int(config.CGOSampleRate),
|
||||
C.int(config.CGOChannels),
|
||||
C.int(config.CGOFrameSize),
|
||||
C.int(config.CGOMaxPacketSize),
|
||||
C.int(config.CGOSleepMicroseconds),
|
||||
)
|
||||
|
||||
result := C.jetkvm_audio_init()
|
||||
if result != 0 {
|
||||
return errAudioInitFailed
|
||||
}
|
||||
return nil
|
||||
|
@ -427,7 +463,7 @@ func cgoAudioClose() {
|
|||
}
|
||||
|
||||
func cgoAudioReadEncode(buf []byte) (int, error) {
|
||||
if len(buf) < 1276 {
|
||||
if len(buf) < GetConfig().MinReadEncodeBuffer {
|
||||
return 0, errBufferTooSmall
|
||||
}
|
||||
|
||||
|
@ -461,7 +497,7 @@ func cgoAudioDecodeWrite(buf []byte) (int, error) {
|
|||
if buf == nil {
|
||||
return 0, errNilBuffer
|
||||
}
|
||||
if len(buf) > 4096 {
|
||||
if len(buf) > GetConfig().MaxDecodeWriteBuffer {
|
||||
return 0, errBufferTooLarge
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -16,14 +16,19 @@ import (
|
|||
"github.com/jetkvm/kvm/internal/logging"
|
||||
)
|
||||
|
||||
const (
|
||||
inputMagicNumber uint32 = 0x4A4B4D49 // "JKMI" (JetKVM Microphone Input)
|
||||
var (
|
||||
inputMagicNumber uint32 = GetConfig().InputMagicNumber // "JKMI" (JetKVM Microphone Input)
|
||||
inputSocketName = "audio_input.sock"
|
||||
maxFrameSize = 4096 // Maximum Opus frame size
|
||||
writeTimeout = 15 * time.Millisecond // Non-blocking write timeout (increased for high load)
|
||||
maxDroppedFrames = 100 // Maximum consecutive dropped frames before reconnect
|
||||
)
|
||||
|
||||
const (
|
||||
headerSize = 17 // Fixed header size: 4+1+4+8 bytes
|
||||
messagePoolSize = 256 // Pre-allocated message pool size
|
||||
)
|
||||
|
||||
var (
|
||||
maxFrameSize = GetConfig().MaxFrameSize // Maximum Opus frame size
|
||||
messagePoolSize = GetConfig().MessagePoolSize // Pre-allocated message pool size
|
||||
)
|
||||
|
||||
// InputMessageType represents the type of IPC message
|
||||
|
@ -79,9 +84,9 @@ var messagePoolInitOnce sync.Once
|
|||
func initializeMessagePool() {
|
||||
messagePoolInitOnce.Do(func() {
|
||||
// Pre-allocate 30% of pool size for immediate availability
|
||||
preallocSize := messagePoolSize * 30 / 100
|
||||
preallocSize := messagePoolSize * GetConfig().InputPreallocPercentage / 100
|
||||
globalMessagePool.preallocSize = preallocSize
|
||||
globalMessagePool.maxPoolSize = messagePoolSize * 2 // Allow growth up to 2x
|
||||
globalMessagePool.maxPoolSize = messagePoolSize * GetConfig().PoolGrowthMultiplier // Allow growth up to 2x
|
||||
globalMessagePool.preallocated = make([]*OptimizedIPCMessage, 0, preallocSize)
|
||||
|
||||
// Pre-allocate messages to reduce initial allocation overhead
|
||||
|
@ -315,7 +320,7 @@ func (ais *AudioInputServer) handleConnection(conn net.Conn) {
|
|||
if ais.conn == nil {
|
||||
return
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(GetConfig().DefaultSleepDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -345,7 +350,7 @@ func (ais *AudioInputServer) readMessage(conn net.Conn) (*InputIPCMessage, error
|
|||
}
|
||||
|
||||
// Validate message length
|
||||
if msg.Length > maxFrameSize {
|
||||
if msg.Length > uint32(maxFrameSize) {
|
||||
return nil, fmt.Errorf("message too large: %d bytes", msg.Length)
|
||||
}
|
||||
|
||||
|
@ -711,7 +716,7 @@ func (aic *AudioInputClient) GetDropRate() float64 {
|
|||
if total == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return float64(dropped) / float64(total) * 100.0
|
||||
return float64(dropped) / float64(total) * GetConfig().PercentageMultiplier
|
||||
}
|
||||
|
||||
// ResetStats resets frame statistics
|
||||
|
@ -820,11 +825,11 @@ func (ais *AudioInputServer) startMonitorGoroutine() {
|
|||
}()
|
||||
|
||||
defer ais.wg.Done()
|
||||
ticker := time.NewTicker(100 * time.Millisecond)
|
||||
ticker := time.NewTicker(GetConfig().DefaultTickerInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
// Buffer size update ticker (less frequent)
|
||||
bufferUpdateTicker := time.NewTicker(500 * time.Millisecond)
|
||||
bufferUpdateTicker := time.NewTicker(GetConfig().BufferUpdateInterval)
|
||||
defer bufferUpdateTicker.Stop()
|
||||
|
||||
for {
|
||||
|
@ -917,7 +922,7 @@ func (mp *MessagePool) GetMessagePoolStats() MessagePoolStats {
|
|||
|
||||
var hitRate float64
|
||||
if totalRequests > 0 {
|
||||
hitRate = float64(hitCount) / float64(totalRequests) * 100
|
||||
hitRate = float64(hitCount) / float64(totalRequests) * GetConfig().PercentageMultiplier
|
||||
}
|
||||
|
||||
// Calculate channel pool size
|
||||
|
|
|
@ -41,13 +41,13 @@ func (aim *AudioInputIPCManager) Start() error {
|
|||
}
|
||||
|
||||
config := InputIPCConfig{
|
||||
SampleRate: 48000,
|
||||
Channels: 2,
|
||||
FrameSize: 960,
|
||||
SampleRate: GetConfig().InputIPCSampleRate,
|
||||
Channels: GetConfig().InputIPCChannels,
|
||||
FrameSize: GetConfig().InputIPCFrameSize,
|
||||
}
|
||||
|
||||
// Wait for subprocess readiness
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(GetConfig().LongSleepDuration)
|
||||
|
||||
err = aim.supervisor.SendConfig(config)
|
||||
if err != nil {
|
||||
|
|
|
@ -64,7 +64,7 @@ func RunAudioInputServer() error {
|
|||
server.Stop()
|
||||
|
||||
// Give some time for cleanup
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(GetConfig().DefaultSleepDuration)
|
||||
|
||||
logger.Info().Msg("Audio input server subprocess stopped")
|
||||
return nil
|
||||
|
|
|
@ -128,7 +128,7 @@ func (ais *AudioInputSupervisor) Stop() {
|
|||
select {
|
||||
case <-done:
|
||||
ais.logger.Info().Msg("Audio input server subprocess stopped gracefully")
|
||||
case <-time.After(5 * time.Second):
|
||||
case <-time.After(GetConfig().InputSupervisorTimeout):
|
||||
// Force kill if graceful shutdown failed
|
||||
ais.logger.Warn().Msg("Audio input server subprocess did not stop gracefully, force killing")
|
||||
err := ais.cmd.Process.Kill()
|
||||
|
@ -220,7 +220,7 @@ func (ais *AudioInputSupervisor) monitorSubprocess() {
|
|||
// connectClient attempts to connect the client to the server
|
||||
func (ais *AudioInputSupervisor) connectClient() {
|
||||
// Wait briefly for the server to start (reduced from 500ms)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(GetConfig().DefaultSleepDuration)
|
||||
|
||||
err := ais.client.Connect()
|
||||
if err != nil {
|
||||
|
|
|
@ -16,16 +16,14 @@ import (
|
|||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
const (
|
||||
outputMagicNumber uint32 = 0x4A4B4F55 // "JKOU" (JetKVM Output)
|
||||
var (
|
||||
outputMagicNumber uint32 = GetConfig().OutputMagicNumber // "JKOU" (JetKVM Output)
|
||||
outputSocketName = "audio_output.sock"
|
||||
outputMaxFrameSize = 4096 // Maximum Opus frame size
|
||||
outputWriteTimeout = 10 * time.Millisecond // Non-blocking write timeout (increased for high load)
|
||||
outputMaxDroppedFrames = 50 // Maximum consecutive dropped frames
|
||||
outputHeaderSize = 17 // Fixed header size: 4+1+4+8 bytes
|
||||
outputMessagePoolSize = 128 // Pre-allocated message pool size
|
||||
)
|
||||
|
||||
// Output IPC constants are now centralized in config_constants.go
|
||||
// outputMaxFrameSize, outputWriteTimeout, outputMaxDroppedFrames, outputHeaderSize, outputMessagePoolSize
|
||||
|
||||
// OutputMessageType represents the type of IPC message
|
||||
type OutputMessageType uint8
|
||||
|
||||
|
@ -48,7 +46,7 @@ type OutputIPCMessage struct {
|
|||
|
||||
// OutputOptimizedMessage represents a pre-allocated message for zero-allocation operations
|
||||
type OutputOptimizedMessage struct {
|
||||
header [outputHeaderSize]byte // Pre-allocated header buffer
|
||||
header [17]byte // Pre-allocated header buffer (using constant value since array size must be compile-time constant)
|
||||
data []byte // Reusable data buffer
|
||||
}
|
||||
|
||||
|
@ -66,7 +64,7 @@ func NewOutputMessagePool(size int) *OutputMessagePool {
|
|||
// Pre-allocate messages
|
||||
for i := 0; i < size; i++ {
|
||||
msg := &OutputOptimizedMessage{
|
||||
data: make([]byte, outputMaxFrameSize),
|
||||
data: make([]byte, GetConfig().OutputMaxFrameSize),
|
||||
}
|
||||
pool.pool <- msg
|
||||
}
|
||||
|
@ -82,7 +80,7 @@ func (p *OutputMessagePool) Get() *OutputOptimizedMessage {
|
|||
default:
|
||||
// Pool exhausted, create new message
|
||||
return &OutputOptimizedMessage{
|
||||
data: make([]byte, outputMaxFrameSize),
|
||||
data: make([]byte, GetConfig().OutputMaxFrameSize),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +96,7 @@ func (p *OutputMessagePool) Put(msg *OutputOptimizedMessage) {
|
|||
}
|
||||
|
||||
// Global message pool for output IPC
|
||||
var globalOutputMessagePool = NewOutputMessagePool(outputMessagePoolSize)
|
||||
var globalOutputMessagePool = NewOutputMessagePool(GetConfig().OutputMessagePoolSize)
|
||||
|
||||
type AudioServer struct {
|
||||
// Atomic fields must be first for proper alignment on ARM
|
||||
|
@ -135,7 +133,7 @@ func NewAudioServer() (*AudioServer, error) {
|
|||
}
|
||||
|
||||
// Initialize with adaptive buffer size (start with 500 frames)
|
||||
initialBufferSize := int64(500)
|
||||
initialBufferSize := int64(GetConfig().InitialBufferFrames)
|
||||
|
||||
// Initialize latency monitoring
|
||||
latencyConfig := DefaultLatencyConfig()
|
||||
|
@ -284,8 +282,8 @@ func (s *AudioServer) Close() error {
|
|||
}
|
||||
|
||||
func (s *AudioServer) SendFrame(frame []byte) error {
|
||||
if len(frame) > outputMaxFrameSize {
|
||||
return fmt.Errorf("frame size %d exceeds maximum %d", len(frame), outputMaxFrameSize)
|
||||
if len(frame) > GetConfig().OutputMaxFrameSize {
|
||||
return fmt.Errorf("frame size %d exceeds maximum %d", len(frame), GetConfig().OutputMaxFrameSize)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
|
@ -340,7 +338,7 @@ func (s *AudioServer) sendFrameToClient(frame []byte) error {
|
|||
binary.LittleEndian.PutUint64(optMsg.header[9:17], uint64(start.UnixNano()))
|
||||
|
||||
// Use non-blocking write with timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), outputWriteTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), GetConfig().OutputWriteTimeout)
|
||||
defer cancel()
|
||||
|
||||
// Create a channel to signal write completion
|
||||
|
@ -492,8 +490,8 @@ func (c *AudioClient) ReceiveFrame() ([]byte, error) {
|
|||
}
|
||||
|
||||
size := binary.LittleEndian.Uint32(optMsg.header[5:9])
|
||||
if size > outputMaxFrameSize {
|
||||
return nil, fmt.Errorf("frame size %d exceeds maximum %d", size, outputMaxFrameSize)
|
||||
if int(size) > GetConfig().OutputMaxFrameSize {
|
||||
return nil, fmt.Errorf("frame size %d exceeds maximum %d", size, GetConfig().OutputMaxFrameSize)
|
||||
}
|
||||
|
||||
// Read frame data
|
||||
|
|
|
@ -83,10 +83,10 @@ const (
|
|||
func DefaultLatencyConfig() LatencyConfig {
|
||||
return LatencyConfig{
|
||||
TargetLatency: 50 * time.Millisecond,
|
||||
MaxLatency: 200 * time.Millisecond,
|
||||
MaxLatency: GetConfig().MaxLatencyThreshold,
|
||||
OptimizationInterval: 5 * time.Second,
|
||||
HistorySize: 100,
|
||||
JitterThreshold: 20 * time.Millisecond,
|
||||
HistorySize: GetConfig().LatencyHistorySize,
|
||||
JitterThreshold: GetConfig().JitterThreshold,
|
||||
AdaptiveThreshold: 0.8, // Trigger optimization when 80% above target
|
||||
}
|
||||
}
|
||||
|
|
|
@ -171,8 +171,8 @@ func LogMemoryMetrics() {
|
|||
metrics := CollectMemoryMetrics()
|
||||
|
||||
logger.Info().
|
||||
Uint64("heap_alloc_mb", metrics.RuntimeStats.HeapAlloc/1024/1024).
|
||||
Uint64("heap_sys_mb", metrics.RuntimeStats.HeapSys/1024/1024).
|
||||
Uint64("heap_alloc_mb", metrics.RuntimeStats.HeapAlloc/uint64(GetConfig().BytesToMBDivisor)).
|
||||
Uint64("heap_sys_mb", metrics.RuntimeStats.HeapSys/uint64(GetConfig().BytesToMBDivisor)).
|
||||
Uint64("heap_objects", metrics.RuntimeStats.HeapObjects).
|
||||
Uint32("num_gc", metrics.RuntimeStats.NumGC).
|
||||
Float64("gc_cpu_fraction", metrics.RuntimeStats.GCCPUFraction).
|
||||
|
|
|
@ -451,7 +451,7 @@ func GetLastMetricsUpdate() time.Time {
|
|||
// StartMetricsUpdater starts a goroutine that periodically updates Prometheus metrics
|
||||
func StartMetricsUpdater() {
|
||||
go func() {
|
||||
ticker := time.NewTicker(5 * time.Second) // Update every 5 seconds
|
||||
ticker := time.NewTicker(GetConfig().StatsUpdateInterval) // Update every 5 seconds
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
|
|
|
@ -105,7 +105,7 @@ func GetMicrophoneContentionManager() *MicrophoneContentionManager {
|
|||
}
|
||||
|
||||
if atomic.CompareAndSwapInt32(&micContentionInitialized, 0, 1) {
|
||||
manager := NewMicrophoneContentionManager(200 * time.Millisecond)
|
||||
manager := NewMicrophoneContentionManager(GetConfig().MicContentionTimeout)
|
||||
atomic.StorePointer(&globalMicContentionManager, unsafe.Pointer(manager))
|
||||
return manager
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ func GetMicrophoneContentionManager() *MicrophoneContentionManager {
|
|||
return (*MicrophoneContentionManager)(ptr)
|
||||
}
|
||||
|
||||
return NewMicrophoneContentionManager(200 * time.Millisecond)
|
||||
return NewMicrophoneContentionManager(GetConfig().MicContentionTimeout)
|
||||
}
|
||||
|
||||
func TryMicrophoneOperation() OperationResult {
|
||||
|
|
|
@ -64,7 +64,7 @@ func RunAudioOutputServer() error {
|
|||
StopNonBlockingAudioStreaming()
|
||||
|
||||
// Give some time for cleanup
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(GetConfig().DefaultSleepDuration)
|
||||
|
||||
logger.Info().Msg("Audio output server subprocess stopped")
|
||||
return nil
|
||||
|
|
|
@ -62,7 +62,7 @@ func NewOutputStreamer() (*OutputStreamer, error) {
|
|||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
batchSize: initialBatchSize, // Use adaptive batch size
|
||||
processingChan: make(chan []byte, 500), // Large buffer for smooth processing
|
||||
processingChan: make(chan []byte, GetConfig().ChannelBufferSize), // Large buffer for smooth processing
|
||||
statsInterval: 5 * time.Second, // Statistics every 5 seconds
|
||||
lastStatsTime: time.Now().UnixNano(),
|
||||
}, nil
|
||||
|
@ -127,7 +127,7 @@ func (s *OutputStreamer) streamLoop() {
|
|||
defer ticker.Stop()
|
||||
|
||||
// Batch size update ticker
|
||||
batchUpdateTicker := time.NewTicker(500 * time.Millisecond)
|
||||
batchUpdateTicker := time.NewTicker(GetConfig().BufferUpdateInterval)
|
||||
defer batchUpdateTicker.Stop()
|
||||
|
||||
for {
|
||||
|
@ -233,7 +233,7 @@ func (s *OutputStreamer) reportStatistics() {
|
|||
processingTime := atomic.LoadInt64(&s.processingTime)
|
||||
|
||||
if processed > 0 {
|
||||
dropRate := float64(dropped) / float64(processed+dropped) * 100
|
||||
dropRate := float64(dropped) / float64(processed+dropped) * GetConfig().PercentageMultiplier
|
||||
avgProcessingTime := time.Duration(processingTime)
|
||||
|
||||
getOutputStreamingLogger().Info().Int64("processed", processed).Int64("dropped", dropped).Float64("drop_rate", dropRate).Dur("avg_processing", avgProcessingTime).Msg("Output Audio Stats")
|
||||
|
@ -270,7 +270,7 @@ func (s *OutputStreamer) GetDetailedStats() map[string]interface{} {
|
|||
}
|
||||
|
||||
if processed+dropped > 0 {
|
||||
stats["drop_rate_percent"] = float64(dropped) / float64(processed+dropped) * 100
|
||||
stats["drop_rate_percent"] = float64(dropped) / float64(processed+dropped) * GetConfig().PercentageMultiplier
|
||||
}
|
||||
|
||||
// Add client statistics
|
||||
|
@ -343,7 +343,7 @@ func StartAudioOutputStreaming(send func([]byte)) error {
|
|||
RecordFrameReceived(n)
|
||||
}
|
||||
// Small delay to prevent busy waiting
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
time.Sleep(GetConfig().ShortSleepDuration)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -364,6 +364,6 @@ func StopAudioOutputStreaming() {
|
|||
|
||||
// Wait for streaming to stop
|
||||
for atomic.LoadInt32(&outputStreamingRunning) == 1 {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
time.Sleep(GetConfig().ShortSleepDuration)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,23 +16,17 @@ type SchedParam struct {
|
|||
Priority int32
|
||||
}
|
||||
|
||||
// Priority levels for audio processing
|
||||
const (
|
||||
// SCHED_FIFO priorities (1-99, higher = more priority)
|
||||
AudioHighPriority = 80 // High priority for critical audio processing
|
||||
AudioMediumPriority = 60 // Medium priority for regular audio processing
|
||||
AudioLowPriority = 40 // Low priority for background audio tasks
|
||||
// getPriorityConstants returns priority levels from centralized config
|
||||
func getPriorityConstants() (audioHigh, audioMedium, audioLow, normal int) {
|
||||
config := GetConfig()
|
||||
return config.AudioHighPriority, config.AudioMediumPriority, config.AudioLowPriority, config.NormalPriority
|
||||
}
|
||||
|
||||
// SCHED_NORMAL is the default (priority 0)
|
||||
NormalPriority = 0
|
||||
)
|
||||
|
||||
// Scheduling policies
|
||||
const (
|
||||
SCHED_NORMAL = 0
|
||||
SCHED_FIFO = 1
|
||||
SCHED_RR = 2
|
||||
)
|
||||
// getSchedulingPolicies returns scheduling policies from centralized config
|
||||
func getSchedulingPolicies() (schedNormal, schedFIFO, schedRR int) {
|
||||
config := GetConfig()
|
||||
return config.SchedNormal, config.SchedFIFO, config.SchedRR
|
||||
}
|
||||
|
||||
// PriorityScheduler manages thread priorities for audio processing
|
||||
type PriorityScheduler struct {
|
||||
|
@ -73,7 +67,8 @@ func (ps *PriorityScheduler) SetThreadPriority(priority int, policy int) error {
|
|||
|
||||
if errno != 0 {
|
||||
// If we can't set real-time priority, try nice value instead
|
||||
if policy != SCHED_NORMAL {
|
||||
schedNormal, _, _ := getSchedulingPolicies()
|
||||
if policy != schedNormal {
|
||||
ps.logger.Warn().Int("errno", int(errno)).Msg("Failed to set real-time priority, falling back to nice")
|
||||
return ps.setNicePriority(priority)
|
||||
}
|
||||
|
@ -89,11 +84,11 @@ func (ps *PriorityScheduler) setNicePriority(rtPriority int) error {
|
|||
// Convert real-time priority to nice value (inverse relationship)
|
||||
// RT priority 80 -> nice -10, RT priority 40 -> nice 0
|
||||
niceValue := (40 - rtPriority) / 4
|
||||
if niceValue < -20 {
|
||||
niceValue = -20
|
||||
if niceValue < GetConfig().MinNiceValue {
|
||||
niceValue = GetConfig().MinNiceValue
|
||||
}
|
||||
if niceValue > 19 {
|
||||
niceValue = 19
|
||||
if niceValue > GetConfig().MaxNiceValue {
|
||||
niceValue = GetConfig().MaxNiceValue
|
||||
}
|
||||
|
||||
err := syscall.Setpriority(syscall.PRIO_PROCESS, 0, niceValue)
|
||||
|
@ -108,22 +103,30 @@ func (ps *PriorityScheduler) setNicePriority(rtPriority int) error {
|
|||
|
||||
// SetAudioProcessingPriority sets high priority for audio processing threads
|
||||
func (ps *PriorityScheduler) SetAudioProcessingPriority() error {
|
||||
return ps.SetThreadPriority(AudioHighPriority, SCHED_FIFO)
|
||||
audioHigh, _, _, _ := getPriorityConstants()
|
||||
_, schedFIFO, _ := getSchedulingPolicies()
|
||||
return ps.SetThreadPriority(audioHigh, schedFIFO)
|
||||
}
|
||||
|
||||
// SetAudioIOPriority sets medium priority for audio I/O threads
|
||||
func (ps *PriorityScheduler) SetAudioIOPriority() error {
|
||||
return ps.SetThreadPriority(AudioMediumPriority, SCHED_FIFO)
|
||||
_, audioMedium, _, _ := getPriorityConstants()
|
||||
_, schedFIFO, _ := getSchedulingPolicies()
|
||||
return ps.SetThreadPriority(audioMedium, schedFIFO)
|
||||
}
|
||||
|
||||
// SetAudioBackgroundPriority sets low priority for background audio tasks
|
||||
func (ps *PriorityScheduler) SetAudioBackgroundPriority() error {
|
||||
return ps.SetThreadPriority(AudioLowPriority, SCHED_FIFO)
|
||||
_, _, audioLow, _ := getPriorityConstants()
|
||||
_, schedFIFO, _ := getSchedulingPolicies()
|
||||
return ps.SetThreadPriority(audioLow, schedFIFO)
|
||||
}
|
||||
|
||||
// ResetPriority resets thread to normal scheduling
|
||||
func (ps *PriorityScheduler) ResetPriority() error {
|
||||
return ps.SetThreadPriority(NormalPriority, SCHED_NORMAL)
|
||||
_, _, _, normal := getPriorityConstants()
|
||||
schedNormal, _, _ := getSchedulingPolicies()
|
||||
return ps.SetThreadPriority(normal, schedNormal)
|
||||
}
|
||||
|
||||
// Disable disables priority scheduling (useful for testing or fallback)
|
||||
|
|
|
@ -13,26 +13,29 @@ import (
|
|||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// Constants for process monitoring
|
||||
const (
|
||||
// Variables for process monitoring (using configuration)
|
||||
var (
|
||||
// System constants
|
||||
pageSize = 4096
|
||||
maxCPUPercent = 100.0
|
||||
minCPUPercent = 0.01
|
||||
defaultClockTicks = 250.0 // Common for embedded ARM systems
|
||||
defaultMemoryGB = 8
|
||||
maxCPUPercent = GetConfig().MaxCPUPercent
|
||||
minCPUPercent = GetConfig().MinCPUPercent
|
||||
defaultClockTicks = GetConfig().DefaultClockTicks
|
||||
defaultMemoryGB = GetConfig().DefaultMemoryGB
|
||||
|
||||
// Monitoring thresholds
|
||||
maxWarmupSamples = 3
|
||||
warmupCPUSamples = 2
|
||||
logThrottleInterval = 10
|
||||
maxWarmupSamples = GetConfig().MaxWarmupSamples
|
||||
warmupCPUSamples = GetConfig().WarmupCPUSamples
|
||||
|
||||
// Channel buffer size
|
||||
metricsChannelBuffer = 100
|
||||
metricsChannelBuffer = GetConfig().MetricsChannelBuffer
|
||||
|
||||
// Clock tick detection ranges
|
||||
minValidClockTicks = 50
|
||||
maxValidClockTicks = 1000
|
||||
minValidClockTicks = float64(GetConfig().MinValidClockTicks)
|
||||
maxValidClockTicks = float64(GetConfig().MaxValidClockTicks)
|
||||
)
|
||||
|
||||
// Variables for process monitoring
|
||||
var (
|
||||
pageSize = GetConfig().PageSize
|
||||
)
|
||||
|
||||
// ProcessMetrics represents CPU and memory usage metrics for a process
|
||||
|
@ -217,7 +220,7 @@ func (pm *ProcessMonitor) collectMetrics(pid int, state *processState) (ProcessM
|
|||
vsize, _ := strconv.ParseInt(fields[22], 10, 64)
|
||||
rss, _ := strconv.ParseInt(fields[23], 10, 64)
|
||||
|
||||
metric.MemoryRSS = rss * pageSize
|
||||
metric.MemoryRSS = rss * int64(pageSize)
|
||||
metric.MemoryVMS = vsize
|
||||
|
||||
// Calculate CPU percentage
|
||||
|
@ -230,7 +233,7 @@ func (pm *ProcessMonitor) collectMetrics(pid int, state *processState) (ProcessM
|
|||
|
||||
// Calculate memory percentage (RSS / total system memory)
|
||||
if totalMem := pm.getTotalMemory(); totalMem > 0 {
|
||||
metric.MemoryPercent = float64(metric.MemoryRSS) / float64(totalMem) * 100.0
|
||||
metric.MemoryPercent = float64(metric.MemoryRSS) / float64(totalMem) * GetConfig().PercentageMultiplier
|
||||
}
|
||||
|
||||
// Update state for next calculation
|
||||
|
@ -261,7 +264,7 @@ func (pm *ProcessMonitor) calculateCPUPercent(totalCPUTime int64, state *process
|
|||
// Convert from clock ticks to seconds using actual system clock ticks
|
||||
clockTicks := pm.getClockTicks()
|
||||
cpuSeconds := cpuDelta / clockTicks
|
||||
cpuPercent := (cpuSeconds / timeDelta) * 100.0
|
||||
cpuPercent := (cpuSeconds / timeDelta) * GetConfig().PercentageMultiplier
|
||||
|
||||
// Apply bounds
|
||||
if cpuPercent > maxCPUPercent {
|
||||
|
@ -341,7 +344,7 @@ func (pm *ProcessMonitor) getTotalMemory() int64 {
|
|||
pm.memoryOnce.Do(func() {
|
||||
file, err := os.Open("/proc/meminfo")
|
||||
if err != nil {
|
||||
pm.totalMemory = defaultMemoryGB * 1024 * 1024 * 1024
|
||||
pm.totalMemory = int64(defaultMemoryGB) * 1024 * 1024 * 1024
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
@ -360,7 +363,7 @@ func (pm *ProcessMonitor) getTotalMemory() int64 {
|
|||
break
|
||||
}
|
||||
}
|
||||
pm.totalMemory = defaultMemoryGB * 1024 * 1024 * 1024 // Fallback
|
||||
pm.totalMemory = int64(defaultMemoryGB) * 1024 * 1024 * 1024 // Fallback
|
||||
})
|
||||
return pm.totalMemory
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ func (r *AudioRelay) relayLoop() {
|
|||
r.logger.Error().Msg("Too many consecutive errors, stopping relay")
|
||||
return
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
time.Sleep(GetConfig().ShortSleepDuration)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,7 @@ import (
|
|||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
// Socket buffer sizes optimized for JetKVM's audio workload
|
||||
OptimalSocketBuffer = 128 * 1024 // 128KB (32 frames @ 4KB each)
|
||||
MaxSocketBuffer = 256 * 1024 // 256KB for high-load scenarios
|
||||
MinSocketBuffer = 32 * 1024 // 32KB minimum for basic functionality
|
||||
)
|
||||
// Socket buffer sizes are now centralized in config_constants.go
|
||||
|
||||
// SocketBufferConfig holds socket buffer configuration
|
||||
type SocketBufferConfig struct {
|
||||
|
@ -23,8 +18,8 @@ type SocketBufferConfig struct {
|
|||
// DefaultSocketBufferConfig returns the default socket buffer configuration
|
||||
func DefaultSocketBufferConfig() SocketBufferConfig {
|
||||
return SocketBufferConfig{
|
||||
SendBufferSize: OptimalSocketBuffer,
|
||||
RecvBufferSize: OptimalSocketBuffer,
|
||||
SendBufferSize: GetConfig().SocketOptimalBuffer,
|
||||
RecvBufferSize: GetConfig().SocketOptimalBuffer,
|
||||
Enabled: true,
|
||||
}
|
||||
}
|
||||
|
@ -32,8 +27,8 @@ func DefaultSocketBufferConfig() SocketBufferConfig {
|
|||
// HighLoadSocketBufferConfig returns configuration for high-load scenarios
|
||||
func HighLoadSocketBufferConfig() SocketBufferConfig {
|
||||
return SocketBufferConfig{
|
||||
SendBufferSize: MaxSocketBuffer,
|
||||
RecvBufferSize: MaxSocketBuffer,
|
||||
SendBufferSize: GetConfig().SocketMaxBuffer,
|
||||
RecvBufferSize: GetConfig().SocketMaxBuffer,
|
||||
Enabled: true,
|
||||
}
|
||||
}
|
||||
|
@ -112,20 +107,20 @@ func ValidateSocketBufferConfig(config SocketBufferConfig) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if config.SendBufferSize < MinSocketBuffer {
|
||||
return fmt.Errorf("send buffer size %d is below minimum %d", config.SendBufferSize, MinSocketBuffer)
|
||||
if config.SendBufferSize < GetConfig().SocketMinBuffer {
|
||||
return fmt.Errorf("send buffer size %d is below minimum %d", config.SendBufferSize, GetConfig().SocketMinBuffer)
|
||||
}
|
||||
|
||||
if config.RecvBufferSize < MinSocketBuffer {
|
||||
return fmt.Errorf("receive buffer size %d is below minimum %d", config.RecvBufferSize, MinSocketBuffer)
|
||||
if config.RecvBufferSize < GetConfig().SocketMinBuffer {
|
||||
return fmt.Errorf("receive buffer size %d is below minimum %d", config.RecvBufferSize, GetConfig().SocketMinBuffer)
|
||||
}
|
||||
|
||||
if config.SendBufferSize > MaxSocketBuffer {
|
||||
return fmt.Errorf("send buffer size %d exceeds maximum %d", config.SendBufferSize, MaxSocketBuffer)
|
||||
if config.SendBufferSize > GetConfig().SocketMaxBuffer {
|
||||
return fmt.Errorf("send buffer size %d exceeds maximum %d", config.SendBufferSize, GetConfig().SocketMaxBuffer)
|
||||
}
|
||||
|
||||
if config.RecvBufferSize > MaxSocketBuffer {
|
||||
return fmt.Errorf("receive buffer size %d exceeds maximum %d", config.RecvBufferSize, MaxSocketBuffer)
|
||||
if config.RecvBufferSize > GetConfig().SocketMaxBuffer {
|
||||
return fmt.Errorf("receive buffer size %d exceeds maximum %d", config.RecvBufferSize, GetConfig().SocketMaxBuffer)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -131,7 +131,7 @@ func (s *AudioServerSupervisor) Stop() error {
|
|||
select {
|
||||
case <-s.processDone:
|
||||
s.logger.Info().Msg("audio server process stopped gracefully")
|
||||
case <-time.After(10 * time.Second):
|
||||
case <-time.After(GetConfig().SupervisorTimeout):
|
||||
s.logger.Warn().Msg("audio server process did not stop gracefully, forcing termination")
|
||||
s.forceKillProcess()
|
||||
}
|
||||
|
@ -365,7 +365,7 @@ func (s *AudioServerSupervisor) terminateProcess() {
|
|||
select {
|
||||
case <-done:
|
||||
s.logger.Info().Int("pid", pid).Msg("audio server process terminated gracefully")
|
||||
case <-time.After(5 * time.Second):
|
||||
case <-time.After(GetConfig().InputSupervisorTimeout):
|
||||
s.logger.Warn().Int("pid", pid).Msg("process did not terminate gracefully, sending SIGKILL")
|
||||
s.forceKillProcess()
|
||||
}
|
||||
|
|
|
@ -234,7 +234,7 @@ func (p *ZeroCopyFramePool) GetZeroCopyPoolStats() ZeroCopyFramePoolStats {
|
|||
|
||||
var hitRate float64
|
||||
if totalRequests > 0 {
|
||||
hitRate = float64(hitCount) / float64(totalRequests) * 100
|
||||
hitRate = float64(hitCount) / float64(totalRequests) * GetConfig().PercentageMultiplier
|
||||
}
|
||||
|
||||
return ZeroCopyFramePoolStats{
|
||||
|
|
Loading…
Reference in New Issue