TiXL FreeDInput 算子详解:基于 UDP 的 FreeD 摄像机跟踪数据接收与虚拟制片集成
【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3
FreeDInput 是 TiXL(tooll3)开源实时动态图形软件中 Lib.io.freed 命名空间下负责接收FreeD 摄像机跟踪数据的核心算子:它通过 UDP 监听端口,解析标准的 29 字节 FreeD 数据包,并把每台摄像机的旋转、位置、变焦、聚焦等信息实时输出为 GPU 缓冲、字典与单值输出,供虚拟制片、增强现实与广电制播场景使用。读完本文,你将掌握 FreeDInput 的全部输入参数与输出语义、底层数据包解析原理(24 位大端整数、校验和、缩放比例),并能在 TiXL 中完成与 Unreal Engine、Vizrt、Aximmetry 等系统的实时联调。
背景:FreeD 协议与虚拟制片中的摄像机跟踪
FreeD 是一种轻量级 UDP 协议,广泛用于将摄像机云台跟踪数据(Pan/Tilt/Roll、X/Y/Z 位置、镜头 Zoom/Focus 编码值)从跟踪系统实时传输给渲染引擎或合成系统。在虚拟制片(Virtual Production)、增强现实(AR)和广电制播流程中,虚拟摄像机必须与物理摄像机保持完全同步——这正是 FreeDInput 的用武之地:
- Unreal Engine / Vizrt / Aximmetry等系统通常作为 FreeD 数据包发送端或接收端;
- TiXL 通过 FreeDInput 以同等身份接入这条数据链路,把跟踪数据驱动场景中的虚拟摄像机、匹配前景与背景画面;
- 配合同一命名空间下的 FreeDOutput(发送端),可以在 TiXL 内部完成"接收—处理—再发送"的完整闭环。
该算子定义于 Operators/Lib/Symbols/io/freed/FreeDInput.cs,与其配套的FreeDInput.t3/FreeDInput.t3ui符号与界面文件位于同一目录,文档由算子库自动生成。
输入参数
FreeDInput 共提供 5 个输入参数,覆盖了 UDP 监听所需的全部关键设置:
| 名称(相关性与类型) | 说明 | 默认值与源码依据 |
|---|---|---|
| Listen(Boolean) | 启用后打开 UDP 套接字并开始监听;禁用则关闭连接 | 默认false。每次更新时比较新旧状态,发生变化才执行启停(见 FreeDInput.cs) |
| LocalIpAddress(String) | 监听所用的本机网卡 IP;0.0.0.0 (Any)表示监听所有可用网卡,通常直接使用该值 | 默认"0.0.0.0 (Any)"。该输入是一个下拉框(ICustomDropdownHolder),自动枚举本机所有 IPv4 地址,见 FreeDInput.cs |
| Port(Int32) | 监听的 UDP 端口,必须与发送方广播的目标端口一致 | 默认6000(FreeDInput.cs) |
| CameraId(Int32) | 选择输出到单摄像机输出(CameraPos、CameraRot 等)的特定摄像机 ID;设为-1时自动选择 ID 最小的摄像机 | 默认-1,见 FreeDInput.cs |
| PrintToLog(Boolean) | 启用后把详细活动与错误信息打印到 T3 日志控制台,便于调试 | 默认false |
参数源码解读
- 设置变更检测:算子每帧比较
Listen、LocalIpAddress、Port的当前值与上次状态,只有发生变化时才关闭旧套接字并重新监听,避免频繁重建 UDP 连接造成丢包或抖动(FreeDInput.cs)。 - 网卡下拉框:
GetOptionsForInput会枚举所有"状态为 Up 且非回环"的网卡的 IPv4 单播地址,并在列表头部固定提供0.0.0.0 (Any)与127.0.0.1,选择后通过SetTypedInputValue写回输入(FreeDInput.cs)。 - 地址解析兜底:监听循环中,若 IP 无法解析或为空,则回退到
IPAddress.Any(监听所有网卡),行为与0.0.0.0 (Any)一致(FreeDInput.cs)。
输出参数
FreeDInput 提供 8 个输出,分为"全部摄像机(批量)"与"单摄像机(精选)"两组:
| 名称 | 类型 | 说明 |
|---|---|---|
| CamerasAsBuffer | T3.Core.DataTypes.BufferWithViews | 所有已跟踪摄像机的 GPU 结构化缓冲(每个元素一个Point),可直接送入渲染管线 |
| CameraDataAsDict | T3.Core.DataTypes.Dict`1[System.Single] | 以路径为键的字典,涵盖每台摄像机的 Pan/Tilt/Roll、PosX/PosY/PosZ、Zoom、Focus、User |
| IsListening | System.Boolean | 当前是否正在监听(监听线程处于 Running 状态) |
| CameraPos | System.Numerics.Vector3 | 选定摄像机的世界位置(米) |
| CameraRot | System.Numerics.Vector3 | 选定摄像机的旋转(度) |
| Zoom | System.Single | 选定摄像机的变焦编码值 |
| Focus | System.Single | 选定摄像机的聚焦编码值 |
| User | System.Single | 选定摄像机的自定义用户数据 |
所有输出均标记为DirtyFlagTrigger.Animated,并由同一个Update方法驱动(FreeDInput.cs)。
批量输出:CamerasAsBuffer 与 CameraDataAsDict
当至少收到一台摄像机的数据后,算子会按摄像机 ID 升序遍历内部字典,生成两个并行视图:
- GPU 缓冲:每个摄像机映射为一个 Point 结构体(64 字节,
Stride = 16 * 4),其中:Position保存摄像机位置;Orientation保存由yaw/pitch/roll(度转弧度)经Quaternion.CreateFromYawPitchRoll构造的四元数;Scale编码为(Focus, Zoom, User);F1存放摄像机 ID,Color为Vector4.One(FreeDInput.cs)。
- 字典输出:键形如
/{id}/Pan、/{id}/Tilt、/{id}/Roll、/{id}/PosX、/{id}/PosY、/{id}/PosZ、/{id}/Zoom、/{id}/Focus、/{id}/User,方便用字符串路径在算子图中取任意一台摄像机的任意通道。
缓冲通过ResourceManager.SetupStructuredBuffer创建,并同时生成 SRV 与 UAV(FreeDInput.cs);缓冲区尺寸不变时仅用UpdateSubresource增量上传,避免每帧重新分配 GPU 资源。
单摄像机输出
CameraId指定要读取的摄像机:大于等于 0 时按该 ID 查找;为-1时自动选择当前已跟踪摄像机中 ID 最小的那台。找到后,其位置、旋转、变焦、聚焦与用户数据分别写入CameraPos、CameraRot、Zoom、Focus、User五个输出(FreeDInput.cs)。
FreeD 数据包解析原理
FreeDInput 内置了完整的 FreeD 协议解析实现,这对理解数据精度与排查联调问题至关重要。
包格式(29 字节)
解析入口ParseFreeDDataPacket定义了如下布局(FreeDInput.cs):
| 偏移 | 长度 | 字段 | 解析方式 |
|---|---|---|---|
| 0 | 1 | 协议标识符 | 必须为0xD1,否则丢弃该包 |
| 1 | 1 | 摄像机 ID | byte,作为字典键 |
| 2 / 5 / 8 | 各 3 | Pan / Tilt / Roll | 24 位有符号大端整数,除以32768.0得到度 |
| 11 / 14 / 17 | 各 3 | PosX / PosY / PosZ | 24 位有符号大端整数,除以64000.0得到米 |
| 20 | 3 | Zoom | 24 位无符号大端整数 |
| 23 | 3 | Focus | 24 位无符号大端整数 |
| 26 | 2 | User | 16 位大端整数(data[26] << 8) \| data[27] |
| 28 | 1 | 校验和 | 必须等于0x40 - Σ(前 28 字节) & 0xFF,否则丢弃 |
关键实现细节
- 校验和:
CalculateFreeDChecksum取前 28 字节求和,再用0x40u - sum截断为单字节;解析时若与末字节不符则整包丢弃,有效防止错包污染跟踪数据(FreeDInput.cs)。 - 24 位有符号数的符号扩展:
ReadInt24BigEndian将三个字节拼成 32 位整数,若最高位(0x80)置位则或上0xFF000000完成符号扩展,正确处理负角度与负坐标(FreeDInput.cs)。 - 角度/距离缩放:旋转原始值除以
32768.0即得角度(度);位置原始值除以64000.0即得米。发送端 FreeDOutput.cs 使用完全相同的两个缩放常量做编码,二者严格互逆。 - 去重与覆盖:同一摄像机 ID 的新包直接覆盖旧值(
_trackedValues[newData.CameraId] = newData),因此同一 ID 的跟踪数据天然"最新值优先"(FreeDInput.cs)。
内部工作流:从 UDP 数据报到 GPU 缓冲
从网络字节流到可渲染数据,FreeDInput 内部按如下流程工作:
- 后台监听线程:启用
Listen后,StartListening启动一个Task.Run的后台任务,创建UdpClient,设置ReuseAddress套接字选项后Bind到目标 IP 与端口,随后循环ReceiveAsync(FreeDInput.cs)。 - 队列缓冲:收到的原始字节包压入
ConcurrentQueue<byte[]>(FreeDInput.cs),网络线程与主线程解耦,避免阻塞渲染循环。 - 主线程解析:算子在每次求值更新时把队列中的包全部取出并解析,写入以摄像机 ID 为键的
ConcurrentDictionary<byte, FreeDCameraData>(FreeDInput.cs)。 - 输出装配:如上文所述,生成字典输出、单摄像机输出与 GPU 结构化缓冲。
- 状态报告:算子实现 IStatusProvider,通过状态栏向用户反馈:
- 监听中但未收到数据:
"Listening, no camera data received yet."(Notice); - 正常跟踪:
"Tracking N cameras."(Success); - 监听线程异常退出:
"Listener failed: ..."(Error); - 未启用监听:
"Not listening."。
- 监听中但未收到数据:
实操指南:在 TiXL 中接入 FreeD 跟踪源
以下是以 FreeDInput 为核心的最小联调流程:
- 创建算子:在算子库的
Lib.io.freed分组中拖入FreeDInput。 - 配置监听:
Listen设为true;LocalIpAddress保留0.0.0.0 (Any)(监听所有网卡,通常最省心);若主机存在多网卡且需要指定,可从此参数的下拉框中直接选择具体 IPv4 地址;Port设置为与发送端一致的端口(双方默认都是6000)。
- 确认数据进入:观察算子状态栏应显示
Tracking N cameras.;若一直显示"no camera data received",检查发送端是否已启动、端口与目标 IP 是否正确。 - 消费数据:
- 驱动虚拟摄像机:把
CameraPos、CameraRot、Zoom连接到对应算子的位置/旋转/参数输入; - 批量渲染:把
CamerasAsBuffer直接接到点云/粒子类算子的输入; - 逐通道取值:用
CameraDataAsDict的字符串键(如/1/Pan)在算子图中按路径读取任意通道。
- 驱动虚拟摄像机:把
- 多摄像机:发送端可同时发送多台摄像机的数据,FreeDInput 会自动按 ID 归类;用
CameraId切换单摄像机输出的目标,或用字典/缓冲输出批量处理全部摄像机。 - 调试:打开
PrintToLog,T3 日志控制台会输出监听绑定信息(Bound to ip:port)、解析摘要(含 Zoom/Focus/User)与队列长度等详细日志(FreeDInput.cs)。
无发送端时的自测方法
仓库中的 FreeDOutput 算子与 FreeDInput 使用相同的包格式、缩放常量与默认端口 6000。在没有外部跟踪设备时,可以用 FreeDOutput 指向 FreeDInput 的监听地址发送测试包,验证整条解析链路;这也证明了两个算子可以在同一台机器上完成"发送—接收"闭环联调。
常见问题与排查
| 现象 | 可能原因与处理 |
|---|---|
| 状态栏一直显示"no camera data received" | 发送端未启动、端口不一致、或发送目标 IP 未指向本机;确认双方端口一致 |
| 监听失败(Listener failed) | 端口被占用或 IP 无效;更换端口,或改选下拉框中的具体网卡 IP |
| 单摄像机输出无数据 | 检查CameraId是否为-1或对应的合法 ID;发送端可能没有发送该 ID 的数据 |
| 数据抖动/乱跳 | 校验和不过的包已被丢弃;确认发送端严格遵循 29 字节、0xD1标识与校验规则 |
| 需要同时处理多台摄像机 | 使用CamerasAsBuffer或CameraDataAsDict批量输出,它们会覆盖所有已跟踪的摄像机 ID |
扩展阅读
- 发送端算子:FreeDOutput 与 FreeDOutput.cs
- 命名空间总览:Lib.io.freed README、Lib.io
- GPU 缓冲元素定义:Point(
Position/Orientation/Scale/F1布局) - 状态反馈接口:IStatusProvider
【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考