fix: accept low-latency desktop move channel

This commit is contained in:
Haitao Pan 2026-06-09 15:55:19 +08:00
parent 4859396051
commit 3dd2359ca3
3 changed files with 47 additions and 14 deletions

View File

@ -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: ...
```

View File

@ -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)

View File

@ -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")
}
}