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)
}
// 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]))
if n < 0 {
return 0, newAudioReadEncodeError(int(n))

View File

@ -141,7 +141,8 @@ func (ais *AudioInputSupervisor) Stop() {
// Try graceful termination first
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
err := ais.cmd.Process.Signal(syscall.SIGTERM)
@ -161,10 +162,18 @@ func (ais *AudioInputSupervisor) Stop() {
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")
// 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()
if err != nil {
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"
"fmt"
"runtime"
"strings"
"sync"
"sync/atomic"
"time"
@ -311,10 +312,18 @@ func StartAudioOutputStreaming(send func([]byte)) error {
return ErrAudioAlreadyRunning
}
// Initialize CGO audio capture
if err := CGOAudioInit(); err != nil {
// Initialize CGO audio capture with retry logic
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)
return err
return fmt.Errorf("failed to initialize audio after 3 attempts: %w", initErr)
}
ctx, cancel := context.WithCancel(context.Background())
@ -341,7 +350,7 @@ func StartAudioOutputStreaming(send func([]byte)) error {
case <-ctx.Done():
return
default:
// Capture audio frame with enhanced error handling
// Capture audio frame with enhanced error handling and initialization checking
n, err := CGOAudioReadEncode(buffer)
if err != nil {
consecutiveErrors++
@ -350,6 +359,13 @@ func StartAudioOutputStreaming(send func([]byte)) error {
Int("consecutive_errors", consecutiveErrors).
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
if consecutiveErrors >= maxConsecutiveErrors {
getOutputStreamingLogger().Error().