412 lines
17 KiB
Markdown
412 lines
17 KiB
Markdown
|
|
# 视频播放器示例
|
|||
|
|
|
|||
|
|
<cite>
|
|||
|
|
**本文档引用的文件**
|
|||
|
|
- [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)
|
|||
|
|
</cite>
|
|||
|
|
|
|||
|
|
## 目录
|
|||
|
|
1. [简介](#简介)
|
|||
|
|
2. [项目结构](#项目结构)
|
|||
|
|
3. [核心组件](#核心组件)
|
|||
|
|
4. [架构总览](#架构总览)
|
|||
|
|
5. [详细组件分析](#详细组件分析)
|
|||
|
|
6. [依赖关系分析](#依赖关系分析)
|
|||
|
|
7. [性能考虑](#性能考虑)
|
|||
|
|
8. [故障排除指南](#故障排除指南)
|
|||
|
|
9. [结论](#结论)
|
|||
|
|
10. [附录](#附录)
|
|||
|
|
|
|||
|
|
## 简介
|
|||
|
|
本示例演示了如何在浏览器中接收 Unity 渲染的相机图像,并通过浏览器端操作 Unity 中的相机。系统采用 WebRTC 进行媒体流传输,同时通过数据通道发送用户输入事件(键盘、鼠标、触摸、游戏手柄)。播放器支持主画中画双路视频切换、缩放与坐标转换、以及多种输入设备的实时控制。
|
|||
|
|
|
|||
|
|
## 项目结构
|
|||
|
|
视频播放器示例位于 Public 模式下的 videoplayer 子目录,主要由以下层次组成:
|
|||
|
|
- 视频播放层:负责建立 WebRTC 连接、接收媒体轨道、管理数据通道、处理来自 Unity 的控制消息。
|
|||
|
|
- 事件注册层:封装各类输入事件的监听与打包,统一通过数据通道发送至 Unity。
|
|||
|
|
- WebRTC 基础层:封装 PeerConnection、信令协议、ICE 候选等底层细节。
|
|||
|
|
- 输入设备抽象层:定义输入设备、状态与事件的数据格式,便于序列化与跨平台传输。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
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
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图表来源**
|
|||
|
|
- [video-player.js:18-247](file://client/public/videoplayer/js/video-player.js#L18-L247)
|
|||
|
|
- [register-events.js:1-308](file://client/public/videoplayer/js/register-events.js#L1-L308)
|
|||
|
|
- [gamepadEvents.js:1-147](file://client/public/videoplayer/js/gamepadEvents.js#L1-L147)
|
|||
|
|
- [peer.js:1-188](file://client/src/peer.js#L1-L188)
|
|||
|
|
- [signaling.js:1-292](file://client/src/signaling.js#L1-L292)
|
|||
|
|
- [renderstreaming.js:1-317](file://client/src/renderstreaming.js#L1-L317)
|
|||
|
|
- [sender.js:1-209](file://client/src/sender.js#L1-L209)
|
|||
|
|
- [inputdevice.js:1-719](file://client/src/inputdevice.js#L1-L719)
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [index.html:1-38](file://client/public/videoplayer/index.html#L1-L38)
|
|||
|
|
- [main.js:1-157](file://client/public/videoplayer/js/main.js#L1-L157)
|
|||
|
|
|
|||
|
|
## 核心组件
|
|||
|
|
- VideoPlayer:负责创建 RTCPeerConnection、订阅媒体轨道、管理数据通道、解析来自 Unity 的控制消息(如切换视频源)。
|
|||
|
|
- register-events.js:集中注册键盘、鼠标、触摸、游戏手柄事件,将事件打包为二进制消息并通过数据通道发送。
|
|||
|
|
- gamepadEvents.js:对原生游戏手柄 API 进行封装,生成统一的自定义事件(按钮按下/抬起/持续按住、轴值变化),并维护设备状态。
|
|||
|
|
- Peer:封装 RTCPeerConnection 生命周期、SDP 协商、ICE 候选处理与断线检测。
|
|||
|
|
- Signaling/WebSocketSignaling:HTTP 轮询或 WebSocket 实现的信令通道,承载 offer/answer/candidate/on-message 等信令。
|
|||
|
|
- RenderStreaming/Sender/InputDevice:输入遥测链路的完整实现,定义输入设备、状态与事件的内存布局,支持鼠标、键盘、触摸、游戏手柄。
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [video-player.js:18-247](file://client/public/videoplayer/js/video-player.js#L18-L247)
|
|||
|
|
- [register-events.js:1-308](file://client/public/videoplayer/js/register-events.js#L1-L308)
|
|||
|
|
- [gamepadEvents.js:1-147](file://client/public/videoplayer/js/gamepadEvents.js#L1-L147)
|
|||
|
|
- [peer.js:1-188](file://client/src/peer.js#L1-L188)
|
|||
|
|
- [signaling.js:1-292](file://client/src/signaling.js#L1-L292)
|
|||
|
|
- [renderstreaming.js:1-317](file://client/src/renderstreaming.js#L1-L317)
|
|||
|
|
- [sender.js:1-209](file://client/src/sender.js#L1-L209)
|
|||
|
|
- [inputdevice.js:1-719](file://client/src/inputdevice.js#L1-L719)
|
|||
|
|
|
|||
|
|
## 架构总览
|
|||
|
|
系统采用“浏览器播放器 + 信令 + WebRTC + 数据通道”的架构。浏览器端通过 Signaling 与服务器建立连接,协商 SDP 并交换 ICE 候选;媒体轨道通过 RTCPeerConnection 接收,数据通道用于传输输入事件。Unity 侧通过 RenderStreaming/Sender 将输入事件投递到远程渲染环境,同时可向浏览器发送控制消息(如切换视频源)。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
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 : 解析消息并执行操作
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图表来源**
|
|||
|
|
- [video-player.js:49-155](file://client/public/videoplayer/js/video-player.js#L49-L155)
|
|||
|
|
- [peer.js:57-173](file://client/src/peer.js#L57-L173)
|
|||
|
|
- [signaling.js:30-149](file://client/src/signaling.js#L30-L149)
|
|||
|
|
|
|||
|
|
## 详细组件分析
|
|||
|
|
|
|||
|
|
### VideoPlayer 组件分析
|
|||
|
|
VideoPlayer 是播放器的核心,负责:
|
|||
|
|
- 建立 RTCPeerConnection 并绑定事件(trackevent、sendoffer、sendanswer、sendcandidate)。
|
|||
|
|
- 订阅媒体轨道,维护主/副视频轨道列表,支持动态切换。
|
|||
|
|
- 创建数据通道,接收来自 Unity 的控制消息并执行相应操作(如切换视频源)。
|
|||
|
|
- 提供尺寸与坐标转换能力,确保输入事件映射到正确的视频区域。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
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 : "使用"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图表来源**
|
|||
|
|
- [video-player.js:18-247](file://client/public/videoplayer/js/video-player.js#L18-L247)
|
|||
|
|
- [peer.js:1-188](file://client/src/peer.js#L1-L188)
|
|||
|
|
- [signaling.js:1-292](file://client/src/signaling.js#L1-L292)
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [video-player.js:18-247](file://client/public/videoplayer/js/video-player.js#L18-L247)
|
|||
|
|
|
|||
|
|
### register-events.js 事件注册与处理
|
|||
|
|
该模块负责:
|
|||
|
|
- 键盘事件:按键按下/抬起,映射键码并发送二进制消息。
|
|||
|
|
- 鼠标事件:点击/移动/滚轮,计算相对于视频的坐标并发送。
|
|||
|
|
- 触摸事件:多点触控,记录每个触点的阶段、坐标、压力等信息。
|
|||
|
|
- 游戏手柄事件:统一的按钮与轴事件,生成自定义事件并转发给 VideoPlayer。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
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(["结束"])
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图表来源**
|
|||
|
|
- [register-events.js:120-281](file://client/public/videoplayer/js/register-events.js#L120-L281)
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [register-events.js:1-308](file://client/public/videoplayer/js/register-events.js#L1-L308)
|
|||
|
|
|
|||
|
|
### gamepadEvents.js 游戏手柄事件处理
|
|||
|
|
该模块对原生游戏手柄 API 进行封装:
|
|||
|
|
- 维护设备连接时间戳与前一帧状态,避免重复触发。
|
|||
|
|
- 定时循环(约 60fps)扫描按钮与轴值变化,生成统一的自定义事件。
|
|||
|
|
- 支持不同浏览器差异(如 getGamepads 的兼容性)。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
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(二进制消息)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图表来源**
|
|||
|
|
- [gamepadEvents.js:68-94](file://client/public/videoplayer/js/gamepadEvents.js#L68-L94)
|
|||
|
|
- [register-events.js:42-101](file://client/public/videoplayer/js/register-events.js#L42-L101)
|
|||
|
|
- [video-player.js:215-233](file://client/public/videoplayer/js/video-player.js#L215-L233)
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [gamepadEvents.js:1-147](file://client/public/videoplayer/js/gamepadEvents.js#L1-L147)
|
|||
|
|
- [register-events.js:42-101](file://client/public/videoplayer/js/register-events.js#L42-L101)
|
|||
|
|
|
|||
|
|
### 输入设备抽象与序列化
|
|||
|
|
输入设备层定义了统一的状态与事件格式,便于跨平台传输:
|
|||
|
|
- InputDevice:抽象基类,维护设备描述与当前状态。
|
|||
|
|
- IInputState:抽象状态接口,提供 buffer 与 format 字段。
|
|||
|
|
- 具体设备状态:MouseState、KeyboardState、TouchscreenState、GamepadState。
|
|||
|
|
- 事件封装:StateEvent、TextEvent,支持二进制序列化。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
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 : "包含基础事件"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图表来源**
|
|||
|
|
- [inputdevice.js:40-719](file://client/src/inputdevice.js#L40-L719)
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [inputdevice.js:1-719](file://client/src/inputdevice.js#L1-L719)
|
|||
|
|
|
|||
|
|
### 信令与 WebRTC 协商
|
|||
|
|
- Signaling:基于 HTTP 的轮询信令,支持 offer/answer/candidate/on-message。
|
|||
|
|
- WebSocketSignaling:基于 WebSocket 的实时信令,支持广播与参与者事件。
|
|||
|
|
- Peer:封装 RTCPeerConnection,自动处理 SDP 协商、ICE 候选、断线检测与重发 offer。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
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 候选
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图表来源**
|
|||
|
|
- [peer.js:57-173](file://client/src/peer.js#L57-L173)
|
|||
|
|
- [signaling.js:117-146](file://client/src/signaling.js#L117-L146)
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [peer.js:1-188](file://client/src/peer.js#L1-L188)
|
|||
|
|
- [signaling.js:1-292](file://client/src/signaling.js#L1-L292)
|
|||
|
|
|
|||
|
|
## 依赖关系分析
|
|||
|
|
- VideoPlayer 依赖 Peer 与 Signaling,用于媒体与信令。
|
|||
|
|
- register-events 依赖 gamepadEvents 与 keymap,用于事件注册与键码映射。
|
|||
|
|
- main.js 作为入口,协调 VideoPlayer 与事件注册模块。
|
|||
|
|
- 输入遥测链路由 Sender -> RenderStreaming -> Peer -> Signaling,最终到达 Unity。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
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
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图表来源**
|
|||
|
|
- [main.js:1-157](file://client/public/videoplayer/js/main.js#L1-L157)
|
|||
|
|
- [video-player.js:1-247](file://client/public/videoplayer/js/video-player.js#L1-L247)
|
|||
|
|
- [register-events.js:1-308](file://client/public/videoplayer/js/register-events.js#L1-L308)
|
|||
|
|
- [gamepadEvents.js:1-147](file://client/public/videoplayer/js/gamepadEvents.js#L1-L147)
|
|||
|
|
- [peer.js:1-188](file://client/src/peer.js#L1-L188)
|
|||
|
|
- [signaling.js:1-292](file://client/src/signaling.js#L1-L292)
|
|||
|
|
- [sender.js:1-209](file://client/src/sender.js#L1-L209)
|
|||
|
|
- [renderstreaming.js:1-317](file://client/src/renderstreaming.js#L1-L317)
|
|||
|
|
- [inputdevice.js:1-719](file://client/src/inputdevice.js#L1-L719)
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [main.js:1-157](file://client/public/videoplayer/js/main.js#L1-L157)
|
|||
|
|
|
|||
|
|
## 性能考虑
|
|||
|
|
- 事件采样频率:游戏手柄事件以约 60fps 轮询,建议根据设备刷新率调整间隔,避免过度 CPU 占用。
|
|||
|
|
- 坐标转换:VideoPlayer 提供视频缩放与偏移计算,确保输入事件映射准确,减少误触。
|
|||
|
|
- 数据通道:仅在通道打开时发送消息,避免在关闭或连接中发送导致异常。
|
|||
|
|
- 媒体轨道切换:使用 replaceTrack 替换视频轨道,避免频繁创建/销毁媒体流带来的抖动。
|
|||
|
|
|
|||
|
|
## 故障排除指南
|
|||
|
|
- 无法建立 WebRTC 连接
|
|||
|
|
- 检查信令是否正常启动与停止。
|
|||
|
|
- 确认 offer/answer 协商过程无异常,ICE 候选是否正确交换。
|
|||
|
|
- 查看断线事件与日志输出。
|
|||
|
|
- 数据通道不可用
|
|||
|
|
- 确认数据通道已创建且处于 open 状态。
|
|||
|
|
- 检查 sendMsg 的状态分支,避免在 closing/closed 状态发送。
|
|||
|
|
- 输入事件未生效
|
|||
|
|
- 确认事件监听已注册,且消息格式与 Unity 期望一致。
|
|||
|
|
- 检查坐标转换参数(videoScale、videoOriginX/Y)是否正确。
|
|||
|
|
- 游戏手柄无响应
|
|||
|
|
- 确认浏览器支持 getGamepads 或 webkitGetGamepads。
|
|||
|
|
- 检查设备连接时间戳与状态缓存是否正确初始化。
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [video-player.js:128-154](file://client/public/videoplayer/js/video-player.js#L128-L154)
|
|||
|
|
- [register-events.js:237-263](file://client/public/videoplayer/js/register-events.js#L237-L263)
|
|||
|
|
- [gamepadEvents.js:112-146](file://client/public/videoplayer/js/gamepadEvents.js#L112-L146)
|
|||
|
|
- [logger.js:1-30](file://client/src/logger.js#L1-L30)
|
|||
|
|
|
|||
|
|
## 结论
|
|||
|
|
该视频播放器示例完整展示了从浏览器接收 Unity 渲染视频、通过数据通道发送用户输入、以及在 Unity 中进行相机控制的端到端流程。其模块化设计使得事件注册、输入抽象与 WebRTC 协商清晰分离,便于扩展与维护。通过合理的坐标转换与事件采样策略,系统在多设备环境下具备良好的交互体验。
|
|||
|
|
|
|||
|
|
## 附录
|
|||
|
|
|
|||
|
|
### 使用指南
|
|||
|
|
- Public 模式配置
|
|||
|
|
- 通过配置接口获取服务器设置,决定使用 HTTP 轮询信令还是 WebSocket 信令。
|
|||
|
|
- 若启动模式为私有模式,页面会显示警告提示。
|
|||
|
|
- 启动播放器
|
|||
|
|
- 点击播放按钮后,页面动态创建主视频与缩略视频元素。
|
|||
|
|
- 初始化 VideoPlayer 并建立 WebRTC 连接。
|
|||
|
|
- 控制 Unity 中的相机
|
|||
|
|
- 通过数据通道发送控制消息(如切换视频源)。
|
|||
|
|
- 可通过按钮事件向 Unity 发送按钮点击消息。
|
|||
|
|
- 输入设备使用
|
|||
|
|
- 键盘:按键按下/抬起事件会被打包发送。
|
|||
|
|
- 鼠标:点击/移动/滚轮事件,坐标经视频缩放与偏移转换。
|
|||
|
|
- 触摸:多点触控,记录触点阶段、坐标、压力等。
|
|||
|
|
- 游戏手柄:按钮与轴值变化统一事件,支持不同浏览器差异。
|
|||
|
|
- 设备适配
|
|||
|
|
- 坐标系适配:VideoPlayer 提供缩放与偏移计算,确保事件映射到正确区域。
|
|||
|
|
- 全屏模式:支持全屏切换,改变按钮可见性与布局。
|
|||
|
|
- 断线处理:断线时清理 DOM、停止播放器并重新显示播放按钮。
|
|||
|
|
|
|||
|
|
**章节来源**
|
|||
|
|
- [main.js:23-157](file://client/public/videoplayer/js/main.js#L23-L157)
|
|||
|
|
- [video-player.js:49-247](file://client/public/videoplayer/js/video-player.js#L49-L247)
|
|||
|
|
- [register-events.js:120-308](file://client/public/videoplayer/js/register-events.js#L120-L308)
|
|||
|
|
- [gamepadEvents.js:112-147](file://client/public/videoplayer/js/gamepadEvents.js#L112-L147)
|