fix(audio): improve audio initialization and process termination handling

- Add retry logic for CGO audio initialization
- Enhance process termination checks in input supervisor
- Skip initialization check in cgo_audio to avoid compilation issues
- Add proper error handling for audio system initialization failures
This commit is contained in:
Alex P 2025-09-01 10:24:26 +00:00
parent f51f6da2de
commit dfbf9249b9
3 changed files with 36 additions and 8 deletions

View File

@ -709,6 +709,9 @@ func cgoAudioReadEncode(buf []byte) (int, error) {
return 0, newBufferTooSmallError(len(buf), minRequired) return 0, newBufferTooSmallError(len(buf), minRequired)
} }
// Skip initialization check for now to avoid CGO compilation issues
// TODO: Add proper initialization state checking
n := C.jetkvm_audio_read_encode(unsafe.Pointer(&buf[0])) n := C.jetkvm_audio_read_encode(unsafe.Pointer(&buf[0]))
if n < 0 { if n < 0 {
return 0, newAudioReadEncodeError(int(n)) return 0, newAudioReadEncodeError(int(n))

View File

@ -141,7 +141,8 @@ func (ais *AudioInputSupervisor) Stop() {
// Try graceful termination first // Try graceful termination first
if ais.cmd != nil && ais.cmd.Process != nil { if ais.cmd != nil && ais.cmd.Process != nil {
ais.logger.Info().Int("pid", ais.cmd.Process.Pid).Msg("Stopping audio input server subprocess") pid := ais.cmd.Process.Pid
ais.logger.Info().Int("pid", pid).Msg("Stopping audio input server subprocess")
// Send SIGTERM // Send SIGTERM
err := ais.cmd.Process.Signal(syscall.SIGTERM) err := ais.cmd.Process.Signal(syscall.SIGTERM)
@ -161,10 +162,18 @@ func (ais *AudioInputSupervisor) Stop() {
case <-time.After(GetConfig().InputSupervisorTimeout): case <-time.After(GetConfig().InputSupervisorTimeout):
// Force kill if graceful shutdown failed // Force kill if graceful shutdown failed
ais.logger.Warn().Msg("Audio input server subprocess did not stop gracefully, force killing") ais.logger.Warn().Msg("Audio input server subprocess did not stop gracefully, force killing")
// Check if process is still alive before attempting to kill
if ais.cmd != nil && ais.cmd.Process != nil {
// Check process state to avoid "process already finished" error
if ais.cmd.ProcessState == nil {
err := ais.cmd.Process.Kill() err := ais.cmd.Process.Kill()
if err != nil { if err != nil {
ais.logger.Error().Err(err).Msg("Failed to kill audio input server subprocess") ais.logger.Error().Err(err).Msg("Failed to kill audio input server subprocess")
} }
} else {
ais.logger.Info().Msg("Audio input server subprocess already finished")
}
}
} }
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"runtime" "runtime"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -311,10 +312,18 @@ func StartAudioOutputStreaming(send func([]byte)) error {
return ErrAudioAlreadyRunning return ErrAudioAlreadyRunning
} }
// Initialize CGO audio capture // Initialize CGO audio capture with retry logic
if err := CGOAudioInit(); err != nil { var initErr error
for attempt := 0; attempt < 3; attempt++ {
if initErr = CGOAudioInit(); initErr == nil {
break
}
getOutputStreamingLogger().Warn().Err(initErr).Int("attempt", attempt+1).Msg("Audio initialization failed, retrying")
time.Sleep(time.Duration(attempt+1) * 100 * time.Millisecond)
}
if initErr != nil {
atomic.StoreInt32(&outputStreamingRunning, 0) atomic.StoreInt32(&outputStreamingRunning, 0)
return err return fmt.Errorf("failed to initialize audio after 3 attempts: %w", initErr)
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -341,7 +350,7 @@ func StartAudioOutputStreaming(send func([]byte)) error {
case <-ctx.Done(): case <-ctx.Done():
return return
default: default:
// Capture audio frame with enhanced error handling // Capture audio frame with enhanced error handling and initialization checking
n, err := CGOAudioReadEncode(buffer) n, err := CGOAudioReadEncode(buffer)
if err != nil { if err != nil {
consecutiveErrors++ consecutiveErrors++
@ -350,6 +359,13 @@ func StartAudioOutputStreaming(send func([]byte)) error {
Int("consecutive_errors", consecutiveErrors). Int("consecutive_errors", consecutiveErrors).
Msg("Failed to read/encode audio") Msg("Failed to read/encode audio")
// Check if this is an initialization error (C error code -1)
if strings.Contains(err.Error(), "C error code -1") {
getOutputStreamingLogger().Error().Msg("Audio system not initialized properly, forcing reinitialization")
// Force immediate reinitialization for init errors
consecutiveErrors = maxConsecutiveErrors
}
// Implement progressive backoff for consecutive errors // Implement progressive backoff for consecutive errors
if consecutiveErrors >= maxConsecutiveErrors { if consecutiveErrors >= maxConsecutiveErrors {
getOutputStreamingLogger().Error(). getOutputStreamingLogger().Error().