From 25363cef900153732d5cdc29ac3cf14dfda26f30 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 27 Aug 2025 22:44:34 +0000 Subject: [PATCH] perf(audio): replace frame validation with ultra-fast version Use ValidateAudioFrameUltraFast in critical audio paths to reduce processing overhead Reduce minimum frame size to 1 byte to allow smaller frames --- internal/audio/config_constants.go | 2 +- internal/audio/input.go | 4 +-- internal/audio/input_ipc.go | 6 ++--- internal/audio/input_ipc_manager.go | 2 +- internal/audio/ipc.go | 4 +-- internal/audio/relay.go | 4 +-- internal/audio/validation.go | 41 +++++++++-------------------- internal/audio/validation_test.go | 35 +++++------------------- 8 files changed, 31 insertions(+), 67 deletions(-) diff --git a/internal/audio/config_constants.go b/internal/audio/config_constants.go index bc4e7a4..3f1c617 100644 --- a/internal/audio/config_constants.go +++ b/internal/audio/config_constants.go @@ -2388,7 +2388,7 @@ func DefaultAudioConfig() *AudioConfigConstants { // Validation Configuration MaxValidationTime: 5 * time.Second, // 5s maximum validation timeout - MinFrameSize: 64, // 64 bytes minimum frame size + MinFrameSize: 1, // 1 byte minimum frame size (allow small frames) FrameSizeTolerance: 512, // 512 bytes frame size tolerance // Device Health Monitoring Configuration diff --git a/internal/audio/input.go b/internal/audio/input.go index f9cca8a..192e367 100644 --- a/internal/audio/input.go +++ b/internal/audio/input.go @@ -83,8 +83,8 @@ func (aim *AudioInputManager) WriteOpusFrame(frame []byte) error { return nil // Not running, silently drop } - // Validate frame before processing - if err := ValidateFrameData(frame); err != nil { + // Use ultra-fast validation for critical audio path + if err := ValidateAudioFrameUltraFast(frame); err != nil { aim.logComponentError(AudioInputManagerComponent, err, "Frame validation failed") return fmt.Errorf("input frame validation failed: %w", err) } diff --git a/internal/audio/input_ipc.go b/internal/audio/input_ipc.go index 1dfc19c..a7cece1 100644 --- a/internal/audio/input_ipc.go +++ b/internal/audio/input_ipc.go @@ -477,8 +477,8 @@ func (ais *AudioInputServer) processOpusFrame(data []byte) error { return nil // Empty frame, ignore } - // Validate frame data before processing - if err := ValidateFrameData(data); err != nil { + // Use ultra-fast validation for critical audio path + if err := ValidateAudioFrameUltraFast(data); err != nil { logger := logging.GetDefaultLogger().With().Str("component", AudioInputServerComponent).Logger() logger.Error().Err(err).Msg("Frame validation failed") return fmt.Errorf("input frame validation failed: %w", err) @@ -635,7 +635,7 @@ func (aic *AudioInputClient) SendFrame(frame []byte) error { } // Validate frame data before sending - if err := ValidateFrameData(frame); err != nil { + if err := ValidateAudioFrameUltraFast(frame); err != nil { logger := logging.GetDefaultLogger().With().Str("component", AudioInputClientComponent).Logger() logger.Error().Err(err).Msg("Frame validation failed") return fmt.Errorf("input frame validation failed: %w", err) diff --git a/internal/audio/input_ipc_manager.go b/internal/audio/input_ipc_manager.go index 5e39bf0..7ac0c49 100644 --- a/internal/audio/input_ipc_manager.go +++ b/internal/audio/input_ipc_manager.go @@ -103,7 +103,7 @@ func (aim *AudioInputIPCManager) WriteOpusFrame(frame []byte) error { } // Validate frame data - if err := ValidateFrameData(frame); err != nil { + if err := ValidateAudioFrameUltraFast(frame); err != nil { atomic.AddInt64(&aim.metrics.FramesDropped, 1) aim.logger.Debug().Err(err).Msg("invalid frame data") return err diff --git a/internal/audio/ipc.go b/internal/audio/ipc.go index fd92164..2cd2efb 100644 --- a/internal/audio/ipc.go +++ b/internal/audio/ipc.go @@ -260,8 +260,8 @@ func (s *AudioOutputServer) Close() error { } func (s *AudioOutputServer) SendFrame(frame []byte) error { - // Comprehensive frame validation - if err := ValidateFrameData(frame); err != nil { + // Use ultra-fast validation for critical audio path + if err := ValidateAudioFrameUltraFast(frame); err != nil { logger := logging.GetDefaultLogger().With().Str("component", AudioOutputServerComponent).Logger() logger.Error().Err(err).Msg("Frame validation failed") return fmt.Errorf("output frame validation failed: %w", err) diff --git a/internal/audio/relay.go b/internal/audio/relay.go index 3e9f7c4..0913599 100644 --- a/internal/audio/relay.go +++ b/internal/audio/relay.go @@ -170,8 +170,8 @@ func (r *AudioRelay) relayLoop() { // forwardToWebRTC forwards a frame to the WebRTC audio track func (r *AudioRelay) forwardToWebRTC(frame []byte) error { - // Validate frame data before processing - if err := ValidateFrameData(frame); err != nil { + // Use ultra-fast validation for critical audio path + if err := ValidateAudioFrameUltraFast(frame); err != nil { r.incrementDropped() r.logger.Debug().Err(err).Msg("invalid frame data in relay") return err diff --git a/internal/audio/validation.go b/internal/audio/validation.go index 626b910..ec9daf5 100644 --- a/internal/audio/validation.go +++ b/internal/audio/validation.go @@ -35,34 +35,6 @@ func ValidateAudioQuality(quality AudioQuality) error { return nil } -// ValidateFrameData validates audio frame data with comprehensive boundary checks -func ValidateFrameData(data []byte) error { - if data == nil { - return fmt.Errorf("%w: frame data is nil", ErrInvalidFrameData) - } - if len(data) == 0 { - return fmt.Errorf("%w: frame data is empty", ErrInvalidFrameData) - } - - config := GetConfig() - // Check minimum frame size - if len(data) < config.MinFrameSize { - return fmt.Errorf("%w: frame size %d below minimum %d", - ErrInvalidFrameSize, len(data), config.MinFrameSize) - } - // Check maximum frame size - if len(data) > config.MaxAudioFrameSize { - return fmt.Errorf("%w: frame size %d exceeds maximum %d", - ErrInvalidFrameSize, len(data), config.MaxAudioFrameSize) - } - // Validate frame alignment for audio samples (must be even for 16-bit samples) - if len(data)%2 != 0 { - return fmt.Errorf("%w: frame size %d not aligned for 16-bit samples", - ErrInvalidFrameSize, len(data)) - } - return nil -} - // ValidateZeroCopyFrame validates zero-copy audio frame func ValidateZeroCopyFrame(frame *ZeroCopyAudioFrame) error { if frame == nil { @@ -327,6 +299,8 @@ func ValidateAudioConfigConstants(config *AudioConfigConstants) error { } // ValidateAudioFrameFast performs fast validation of audio frame data +// ValidateAudioFrameFast provides minimal validation for critical audio processing paths +// This function is optimized for performance and only checks essential safety bounds func ValidateAudioFrameFast(data []byte) error { if len(data) == 0 { return fmt.Errorf("%w: frame data is empty", ErrInvalidFrameData) @@ -338,6 +312,17 @@ func ValidateAudioFrameFast(data []byte) error { return nil } +// ValidateAudioFrameUltraFast provides zero-overhead validation for ultra-critical paths +// This function only checks for nil/empty data and maximum size to prevent buffer overruns +// Use this in hot audio processing loops where every microsecond matters +func ValidateAudioFrameUltraFast(data []byte) error { + // Only check for catastrophic failures that could crash the system + if len(data) == 0 || len(data) > 8192 { // Hard-coded 8KB safety limit + return ErrInvalidFrameData + } + return nil +} + // WrapWithMetadata wraps error with metadata for enhanced validation context func WrapWithMetadata(err error, component, operation string, metadata map[string]interface{}) error { if err == nil { diff --git a/internal/audio/validation_test.go b/internal/audio/validation_test.go index 8803df1..d24d4b2 100644 --- a/internal/audio/validation_test.go +++ b/internal/audio/validation_test.go @@ -62,46 +62,25 @@ func testAudioQualityValidation(t *testing.T) { } } -// testFrameDataValidation tests frame data validation with various edge cases +// testFrameDataValidation tests frame data validation with various edge cases using modern validation func testFrameDataValidation(t *testing.T) { config := GetConfig() - // Test nil data - err := ValidateFrameData(nil) - assert.Error(t, err) - assert.Contains(t, err.Error(), "frame data is nil") - // Test empty data - err = ValidateFrameData([]byte{}) + err := ValidateAudioFrameFast([]byte{}) assert.Error(t, err) assert.Contains(t, err.Error(), "frame data is empty") - // Test data below minimum size - if config.MinFrameSize > 0 { - smallData := make([]byte, config.MinFrameSize-1) - err = ValidateFrameData(smallData) - assert.Error(t, err) - assert.Contains(t, err.Error(), "below minimum") - } - // Test data above maximum size largeData := make([]byte, config.MaxAudioFrameSize+1) - err = ValidateFrameData(largeData) + err = ValidateAudioFrameFast(largeData) assert.Error(t, err) assert.Contains(t, err.Error(), "exceeds maximum") - // Test unaligned data (odd number of bytes for 16-bit samples) - oddData := make([]byte, 1001) // Odd number - if len(oddData) >= config.MinFrameSize { - err = ValidateFrameData(oddData) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not aligned") - } - - // Test valid aligned data - validData := make([]byte, 1000) // Even number, within bounds - if len(validData) >= config.MinFrameSize && len(validData) <= config.MaxAudioFrameSize { - err = ValidateFrameData(validData) + // Test valid data + validData := make([]byte, 1000) // Within bounds + if len(validData) <= config.MaxAudioFrameSize { + err = ValidateAudioFrameFast(validData) assert.NoError(t, err) } }