Compare commits

..

1 Commits

Author SHA1 Message Date
Alex f55b2362e9
Merge 0eaad6ba16 into 1717549578 2025-09-13 08:35:38 +02:00
1 changed files with 39 additions and 48 deletions

View File

@ -8,7 +8,7 @@
#include <sys/mman.h> #include <sys/mman.h>
// C state for ALSA/Opus with safety flags // C state for ALSA/Opus with safety flags
static snd_pcm_t *pcm_capture_handle = NULL; static snd_pcm_t *pcm_handle = NULL;
static snd_pcm_t *pcm_playback_handle = NULL; static snd_pcm_t *pcm_playback_handle = NULL;
static OpusEncoder *encoder = NULL; static OpusEncoder *encoder = NULL;
static OpusDecoder *decoder = NULL; static OpusDecoder *decoder = NULL;
@ -29,8 +29,8 @@ static int sleep_microseconds = 1000; // Will be set from Config.CGOUsleepMicr
static int max_attempts_global = 5; // Will be set from Config.CGOMaxAttempts static int max_attempts_global = 5; // Will be set from Config.CGOMaxAttempts
static int max_backoff_us_global = 500000; // Will be set from Config.CGOMaxBackoffMicroseconds static int max_backoff_us_global = 500000; // Will be set from Config.CGOMaxBackoffMicroseconds
// Hardware optimization flags for constrained environments // Hardware optimization flags for constrained environments
static const int use_mmap_access = 1; // Enable MMAP for improved performance static int use_mmap_access = 0; // Disable MMAP for compatibility (was 1)
static const int optimized_buffer_size = 0; // Disable optimized buffer sizing for stability (was 1) static int optimized_buffer_size = 0; // Disable optimized buffer sizing for stability (was 1)
// C function declarations (implementations are below) // C function declarations (implementations are below)
int jetkvm_audio_init(); int jetkvm_audio_init();
@ -242,23 +242,23 @@ int jetkvm_audio_init() {
opus_encoder_destroy(encoder); opus_encoder_destroy(encoder);
encoder = NULL; encoder = NULL;
} }
if (pcm_capture_handle) { if (pcm_handle) {
snd_pcm_close(pcm_capture_handle); snd_pcm_close(pcm_handle);
pcm_capture_handle = NULL; pcm_handle = NULL;
} }
// Try to open ALSA capture device // Try to open ALSA capture device
err = safe_alsa_open(&pcm_capture_handle, "hw:1,0", SND_PCM_STREAM_CAPTURE); err = safe_alsa_open(&pcm_handle, "hw:1,0", SND_PCM_STREAM_CAPTURE);
if (err < 0) { if (err < 0) {
capture_initializing = 0; capture_initializing = 0;
return -1; return -1;
} }
// Configure the device // Configure the device
err = configure_alsa_device(pcm_capture_handle, "capture"); err = configure_alsa_device(pcm_handle, "capture");
if (err < 0) { if (err < 0) {
snd_pcm_close(pcm_capture_handle); snd_pcm_close(pcm_handle);
pcm_capture_handle = NULL; pcm_handle = NULL;
capture_initializing = 0; capture_initializing = 0;
return -1; return -1;
} }
@ -267,7 +267,7 @@ int jetkvm_audio_init() {
int opus_err = 0; int opus_err = 0;
encoder = opus_encoder_create(sample_rate, channels, OPUS_APPLICATION_AUDIO, &opus_err); encoder = opus_encoder_create(sample_rate, channels, OPUS_APPLICATION_AUDIO, &opus_err);
if (!encoder || opus_err != OPUS_OK) { if (!encoder || opus_err != OPUS_OK) {
if (pcm_capture_handle) { snd_pcm_close(pcm_capture_handle); pcm_capture_handle = NULL; } if (pcm_handle) { snd_pcm_close(pcm_handle); pcm_handle = NULL; }
capture_initializing = 0; capture_initializing = 0;
return -2; return -2;
} }
@ -303,12 +303,13 @@ int jetkvm_audio_read_encode(void *opus_buf) {
const int max_recovery_attempts = 3; const int max_recovery_attempts = 3;
// Safety checks // Safety checks
if (!capture_initialized || !pcm_capture_handle || !encoder || !opus_buf) { if (!capture_initialized || !pcm_handle || !encoder || !opus_buf) {
return -1; return -1;
} }
retry_read: retry_read:
int pcm_rc = snd_pcm_readi(pcm_capture_handle, pcm_buffer, frame_size); ;
int pcm_rc = snd_pcm_readi(pcm_handle, pcm_buffer, frame_size);
// Handle ALSA errors with robust recovery strategies // Handle ALSA errors with robust recovery strategies
if (pcm_rc < 0) { if (pcm_rc < 0) {
@ -320,11 +321,11 @@ retry_read:
} }
// Try to recover with prepare // Try to recover with prepare
err = snd_pcm_prepare(pcm_capture_handle); err = snd_pcm_prepare(pcm_handle);
if (err < 0) { if (err < 0) {
// If prepare fails, try drop and prepare // If prepare fails, try drop and prepare
snd_pcm_drop(pcm_capture_handle); snd_pcm_drop(pcm_handle);
err = snd_pcm_prepare(pcm_capture_handle); err = snd_pcm_prepare(pcm_handle);
if (err < 0) return -1; if (err < 0) return -1;
} }
@ -343,16 +344,17 @@ retry_read:
// Try to resume with timeout // Try to resume with timeout
int resume_attempts = 0; int resume_attempts = 0;
while ((err = snd_pcm_resume(pcm_capture_handle)) == -EAGAIN && resume_attempts < 10) { while ((err = snd_pcm_resume(pcm_handle)) == -EAGAIN && resume_attempts < 10) {
usleep(sleep_microseconds); usleep(sleep_microseconds);
resume_attempts++; resume_attempts++;
} }
if (err < 0) { if (err < 0) {
// Resume failed, try prepare as fallback // Resume failed, try prepare as fallback
err = snd_pcm_prepare(pcm_capture_handle); err = snd_pcm_prepare(pcm_handle);
if (err < 0) return -1; if (err < 0) return -1;
} }
// Wait before retry to allow device to stabilize
usleep(sleep_microseconds * recovery_attempts);
return 0; // Skip this frame but don't fail return 0; // Skip this frame but don't fail
} else if (pcm_rc == -ENODEV) { } else if (pcm_rc == -ENODEV) {
// Device disconnected - critical error // Device disconnected - critical error
@ -361,9 +363,10 @@ retry_read:
// I/O error - try recovery once // I/O error - try recovery once
recovery_attempts++; recovery_attempts++;
if (recovery_attempts <= max_recovery_attempts) { if (recovery_attempts <= max_recovery_attempts) {
snd_pcm_drop(pcm_capture_handle); snd_pcm_drop(pcm_handle);
err = snd_pcm_prepare(pcm_capture_handle); err = snd_pcm_prepare(pcm_handle);
if (err >= 0) { if (err >= 0) {
usleep(sleep_microseconds);
goto retry_read; goto retry_read;
} }
} }
@ -371,14 +374,8 @@ retry_read:
} else { } else {
// Other errors - limited retry for transient issues // Other errors - limited retry for transient issues
recovery_attempts++; recovery_attempts++;
if (recovery_attempts <= 1) { if (recovery_attempts <= 1 && (pcm_rc == -EINTR || pcm_rc == -EBUSY)) {
if (pcm_rc == -EINTR) { usleep(sleep_microseconds / 2);
// Signal interrupted - wait for device readiness
snd_pcm_wait(pcm_capture_handle, sleep_microseconds / 1000);
} else if (pcm_rc == -EBUSY) {
// Device busy - brief sleep to let conflict resolve
usleep(sleep_microseconds / 2);
}
goto retry_read; goto retry_read;
} }
return -1; return -1;
@ -473,15 +470,11 @@ int jetkvm_audio_decode_write(void *opus_buf, int opus_size) {
return -1; return -1;
} }
/** // Decode Opus to PCM with error handling
* @note Passing NULL for data and 0 for len is the documented way to indicate int pcm_frames = opus_decode(decoder, in, opus_size, pcm_buffer, frame_size, 0);
* packet loss according to the Opus API documentation.
* @see https://www.opus-codec.org/docs/html_api/group__opusdecoder.html#ga1a8b923c1041ad4976ceada237e117ba
*/
int pcm_frames = opus_decode(decoder, in, opus_size, pcm_buffer, frame_size, 1);
if (pcm_frames < 0) { if (pcm_frames < 0) {
// Try packet loss concealment on decode error // Try packet loss concealment on decode error
pcm_frames = opus_decode(decoder, NULL, 0, pcm_buffer, frame_size, 1); pcm_frames = opus_decode(decoder, NULL, 0, pcm_buffer, frame_size, 0);
if (pcm_frames < 0) return -1; if (pcm_frames < 0) return -1;
} }
@ -507,7 +500,7 @@ retry_write:
} }
// Wait before retry to allow device to stabilize // Wait before retry to allow device to stabilize
snd_pcm_wait(pcm_playback_handle, sleep_microseconds * recovery_attempts / 1000); usleep(sleep_microseconds * recovery_attempts);
goto retry_write; goto retry_write;
} else if (pcm_rc == -ESTRPIPE) { } else if (pcm_rc == -ESTRPIPE) {
// Device suspended, implement robust resume logic // Device suspended, implement robust resume logic
@ -527,6 +520,8 @@ retry_write:
err = snd_pcm_prepare(pcm_playback_handle); err = snd_pcm_prepare(pcm_playback_handle);
if (err < 0) return -2; if (err < 0) return -2;
} }
// Wait before retry to allow device to stabilize
usleep(sleep_microseconds * recovery_attempts);
return 0; // Skip this frame but don't fail return 0; // Skip this frame but don't fail
} else if (pcm_rc == -ENODEV) { } else if (pcm_rc == -ENODEV) {
// Device disconnected - critical error // Device disconnected - critical error
@ -538,6 +533,7 @@ retry_write:
snd_pcm_drop(pcm_playback_handle); snd_pcm_drop(pcm_playback_handle);
err = snd_pcm_prepare(pcm_playback_handle); err = snd_pcm_prepare(pcm_playback_handle);
if (err >= 0) { if (err >= 0) {
usleep(sleep_microseconds);
goto retry_write; goto retry_write;
} }
} }
@ -546,6 +542,7 @@ retry_write:
// Device not ready - brief wait and retry // Device not ready - brief wait and retry
recovery_attempts++; recovery_attempts++;
if (recovery_attempts <= max_recovery_attempts) { if (recovery_attempts <= max_recovery_attempts) {
usleep(sleep_microseconds / 4);
goto retry_write; goto retry_write;
} }
return -2; return -2;
@ -553,13 +550,7 @@ retry_write:
// Other errors - limited retry for transient issues // Other errors - limited retry for transient issues
recovery_attempts++; recovery_attempts++;
if (recovery_attempts <= 1 && (pcm_rc == -EINTR || pcm_rc == -EBUSY)) { if (recovery_attempts <= 1 && (pcm_rc == -EINTR || pcm_rc == -EBUSY)) {
if (pcm_rc == -EINTR) { usleep(sleep_microseconds / 2);
// Signal interrupted - wait for device readiness
snd_pcm_wait(pcm_playback_handle, sleep_microseconds / 1000);
} else if (pcm_rc == -EBUSY) {
// Device busy - brief sleep to let conflict resolve
usleep(sleep_microseconds / 2);
}
goto retry_write; goto retry_write;
} }
return -2; return -2;
@ -608,9 +599,9 @@ void jetkvm_audio_close() {
opus_encoder_destroy(encoder); opus_encoder_destroy(encoder);
encoder = NULL; encoder = NULL;
} }
if (pcm_capture_handle) { if (pcm_handle) {
snd_pcm_drain(pcm_capture_handle); snd_pcm_drain(pcm_handle);
snd_pcm_close(pcm_capture_handle); snd_pcm_close(pcm_handle);
pcm_capture_handle = NULL; pcm_handle = NULL;
} }
} }