Files
video_socket-server/.qoder/repowiki/zh/content/客户端示例/视频播放器示例.md
2026-05-16 13:24:02 +08:00

17 KiB
Raw Permalink Blame History

视频播放器示例

**本文档引用的文件** - [video-player.js](file://client/public/videoplayer/js/video-player.js) - [gamepadEvents.js](file://client/public/videoplayer/js/gamepadEvents.js) - [register-events.js](file://client/public/videoplayer/js/register-events.js) - [main.js](file://client/public/videoplayer/js/main.js) - [index.html](file://client/public/videoplayer/index.html) - [peer.js](file://client/src/peer.js) - [signaling.js](file://client/src/signaling.js) - [renderstreaming.js](file://client/src/renderstreaming.js) - [sender.js](file://client/src/sender.js) - [inputdevice.js](file://client/src/inputdevice.js) - [inputremoting.js](file://client/src/inputremoting.js) - [logger.js](file://client/src/logger.js) - [keymap.js](file://client/src/keymap.js) - [mousebutton.js](file://client/src/mousebutton.js) - [gamepadbutton.js](file://client/src/gamepadbutton.js)

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能考虑
  8. 故障排除指南
  9. 结论
  10. 附录

简介

本示例演示了如何在浏览器中接收 Unity 渲染的相机图像,并通过浏览器端操作 Unity 中的相机。系统采用 WebRTC 进行媒体流传输,同时通过数据通道发送用户输入事件(键盘、鼠标、触摸、游戏手柄)。播放器支持主画中画双路视频切换、缩放与坐标转换、以及多种输入设备的实时控制。

项目结构

视频播放器示例位于 Public 模式下的 videoplayer 子目录,主要由以下层次组成:

  • 视频播放层:负责建立 WebRTC 连接、接收媒体轨道、管理数据通道、处理来自 Unity 的控制消息。
  • 事件注册层:封装各类输入事件的监听与打包,统一通过数据通道发送至 Unity。
  • WebRTC 基础层:封装 PeerConnection、信令协议、ICE 候选等底层细节。
  • 输入设备抽象层:定义输入设备、状态与事件的数据格式,便于序列化与跨平台传输。
graph TB
subgraph "浏览器前端"
VP["VideoPlayer<br/>视频播放器"]
RE["register-events.js<br/>事件注册"]
GP["gamepadEvents.js<br/>游戏手柄事件"]
PEER["Peer<br/>RTCPeerConnection封装"]
SIG["Signaling/WebSocketSignaling<br/>信令"]
end
subgraph "Unity 后端"
RS["RenderStreaming<br/>渲染流封装"]
SENDER["Sender<br/>输入发送器"]
INPUTDEV["InputDevice/IInputState<br/>输入设备与状态"]
end
VP --> PEER
VP --> SIG
RE --> VP
GP --> RE
PEER --> SIG
RS --> PEER
SENDER --> RS
INPUTDEV --> SENDER

图表来源

章节来源

核心组件

  • VideoPlayer负责创建 RTCPeerConnection、订阅媒体轨道、管理数据通道、解析来自 Unity 的控制消息(如切换视频源)。
  • register-events.js集中注册键盘、鼠标、触摸、游戏手柄事件将事件打包为二进制消息并通过数据通道发送。
  • gamepadEvents.js对原生游戏手柄 API 进行封装,生成统一的自定义事件(按钮按下/抬起/持续按住、轴值变化),并维护设备状态。
  • Peer封装 RTCPeerConnection 生命周期、SDP 协商、ICE 候选处理与断线检测。
  • Signaling/WebSocketSignalingHTTP 轮询或 WebSocket 实现的信令通道,承载 offer/answer/candidate/on-message 等信令。
  • RenderStreaming/Sender/InputDevice输入遥测链路的完整实现定义输入设备、状态与事件的内存布局支持鼠标、键盘、触摸、游戏手柄。

章节来源

架构总览

系统采用“浏览器播放器 + 信令 + WebRTC + 数据通道”的架构。浏览器端通过 Signaling 与服务器建立连接,协商 SDP 并交换 ICE 候选;媒体轨道通过 RTCPeerConnection 接收数据通道用于传输输入事件。Unity 侧通过 RenderStreaming/Sender 将输入事件投递到远程渲染环境,同时可向浏览器发送控制消息(如切换视频源)。

sequenceDiagram
participant Browser as "浏览器"
participant VP as "VideoPlayer"
participant PEER as "Peer"
participant SIG as "Signaling/WebSocketSignaling"
participant Unity as "Unity"
Browser->>VP : 初始化并调用 setupConnection()
VP->>SIG : start() 启动信令
VP->>PEER : 创建 RTCPeerConnection
PEER->>SIG : 发送 offer
SIG-->>PEER : 下发 offer
PEER->>SIG : 发送 answer
SIG-->>PEER : 下发 answer
PEER->>SIG : 发送 ICE 候选
SIG-->>PEER : 下发 ICE 候选
PEER-->>VP : 触发 ontrack 获取媒体轨道
VP->>VP : 切换主/副视频轨道
Unity-->>VP : 数据通道下发控制消息
VP->>VP : 解析消息并执行操作

图表来源

详细组件分析

VideoPlayer 组件分析

VideoPlayer 是播放器的核心,负责:

  • 建立 RTCPeerConnection 并绑定事件trackevent、sendoffer、sendanswer、sendcandidate
  • 订阅媒体轨道,维护主/副视频轨道列表,支持动态切换。
  • 创建数据通道,接收来自 Unity 的控制消息并执行相应操作(如切换视频源)。
  • 提供尺寸与坐标转换能力,确保输入事件映射到正确的视频区域。
classDiagram
class VideoPlayer {
+constructor(elements)
+setupConnection(useWebSocket)
+resizeVideo()
+switchVideo(indexVideoTrack)
+replaceTrack(stream, newTrack)
+sendMsg(msg)
+stop()
+ondisconnect
+videoWidth
+videoHeight
+videoOriginX
+videoOriginY
+videoScale
}
class Peer {
+addEventListener(event, handler)
+createDataChannel(connectionId, label)
+onGotDescription(connectionId, description)
+onGotCandidate(connectionId, candidate)
+close()
}
class Signaling {
+start()
+sendOffer(connectionId, sdp)
+sendAnswer(connectionId, sdp)
+sendCandidate(connectionId, candidate, sdpMid, sdpMLineIndex)
+addEventListener(event, handler)
+stop()
}
VideoPlayer --> Peer : "使用"
VideoPlayer --> Signaling : "使用"

图表来源

章节来源

register-events.js 事件注册与处理

该模块负责:

  • 键盘事件:按键按下/抬起,映射键码并发送二进制消息。
  • 鼠标事件:点击/移动/滚轮,计算相对于视频的坐标并发送。
  • 触摸事件:多点触控,记录每个触点的阶段、坐标、压力等信息。
  • 游戏手柄事件:统一的按钮与轴事件,生成自定义事件并转发给 VideoPlayer。
flowchart TD
Start(["开始"]) --> Detect["检测输入事件类型"]
Detect --> |键盘| BuildKey["构建键盘消息<br/>类型/方向/重复/键码/字符"]
Detect --> |鼠标| BuildMouse["构建鼠标消息<br/>坐标/按钮"]
Detect --> |触摸| BuildTouch["构建触摸消息<br/>触点数量/阶段/坐标/压力"]
Detect --> |游戏手柄| BuildGP["构建手柄消息<br/>按钮/轴值"]
BuildKey --> Send["通过数据通道发送"]
BuildMouse --> Send
BuildTouch --> Send
BuildGP --> Send
Send --> End(["结束"])

图表来源

章节来源

gamepadEvents.js 游戏手柄事件处理

该模块对原生游戏手柄 API 进行封装:

  • 维护设备连接时间戳与前一帧状态,避免重复触发。
  • 定时循环(约 60fps扫描按钮与轴值变化生成统一的自定义事件。
  • 支持不同浏览器差异(如 getGamepads 的兼容性)。
sequenceDiagram
participant Browser as "浏览器"
participant GPH as "gamepadEvents.js"
participant RE as "register-events.js"
participant VP as "VideoPlayer"
Browser->>GPH : 设备连接/断开
GPH->>GPH : 记录连接时间戳/初始化状态
loop 60fps
GPH->>GPH : 扫描按钮/轴值变化
alt 按钮状态变化
GPH-->>RE : 触发 gamepadButtonDown/gamepadButtonUp
else 按钮持续按住
GPH-->>RE : 触发 gamepadButtonPressed
end
alt 轴值变化
GPH-->>RE : 触发 gamepadAxis
end
end
RE->>VP : sendMsg(二进制消息)

图表来源

章节来源

输入设备抽象与序列化

输入设备层定义了统一的状态与事件格式,便于跨平台传输:

  • InputDevice抽象基类维护设备描述与当前状态。
  • IInputState抽象状态接口提供 buffer 与 format 字段。
  • 具体设备状态MouseState、KeyboardState、TouchscreenState、GamepadState。
  • 事件封装StateEvent、TextEvent支持二进制序列化。
classDiagram
class InputDevice {
+name
+layout
+deviceId
+usages
+description
+updateState(state)
+queueEvent(event)
+currentState
}
class IInputState {
+buffer
+format
}
class MouseState
class KeyboardState
class TouchscreenState
class GamepadState
class StateEvent
class TextEvent
InputDevice <|-- Mouse
InputDevice <|-- Keyboard
InputDevice <|-- Touchscreen
InputDevice <|-- Gamepad
IInputState <|-- MouseState
IInputState <|-- KeyboardState
IInputState <|-- TouchscreenState
IInputState <|-- GamepadState
StateEvent --> IInputState : "包含状态"
TextEvent --> InputEvent : "包含基础事件"

图表来源

章节来源

信令与 WebRTC 协商

  • Signaling基于 HTTP 的轮询信令,支持 offer/answer/candidate/on-message。
  • WebSocketSignaling基于 WebSocket 的实时信令,支持广播与参与者事件。
  • Peer封装 RTCPeerConnection自动处理 SDP 协商、ICE 候选、断线检测与重发 offer。
sequenceDiagram
participant A as "Peer A"
participant SIG as "Signaling"
participant B as "Peer B"
A->>SIG : 发送 offer
SIG-->>B : 下发 offer
B->>B : 设置远端 offer
B->>SIG : 发送 answer
SIG-->>A : 下发 answer
A->>A : 设置远端 answer
A->>SIG : 发送 ICE 候选
B->>SIG : 发送 ICE 候选
SIG-->>A : 下发 ICE 候选
SIG-->>B : 下发 ICE 候选

图表来源

章节来源

依赖关系分析

  • VideoPlayer 依赖 Peer 与 Signaling用于媒体与信令。
  • register-events 依赖 gamepadEvents 与 keymap用于事件注册与键码映射。
  • main.js 作为入口,协调 VideoPlayer 与事件注册模块。
  • 输入遥测链路由 Sender -> RenderStreaming -> Peer -> Signaling最终到达 Unity。
graph LR
MAIN["main.js"] --> VP["video-player.js"]
MAIN --> REG["register-events.js"]
REG --> GPE["gamepadEvents.js"]
VP --> PEER["peer.js"]
VP --> SIG["signaling.js"]
SENDER["sender.js"] --> RS["renderstreaming.js"]
RS --> PEER
INPUTDEV["inputdevice.js"] --> SENDER

图表来源

章节来源

性能考虑

  • 事件采样频率:游戏手柄事件以约 60fps 轮询,建议根据设备刷新率调整间隔,避免过度 CPU 占用。
  • 坐标转换VideoPlayer 提供视频缩放与偏移计算,确保输入事件映射准确,减少误触。
  • 数据通道:仅在通道打开时发送消息,避免在关闭或连接中发送导致异常。
  • 媒体轨道切换:使用 replaceTrack 替换视频轨道,避免频繁创建/销毁媒体流带来的抖动。

故障排除指南

  • 无法建立 WebRTC 连接
    • 检查信令是否正常启动与停止。
    • 确认 offer/answer 协商过程无异常ICE 候选是否正确交换。
    • 查看断线事件与日志输出。
  • 数据通道不可用
    • 确认数据通道已创建且处于 open 状态。
    • 检查 sendMsg 的状态分支,避免在 closing/closed 状态发送。
  • 输入事件未生效
    • 确认事件监听已注册,且消息格式与 Unity 期望一致。
    • 检查坐标转换参数videoScale、videoOriginX/Y是否正确。
  • 游戏手柄无响应
    • 确认浏览器支持 getGamepads 或 webkitGetGamepads。
    • 检查设备连接时间戳与状态缓存是否正确初始化。

章节来源

结论

该视频播放器示例完整展示了从浏览器接收 Unity 渲染视频、通过数据通道发送用户输入、以及在 Unity 中进行相机控制的端到端流程。其模块化设计使得事件注册、输入抽象与 WebRTC 协商清晰分离,便于扩展与维护。通过合理的坐标转换与事件采样策略,系统在多设备环境下具备良好的交互体验。

附录

使用指南

  • Public 模式配置
    • 通过配置接口获取服务器设置,决定使用 HTTP 轮询信令还是 WebSocket 信令。
    • 若启动模式为私有模式,页面会显示警告提示。
  • 启动播放器
    • 点击播放按钮后,页面动态创建主视频与缩略视频元素。
    • 初始化 VideoPlayer 并建立 WebRTC 连接。
  • 控制 Unity 中的相机
    • 通过数据通道发送控制消息(如切换视频源)。
    • 可通过按钮事件向 Unity 发送按钮点击消息。
  • 输入设备使用
    • 键盘:按键按下/抬起事件会被打包发送。
    • 鼠标:点击/移动/滚轮事件,坐标经视频缩放与偏移转换。
    • 触摸:多点触控,记录触点阶段、坐标、压力等。
    • 游戏手柄:按钮与轴值变化统一事件,支持不同浏览器差异。
  • 设备适配
    • 坐标系适配VideoPlayer 提供缩放与偏移计算,确保事件映射到正确区域。
    • 全屏模式:支持全屏切换,改变按钮可见性与布局。
    • 断线处理:断线时清理 DOM、停止播放器并重新显示播放按钮。

章节来源