Unity RTSP推流实战:FFmpegOut插件改造与RtspSimpleServer部署
2026/7/13 10:45:31
网站开发
1. 项目概述为什么要在Unity里折腾RTSP如果你正在开发一个需要将虚拟世界画面实时分享出去的Unity项目比如远程监控模拟、虚拟直播、多人协作的AR/VR应用或者一个需要将游戏内视角作为视频源进行二次处理的系统那么你很可能已经遇到了一个核心需求如何把Unity摄像机渲染的画面像一台真实的网络摄像头一样稳定、低延迟地推送出去让其他设备或软件能够通过网络流地址URL直接观看。这就是RTSPReal Time Streaming Protocol实时流传输协议的用武之地。它不像录屏那样生成一个巨大的视频文件而是建立一个持续的、可控制的视频流。想象一下你正在Unity里构建一个数字孪生的工厂监控系统操作员在网页上看到的不是一张张静态截图而是一个可以暂停、播放、快进的实时视频流这就是RTSP带来的体验。然而Unity引擎本身并没有提供开箱即用的RTSP推流功能这成了很多开发者面前的一道坎。网上能找到的方案往往比较零散有的只讲怎么用FFmpeg命令行推流但没讲怎么和Unity联动有的提到了某个插件但配置步骤语焉不详一运行就报错还有的服务器搭建教程复杂到让人望而却步。我当初为了把这个流程跑通也踩了不少坑比如编码器不兼容导致画面绿屏、服务器端口冲突、推流地址格式错误等等。所以今天我就把自己趟过这条路后总结的完整方案分享出来。这个方案的核心思路非常清晰在Unity内部我们通过一个修改版的FFmpegOut插件将摄像机画面编码成H.264视频流在外部我们搭建一个轻量级的RTSP服务器比如RtspSimpleServer作为视频流的“中转站”和“发布点”。最终你会在Unity编辑器中点击一个按钮就能在VLC等播放器中输入一个类似rtsp://localhost:8554/unity的地址看到实时的Unity画面。这个教程的目标是让你能独立复现整个过程。无论你是想用于项目原型验证、学术研究还是产品功能开发这套方法都能提供一个坚实可靠的起点。接下来我们就从最核心的准备工作开始。2. 核心思路与工具选型为什么是FFmpegOut RtspSimpleServer在动手之前我们必须搞清楚整个技术栈的构成和每个环节的选型理由。一个完整的RTSP推流链路可以拆解为三个核心环节视频采集与编码、流媒体传输、流媒体服务与分发。我们的方案就是为这三个环节选择了最合适的“零件”。2.1 视频采集与编码为什么选择修改版FFmpegOutUnity摄像机渲染出的是一帧帧的纹理RenderTexture我们需要一个高效、稳定的工具将这些纹理数据压缩成标准的视频编码格式如H.264并打包成流媒体传输格式如RTMP或RTP。这里有几个备选方案Unity自带的VideoCapture或WebCamTexture前者主要针对真实摄像头后者虽能捕获屏幕但功能单一且都不直接支持RTSP推流需要大量底层开发不划算。编写原生插件调用FFmpeg库性能最好控制最细但开发门槛极高涉及C/C、跨平台编译、线程安全等一系列复杂问题不适合快速实现。使用现有的Unity插件这是平衡开发效率与功能性的最佳路径。经过对比FFmpegOut插件进入了视野。FFmpegOut原本是一个用于将Unity画面录制为视频文件如MP4的插件它内部封装了强大的FFmpeg库。它的工作原理是在每一帧渲染结束后将摄像机的渲染目标RenderTexture读取到内存然后通过FFmpeg进行编码。我们需要的正是它这个强大的编码能力。虽然它原生不支持“推流”只支持“写文件”但其架构允许我们通过修改输出参数将数据“推”向一个网络地址而非本地文件。为什么必须用修改版原版FFmpegOut的代码逻辑是硬编码为文件输出的。我们需要修改其核心脚本将输出目标从-i pipe:0 -c:v libx264 -f mp4 output.mp4这样的文件格式命令改为-i pipe:0 -c:v libx264 -f rtsp rtsp://server/stream这样的推流命令。这涉及到对插件内部FFmpegPipe或FFmpegSession类的修改。网上有一些开源分支已经做了这个工作我们可以基于此进行这比从头造轮子快得多。选型总结修改FFmpegOut是利用了其成熟的Unity-FFmpeg桥接框架我们只需改动其“出口”就能以最小成本获得一个高性能的Unity内视频编码与推流模块。2.2 流媒体服务器为什么选择RtspSimpleServer编码后的视频流需要有一个“家”这个家就是RTSP服务器。它的职责是接收来自UnityFFmpeg的推流管理这些流如创建、销毁、列出并响应来自播放器如VLC的RTSP协议请求DESCRIBE, SETUP, PLAY等将视频流分发给它们。可供选择的RTSP服务器很多Mediainfo功能强大但配置复杂更适合大型流媒体服务。Live555经典但古老搭建和交叉编译比较麻烦。RtspSimpleServer一个用Go语言编写的、极简的RTSP服务器。它的特点就是“小而美”单个可执行文件无需安装配置文件简单明了完全满足我们“接收推流并分发”的核心需求。选型理由零依赖开箱即用下载对应平台的二进制文件Windows/Linux/macOS直接运行即可无需配置环境变量或安装额外服务。配置简单通常只需要一个rtsp-simple-server.yml配置文件里面写上监听的端口、认证信息可选、日志级别等几分钟就能搞定。资源占用低作为中转服务器它本身不进行转码只负责流的转发CPU和内存消耗非常小。支持多种推流协议不仅支持RTSP推流还支持RTMP、HLS等为未来扩展留有余地。对于我们的Unity RTSP推流场景RtspSimpleServer是完全够用且最省心的选择。2.3 整体工作流程理解了核心组件整个流程就一目了然了启动服务首先在电脑上运行RtspSimpleServer它开始在指定端口默认8554等待连接。Unity配置在Unity项目中导入并配置修改后的FFmpegOut插件将其指向我们的RTSP服务器地址和流名称如rtsp://127.0.0.1:8554/unity。开始推流在Unity中点击Play插件开始工作。它捕获摄像机画面通过FFmpeg编码为H.264并使用RTSP协议将数据包持续推送到rtsp://127.0.0.1:8554/unity这个地址。服务器接收与分发RtspSimpleServer接收到流将其保存在内存中并标识为unity这个流路径。客户端播放在任何支持RTSP的播放器VLC、FFplay、甚至一些网络摄像头APP中输入上述地址即可实时观看Unity摄像机画面。整个架构是典型的生产者-消费者模型Unity是生产者RTSP服务器是消息队列经纪人播放器是消费者。3. 实战准备搭建你的RTSP服务器RtspSimpleServer理论清晰了我们开始动手。第一步是把流媒体的“中转站”建起来。我强烈建议在Windows上使用它的便携版本最简单。3.1 下载与部署访问 RtspSimpleServer 的 GitHub Releases 页面搜索rtsp-simple-server release即可找到。根据你的操作系统下载最新的压缩包。对于Windows下载rtsp-simple-server_vX.X.X_windows_amd64.zip。解压到一个你喜欢的目录比如D:\rtsp_server。解压后你会看到几个文件其中最关键的是rtsp-simple-server.exe和rtsp-simple-server.yml。3.2 基础配置详解默认的rtsp-simple-server.yml配置文件已经可以工作但了解关键配置项能帮你更好地排查问题。用记事本或VS Code打开它我们关注以下几项# 服务器通用设置 rtspPort: 8554 # RTSP协议监听端口默认8554。如果被占用可以改为其他端口如5554。 rtmpPort: 1935 # RTMP协议端口我们暂时用不到但不用关闭。 httpPort: 8888 # 一个内置的Web管理界面端口可以用来查看服务器状态和当前流列表。 authMethods: [digest] # 认证方式none表示无需认证digest是摘要认证。为了简单测试可以先设为 none。 # 路径配置核心 paths: all: # 这是一个路径模板名可以自定义 source: publisher # 流来源必须是 publisher表示接受外部推流 sourceOnDemand: no # 这里可以配置用户名密码例如 # publishUser: myuser # publishPass: mypass # publishIPs: [127.0.0.1] # 限制推流客户端IP本地测试可以注释掉一个极简的、用于本地测试的配置可以是这样rtspPort: 8554 rtmpPort: :1935 httpPort: :8888 authMethods: [none] paths: unity: # 这里定义了一个名为 unity 的流路径 source: publisher这个配置意味着服务器在8554端口监听无需认证并且定义了一个叫unity的“房间”等待别人往里推流。3.3 启动与验证启动服务器双击运行rtsp-simple-server.exe。你会看到一个命令行窗口弹出如果看到类似[RTSP] listener opened on :8554 (TCP)和[HTTP] listener opened on :8888 (TCP)的日志说明服务器启动成功。验证Web管理界面打开浏览器访问http://localhost:8888。你应该能看到一个简单的页面显示服务器状态和当前流列表目前是空的。这个页面在后续推流成功后非常有用可以确认服务器是否收到了流。测试服务器是否存活在命令行另一个窗口中使用curl或直接在浏览器访问http://localhost:8888/v1/paths/list如果返回一个JSON数据即使为空数组[]也说明HTTP接口正常。注意事项防火墙第一次运行时Windows防火墙可能会弹出警告务必选择“允许访问”否则外部或Unity无法连接到服务器的8554端口。端口占用如果启动失败提示端口被占用请检查是否有其他软件如旧版本的服务器、其他视频服务占用了8554、1935或8888端口。可以通过netstat -ano | findstr :8554命令查找并终止相应进程或者直接修改配置文件中的端口号。后台运行如果你希望服务器在后台静默运行可以将启动命令写成批处理文件或者使用nssm将其注册为Windows服务。对于开发和测试前台运行更方便看日志。至此你的RTSP服务器已经就绪正在安静地等待Unity发来的视频流。4. Unity端集成修改与配置FFmpegOut插件这是整个流程中最关键、也最容易出错的一步。我们的目标是将一个用于录屏的插件改造成一个推流工具。4.1 获取与导入基础插件首先你需要基础的FFmpegOut插件。你可以从Asset Store搜索“FFmpegOut”购买或者从GitHub等开源平台寻找其开源版本。将其导入你的Unity项目通常是一个.unitypackage文件。导入后项目中应该会出现FFmpegOut或类似名称的文件夹里面包含脚本、预制体等。先不要急着运行原版插件只支持输出到文件。4.2 核心修改从输出文件到输出RTSP流我们需要修改插件的核心代码改变FFmpeg的命令行参数。关键文件通常是FFmpegPipe.cs或FFmpegSession.cs这个类负责构建FFmpeg命令并启动进程。查找并修改命令构建逻辑用VS Code或Rider打开核心脚本文件。找到构建FFmpeg命令行参数的部分。原版代码可能类似这样string arguments $-y -f rawvideo -vcodec rawvideo -pixel_format rgb24 ... -i pipe:0 -c:v libx264 -pix_fmt yuv420p \{outputPath}\;关键在最后的\{outputPath}\它指定了输出文件。我们需要将其改为RTSP推流地址。修改后应类似string rtspUrl rtsp://127.0.0.1:8554/unity; // 这里对应你服务器配置的流路径 string arguments $-y -f rawvideo -vcodec rawvideo -pixel_format rgb24 ... -i pipe:0 -c:v libx264 -pix_fmt yuv420p -f rtsp \{rtspUrl}\;两个关键改动将outputPath替换为你的RTSP服务器URL。在-c:v libx264之后必须添加-f rtsp参数强制指定输出格式为RTSP。没有这个参数FFmpeg可能会尝试其他不正确的格式导致推流失败。实操心得编码器参数调优-c:v libx264是使用CPU进行软件编码。你可以添加一些参数优化例如-preset ultrafast -tune zerolatency。ultrafast追求最快的编码速度但压缩率低带宽占用大zerolatency专为低延迟流媒体设计。对于实时推流低延迟比高画质更重要。分辨率与帧率这些通常在Unity端设置。确保-s分辨率和-r帧率参数与你的Camera渲染设置或RenderTexture设置一致否则FFmpeg会进行缩放和重采样增加延迟和CPU负担。最好在Unity脚本里动态计算并传递给参数。关于音频如果你的场景需要同步推送音频命令会更复杂需要混合音视频流。本教程聚焦视频暂不展开。如果不需要确保没有音频相关的输入参数避免FFmpeg报错。4.3 在场景中配置与使用修改完脚本后在Unity中的使用方式就和原版插件类似了。添加组件给你需要推流的摄像机GameObject添加FFmpegOut或CameraCapture组件具体名称取决于插件版本。配置组件Output Type可能会有一个下拉菜单选择“Stream”或“Custom”或者在代码中我们已经硬编码了RTSP URL。Frame Rate设置与你项目目标帧率一致的推流帧率如30。Resolution设置推流分辨率如1920x1080。不建议超过摄像机实际渲染分辨率。Codec选择H.264。编写控制脚本通常插件会提供API你需要编写一个简单的脚本在运行时控制开始/停止推流。例如using UnityEngine; using FFmpegOut; // 根据实际命名空间调整 public class RTSPStreamController : MonoBehaviour { public Camera streamCamera; private FFmpegSession session; void Start() { // 假设插件提供了 StartStreaming 方法 session FFmpegSession.Create(streamCamera, “RTSP推流”); // 或者通过组件的引用来控制 } void OnDestroy() { if (session ! null) { session.Stop(); // 确保退出时停止推流释放资源 } } }运行测试确保RtspSimpleServer正在运行。在Unity编辑器中点击Play。观察FFmpegOut组件的日志或控制台输出如果看到FFmpeg进程启动并开始输出编码信息可能有很多行且没有立即报错退出就说明推流进程启动了。5. 测试、验证与播放推流启动后如何验证它真的成功了5.1 使用VLC Media Player验证VLC是流媒体测试的瑞士军刀。打开VLC播放器。点击“媒体” - “打开网络串流”。在网络URL中输入你的RTSP地址rtsp://127.0.0.1:8554/unity点击“播放”。如果成功你将看到Unity游戏视图的画面在VLC窗口中实时播放。你可以尝试在Unity中移动摄像机或物体VLC中的画面应该会同步更新会有1-3秒不等的延迟取决于编码设置和网络。如果失败VLC通常会提示“无法打开”或长时间缓冲后失败。这时就需要排查了。5.2 使用FFplay验证更底层的工具FFplay是FFmpeg套件中的播放器它能提供更详细的连接和流信息适合调试。打开命令行CMD或PowerShell确保ffmpeg/bin目录在系统PATH中或者直接切换到ffmpeg所在目录。输入命令ffplay -rtsp_transport tcp -i rtsp://127.0.0.1:8554/unity-rtsp_transport tcp参数强制使用TCP传输RTSP默认可能尝试UDP在本地网络或某些防火墙环境下更稳定。如果连接成功会弹出一个播放窗口并显示实时画面同时命令行会输出详细的流信息编码格式、分辨率、码率等。5.3 查看服务器状态别忘了之前启动的RtspSimpleServer的Web管理界面http://localhost:8888。刷新页面你应该能在“Paths”列表里看到名为unity的流其状态应该是“ready”或“publishing”。这里会显示推流客户端的IP、开始时间等信息是确认服务器端已收到流的最直接证据。6. 深度优化与高级配置基础流程跑通后我们可以针对画质、延迟、稳定性进行优化。6.1 降低推流延迟实时传输中延迟是首要敌人。可以从以下几方面优化FFmpeg编码参数-preset ultrafast使用最快的编码预设牺牲画质换取速度。-tune zerolatency启用零延迟调优减少编码缓冲。-g 1或-g 2设置极小的GOP关键帧间隔。GOP越小解码端能越快恢复但压缩效率会降低。对于实时流可以设置为帧率的1-2倍如-g 30对于30fps。-bf 0禁用B帧。B帧虽然提高压缩率但会增加编码和解码延迟。Unity渲染设置尽可能降低游戏视图的分辨率。推流分辨率不需要和显示器分辨率一致720p甚至480p对于很多监控场景已经足够能大幅减少需要编码的数据量。关闭或降低不必要的后期处理效果如抗锯齿、Bloom它们会增加GPU渲染时间。使用Application.targetFrameRate限制游戏帧率避免帧率波动导致编码器输入不稳定。传输协议在RTSP服务器和播放器端都使用TCP传输-rtsp_transport tcp虽然理论上比UDP延迟高一点但能避免丢包导致的卡顿和花屏整体体验更稳定。6.2 提升画质与控制码率在带宽允许的情况下我们当然希望画质更好。控制码率使用-b:v参数指定目标视频码率。例如-b:v 2M表示2 Mbps。码率越高画质越好但需要更高的带宽。你需要根据目标网络环境和分辨率找到一个平衡点。对于1080p 30fps2-4 Mbps是一个合理的起点。CRF模式如果不方便指定固定码率可以使用恒定质量模式。参数-crf 23。CRF值范围是0-51值越小画质越好文件越大。23是默认值18-28是常用范围。在实时流中固定码率CBR模式更常见因为更容易预测带宽占用。分辨率与帧率权衡在带宽有限时优先保证帧率流畅性适当降低分辨率。人眼对卡顿比对模糊更敏感。6.3 处理音频如果需要如果需要同步推送游戏内的音频流程会复杂一些Unity端FFmpegOut插件可能需要扩展以捕获AudioListener的音频数据。这通常需要修改插件从UnityEngine.Microphone或OnAudioFilterRead回调中获取音频样本并通过另一个管道传递给FFmpeg。FFmpeg命令命令需要合并音视频输入。一个简化的概念性命令如下-f rawvideo -i video_pipe ... -f f32le -ar 44100 -ac 2 -i audio_pipe ... -c:v libx264 -c:a aac -f rtsp rtsp://...这需要插件能同时管理视频和音频两个数据管道并正确同步时间戳。除非插件原生支持否则自行实现难度较大。一个折中方案是如果只是需要环境音可以考虑在服务器端混合一个静态音频流但这与游戏内声音不同步。6.4 网络与安全配置局域网访问现在你的流只能在本地127.0.0.1访问。要让同一局域网下的其他设备如手机、平板观看需要找到你电脑的局域网IP如192.168.1.100。在播放器中使用rtsp://192.168.1.100:8554/unity进行播放。确保电脑防火墙允许8554端口的入站连接。简单认证在rtsp-simple-server.yml中将authMethods设为[digest]并在paths下配置publishUser和publishPass。这样Unity推流和客户端播放都需要输入用户名密码。IP白名单使用publishIPs和readIPs可以限制推流和拉流的客户端IP地址增加安全性。7. 常见问题排查与解决实录即使按照教程操作你也可能会遇到一些问题。下面是我在多次实践中遇到的典型问题及其解决方法。7.1 Unity端问题问题1点击Play后FFmpeg进程启动但立刻退出Unity控制台报错“FFmpeg exited with code 1”。排查思路这是最常见的问题根本原因是FFmpeg命令执行失败。解决步骤查看详细日志修改FFmpegOut插件的代码让它打印出完整的FFmpeg命令行。将这个命令行复制到系统的CMD中手动执行通常会得到更明确的错误信息。检查RTSP URL格式确保URL中没有多余的空格或非法字符。特别是如果流名称或密码包含特殊字符需要进行URL编码。检查编码器支持运行ffmpeg -encoders查看是否有libx264。如果没有你需要一个包含H.264编码器的FFmpeg版本。建议使用官方构建的ffmpeg-git-full版本。检查输入参数确保-pixel_format、-s分辨率、-r帧率等参数与Unity端传递的数据完全匹配。一个常见的错误是分辨率设置成了带冒号的字符串如“1920:1080”而FFmpeg期望的是“1920x1080”。服务器是否可达在Unity运行前先用ping 127.0.0.1和telnet 127.0.0.1 8554如果telnet可用检查服务器端口是否开放。问题2推流成功但VLC播放画面是绿色的或有大量马赛克、卡顿。排查思路通常是解码器问题或数据不完整。解决步骤关键帧间隔检查FFmpeg命令是否设置了-g参数。如果GOP太大播放器在刚开始接收或网络丢包后需要等待很久才能收到下一个关键帧来重建画面导致长时间绿屏或卡住。尝试设置为较小的值如-g 30。像素格式确保-pix_fmt设置为yuv420p。这是最广泛兼容的像素格式。rgb24等格式很多硬件解码器不支持。使用TCP传输在VLC或ffplay播放时添加:rtsp-tcp参数VLC或-rtsp_transport tcp参数ffplay。UDP可能在本地回环或虚拟网络环境下不稳定。降低推流参数尝试降低分辨率如到720p和帧率如15fps看是否改善。如果改善说明原参数对CPU/GPU压力太大编码跟不上。问题3推流延迟非常高超过5秒。排查思路延迟是累积的来自渲染、编码、网络、缓冲、解码多个环节。解决步骤检查FFmpeg参数务必加上-preset ultrafast -tune zerolatency。移除或减少-bufsize和-maxrate等缓冲控制参数如果设置了。关闭VLC的网络缓存VLC默认缓存较高。在VLC的“工具”-“偏好设置”-“输入/编解码器”中将“网络缓存”值调低如300ms。使用更专业的低延迟播放器如 ffplay 或一些专用的RTSP测试工具它们的默认缓冲更小。7.2 服务器端问题问题4RtspSimpleServer启动失败提示“address already in use”。解决端口被占用。通过netstat -ano | findstr :8554找到占用进程的PID在任务管理器中结束它或者修改配置文件中的rtspPort为其他未被占用的端口如8555并同步修改Unity中的推流地址。问题5服务器运行正常但Web管理界面 (http://localhost:8888) 看不到推上来的流。排查思路流没有成功推送到服务器或者推送到了错误的路径。解决步骤检查Unity中的推流地址是否和服务器配置的paths下的路径名一致。服务器配置了paths: unity:地址就应该是rtsp://.../unity。查看服务器的命令行窗口日志。当有客户端连接、推流时会有详细的日志输出。根据日志错误信息排查。检查防火墙确保Unity进程或FFmpeg进程被允许访问网络。7.3 网络与播放端问题问题6局域网内其他设备无法播放。解决确认IP地址确保播放设备输入的IP是推流服务器的局域网IP不是127.0.0.1。关闭防火墙在服务器电脑上暂时关闭防火墙进行测试以确认是否是防火墙拦截。如果关闭后能连通则需要为rtsp-simple-server.exe在防火墙中添加入站规则允许TCP端口8554。路由器问题极少数情况下需要检查路由器是否禁用了本地设备间的通信AP隔离这种模式通常存在于公共Wi-Fi。问题7播放时音画不同步。解决这通常是由于时间戳错误引起的。在FFmpeg推流命令中可以尝试添加-use_wallclock_as_timestamps 1参数让FFmpeg使用系统时钟作为时间戳源有时能改善同步问题。更根本的解决方案是确保Unity端提供给FFmpeg的每一帧视频和音频数据都带有正确、递增的时间戳这需要对插件有更深度的修改。整个流程从服务器搭建、插件修改到最终播放涉及多个环节的联调。我的建议是分步验证先确保服务器能独立运行并被本地VLC访问可以先用一个假的FFmpeg命令推一个测试视频再确保Unity修改后的插件能正确生成FFmpeg命令通过打印日志最后将两者连接。每次只变动一个环节遇到问题就利用好服务器日志、FFmpeg控制台输出和VLC的错误信息大部分问题都能定位。最后关于性能在普通PC上推流一个720p 30fps的场景CPU占用可能会达到10%-30%取决于场景复杂度这是软件编码libx264的常态。如果对性能有极致要求可以考虑探索硬件编码如NVENC通过-c:v h264_nvenc但这需要NVIDIA显卡和FFmpeg的相应支持配置更为复杂。对于大多数开发、演示和原型验证场景本文所述的软件编码方案已经足够可靠和实用了。