Commit Graph

736 Commits

Author SHA1 Message Date
Alex P 051950f220 Fix critical deadlock when switching audio sources
Problem:
When switching audio sources (USB to HDMI or vice versa), the application
would hang indefinitely. This was caused by a deadlock between Go and C
layers:

1. Main thread calls SetAudioOutputSource() → stopOutputAudio()
2. stopOutputAudio() calls outputRelay.Stop() which waits for goroutine
3. Goroutine is blocked in ReadMessage() holding Go mutex
4. ReadMessage() calls blocking C function jetkvm_audio_read_encode()
5. C function is blocked reading from ALSA device
6. Disconnect() can't acquire Go mutex to clean up
7. Deadlock: Main thread waiting for goroutine, goroutine waiting for ALSA

Solution:
Release the Go mutex BEFORE calling blocking C functions in ReadMessage()
and WriteMessage(). The C layer has its own pthread mutex protection and
handles stop requests via atomic flags. This allows:
- Disconnect() to acquire the mutex immediately
- C layer to detect stop request and return quickly
- Goroutines to exit cleanly
- Audio source switching to work flawlessly

Fixes:
- internal/audio/cgo_source.go:ReadMessage() - Release mutex before C call
- internal/audio/cgo_source.go:WriteMessage() - Release mutex before C call

This fix eliminates the hang when switching between USB and HDMI audio
sources.
2025-11-18 01:41:46 +02:00
Alex a305217e04
Merge branch 'dev' into feat/audio-support 2025-11-18 01:36:46 +02:00
Alex P 437a63d7d4 Add bounds check for Opus packets in Go layer
Validate packet size <= 1500 bytes before passing to C code to provide
defense-in-depth alongside existing C-layer validation.
2025-11-18 01:22:15 +02:00
Alex P 6a7f9e9996 Fix race condition in setAudioTrack by using single critical section
Reduced mutex locking from 3 separate lock/unlock cycles to 1, eliminating
race window and improving performance. New relay is prepared within mutex,
then old resources are stopped and new relay started outside mutex to avoid
blocking during CGO calls.
2025-11-18 01:15:51 +02:00
Alex P d7dc8c2075 Update audio.c header comment to reflect variable sample rate 2025-11-18 00:54:04 +02:00
Alex P a6c5e46a61 Align inline comments in Config struct 2025-11-18 00:53:15 +02:00
Alex P 2b276e1ba5 Remove unused update_opus_encoder_params function 2025-11-18 00:53:15 +02:00
Alex P 94cab8b2ac Fix: prevent race condition crash in audio playback using pthread mutexes
Root cause: ALSA assertion failure `snd_pcm_writei: Assertion 'pcm' failed`
when pcm_playback_handle became NULL during concurrent write operations.

The crash occurred because:
1. Thread A checks pcm_playback_handle != NULL (passes)
2. Thread B calls jetkvm_audio_playback_close(), sets handle = NULL
3. Thread A calls snd_pcm_writei(NULL, ...) → SIGABRT

Solution: Added pthread mutexes to protect concurrent access:
- playback_mutex protects pcm_playback_handle in decode_write and close
- capture_mutex protects pcm_capture_handle in read_encode and close

All critical sections now acquire mutex before accessing ALSA handles,
preventing the NULL pointer from being passed to ALSA functions.
2025-11-18 00:53:15 +02:00
Alex P 3f141c8e9d Add log files and development artifacts to gitignore 2025-11-18 00:53:15 +02:00
Alex P 9c57fe86c4 Change: make sample rate read-only and auto-detected
The sample rate cannot be configured by users - it's determined by the audio
source (HDMI device or USB gadget client). The previous UI gave the false
impression that users could select a sample rate, but the value was always
overridden by hardware detection.

Changes:
- Convert sample rate UI from dropdown to read-only display
- Show "(auto-detected from source)" label next to the value
- Remove sampleRate parameter from setAudioConfig RPC
- Update translations to clarify auto-detection
- Backend sample rate validation remains for backwards compatibility

The C code now automatically detects and adapts to whatever rate the hardware
supports, creating the Opus encoder/decoder with matching parameters to
eliminate pitch/speed distortion.
2025-11-18 00:53:15 +02:00
Alex P 236291a454 Fix: eliminate audio warping by auto-adapting to actual device sample rate
Root cause: ALSA was silently using a different sample rate than configured,
causing severe pitch/speed distortion (the "cassette player" warping effect).

The bug occurred when:
- User configured 48 kHz in UI
- HDMI source output 44.1 kHz audio
- set_rate() failed, set_rate_near() chose 44.1 kHz
- Code never checked what rate was actually set
- Opus encoder created for 48 kHz but received 44.1 kHz audio
- Result: ~9% pitch shift and timing mismatch

Fix:
- Always use set_rate_near() and check the actual rate returned
- Pass detected rate and frame size to Opus encoder/decoder creation
- Avoid modifying global state to prevent capture/playback interference
- Recalculate frame_size for 20ms at the actual rate
- Verify rate after hw_params application
- Add detailed logging for rate adaptation

This ensures Opus encoder/decoder use the correct rate matching the hardware,
regardless of what the HDMI source outputs.
2025-11-18 00:53:15 +02:00
Alex P 11dadebb93 Fix: improve EDID compatibility and add audio configuration options
- Update default EDID with registered manufacturer ID (Dell) and proper 24-inch display dimensions (52x32cm) for better macOS/OS compatibility
- Add configurable sample rate (32/44.1/48/96 kHz) to support different HDMI audio sources
- Add packet loss compensation percentage control for FEC overhead tuning
- Fix config migration to ensure new audio parameters get defaults for existing configs
- Update all language translations for new audio settings
2025-11-18 00:53:15 +02:00
Alex P df76cd0a3e Fix: reopen HID files on USB state transitions to prevent stale handles
When USB state transitions to "configured" (after reconnection or state
changes), automatically close and reopen HID file handles to ensure they
remain valid.
2025-11-17 23:18:58 +02:00
Alex P 9f0d9c4689 Fix: skip redundant USB gadget reconfigurations to prevent HID disruption
- Add Devices.Equals() method to compare USB device configurations
- Add GetGadgetDevices() to retrieve current device state
- Skip gadget reconfiguration when device state is unchanged
- Remove 6 unused audio translation keys from all language files
2025-11-17 22:48:02 +02:00
Alex P 9371868b14 Fix: increase Opus buffer size to 1500 bytes and add bounds check 2025-11-17 22:48:02 +02:00
Alex P 1e22e007ea Auto-switch audio output to HDMI when USB audio emulation is disabled 2025-11-17 22:48:02 +02:00
Alex P 0fe9cab988 Remove unused audio_settings_input translations 2025-11-17 22:48:02 +02:00
Alex 5b5fc40a08 Merge branch 'dev' into feat/audio-support 2025-11-17 22:19:44 +02:00
Alex P 922a7158e7 Add runtime configurable audio parameters with UI controls
- Add config fields for bitrate, complexity, DTX, FEC, buffer periods
- Add RPC methods for get/set audio config and restart
- Add UI settings page with controls for all audio parameters
- Add Apply Settings button to restart audio with new config
- Add config migration for backwards compatibility
- Add translations for all 9 languages
- Clean up redundant comments and optimize log levels
2025-11-17 21:51:08 +02:00
Alex P e79c6f730e Add audio output source switching and improve shutdown handling
- Add HDMI/USB audio source selection in settings UI
- Add stop flags for graceful audio shutdown
- Use immediate PCM drop instead of drain for faster switching
- Add HDMI connection refresh RPC method
- Add GetDefaultEDID helper method
2025-11-17 20:45:34 +02:00
Alex P 9e69a0cb97 Fix: set EDID before video init to ensure audio capability detection 2025-11-17 17:36:53 +02:00
Nitish Agarwal d49e2680d0
fix: show HDMI overlays in fullscreen mode (#974) 2025-11-17 14:28:28 +01:00
Nitish Agarwal e0b1d5fee3 fix: show HDMI overlays in fullscreen mode (#974) 2025-11-17 14:28:28 +01:00
Alex P 17f52414ac Fix: increase ALSA buffer size to reduce audio crackling
Increase buffer from 80ms (4 periods) to 240ms (12 periods) for
better jitter tolerance on USB gadget audio capture.
2025-11-17 13:50:21 +02:00
Alex P 400b3473ae Fix: add stub implementations for non-ARM platforms to fix lint
Adds cgo_source_stub.go with inverse build tag (!linux || (!arm && !arm64))
to provide stub function signatures for CI linting on amd64 runners.
2025-11-17 12:33:07 +02:00
Nitish Agarwal e293edb069
fix: normalize Unicode characters in paste modal for proper detection (#976) 2025-11-17 10:15:03 +01:00
Nitish Agarwal cedd086602 fix: normalize Unicode characters in paste modal for proper detection (#976) 2025-11-17 10:15:03 +01:00
Alex P 31903c4b61 Fix: extend audio mutex to prevent concurrent initialization crashes
Previous fix protected WriteMessage() but missed Connect() calls. During
session transfers, multiple goroutines could simultaneously initialize the
audio playback system, causing SIGABRT crashes in jetkvm_audio_playback_init.

Extended inputSourceMutex to serialize both Connect() and WriteMessage()
operations, ensuring atomic state transitions and preventing concurrent
C library initialization.
2025-11-14 10:21:15 +02:00
Adam Shiervani b074462ee7
feat: wait for channel to open before triggering initial state updates (#963) 2025-11-13 10:48:03 +01:00
Adam Shiervani d3a3846aa1 feat: wait for channel to open before triggering initial state updates (#963) 2025-11-13 10:48:03 +01:00
Alex P 33ee474865 Fix: prevent concurrent Opus decoder access during session transfers
During session transfers, multiple handleInputTrackForSession goroutines
could call WriteMessage() concurrently on the shared inputSource, causing
Opus SILK decoder state corruption and assertion failures.

Added inputSourceMutex to serialize WriteMessage() calls and prevent
race conditions in both input and output audio paths.
2025-11-13 10:58:47 +02:00
Alex P abe61c8959 Fixes / Updates: Update outside mutex comment, move outputSource, outputRelay assignments 2025-11-12 09:07:22 +02:00
Alex P 22a16c7f17 Fix: coredump issue 2025-11-11 22:41:46 +02:00
Alex P 98d20d4ffa Merge branch 'dev' into feat/audio-support 2025-11-11 21:40:29 +02:00
Aveline 5fb4c629dd
feat: failsafe mode (#952)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Adam Shiervani <adam.shiervani@gmail.com>
Co-authored-by: Marc Brooks <IDisposable@gmail.com>
2025-11-11 13:13:42 +01:00
Aveline cf629bed46
feat: add remote IP address to connection stats sidebar (#944) 2025-11-11 12:00:37 +01:00
Alex P 0e63e6d5ed Updates: add logging on error, revert Makefile lint targets 2025-11-11 11:25:49 +02:00
Alex P 0514a70913 Enhancement: Make the code more DRY 2025-11-11 11:18:12 +02:00
Alex P 82977ab76c Enhancement: Make the code more DRY 2025-11-10 19:09:22 +02:00
Alex P 3261efaeb7 fix: prevent audio disconnect from blocking new WebRTC sessions
When an old connection closed while a new one started, the audio cleanup
would hold audioMutex for up to 37 seconds during CGO disconnect calls,
blocking the new session from initializing.

Use capture-clear-release pattern to minimize mutex hold time:
- Capture references to sources/relays while holding mutex
- Clear globals immediately so new sessions can proceed
- Release mutex before calling blocking Stop/Disconnect operations

This eliminates the 37-second hang during connection transitions.
2025-11-10 16:54:31 +02:00
Marc Brooks 31ea366e51
chore: upgrade UI packages (except vite) (#954) 2025-11-10 14:53:18 +01:00
Alex P 522f6cf489 Revert "refactor: use atomic.Pointer for thread-safe inputSource access"
This reverts commit 41345b0527.
2025-11-10 15:42:10 +02:00
Marc Brooks 7f2dcc84b4
fix: don't reload page if we didn't attempt an upgrade. (#955) 2025-11-10 12:58:57 +01:00
Adam Shiervani c34440b43f refactor: fix infinite useEffect 2025-11-10 09:43:10 +01:00
Alex P f20fac7dd6 fix: improve microphone handling and cleanup
- Use mono audio instead of stereo for microphone input
- Add cleanup to stop audio track on component unmount
- Explicitly set AudioTrack to nil when creation fails
2025-11-09 13:39:38 +02:00
Alex P af0e0441a8 refactor: use atomic.Pointer for thread-safe inputSource access
- Replace mutex-protected inputSource with atomic.Pointer for lock-free reads
- Eliminate ~100 mutex operations per second in audio packet hot path
- Add defer pattern for safer mutex unlock in enable/disable functions
- Add error logging for audio write failures
- Consolidate Makefile lint commands for brevity
2025-11-08 00:43:09 +02:00
Marc Brooks 6e1b84f39b
chore: clean up logger messaging 2025-11-07 13:21:38 +01:00
Alex P f3fc4f4669 fix: improve microphone reliability and auto-enable behavior
- Prevent concurrent getUserMedia requests to fix toggle flakiness
- Add automatic track recovery when microphone track ends unexpectedly
- Fix auto-enable to only trigger on session start, not when toggling setting
- Ensure backend RPC is sent when auto-enable triggers
- Properly clean up old tracks before requesting new ones
2025-11-07 14:20:59 +02:00
tadic-luka 36f06a064a
feat: add web route for sending WOL package to given mac addr (#945)
* feat: add web route for sending WOL package to given mac addr

```

adds a new route /device/send-wol/:mac-addr to send the magic WOL package
to the specified mac-addr.
Method is POST and is protected.

Useful for custom wake up scripts: example is sending HTTP request through iOS shortcut

Test plan:
calling the API with curl
```
$ curl -X POST   http://<jetkvm-ip>/device/send-wol/xx:xx:xx:xx:xx:xx
WOL sent to xx:xx:xx:xx:xx:xx
```

and observing the magic packet on my laptop/PC:
```
$ ncat -u -l 9 -k | xxd
00000000: ffff ffff ffff d050 9978 a620 d050 9978  .......P.x. .P.x
00000010: a620 d050 9978 a620 d050 9978 a620 d050  . .P.x. .P.x. .P
00000020: 9978 a620 d050 9978 a620 d050 9978 a620  .x. .P.x. .P.x.
00000030: d050 9978 a620 d050 9978 a620 d050 9978  .P.x. .P.x. .P.x
00000040: a620 d050 9978 a620 d050 9978 a620 d050  . .P.x. .P.x. .P
00000050: 9978 a620 d050 9978 a620 d050 9978 a620  .x. .P.x. .P.x.
```

calling the api with invalid mac addr returns HTTP 400 error
```
$ curl -X POST -v http://<jetkvm-ip>/device/send-wol/abcd
...
* Request completely sent off
< HTTP/1.1 400 Bad Request
...
...
Invalid mac address provided

* Resolve golint complaint

---------

Co-authored-by: Marc Brooks <IDisposable@gmail.com>
2025-11-06 21:39:22 +01:00
Adam Shiervani 5f15d8b2f6
refactor: More robust handling of jsonrpc calls (#915)
Co-authored-by: Marc Brooks <IDisposable@gmail.com>
2025-11-06 11:12:19 +01:00