mirror of https://github.com/jetkvm/kvm.git
temp: disable audio source selection while HDMI audio issues are diagnosed
Temporarily remove the ability to switch between HDMI and USB audio output sources. The application now uses USB audio (hw:1,0) exclusively until HDMI audio capture issues are resolved. Changes: - Remove AudioOutputSource config field - Remove audio source switching logic and UI - Hardcode USB audio output device - Remove related RPC methods
This commit is contained in:
parent
de74ae1a12
commit
14b741c3dd
85
audio.go
85
audio.go
|
|
@ -12,35 +12,27 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
audioMutex sync.Mutex
|
audioMutex sync.Mutex
|
||||||
outputSource audio.AudioSource
|
outputSource audio.AudioSource
|
||||||
inputSource audio.AudioSource
|
inputSource audio.AudioSource
|
||||||
outputRelay *audio.OutputRelay
|
outputRelay *audio.OutputRelay
|
||||||
inputRelay *audio.InputRelay
|
inputRelay *audio.InputRelay
|
||||||
audioInitialized bool
|
audioInitialized bool
|
||||||
activeConnections atomic.Int32
|
activeConnections atomic.Int32
|
||||||
audioLogger zerolog.Logger
|
audioLogger zerolog.Logger
|
||||||
currentAudioTrack *webrtc.TrackLocalStaticSample
|
currentAudioTrack *webrtc.TrackLocalStaticSample
|
||||||
inputTrackHandling atomic.Bool
|
inputTrackHandling atomic.Bool
|
||||||
useUSBForAudioOutput atomic.Bool
|
audioOutputEnabled atomic.Bool
|
||||||
audioOutputEnabled atomic.Bool
|
audioInputEnabled atomic.Bool
|
||||||
audioInputEnabled atomic.Bool
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func initAudio() {
|
func initAudio() {
|
||||||
audioLogger = logging.GetDefaultLogger().With().Str("component", "audio-manager").Logger()
|
audioLogger = logging.GetDefaultLogger().With().Str("component", "audio-manager").Logger()
|
||||||
|
|
||||||
// Load audio output source from config
|
|
||||||
ensureConfigLoaded()
|
|
||||||
useUSBForAudioOutput.Store(config.AudioOutputSource == "usb")
|
|
||||||
|
|
||||||
// Enable both by default
|
|
||||||
audioOutputEnabled.Store(true)
|
audioOutputEnabled.Store(true)
|
||||||
audioInputEnabled.Store(true)
|
audioInputEnabled.Store(true)
|
||||||
|
|
||||||
audioLogger.Debug().
|
audioLogger.Debug().Msg("Audio subsystem initialized")
|
||||||
Str("source", config.AudioOutputSource).
|
|
||||||
Msg("Audio subsystem initialized")
|
|
||||||
audioInitialized = true
|
audioInitialized = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,12 +48,8 @@ func startAudio() error {
|
||||||
|
|
||||||
// Start output audio if not running and enabled
|
// Start output audio if not running and enabled
|
||||||
if outputSource == nil && audioOutputEnabled.Load() {
|
if outputSource == nil && audioOutputEnabled.Load() {
|
||||||
alsaDevice := "hw:0,0" // HDMI
|
alsaDevice := "hw:1,0" // USB audio
|
||||||
if useUSBForAudioOutput.Load() {
|
|
||||||
alsaDevice = "hw:1,0" // USB
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create CGO audio source
|
|
||||||
outputSource = audio.NewCgoOutputSource(alsaDevice)
|
outputSource = audio.NewCgoOutputSource(alsaDevice)
|
||||||
|
|
||||||
if currentAudioTrack != nil {
|
if currentAudioTrack != nil {
|
||||||
|
|
@ -162,51 +150,6 @@ func setAudioTrack(audioTrack *webrtc.TrackLocalStaticSample) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAudioOutputSource switches between HDMI and USB audio output
|
|
||||||
func SetAudioOutputSource(useUSB bool) error {
|
|
||||||
audioMutex.Lock()
|
|
||||||
defer audioMutex.Unlock()
|
|
||||||
|
|
||||||
if useUSBForAudioOutput.Load() == useUSB {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
audioLogger.Info().
|
|
||||||
Bool("old_usb", useUSBForAudioOutput.Load()).
|
|
||||||
Bool("new_usb", useUSB).
|
|
||||||
Msg("Switching audio output source")
|
|
||||||
|
|
||||||
oldValue := useUSBForAudioOutput.Load()
|
|
||||||
useUSBForAudioOutput.Store(useUSB)
|
|
||||||
|
|
||||||
ensureConfigLoaded()
|
|
||||||
if useUSB {
|
|
||||||
config.AudioOutputSource = "usb"
|
|
||||||
} else {
|
|
||||||
config.AudioOutputSource = "hdmi"
|
|
||||||
}
|
|
||||||
if err := SaveConfig(); err != nil {
|
|
||||||
audioLogger.Error().Err(err).Msg("Failed to save config")
|
|
||||||
useUSBForAudioOutput.Store(oldValue)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
stopOutputLocked()
|
|
||||||
|
|
||||||
// Restart if there are active connections
|
|
||||||
if activeConnections.Load() > 0 {
|
|
||||||
audioMutex.Unlock()
|
|
||||||
err := startAudio()
|
|
||||||
audioMutex.Lock()
|
|
||||||
if err != nil {
|
|
||||||
audioLogger.Error().Err(err).Msg("Failed to restart audio output")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setPendingInputTrack(track *webrtc.TrackRemote) {
|
func setPendingInputTrack(track *webrtc.TrackRemote) {
|
||||||
audioMutex.Lock()
|
audioMutex.Lock()
|
||||||
defer audioMutex.Unlock()
|
defer audioMutex.Unlock()
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,6 @@ type Config struct {
|
||||||
DefaultLogLevel string `json:"default_log_level"`
|
DefaultLogLevel string `json:"default_log_level"`
|
||||||
VideoSleepAfterSec int `json:"video_sleep_after_sec"`
|
VideoSleepAfterSec int `json:"video_sleep_after_sec"`
|
||||||
VideoQualityFactor float64 `json:"video_quality_factor"`
|
VideoQualityFactor float64 `json:"video_quality_factor"`
|
||||||
AudioOutputSource string `json:"audio_output_source"` // "hdmi" or "usb"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) GetDisplayRotation() uint16 {
|
func (c *Config) GetDisplayRotation() uint16 {
|
||||||
|
|
@ -180,7 +179,6 @@ func getDefaultConfig() Config {
|
||||||
return c
|
return c
|
||||||
}(),
|
}(),
|
||||||
DefaultLogLevel: "INFO",
|
DefaultLogLevel: "INFO",
|
||||||
AudioOutputSource: "usb",
|
|
||||||
VideoQualityFactor: 1.0,
|
VideoQualityFactor: 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
49
jsonrpc.go
49
jsonrpc.go
|
|
@ -894,33 +894,9 @@ func rpcGetUsbDevices() (usbgadget.Devices, error) {
|
||||||
func updateUsbRelatedConfig(wasAudioEnabled bool) error {
|
func updateUsbRelatedConfig(wasAudioEnabled bool) error {
|
||||||
ensureConfigLoaded()
|
ensureConfigLoaded()
|
||||||
|
|
||||||
audioSourceChanged := false
|
// Stop input audio before USB reconfiguration (input uses USB)
|
||||||
|
|
||||||
// If USB audio is being disabled and audio output source is USB, switch to HDMI
|
|
||||||
if config.UsbDevices != nil && !config.UsbDevices.Audio && config.AudioOutputSource == "usb" {
|
|
||||||
audioMutex.Lock()
|
|
||||||
config.AudioOutputSource = "hdmi"
|
|
||||||
useUSBForAudioOutput.Store(false)
|
|
||||||
audioSourceChanged = true
|
|
||||||
audioMutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// If USB audio is being enabled (was disabled, now enabled), switch to USB
|
|
||||||
if config.UsbDevices != nil && config.UsbDevices.Audio && !wasAudioEnabled {
|
|
||||||
audioMutex.Lock()
|
|
||||||
config.AudioOutputSource = "usb"
|
|
||||||
useUSBForAudioOutput.Store(true)
|
|
||||||
audioSourceChanged = true
|
|
||||||
audioMutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop audio before USB reconfiguration
|
|
||||||
// Input always uses USB, output depends on audioSourceChanged
|
|
||||||
audioMutex.Lock()
|
audioMutex.Lock()
|
||||||
stopInputLocked()
|
stopInputLocked()
|
||||||
if audioSourceChanged {
|
|
||||||
stopOutputLocked()
|
|
||||||
}
|
|
||||||
audioMutex.Unlock()
|
audioMutex.Unlock()
|
||||||
|
|
||||||
if err := gadget.UpdateGadgetConfig(); err != nil {
|
if err := gadget.UpdateGadgetConfig(); err != nil {
|
||||||
|
|
@ -931,9 +907,8 @@ func updateUsbRelatedConfig(wasAudioEnabled bool) error {
|
||||||
return fmt.Errorf("failed to save config: %w", err)
|
return fmt.Errorf("failed to save config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart audio if source changed or USB audio is enabled with active connections
|
// Restart audio if USB audio is enabled with active connections
|
||||||
// The relay handles device readiness via retry logic
|
if activeConnections.Load() > 0 && config.UsbDevices != nil && config.UsbDevices.Audio {
|
||||||
if activeConnections.Load() > 0 && (audioSourceChanged || (config.UsbDevices != nil && config.UsbDevices.Audio)) {
|
|
||||||
if err := startAudio(); err != nil {
|
if err := startAudio(); err != nil {
|
||||||
logger.Warn().Err(err).Msg("Failed to restart audio after USB reconfiguration")
|
logger.Warn().Err(err).Msg("Failed to restart audio after USB reconfiguration")
|
||||||
}
|
}
|
||||||
|
|
@ -970,22 +945,6 @@ func rpcSetUsbDeviceState(device string, enabled bool) error {
|
||||||
return updateUsbRelatedConfig(wasAudioEnabled)
|
return updateUsbRelatedConfig(wasAudioEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcGetAudioOutputSource() (string, error) {
|
|
||||||
if useUSBForAudioOutput.Load() {
|
|
||||||
return "usb", nil
|
|
||||||
}
|
|
||||||
return "hdmi", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func rpcSetAudioOutputSource(source string) error {
|
|
||||||
if source != "hdmi" && source != "usb" {
|
|
||||||
return fmt.Errorf("invalid audio output source: %s (must be 'hdmi' or 'usb')", source)
|
|
||||||
}
|
|
||||||
|
|
||||||
useUSB := source == "usb"
|
|
||||||
return SetAudioOutputSource(useUSB)
|
|
||||||
}
|
|
||||||
|
|
||||||
func rpcGetAudioOutputEnabled() (bool, error) {
|
func rpcGetAudioOutputEnabled() (bool, error) {
|
||||||
return audioOutputEnabled.Load(), nil
|
return audioOutputEnabled.Load(), nil
|
||||||
}
|
}
|
||||||
|
|
@ -1320,8 +1279,6 @@ var rpcHandlers = map[string]RPCHandler{
|
||||||
"getUsbDevices": {Func: rpcGetUsbDevices},
|
"getUsbDevices": {Func: rpcGetUsbDevices},
|
||||||
"setUsbDevices": {Func: rpcSetUsbDevices, Params: []string{"devices"}},
|
"setUsbDevices": {Func: rpcSetUsbDevices, Params: []string{"devices"}},
|
||||||
"setUsbDeviceState": {Func: rpcSetUsbDeviceState, Params: []string{"device", "enabled"}},
|
"setUsbDeviceState": {Func: rpcSetUsbDeviceState, Params: []string{"device", "enabled"}},
|
||||||
"getAudioOutputSource": {Func: rpcGetAudioOutputSource},
|
|
||||||
"setAudioOutputSource": {Func: rpcSetAudioOutputSource, Params: []string{"source"}},
|
|
||||||
"getAudioOutputEnabled": {Func: rpcGetAudioOutputEnabled},
|
"getAudioOutputEnabled": {Func: rpcGetAudioOutputEnabled},
|
||||||
"setAudioOutputEnabled": {Func: rpcSetAudioOutputEnabled, Params: []string{"enabled"}},
|
"setAudioOutputEnabled": {Func: rpcSetAudioOutputEnabled, Params: []string{"enabled"}},
|
||||||
"getAudioInputEnabled": {Func: rpcGetAudioInputEnabled},
|
"getAudioInputEnabled": {Func: rpcGetAudioInputEnabled},
|
||||||
|
|
|
||||||
|
|
@ -370,8 +370,6 @@ export interface SettingsState {
|
||||||
setVideoContrast: (value: number) => void;
|
setVideoContrast: (value: number) => void;
|
||||||
|
|
||||||
// Audio settings
|
// Audio settings
|
||||||
audioOutputSource: string;
|
|
||||||
setAudioOutputSource: (source: string) => void;
|
|
||||||
audioOutputEnabled: boolean;
|
audioOutputEnabled: boolean;
|
||||||
setAudioOutputEnabled: (enabled: boolean) => void;
|
setAudioOutputEnabled: (enabled: boolean) => void;
|
||||||
audioInputEnabled: boolean;
|
audioInputEnabled: boolean;
|
||||||
|
|
@ -425,8 +423,6 @@ export const useSettingsStore = create(
|
||||||
setVideoContrast: (value: number) => set({ videoContrast: value }),
|
setVideoContrast: (value: number) => set({ videoContrast: value }),
|
||||||
|
|
||||||
// Audio settings with defaults
|
// Audio settings with defaults
|
||||||
audioOutputSource: "usb",
|
|
||||||
setAudioOutputSource: (source: string) => set({ audioOutputSource: source }),
|
|
||||||
audioOutputEnabled: true,
|
audioOutputEnabled: true,
|
||||||
setAudioOutputEnabled: (enabled: boolean) => set({ audioOutputEnabled: enabled }),
|
setAudioOutputEnabled: (enabled: boolean) => set({ audioOutputEnabled: enabled }),
|
||||||
audioInputEnabled: true,
|
audioInputEnabled: true,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { SettingsItem } from "@components/SettingsItem";
|
||||||
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
||||||
import { useSettingsStore } from "@/hooks/stores";
|
import { useSettingsStore } from "@/hooks/stores";
|
||||||
import { JsonRpcResponse, useJsonRpc } from "@/hooks/useJsonRpc";
|
import { JsonRpcResponse, useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
import { SelectMenuBasic } from "@components/SelectMenuBasic";
|
// import { SelectMenuBasic } from "@components/SelectMenuBasic";
|
||||||
import Checkbox from "@components/Checkbox";
|
import Checkbox from "@components/Checkbox";
|
||||||
import { m } from "@localizations/messages.js";
|
import { m } from "@localizations/messages.js";
|
||||||
|
|
||||||
|
|
@ -14,15 +14,7 @@ export default function SettingsAudioRoute() {
|
||||||
const { send } = useJsonRpc();
|
const { send } = useJsonRpc();
|
||||||
const settings = useSettingsStore();
|
const settings = useSettingsStore();
|
||||||
|
|
||||||
// Fetch current audio settings on mount
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
send("getAudioOutputSource", {}, (resp: JsonRpcResponse) => {
|
|
||||||
if ("error" in resp) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
settings.setAudioOutputSource(resp.result as string);
|
|
||||||
});
|
|
||||||
|
|
||||||
send("getAudioOutputEnabled", {}, (resp: JsonRpcResponse) => {
|
send("getAudioOutputEnabled", {}, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -39,41 +31,6 @@ export default function SettingsAudioRoute() {
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [send]);
|
}, [send]);
|
||||||
|
|
||||||
const handleAudioOutputSourceChange = (source: string) => {
|
|
||||||
// Update UI immediately for better responsiveness
|
|
||||||
settings.setAudioOutputSource(source);
|
|
||||||
|
|
||||||
send("setAudioOutputSource", { source }, (resp: JsonRpcResponse) => {
|
|
||||||
if ("error" in resp) {
|
|
||||||
// Revert on error by fetching current value from backend
|
|
||||||
send("getAudioOutputSource", {}, (getResp: JsonRpcResponse) => {
|
|
||||||
if ("result" in getResp) {
|
|
||||||
settings.setAudioOutputSource(getResp.result as string);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
notifications.error(
|
|
||||||
m.audio_settings_output_source_failed({ error: String(resp.error.data || m.unknown_error()) }),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the change was applied by fetching the actual value
|
|
||||||
send("getAudioOutputSource", {}, (getResp: JsonRpcResponse) => {
|
|
||||||
if ("result" in getResp) {
|
|
||||||
const actualSource = getResp.result as string;
|
|
||||||
settings.setAudioOutputSource(actualSource);
|
|
||||||
if (actualSource === source) {
|
|
||||||
notifications.success(m.audio_settings_output_source_success());
|
|
||||||
} else {
|
|
||||||
notifications.error(
|
|
||||||
m.audio_settings_output_source_failed({ error: `Expected ${source}, got ${actualSource}` }),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAudioOutputEnabledChange = (enabled: boolean) => {
|
const handleAudioOutputEnabledChange = (enabled: boolean) => {
|
||||||
send("setAudioOutputEnabled", { enabled }, (resp: JsonRpcResponse) => {
|
send("setAudioOutputEnabled", { enabled }, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
|
|
@ -121,26 +78,6 @@ export default function SettingsAudioRoute() {
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
|
||||||
{settings.audioOutputEnabled && (
|
|
||||||
<SettingsItem
|
|
||||||
title={m.audio_settings_output_source_title()}
|
|
||||||
description={m.audio_settings_output_source_description()}
|
|
||||||
>
|
|
||||||
<SelectMenuBasic
|
|
||||||
size="SM"
|
|
||||||
label=""
|
|
||||||
value={settings.audioOutputSource || "usb"}
|
|
||||||
options={[
|
|
||||||
{ value: "hdmi", label: m.audio_settings_hdmi_label() },
|
|
||||||
{ value: "usb", label: m.audio_settings_usb_label() },
|
|
||||||
]}
|
|
||||||
onChange={e => {
|
|
||||||
handleAudioOutputSourceChange(e.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
title={m.audio_settings_input_title()}
|
title={m.audio_settings_input_title()}
|
||||||
description={m.audio_settings_input_description()}
|
description={m.audio_settings_input_description()}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue