mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-29 00:51:53 +08:00
feat(webrtc): 添加公共ICE服务器支持和优化HID延迟
- 重构ICE配置:将TURN配置改为统一的ICE配置,支持STUN和多TURN URL - 添加公共ICE服务器:类似RustDesk,用户留空时使用编译时配置的公共服务器 - 优化DataChannel HID消息:使用tokio::spawn立即处理,避免依赖webrtc-rs轮询 - 添加WebRTCReady事件:客户端等待此事件后再建立连接 - 初始化时启动音频流,确保WebRTC可订阅 - 移除多余的trace/debug日志减少开销 - 更新前端配置界面支持公共ICE服务器显示
This commit is contained in:
@@ -83,6 +83,7 @@ let sessionId: string | null = null
|
||||
let statsInterval: number | null = null
|
||||
let isConnecting = false // Lock to prevent concurrent connect calls
|
||||
let pendingIceCandidates: RTCIceCandidate[] = [] // Queue for ICE candidates before sessionId is set
|
||||
let cachedMediaStream: MediaStream | null = null // Cached MediaStream to avoid recreating
|
||||
|
||||
const state = ref<WebRTCState>('disconnected')
|
||||
const videoTrack = ref<MediaStreamTrack | null>(null)
|
||||
@@ -399,8 +400,28 @@ async function connect(): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
isConnecting = false
|
||||
return true
|
||||
// 等待连接真正建立(最多等待 15 秒)
|
||||
// 直接检查 peerConnection.connectionState 而不是 reactive state
|
||||
// 因为 TypeScript 不知道 state 会被 onconnectionstatechange 回调异步修改
|
||||
const connectionTimeout = 15000
|
||||
const pollInterval = 100
|
||||
let waited = 0
|
||||
|
||||
while (waited < connectionTimeout && peerConnection) {
|
||||
const pcState = peerConnection.connectionState
|
||||
if (pcState === 'connected') {
|
||||
isConnecting = false
|
||||
return true
|
||||
}
|
||||
if (pcState === 'failed' || pcState === 'closed') {
|
||||
throw new Error('Connection failed during ICE negotiation')
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, pollInterval))
|
||||
waited += pollInterval
|
||||
}
|
||||
|
||||
// 超时
|
||||
throw new Error('Connection timeout waiting for ICE negotiation')
|
||||
} catch (err) {
|
||||
state.value = 'failed'
|
||||
error.value = err instanceof Error ? err.message : 'Connection failed'
|
||||
@@ -441,6 +462,7 @@ async function disconnect() {
|
||||
|
||||
videoTrack.value = null
|
||||
audioTrack.value = null
|
||||
cachedMediaStream = null // Clear cached stream on disconnect
|
||||
state.value = 'disconnected'
|
||||
error.value = null
|
||||
|
||||
@@ -493,20 +515,49 @@ function sendMouse(event: HidMouseEvent): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
// Get MediaStream for video element
|
||||
// Get MediaStream for video element (cached to avoid recreating)
|
||||
function getMediaStream(): MediaStream | null {
|
||||
if (!videoTrack.value && !audioTrack.value) {
|
||||
return null
|
||||
}
|
||||
|
||||
const stream = new MediaStream()
|
||||
// Reuse cached stream if tracks match
|
||||
if (cachedMediaStream) {
|
||||
const currentVideoTracks = cachedMediaStream.getVideoTracks()
|
||||
const currentAudioTracks = cachedMediaStream.getAudioTracks()
|
||||
|
||||
const videoMatches = videoTrack.value
|
||||
? currentVideoTracks.includes(videoTrack.value)
|
||||
: currentVideoTracks.length === 0
|
||||
const audioMatches = audioTrack.value
|
||||
? currentAudioTracks.includes(audioTrack.value)
|
||||
: currentAudioTracks.length === 0
|
||||
|
||||
if (videoMatches && audioMatches) {
|
||||
return cachedMediaStream
|
||||
}
|
||||
|
||||
// Tracks changed, update the cached stream
|
||||
// Remove old tracks
|
||||
currentVideoTracks.forEach(t => cachedMediaStream!.removeTrack(t))
|
||||
currentAudioTracks.forEach(t => cachedMediaStream!.removeTrack(t))
|
||||
|
||||
// Add new tracks
|
||||
if (videoTrack.value) cachedMediaStream.addTrack(videoTrack.value)
|
||||
if (audioTrack.value) cachedMediaStream.addTrack(audioTrack.value)
|
||||
|
||||
return cachedMediaStream
|
||||
}
|
||||
|
||||
// Create new cached stream
|
||||
cachedMediaStream = new MediaStream()
|
||||
if (videoTrack.value) {
|
||||
stream.addTrack(videoTrack.value)
|
||||
cachedMediaStream.addTrack(videoTrack.value)
|
||||
}
|
||||
if (audioTrack.value) {
|
||||
stream.addTrack(audioTrack.value)
|
||||
cachedMediaStream.addTrack(audioTrack.value)
|
||||
}
|
||||
return stream
|
||||
return cachedMediaStream
|
||||
}
|
||||
|
||||
// Composable export
|
||||
|
||||
Reference in New Issue
Block a user