mirror of https://github.com/jetkvm/kvm.git
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:
parent
f51f6da2de
commit
dfbf9249b9
|
@ -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))
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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().
|
||||||
|
|
Loading…
Reference in New Issue