From 3dd2359ca3ad41821e4c8e7e2444d42c051863d8 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Tue, 9 Jun 2026 15:55:19 +0800 Subject: [PATCH] fix: accept low-latency desktop move channel --- ...rtc-remote-desktop-white-screen-runbook.md | 9 +++-- internal/desktop/webrtc.go | 37 +++++++++++++------ internal/desktop/webrtc_test.go | 15 ++++++++ 3 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 internal/desktop/webrtc_test.go diff --git a/docs/runbooks/webrtc-remote-desktop-white-screen-runbook.md b/docs/runbooks/webrtc-remote-desktop-white-screen-runbook.md index eaa3904..5f11f5b 100644 --- a/docs/runbooks/webrtc-remote-desktop-white-screen-runbook.md +++ b/docs/runbooks/webrtc-remote-desktop-white-screen-runbook.md @@ -471,12 +471,14 @@ WebRTC RTP stats: packets=... packetDelta=1100 byteDelta=950000 writeErrors=0 - `mouse_move` / hover 约 16ms 最多发送一次 - 同一归一化坐标重复移动不发送 - `mouse_down` 前强制发送最新坐标,保证点击命中 -- data channel `bufferedAmount` 过高时只丢弃 `mouse_move` +- `mouse_move` 走独立 `input-move` data channel,配置为 unordered + 短 packet lifetime +- 点击、键盘、滚轮走可靠有序 `input` data channel +- `input-move` channel `bufferedAmount` 过高时只丢弃 `mouse_move` - `mouse_down`、`mouse_up`、`key_down`、`key_up`、`scroll` 不因背压丢弃 判读: -- 鼠标轨迹延迟但点击最终准确,通常是旧 `mouse_move` 事件排队。 +- 鼠标轨迹延迟但点击最终准确,通常是 `input-move` 背压或 Bridge 侧 mouse move coalescing 积压。 - 点击也延迟或丢失,要查 data channel 状态、Bridge input injector、xdotool 写入。 ### 3. 再看 Bridge input injector @@ -484,13 +486,14 @@ WebRTC RTP stats: packets=... packetDelta=1100 byteDelta=950000 writeErrors=0 Bridge 侧输入链路是: ```text -APP Pointer/Key event -> WebRTC data channel -> Bridge OnDataChannel -> XdotoolInjector.Inject -> persistent xdotool stdin -> X11 +APP Pointer/Key event -> WebRTC input/input-move data channel -> Bridge OnDataChannel -> XdotoolInjector.Inject -> persistent xdotool stdin -> X11 ``` 重点日志: ```text Data channel 'input'-'...' opened +Data channel 'input-move'-'...' opened xdotool write error: ... xdotool mousemove write error: ... ``` diff --git a/internal/desktop/webrtc.go b/internal/desktop/webrtc.go index 9ad8909..065a5d8 100644 --- a/internal/desktop/webrtc.go +++ b/internal/desktop/webrtc.go @@ -13,6 +13,11 @@ import ( "github.com/pion/webrtc/v4" ) +const ( + desktopReliableInputChannelLabel = "input" + desktopMoveInputChannelLabel = "input-move" +) + type WebRTCServer struct { peerConnection *webrtc.PeerConnection videoTrack *webrtc.TrackLocalStaticRTP @@ -88,18 +93,24 @@ func (w *WebRTCServer) InitPeerConnection(iceServers []string) error { // Handle Data Channel for inputs pc.OnDataChannel(func(d *webrtc.DataChannel) { log.Printf("Data channel '%s'-'%d' opened", d.Label(), d.ID()) - if d.Label() == "input" { - d.OnMessage(func(msg webrtc.DataChannelMessage) { - var event InputEvent - if err := json.Unmarshal(msg.Data, &event); err != nil { - log.Printf("Failed to unmarshal input event: %v", err) - return - } - if err := w.inputInjector.Inject(event); err != nil { - log.Printf("Failed to inject input event: %v", err) - } - }) + label := d.Label() + if !isDesktopInputDataChannelLabel(label) { + return } + d.OnMessage(func(msg webrtc.DataChannelMessage) { + var event InputEvent + if err := json.Unmarshal(msg.Data, &event); err != nil { + log.Printf("Failed to unmarshal input event: %v", err) + return + } + if label == desktopMoveInputChannelLabel && event.Type != "mouse_move" { + log.Printf("Ignoring non-mouse_move input event on %s channel: %s", label, event.Type) + return + } + if err := w.inputInjector.Inject(event); err != nil { + log.Printf("Failed to inject input event: %v", err) + } + }) }) w.peerConnection = pc @@ -108,6 +119,10 @@ func (w *WebRTCServer) InitPeerConnection(iceServers []string) error { return nil } +func isDesktopInputDataChannelLabel(label string) bool { + return label == desktopReliableInputChannelLabel || label == desktopMoveInputChannelLabel +} + // StartRTPReceiver listens on local UDP port for GStreamer RTP stream and forwards to WebRTC video track func (w *WebRTCServer) StartRTPReceiver(port int) error { addr := fmt.Sprintf("127.0.0.1:%d", port) diff --git a/internal/desktop/webrtc_test.go b/internal/desktop/webrtc_test.go new file mode 100644 index 0000000..4eb8bec --- /dev/null +++ b/internal/desktop/webrtc_test.go @@ -0,0 +1,15 @@ +package desktop + +import "testing" + +func TestIsDesktopInputDataChannelLabelAllowsReliableAndMoveChannels(t *testing.T) { + if !isDesktopInputDataChannelLabel(desktopReliableInputChannelLabel) { + t.Fatalf("expected reliable input channel label to be accepted") + } + if !isDesktopInputDataChannelLabel(desktopMoveInputChannelLabel) { + t.Fatalf("expected mouse move input channel label to be accepted") + } + if isDesktopInputDataChannelLabel("debug") { + t.Fatalf("expected unrelated data channel label to be ignored") + } +}