Skip to content

Commit a6acc43

Browse files
pennycodersclaude
andcommitted
Add automatic HDMI sample rate change detection
Detects when HDMI source changes audio sample rate (44.1kHz ↔ 48kHz) and triggers automatic reconnection with zero overhead in happy path. Implementation: - Added is_capture parameter to handle_alsa_error() - On 2nd -EPIPE error in capture path, query TC358743 for current rate - If rate changed, log error and return -1 to trigger reconnection - OutputRelay's existing retry logic handles full reinitialization When sample rate changes occur: - Wrong hardware_frame_size causes immediate -EPIPE errors - Detection happens within 40-60ms (2-3 frames) - Automatic recovery via OutputRelay reconnection - No manual RestartAudioOutput() needed Overhead analysis: - Happy path: ZERO overhead (no checks when audio working) - Error path: ~100-500μs ioctl on 2nd failure only - Only checks once per error sequence (not repeatedly) Handles real-world scenarios: - User switches HDMI source device - Source changes video resolution with different audio format - HDMI hotplug/unplug events - Source switches between PCM 44.1kHz and 48kHz 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 357e2e6 commit a6acc43

File tree

1 file changed

+17
-3
lines changed

1 file changed

+17
-3
lines changed

internal/audio/c/audio.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ static inline void swap_stereo_channels(int16_t *buffer, uint16_t num_frames) {
365365
* @param recovery_attempts Pointer to uint8_t recovery attempt counter
366366
* @param sleep_ms Milliseconds to sleep during recovery
367367
* @param max_attempts Maximum recovery attempts allowed
368+
* @param is_capture True for capture path (enables sample rate change detection)
368369
* @return Return codes:
369370
* 1 = Retry operation (error was recovered)
370371
* 0 = Skip this frame and continue
@@ -377,12 +378,25 @@ static inline void swap_stereo_channels(int16_t *buffer, uint16_t num_frames) {
377378
static int handle_alsa_error(snd_pcm_t *handle, snd_pcm_t **valid_handle,
378379
atomic_int *stop_flag,
379380
int pcm_rc, uint8_t *recovery_attempts,
380-
uint32_t sleep_ms, uint8_t max_attempts) {
381+
uint32_t sleep_ms, uint8_t max_attempts, bool is_capture) {
381382
int err;
382383

383384
if (pcm_rc == -EPIPE) {
384385
// Buffer underrun/overrun
385386
(*recovery_attempts)++;
387+
388+
// Check if HDMI sample rate changed (only for capture path on 2nd failure)
389+
if (is_capture && *recovery_attempts == 2) {
390+
unsigned int current_rate = get_hdmi_audio_sample_rate();
391+
if (current_rate != 0 && current_rate != hardware_sample_rate) {
392+
fprintf(stderr, "ERROR: capture: HDMI sample rate changed from %u to %u Hz\n",
393+
hardware_sample_rate, current_rate);
394+
fprintf(stderr, " Reinitialization required - triggering reconnection\n");
395+
fflush(stderr);
396+
return -1;
397+
}
398+
}
399+
386400
if (*recovery_attempts > max_attempts || handle != *valid_handle) {
387401
return -1;
388402
}
@@ -841,7 +855,7 @@ __attribute__((hot)) int jetkvm_audio_read_encode(void * __restrict__ opus_buf)
841855
if (__builtin_expect(pcm_rc < 0, 0)) {
842856
int err_result = handle_alsa_error(handle, &pcm_capture_handle, &capture_stop_requested,
843857
pcm_rc, &recovery_attempts,
844-
sleep_milliseconds, max_recovery_attempts);
858+
sleep_milliseconds, max_recovery_attempts, true);
845859
if (err_result == 1) {
846860
// Recovery successful, retry (mutex still held)
847861
goto retry_read;
@@ -1067,7 +1081,7 @@ __attribute__((hot)) int jetkvm_audio_decode_write(void * __restrict__ opus_buf,
10671081
if (__builtin_expect(pcm_rc < 0, 0)) {
10681082
int err_result = handle_alsa_error(handle, &pcm_playback_handle, &playback_stop_requested,
10691083
pcm_rc, &recovery_attempts,
1070-
sleep_milliseconds, max_recovery_attempts);
1084+
sleep_milliseconds, max_recovery_attempts, false);
10711085
if (err_result == 1) {
10721086
// Recovery successful, retry (mutex still held)
10731087
goto retry_write;

0 commit comments

Comments
 (0)