Files
video_socket-server/.qoder/repowiki/zh/content/WebRTC 实现/输入设备控制/鼠标输入处理.md

370 lines
16 KiB
Markdown
Raw Normal View History

2026-05-16 13:37:04 +08:00
# 鼠标输入处理
<cite>
**本文档引用的文件**
- [inputremoting.js](file://client/src/inputremoting.js)
- [inputdevice.js](file://client/src/inputdevice.js)
- [mousebutton.js](file://client/src/mousebutton.js)
- [pointercorrect.js](file://client/src/pointercorrect.js)
- [sender.js](file://client/src/sender.js)
- [memoryhelper.js](file://client/src/memoryhelper.js)
- [register-events.js](file://client/public/videoplayer/js/register-events.js)
- [main.js视频播放器](file://client/public/videoplayer/js/main.js)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件聚焦于鼠标输入处理的技术文档,涵盖鼠标事件的捕获、转换与转发机制,包括鼠标移动、点击、滚轮等事件类型。文档详细说明鼠标坐标系统、按钮状态映射与事件数据结构,并给出时间戳同步、坐标变换与延迟补偿策略。同时提供具体代码路径示例,展示如何捕获鼠标事件、序列化为二进制消息并通过网络传输,在远端设备上进行重放。
## 项目结构
该仓库包含客户端与服务端两部分。与鼠标输入直接相关的核心代码位于 client/src 与 client/public/videoplayer/js 目录中:
- 输入设备建模与事件序列化client/src/inputdevice.js
- 输入远程转发与消息封装client/src/inputremoting.js
- 鼠标按钮常量定义client/src/mousebutton.js
- 坐标矫正与视频适配client/src/pointercorrect.js
- 本地事件监听与状态队列client/src/sender.js
- 内存位写入工具client/src/memoryhelper.js
- 前端事件注册与消息发送另一套实现client/public/videoplayer/js/register-events.js
- 视频播放器入口与事件注册client/public/videoplayer/js/main.js
```mermaid
graph TB
subgraph "客户端"
A["事件监听<br/>sender.js"]
B["输入设备模型<br/>inputdevice.js"]
C["远程转发与消息封装<br/>inputremoting.js"]
D["坐标矫正<br/>pointercorrect.js"]
E["内存位写入<br/>memoryhelper.js"]
F["鼠标按钮常量<br/>mousebutton.js"]
G["前端事件注册(替代实现)<br/>register-events.js"]
end
A --> B
A --> D
A --> C
B --> E
B --> F
G --> |"注册鼠标/滚轮/触摸事件"| A
```
图表来源
- [sender.js:14-188](file://client/src/sender.js#L14-L188)
- [inputdevice.js:91-272](file://client/src/inputdevice.js#L91-L272)
- [inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169)
- [pointercorrect.js:6-124](file://client/src/pointercorrect.js#L6-L124)
- [memoryhelper.js:1-28](file://client/src/memoryhelper.js#L1-L28)
- [mousebutton.js:1-7](file://client/src/mousebutton.js#L1-L7)
- [register-events.js:161-281](file://client/public/videoplayer/js/register-events.js#L161-L281)
章节来源
- [sender.js:14-188](file://client/src/sender.js#L14-L188)
- [inputdevice.js:91-272](file://client/src/inputdevice.js#L91-L272)
- [inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169)
- [pointercorrect.js:6-124](file://client/src/pointercorrect.js#L6-L124)
- [memoryhelper.js:1-28](file://client/src/memoryhelper.js#L1-L28)
- [mousebutton.js:1-7](file://client/src/mousebutton.js#L1-L7)
- [register-events.js:161-281](file://client/public/videoplayer/js/register-events.js#L161-L281)
## 核心组件
- LocalInputManager本地输入管理基类负责事件分发与时间基准。
- Sender具体实现注册鼠标/键盘/手柄/触摸事件,构建输入设备状态,派发 StateEvent/TextEvent。
- Mouse/MouseState鼠标设备与状态包含位置、相对位移、滚轮、按钮位图等字段。
- InputRemoting将本地事件序列化为消息并转发给订阅者如 RTC 数据通道)。
- Message/NewEventsMsg/NewDeviceMsg消息封装与序列化。
- PointerCorrector将页面坐标映射到视频画布坐标处理留边距与缩放。
- MemoryHelper按位写入布尔状态用于按钮位图。
- MouseButton按钮枚举。
章节来源
- [inputremoting.js:9-49](file://client/src/inputremoting.js#L9-L49)
- [inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169)
- [inputdevice.js:91-272](file://client/src/inputdevice.js#L91-L272)
- [sender.js:14-188](file://client/src/sender.js#L14-L188)
- [pointercorrect.js:6-124](file://client/src/pointercorrect.js#L6-L124)
- [memoryhelper.js:1-28](file://client/src/memoryhelper.js#L1-L28)
- [mousebutton.js:1-7](file://client/src/mousebutton.js#L1-L7)
## 架构总览
鼠标事件从 DOM 事件开始,经由 Sender 注册监听,转换为 MouseState再包装为 StateEvent通过 InputRemoting 序列化为 Message 并发送至远端。
```mermaid
sequenceDiagram
participant U as "用户"
participant DOM as "DOM事件"
participant S as "Sender"
participant M as "Mouse/MouseState"
participant PC as "PointerCorrector"
participant IR as "InputRemoting"
participant MSG as "Message/NewEventsMsg"
participant NET as "网络/数据通道"
U->>DOM : "鼠标移动/点击/滚轮"
DOM->>S : "事件回调"
S->>M : "queueEvent(event)"
M-->>S : "currentState"
S->>PC : "map(position)"
PC-->>S : "矫正后坐标"
S->>IR : "派发 StateEvent"
IR->>MSG : "NewEventsMsg.create(stateEvent)"
MSG-->>IR : "Message"
IR->>NET : "发送 buffer"
```
图表来源
- [sender.js:121-129](file://client/src/sender.js#L121-L129)
- [inputdevice.js:91-98](file://client/src/inputdevice.js#L91-L98)
- [inputdevice.js:195-272](file://client/src/inputdevice.js#L195-L272)
- [pointercorrect.js:20-40](file://client/src/pointercorrect.js#L20-L40)
- [inputremoting.js:138-141](file://client/src/inputremoting.js#L138-L141)
- [inputremoting.js:258-277](file://client/src/inputremoting.js#L258-L277)
## 详细组件分析
### 鼠标事件捕获与转换
- 事件监听:在视频元素上注册 click/mousedown/mouseup/mousemove/wheel 回调。
- 状态构建:将 MouseEvent/WheelEvent 转换为 MouseState记录绝对坐标、相对位移、滚轮增量与按钮位图。
- 按钮映射:使用位图存储左右中及其他扩展按钮状态,通过 MemoryHelper.writeSingleBit 写入。
- 坐标矫正:使用 PointerCorrector 将页面坐标(clientX/Y)映射到视频画布坐标,考虑留边距与缩放。
```mermaid
flowchart TD
Start(["事件到达"]) --> Type{"事件类型"}
Type --> |mousemove| BuildMove["构建 MouseState<br/>position/delta/buttons"]
Type --> |click/mousedown/mouseup| BuildBtn["构建 MouseState<br/>position/buttons"]
Type --> |wheel| BuildWheel["构建 MouseState<br/>scroll"]
BuildMove --> BtnMap["按钮位图写入"]
BuildBtn --> BtnMap
BuildWheel --> BtnMap
BtnMap --> Correct["PointerCorrector.map(position)"]
Correct --> Queue["派发 StateEvent"]
Queue --> End(["完成"])
```
图表来源
- [sender.js:121-129](file://client/src/sender.js#L121-L129)
- [inputdevice.js:195-272](file://client/src/inputdevice.js#L195-L272)
- [memoryhelper.js:7-20](file://client/src/memoryhelper.js#L7-L20)
- [pointercorrect.js:20-40](file://client/src/pointercorrect.js#L20-L40)
章节来源
- [sender.js:31-49](file://client/src/sender.js#L31-L49)
- [sender.js:121-129](file://client/src/sender.js#L121-L129)
- [inputdevice.js:91-98](file://client/src/inputdevice.js#L91-L98)
- [inputdevice.js:195-272](file://client/src/inputdevice.js#L195-L272)
- [memoryhelper.js:7-20](file://client/src/memoryhelper.js#L7-L20)
- [pointercorrect.js:20-40](file://client/src/pointercorrect.js#L20-L40)
### 鼠标坐标系统与按钮状态
- 坐标系统
- 绝对坐标clientX/clientY 来源于 DOM 事件。
- 相对位移movementX/movementY 由浏览器提供Y 方向取负以匹配常见坐标系。
- 留边距与缩放:通过 PointerCorrector 的 contentRect 与 video 尺寸计算映射。
- 按钮状态
- 使用 16 字节位图存储多按钮状态,左/右/中/前进/后退分别对应不同位。
- 通过 MemoryHelper.writeSingleBit 写入位图。
章节来源
- [inputdevice.js:222-244](file://client/src/inputdevice.js#L222-L244)
- [inputdevice.js:249-264](file://client/src/inputdevice.js#L249-L264)
- [memoryhelper.js:7-20](file://client/src/memoryhelper.js#L7-L20)
- [mousebutton.js:1-7](file://client/src/mousebutton.js#L1-L7)
### 事件数据结构与时序
- InputEvent统一事件头包含类型、大小、设备 ID、时间戳。
- StateEvent封装 IInputState携带状态格式与状态数据。
- Message封装 participant_id、type、length 与 data作为网络传输单元。
- 时间戳LocalInputManager 提供基于秒的时间基准Sender 在派发时使用 timeSinceStartup。
```mermaid
classDiagram
class InputEvent {
+number type
+number sizeInBytes
+number deviceId
+number time
+buffer
}
class IInputState {
+buffer
+format
}
class MouseState {
+float32 position[2]
+float32 delta[2]
+float32 scroll[2]
+uint16 buttons
+uint16 displayIndex
+uint16 clickCount
+buffer
+format
}
class StateEvent {
+baseEvent : InputEvent
+stateFormat : number
+stateData : ArrayBuffer
+buffer
}
class Message {
+number participant_id
+number type
+number length
+ArrayBuffer data
+buffer
}
MouseState ..|> IInputState
StateEvent --> InputEvent : "组合"
Message --> StateEvent : "承载"
```
图表来源
- [inputdevice.js:129-178](file://client/src/inputdevice.js#L129-L178)
- [inputdevice.js:180-193](file://client/src/inputdevice.js#L180-L193)
- [inputdevice.js:195-272](file://client/src/inputdevice.js#L195-L272)
- [inputremoting.js:184-232](file://client/src/inputremoting.js#L184-L232)
章节来源
- [inputdevice.js:129-178](file://client/src/inputdevice.js#L129-L178)
- [inputdevice.js:180-193](file://client/src/inputdevice.js#L180-L193)
- [inputdevice.js:195-272](file://client/src/inputdevice.js#L195-L272)
- [inputremoting.js:184-232](file://client/src/inputremoting.js#L184-L232)
### 网络传输与远端重放
- 发送侧
- InputRemoting 订阅 LocalInputManager 的事件,将 StateEvent 包装为 NewEventsMsg再封装为 Message。
- 通过订阅者(如 RTC 数据通道)发送二进制 buffer。
- 接收侧
- 远端需解析 Message识别事件类型与状态格式重建 MouseState 并应用到目标设备或渲染层。
- 由于未在仓库中发现接收与重放的具体实现,建议遵循以下原则:
- 使用与发送端一致的格式标识与字段顺序。
- 使用时间戳进行本地重放缓冲与插值(见“性能考量”)。
```mermaid
sequenceDiagram
participant L as "本地 Sender"
participant IR as "InputRemoting"
participant CH as "数据通道"
participant R as "远端接收器"
L->>IR : "StateEvent"
IR->>IR : "NewEventsMsg.create()"
IR->>CH : "Message.buffer"
CH-->>R : "二进制数据"
R->>R : "解析格式/重建状态"
R-->>R : "重放到目标设备"
```
图表来源
- [inputremoting.js:138-141](file://client/src/inputremoting.js#L138-L141)
- [inputremoting.js:258-277](file://client/src/inputremoting.js#L258-L277)
- [sender.js:170-182](file://client/src/sender.js#L170-L182)
章节来源
- [inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169)
- [inputremoting.js:184-232](file://client/src/inputremoting.js#L184-L232)
- [sender.js:170-182](file://client/src/sender.js#L170-L182)
### 坐标变换与延迟补偿策略
- 坐标变换
- 将 clientX/Y 减去元素边界矩形偏移,反转 Y 轴,减去留边距,再按内容矩形与视频尺寸比例映射。
- 通过 PointerCorrector 的 contentRect 动态计算留边类型(水平/垂直)与尺寸。
- 延迟补偿
- 使用 timeSinceStartup 作为事件时间戳,远端可据此进行缓冲与插值,减少抖动。
- 若网络存在抖动,可在接收端维护一个环形缓冲队列,按时间戳排序后重放。
章节来源
- [pointercorrect.js:20-40](file://client/src/pointercorrect.js#L20-L40)
- [pointercorrect.js:78-119](file://client/src/pointercorrect.js#L78-L119)
- [sender.js:39-41](file://client/src/sender.js#L39-L41)
- [sender.js:170-172](file://client/src/sender.js#L170-L172)
### 另一套前端事件注册实现(对比参考)
- register-events.js 提供了另一种前端事件注册方式,同样处理鼠标、滚轮与触摸事件,并将坐标与按钮状态打包为二进制消息发送。
- 该实现与 sender.js 的差异在于:
- 事件监听对象不同(视频元素 vs 文档/窗口)。
- 坐标映射逻辑略有差异(考虑 videoOriginX/Y 与 videoScale
- 未使用 InputRemoting/Message 体系,而是直接构造 DataView 发送。
章节来源
- [register-events.js:161-281](file://client/public/videoplayer/js/register-events.js#L161-L281)
- [main.js视频播放器:132-142](file://client/public/videoplayer/js/main.js#L132-L142)
## 依赖关系分析
- Sender 依赖 Mouse/MouseState、PointerCorrector、LocalInputManager、StateEvent。
- MouseState 依赖 MemoryHelper 与 MouseButton。
- InputRemoting 依赖 Message/NewEventsMsg/RemoveDeviceMsg/ChangeUsageMsg 与订阅者。
- register-events.js 与 main.js 形成独立的事件注册与发送链路。
```mermaid
graph LR
Sender["sender.js"] --> Mouse["inputdevice.js: Mouse/MouseState"]
Sender --> PC["pointercorrect.js"]
Sender --> LIM["inputremoting.js: LocalInputManager"]
Sender --> SE["inputdevice.js: StateEvent"]
Mouse --> MH["memoryhelper.js"]
Mouse --> MB["mousebutton.js"]
IR["inputremoting.js"] --> MSG["inputremoting.js: Message/NewEventsMsg"]
RE["register-events.js"] --> |"事件注册/发送"| Sender
```
图表来源
- [sender.js:14-188](file://client/src/sender.js#L14-L188)
- [inputdevice.js:91-272](file://client/src/inputdevice.js#L91-L272)
- [inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169)
- [memoryhelper.js:1-28](file://client/src/memoryhelper.js#L1-L28)
- [mousebutton.js:1-7](file://client/src/mousebutton.js#L1-L7)
- [register-events.js:161-281](file://client/public/videoplayer/js/register-events.js#L161-L281)
章节来源
- [sender.js:14-188](file://client/src/sender.js#L14-L188)
- [inputdevice.js:91-272](file://client/src/inputdevice.js#L91-L272)
- [inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169)
- [memoryhelper.js:1-28](file://client/src/memoryhelper.js#L1-L28)
- [mousebutton.js:1-7](file://client/src/mousebutton.js#L1-L7)
- [register-events.js:161-281](file://client/public/videoplayer/js/register-events.js#L161-L281)
## 性能考量
- 事件采样与节流
- 对高频 mousemove 可采用节流/降采样策略,避免带宽与 CPU 压力。
- 时间戳与缓冲
- 使用 timeSinceStartup 作为单调递增时间戳,远端按时间戳排序与重放。
- 建议在接收端维护环形缓冲,按目标帧率进行插值。
- 坐标计算优化
- 将 contentRect 缓存并在 resize 时更新,避免重复计算。
- 位图写入
- 使用 MemoryHelper.writeSingleBit 批量写入按钮状态,减少内存拷贝。
## 故障排查指南
- 事件未触发
- 检查事件监听是否绑定到正确的元素(视频元素)。
- 确认 PointerCorrector 的 videoWidth/videoHeight 已正确初始化。
- 坐标错位
- 核对 contentRect 与留边距计算,确认 videoScale 与 originX/Y 设置。
- 按钮状态异常
- 检查按钮位图写入逻辑,确认 MouseButton 枚举与位索引一致。
- 网络发送失败
- 确认订阅者 readyState 为 openMessage.buffer 构造无误。
章节来源
- [sender.js:114-120](file://client/src/sender.js#L114-L120)
- [pointercorrect.js:71-76](file://client/src/pointercorrect.js#L71-L76)
- [memoryhelper.js:7-20](file://client/src/memoryhelper.js#L7-L20)
- [inputremoting.js:202-208](file://client/src/inputremoting.js#L202-L208)
## 结论
本项目提供了完整的鼠标输入捕获、转换与转发链路DOM 事件 → MouseState → StateEvent → Message → 网络传输。通过 PointerCorrector 实现跨设备坐标一致性MemoryHelper 提供高效的按钮位图写入。若需在远端重放,应严格遵循格式标识与字段顺序,并结合时间戳进行缓冲与插值,以获得流畅体验。
## 附录
- 代码路径示例(不展示具体代码内容)
- 鼠标事件监听与状态派发:[sender.js:121-129](file://client/src/sender.js#L121-L129)
- MouseState 构建与位图写入:[inputdevice.js:222-244](file://client/src/inputdevice.js#L222-L244)
- 坐标矫正:[pointercorrect.js:20-40](file://client/src/pointercorrect.js#L20-L40)
- 事件序列化为 Message[inputremoting.js:258-277](file://client/src/inputremoting.js#L258-L277)
- 前端事件注册(对比实现):[register-events.js:161-281](file://client/public/videoplayer/js/register-events.js#L161-L281)