ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

TiXL 实时图形与 Spout:SpoutOutput 算子跨进程视频发送实战指南

TiXL 实时图形与 Spout:SpoutOutput 算子跨进程视频发送实战指南 TiXL 实时图形与 SpoutSpoutOutput 算子跨进程视频发送实战指南【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3本指南以 TiXL 算子库Lib.io.video命名空间下的SpoutOutput算子为核心讲解如何把 TiXL 渲染的任意纹理Texture以 Spout 协议实时发送给 Resolume、TouchDesigner、OBS 等其他支持 Spout 的应用程序实现多软件间零拷贝、低延迟的实时视频共享。读完本文你将掌握 SpoutOutput 的输入输出参数、推荐纹理格式R8G8B8A8_UNorm、RenderTarget 链路搭建方法以及底层基于 SpoutDX 的发送实现与常见故障排查要点。1. SpoutOutput 是什么SpoutOutput是 TiXL 在Lib.io.video命名空间下提供的实时视频输出算子位于 Operators/Spout/Symbols/lib/io/video/SpoutOutput.cs。它把 TiXL 内部渲染出的纹理通过Spout协议以“发送方Sender”身份广播给同一台机器上的其他应用程序——例如 Resolume、MadMapper、TouchDesigner、OBS 等——从而让 TiXL 生成的实时动态图形realtime motion graphics可以无缝进入对方的合成流程。Spout 是一种基于 GPU 共享纹理shared texture的实时视频共享协议发送方与接收方在同一张显卡上共享纹理内存不需要经过 CPU 回读或编码传输因此延迟极低非常适合现场演出、VJVisual Jockey场景。与同命名空间下的 NdiOutput走网络编码传输不同Spout 是本机进程间传输方案通常延迟更小、带宽消耗为零。与之配套的是SpoutInput算子SpoutInput.cs用于在本机接收其他应用通过 Spout 发送的纹理二者共同构成 TiXL 与外部软件的实时视频互通桥梁。2. 输入输出参数速览SpoutOutput 算子对外暴露两个输入参数和一个输出官方文档原文如下Input ParametersName (Relevancy Type)DescriptionTexture(Texture2D Required)要发送的纹理必填SenderName(String)Spout 发送方名称供接收端识别OutputsNameTypeTextureOutputT3.Core.DataTypes.Texture2D从源码可以确认这两个输入的定义SpoutOutput.cs[Input(Guid d4b5c642-9cb9-4f41-8739-edbb9c6c4857)] public readonly InputSlotTexture2D Texture new(); [Input(Guid 7C27EBD7-3746-4B70-A252-DD0AC0445B74)] public readonly InputSlotstring SenderName new();对应的算子模板文件 SpoutOutput.t3 中保存了这两个输入的默认值其中SenderName的默认值是字符串tixl{ FormatVersion: 3, Id: 13be1e3f-861d-4350-a94e-e083637b3e55/*SpoutOutput*/, Inputs: [ { Id: 7c27ebd7-3746-4b70-a252-dd0ac0445b74/*SenderName*/, DefaultValue: tixl }, { Id: d4b5c642-9cb9-4f41-8739-edbb9c6c4857/*Texture*/, DefaultValue: null } ], Children: [], Connections: [] }要点说明Texture必填这是真正被发送的内容。如果输入纹理为空null发送逻辑会直接返回不会产生任何输出源码SendTexture方法第一行即检查frame null。SenderName发送方的名字接收端应用正是通过这个名字来枚举并选择发送源的。默认值为tixl如果本机存在多个同名发送方Spout 会为实际创建的 sender 追加后缀区分源码在InitializeSpout中会把 Spout 返回的实际名字回写到输入框SenderName.SetTypedInputValue(_senderName)保证界面上显示的名字与真实 sender 名一致。TextureOutput一个直通输出pass-through把输入的纹理原样输出。它被标记为DirtyFlagTrigger.AnimatedSpoutOutput.cs意味着每帧都会重新求值发送保证实时性。3. 推荐的纹理格式R8G8B8A8_UNorm官方文档明确给出建议We recommend using the R8G8B8A8_UNorm texture format for output. You can adjust the render format by using a [RenderTarget] like in this example: [TorusMesh]-[DrawMesh]-[RenderTarget]-[SpoutOutput].即推荐使用R8G8B8A8_UNorm8 位 RGBA非归一化格式作为输出纹理格式。之所以推荐是因为它是 Spout 生态中最通用、兼容性最好的格式——绝大多数接收端Resolume、TouchDesigner 等对 R8G8B8A8 的共享纹理支持最完善。从源码可以进一步看到格式的实际影响。SendTexture中维护了一个支持格式白名单SpoutOutput.csif (readableImage.Description.Format ! Format.B8G8R8A8_UNorm readableImage.Description.Format ! Format.B8G8R8A8_Typeless readableImage.Description.Format ! Format.R8G8B8A8_UNorm readableImage.Description.Format ! Format.R16G16B16A16_UNorm readableImage.Description.Format ! Format.R16G16B16A16_Typeless readableImage.Description.Format ! Format.R16G16B16A16_Float) { if (!_conversionWarning) { Log.Debug($Spout doesnt support {readableImage.Description.Format}, trying to fallback to R16G16B16A16_Float); _conversionWarning true; } dxTex (DXTexture2D)_textureConverter.ConvertToCpuReadableBgra(readableImage); }这意味着如果输入纹理正好是R8G8B8A8_UNorm或白名单中的其他格式纹理会被直接发送不经过任何转换性能最好如果输入是白名单以外的格式例如浮点 HDR 格式 R32G32B32A32_Float 等算子会通过TextureBgraReadAccess转换器实例化为targetFormat: R16G16B16A16_Float做一次格式回退转换并仅在第一次转换时打印一条日志_conversionWarning保证只警告一次。转换会带来额外的 GPU 开销因此提前在渲染管线里把格式设成 R8G8B8A8_UNorm 是避免性能损耗的最佳做法。3.1 如何得到 R8G8B8A8_UNorm 纹理文档给出的推荐链路是[TorusMesh] - [DrawMesh] - [RenderTarget] - [SpoutOutput]即用一个RenderTarget算子接管绘制让 RenderTarget 的输出纹理直接以可控格式交给 SpoutOutput。RenderTarget 算子在仓库中的实现位于 Operators/Lib/Symbols/image/generate/basic/RenderTarget.cs它提供ColorBuffer、DepthBuffer、NormalBuffer三个输出槽并且实现了IRenderStatsProvider可以在渲染统计面板中查看其开销。在实际工程中将 RenderTarget 的ColorBuffer连到 SpoutOutput 的Texture输入即可。如果你的纹理来自其他算子且格式不受控也可以先在中间插入格式转换算子如ConvertFormat把格式归一为 R8G8B8A8_UNorm 再送入 SpoutOutput。4. 发送链路的核心实现从纹理到 SpoutSpoutOutput 的发送逻辑集中在两个方法Update每帧入口与SendTexture实际发送。整体数据流如下Texture 输入 │ ▼ Update() ──► 取 Texture 与 SenderName 的值 │ TextureOutput.Value texture 直通输出 ▼ SendTexture(senderName, ref frame) ├─► 读取纹理宽高与格式 ├─► InitializeSpout()惰性创建 OpenGL 上下文 DirectX11 设备 SpoutDX sender ├─► 按需创建共享纹理含 2 个环形缓冲纹理 ├─► CopyResource 把帧拷贝到可读纹理 ├─► 格式检查白名单 / 回退转换 └─► _spoutDX.SendTexture(_texture) —— 真正发送到 Spout 共享纹理几个值得展开的实现细节4.1 惰性初始化与 OpenGL 上下文InitializeSpoutSpoutOutput.cs在第一次发送时做一次性初始化通过DeviceContext.Create()创建 OpenGL 设备上下文并把当前线程的 OpenGL 上下文切换为“主上下文”——这是 SpoutDX 底层要求的前置条件通过ID3D11Device.__CreateInstance(((IntPtr)ResourceManager.Device))拿到 TiXL 正在使用的 Direct3D 11 设备TiXL 的渲染后端是 D3D11创建SpoutDX.SpoutDX实例并调用OpenDirectX11(_device)随后设置SenderName。初始化失败时会打印错误日志Initialization of Spout failed. Are Spout.dll and SpoutDX.dll present in the executable folder?并清理 sender 对象。这条日志提示了关键前提——Spout 的两个原生 DLL 必须存在见下一节。4.2 共享纹理与环形缓冲发送的核心是把 D3D11 纹理标记为Shared资源ResourceOptionFlags.Shared这样其他进程才能通过共享句柄访问。源码创建纹理的描述如下SpoutOutput.csvar imageDesc new Texture2DDescription { BindFlags BindFlags.ShaderResource, Format currentDesc.Format, Width currentDesc.Width, Height currentDesc.Height, MipLevels 1, SampleDescription new SampleDescription(1, 0), Usage ResourceUsage.Default, OptionFlags ResourceOptionFlags.Shared, // 关键跨进程共享 CpuAccessFlags CpuAccessFlags.None, ArraySize 1 };算子内部维护了2 个NumTextureEntries 2可共享纹理采用环形缓冲_currentIndex (_currentIndex 1) % NumTextureEntries交替使用用空间换时间避免 CPU 等待 GPU 完成回读从而降低阻塞、提高吞吐。4.3 发送失败与资源回收SendTexture对初始化失败和发送异常都做了 try/catch 处理失败时释放 senderReleaseSender、CloseDirectX11、Dispose下一次求值会重新初始化具备一定自愈能力。Dispose方法中也会完整清理纹理、sender 与 OpenGL/D3D 设备并且用静态引用计数_instance保证多个 SpoutOutput 实例并存时设备只在最后一个实例销毁时才真正释放。5. 原生依赖Spout.dll 与 SpoutDX.dllSpoutOutput 依赖两个原生库它们随算子包分发位于 Operators/Spout/dependencies/Spout.dllSpout 核心运行时SpoutDX.dllSpout 的 DirectX 11 接口封装。在 spout.csproj 中这两个 DLL 通过Content Include./dependencies/**/*被复制到输出目录CopyToOutputDirectoryPreserveNewest。如果运行时在可执行文件目录下找不到这两个 DLLSpoutOutput 初始化会直接失败对应 4.1 节那条错误日志。因此在使用 SpoutOutput 前请确认 TiXL 可执行文件所在目录下存在Spout.dll与SpoutDX.dll。与原生库的互操作通过 CppSharp 自动生成的绑定完成Operators/Spout/lib/interop/SpoutDX.cs约 8.5 万行的自动生成代码其中可以看到 spoutDX 类的完整原生接口包括OpenDirectX11、SetSenderName、SendTexture(ID3D11Texture2D*)、ReleaseSender、CloseDirectX11等。SpoutOutput 使用的正是这套接口。6. 快速上手指南6.1 最小发送链路在 TiXL 的图形编辑器Graph Editor中创建如下链路[TorusMesh] - [DrawMesh] - [RenderTarget] - [SpoutOutput] Texture - ColorBuffer从算子库Lib.io.video分类中找到SpoutOutput并放入画布把 RenderTarget 的ColorBuffer输出连接到 SpoutOutput 的Texture输入RenderTarget 的格式建议设为R8G8B8A8_UNormSenderName保留默认值tixl或改成你自己的标识例如MyTiXLOutput以便接收端辨认在另一台支持 Spout 的应用或本机 Spout 查看器中选择名为tixl的 sender 即可看到实时画面。6.2 在接收端验证在 Resolume / TouchDesigner / OBS带 Spout2 插件中新建一个 Spout 接收源从发送方列表中选择你在 SenderName 中填写的名字默认tixl若列表为空或黑屏按第 7 节排查。6.3 配合 SpoutInput 回环在同一台机器上你也可以用SpoutInput把外部应用或另一个 TiXL 工程发送的纹理接收回来继续处理。SpoutInput 的参数为Command与ReceiverName输出为Texture与UpdateCountSpoutInput.mdUpdateCount每收到一帧新画面自增 1可用于驱动节拍或事件逻辑。7. 常见问题与排查现象可能原因处理方式日志报 Initialization of Spout failedSpout.dll/SpoutDX.dll不在可执行文件目录确认两个 DLL 随程序部署见第 5 节重启 TiXL接收端找不到 senderSenderName 与接收端所选名字不一致检查接收端枚举的 sender 名多实例同名时 Spout 会自动改名注意回写后的实际名字画面格式异常或性能下降输入纹理不是推荐格式触发了回退转换在 RenderTarget / ConvertFormat 中把格式设为R8G8B8A8_UNorm多显卡机器上发送失败Spout 依赖当前 OpenGL/D3D 上下文对应的适配器确保 TiXL 运行在与接收端相同的 GPU 上Spout 共享纹理跨 GPU 不可用输出黑屏但无报错Texture 输入为空检查上游 DrawMesh/RenderTarget 是否成功求值并产生纹理8. 延伸阅读SpoutInput本机 Spout 实时视频输入算子NdiOutput基于网络的视频输出方案注意其同样要求 R8G8B8A8_UNorm 或 R8G8B8A8_Typeless 格式Lib.io.video 算子目录视频输入/输出算子全览SpoutOutput 源码发送实现细节RenderTarget 源码推荐链路的渲染目标算子spout.csproj算子包的构建、依赖分发与打包配置【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表