mirror of https://github.com/jetkvm/kvm.git
Fix: USB Gadgets updates
This commit is contained in:
parent
bc53523fbb
commit
2afe2ca539
|
@ -23,6 +23,7 @@ const (
|
|||
AudioEventMicrophoneMetrics AudioEventType = "microphone-metrics-update"
|
||||
AudioEventProcessMetrics AudioEventType = "audio-process-metrics"
|
||||
AudioEventMicProcessMetrics AudioEventType = "microphone-process-metrics"
|
||||
AudioEventDeviceChanged AudioEventType = "audio-device-changed"
|
||||
)
|
||||
|
||||
// AudioEvent represents a WebSocket audio event
|
||||
|
@ -73,6 +74,12 @@ type ProcessMetricsData struct {
|
|||
ProcessName string `json:"process_name"`
|
||||
}
|
||||
|
||||
// AudioDeviceChangedData represents audio device configuration change data
|
||||
type AudioDeviceChangedData struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// AudioEventSubscriber represents a WebSocket connection subscribed to audio events
|
||||
type AudioEventSubscriber struct {
|
||||
conn *websocket.Conn
|
||||
|
@ -164,6 +171,15 @@ func (aeb *AudioEventBroadcaster) BroadcastMicrophoneStateChanged(running, sessi
|
|||
aeb.broadcast(event)
|
||||
}
|
||||
|
||||
// BroadcastAudioDeviceChanged broadcasts audio device configuration changes
|
||||
func (aeb *AudioEventBroadcaster) BroadcastAudioDeviceChanged(enabled bool, reason string) {
|
||||
event := createAudioEvent(AudioEventDeviceChanged, AudioDeviceChangedData{
|
||||
Enabled: enabled,
|
||||
Reason: reason,
|
||||
})
|
||||
aeb.broadcast(event)
|
||||
}
|
||||
|
||||
// sendInitialState sends current audio state to a new subscriber
|
||||
func (aeb *AudioEventBroadcaster) sendInitialState(connectionID string) {
|
||||
aeb.mutex.RLock()
|
||||
|
|
11
jsonrpc.go
11
jsonrpc.go
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/pion/webrtc/v4"
|
||||
"go.bug.st/serial"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/audio"
|
||||
"github.com/jetkvm/kvm/internal/usbgadget"
|
||||
)
|
||||
|
||||
|
@ -956,6 +957,11 @@ func rpcSetUsbDevices(usbDevices usbgadget.Devices) error {
|
|||
if err := audioSupervisor.Start(); err != nil {
|
||||
logger.Error().Err(err).Msg("failed to start audio supervisor")
|
||||
// Don't return error here as USB reconfiguration was successful
|
||||
} else {
|
||||
// Broadcast audio device change event to notify WebRTC session
|
||||
broadcaster := audio.GetAudioEventBroadcaster()
|
||||
broadcaster.BroadcastAudioDeviceChanged(true, "usb_reconfiguration")
|
||||
logger.Info().Msg("broadcasted audio device change event after USB reconfiguration")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -999,6 +1005,11 @@ func rpcSetUsbDeviceState(device string, enabled bool) error {
|
|||
logger.Info().Msg("starting audio processes due to audio device being enabled")
|
||||
if err := audioSupervisor.Start(); err != nil {
|
||||
logger.Error().Err(err).Msg("failed to start audio supervisor")
|
||||
} else {
|
||||
// Broadcast audio device change event to notify WebRTC session
|
||||
broadcaster := audio.GetAudioEventBroadcaster()
|
||||
broadcaster.BroadcastAudioDeviceChanged(true, "device_enabled")
|
||||
logger.Info().Msg("broadcasted audio device change event after enabling audio device")
|
||||
}
|
||||
}
|
||||
config.UsbDevices.Audio = enabled
|
||||
|
|
|
@ -8,7 +8,8 @@ export type AudioEventType =
|
|||
| 'microphone-state-changed'
|
||||
| 'microphone-metrics-update'
|
||||
| 'audio-process-metrics'
|
||||
| 'microphone-process-metrics';
|
||||
| 'microphone-process-metrics'
|
||||
| 'audio-device-changed';
|
||||
|
||||
// Audio event data interfaces
|
||||
export interface AudioMuteData {
|
||||
|
@ -48,10 +49,15 @@ export interface ProcessMetricsData {
|
|||
process_name: string;
|
||||
}
|
||||
|
||||
export interface AudioDeviceChangedData {
|
||||
enabled: boolean;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
// Audio event structure
|
||||
export interface AudioEvent {
|
||||
type: AudioEventType;
|
||||
data: AudioMuteData | AudioMetricsData | MicrophoneStateData | MicrophoneMetricsData | ProcessMetricsData;
|
||||
data: AudioMuteData | AudioMetricsData | MicrophoneStateData | MicrophoneMetricsData | ProcessMetricsData | AudioDeviceChangedData;
|
||||
}
|
||||
|
||||
// Hook return type
|
||||
|
@ -72,6 +78,9 @@ export interface UseAudioEventsReturn {
|
|||
audioProcessMetrics: ProcessMetricsData | null;
|
||||
microphoneProcessMetrics: ProcessMetricsData | null;
|
||||
|
||||
// Device change events
|
||||
onAudioDeviceChanged?: (data: AudioDeviceChangedData) => void;
|
||||
|
||||
// Manual subscription control
|
||||
subscribe: () => void;
|
||||
unsubscribe: () => void;
|
||||
|
@ -84,7 +93,7 @@ const globalSubscriptionState = {
|
|||
connectionId: null as string | null
|
||||
};
|
||||
|
||||
export function useAudioEvents(): UseAudioEventsReturn {
|
||||
export function useAudioEvents(onAudioDeviceChanged?: (data: AudioDeviceChangedData) => void): UseAudioEventsReturn {
|
||||
// State for audio data
|
||||
const [audioMuted, setAudioMuted] = useState<boolean | null>(null);
|
||||
const [audioMetrics, setAudioMetrics] = useState<AudioMetricsData | null>(null);
|
||||
|
@ -244,6 +253,15 @@ export function useAudioEvents(): UseAudioEventsReturn {
|
|||
break;
|
||||
}
|
||||
|
||||
case 'audio-device-changed': {
|
||||
const deviceChangedData = audioEvent.data as AudioDeviceChangedData;
|
||||
console.log('[AudioEvents] Audio device changed:', deviceChangedData);
|
||||
if (onAudioDeviceChanged) {
|
||||
onAudioDeviceChanged(deviceChangedData);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Ignore other message types (WebRTC signaling, etc.)
|
||||
break;
|
||||
|
@ -256,7 +274,7 @@ export function useAudioEvents(): UseAudioEventsReturn {
|
|||
}
|
||||
}
|
||||
}
|
||||
}, [lastMessage]);
|
||||
}, [lastMessage, onAudioDeviceChanged]);
|
||||
|
||||
// Auto-subscribe when connected
|
||||
useEffect(() => {
|
||||
|
@ -309,6 +327,9 @@ export function useAudioEvents(): UseAudioEventsReturn {
|
|||
audioProcessMetrics,
|
||||
microphoneProcessMetrics,
|
||||
|
||||
// Device change events
|
||||
onAudioDeviceChanged,
|
||||
|
||||
// Manual subscription control
|
||||
subscribe,
|
||||
unsubscribe,
|
||||
|
|
|
@ -34,6 +34,7 @@ import {
|
|||
VideoState,
|
||||
} from "@/hooks/stores";
|
||||
import { useMicrophone } from "@/hooks/useMicrophone";
|
||||
import { useAudioEvents } from "@/hooks/useAudioEvents";
|
||||
import WebRTCVideo from "@components/WebRTCVideo";
|
||||
import { checkAuth, isInCloud, isOnDevice } from "@/main";
|
||||
import DashboardNavbar from "@components/Header";
|
||||
|
@ -655,6 +656,9 @@ export default function KvmIdRoute() {
|
|||
const rpcDataChannel = useRTCStore(state => state.rpcDataChannel);
|
||||
const { send } = useJsonRpc(onJsonRpcRequest);
|
||||
|
||||
// Use audio events hook without device change handler to avoid subscription loops
|
||||
useAudioEvents();
|
||||
|
||||
useEffect(() => {
|
||||
if (rpcDataChannel?.readyState !== "open") return;
|
||||
send("getVideoState", {}, (resp: JsonRpcResponse) => {
|
||||
|
|
Loading…
Reference in New Issue