☰
UE5接入RTSP流实战:FFMPEGMedia插件安装、调优与崩溃排查
2026/10/10 14:56:09 网站建设 项目流程

1. 为什么UE5原生路子撑不起RTSP拉流,我最后选了FFMPEGMedia

做UE5项目碰到要接实时视频流的需求,第一反应都是想用引擎自带的Media Framework。但真把RTSP地址填进去之后,很多人才反应过来:UE5原生的媒体播放框架对RTSP的支持其实很弱,尤其是Windows平台,底层走的是Media Foundation那套老牌管线,对RTSP的兼容完全看脸。同一个地址,有些版本能拉出来,换个分辨率、换台摄像头就黑屏,更不用说低延迟这种本来就不该指望的原生方案。做数字孪生、智慧园区大屏这类项目的人,应该都对这种"夹生饭"有印象。

我当时的需求其实很朴素:把几路海康和萤石的网络摄像头画面拉到UE5的场景里,要能做到多路同时显示,延迟别太离谱,而且最好不用每台机器都跑一套复杂的VLC方案。查了一圈市面上的方案,要么是商业SDK授权贵,要么是需要自己拉FFmpeg源码编译然后封装,调试成本高。这时候盯上了FFMPEGMedia这个插件——它本质上是把FFmpeg的解码能力包成UE5的Media Player插件,这样引擎里MediaTexture那一整套资源链路都能复用,蓝图里就能干活,不用自己从头写解码器封装。

FFMPEGMedia能解决的事很简单:它让UE5里播放RTSP流,就像播放本地视频文件一样方便。你给它一个RTSP地址,它通过FFmpeg把网络流解出来,一帧一帧地往MediaTexture上刷,之后无论是喂给材质做3D屏幕,还是直接丢到UI的Image控件上,都是引擎的标准玩法。对于我这种要同时兼顾效果和周期的项目来说,这是性价比最高的路。

适合谁看这篇东西?如果你在UE5里接过任何网络摄像头流,或者准备做多路视频上屏、大屏监控、虚拟演播厅,又或者已经被lowlevelfatalerror这类渲染崩溃报错折磨过——那这篇文章能省你不少排查时间。下面写的都是我在Windows平台和安卓真机上实际踩过的路子,版本以UE 5.2为主,但5.1、5.3、5.4的注意点基本通用。

2. 插件的安装与编译:版本、目录和工具链一个都不能错

2.1 插件从哪里拿、往哪里放

FFMPEGMedia不是官方商店里那种一键安装的资源包,我接触到的版本基本都是GitHub上的开源源码版,仓库名直接搜"FFMPEGMediaUE5"就能找到。拿到压缩包之后,解压出来的文件夹名称是类似FFMPEGMedia的插件目录,里面有FFMPEGMedia.uplugin这种插件描述文件。

放置位置有两个选择:一是放到项目的Plugins目录下(只在这个项目里生效),二是放到引擎安装目录的Engine/Plugins下(所有项目生效)。我强烈建议放在项目Plugins目录。原因很实际:这插件跟项目绑在一起,团队成员拉完代码就能用,不会出现"我这边能跑你那边报错"这种经典问题。而且升级引擎版本时,项目级插件可以单独处理,动引擎目录容易把环境搞得一团糟。

放好后启动项目,正常情况下会弹出一个“重新编译”之类的提示,或者等编辑器打开后在Edit → Plugins里能看到FFMPEGMedia出现在列表里。如果你打开编辑器发现插件状态是"Unavailable"或者"Missing",基本就是下面几个原因之一。

2.2 Windows下最容易翻车的步骤

先说最容易翻车的环境准备。FFMPEGMedia源码版需要自己编译,所以Windows上必须装好以下几样:

  • UE5.x(我用的是5.2,其他版本大差不差)
  • Visual Studio 2022,记得勾选“使用C++的游戏开发”工作负载
  • 项目的C++工程结构。如果本身是Blueprint项目,想用这插件也得先添加一个C++类的空壳,让项目生成*.sln,否则插件没法编译

第一次使用,在项目上右键选择"Generate Visual Studio project files",然后用VS打开sln,等待编译完成再启动UE5编辑器。这一步很多人会忽略:直接双击uproject打开编辑器,结果插件没编译,报一堆link错误。

编译过程中,插件会去获取FFmpeg的库文件。这个环节国内网络环境经常会超时或者下载失败。我的处理办法是:先手动把FFmpeg的预编译库下好,按插件README里要求的目录结构放进去,通常是ThirdParty或者Binaries下,然后再编译。这一步如果卡住,别反复重试,去仓库的issue区翻一下对应版本的依赖清单,按清单手动放。

2.3 安卓端的特殊差别

这个插件不是只跑Windows。我的项目还要出安卓包,这就牵扯到另外一套问题。PC上用的FFmpeg DLL(avcodec-*.dll、avformat-*.dll这些)到了安卓上必须换成libavcodec-*.so,而且架构要对上。UE5打包安卓时会根据Target Architecture选ARM64或者x86_64。如果你直接用Windows版的插件包打安卓包,大概率会有一堆找不到libavformat.so的报错。

解决办法是在项目设置里把Targeted RHIs和Target Architecture固定下来,同时确认插件自带安卓用的.so文件。我踩过一个更细的坑:插件在安卓上加载库文件的时机跟UE的JNI环境挂钩,如果加载太早会直接崩溃。后来我给它加了延迟加载的逻辑,才稳定下来。安卓真机接入RTSP的表现参看后面第4节的实测数据。

2.4 版本选择建议

GitHub上这个插件有几个活跃分支,老一点的版本对标UE4,新的才行。我建议优先找支持你当前引擎小版本的分支,而不是最新版。我实际对比过:支持UE5.1的老分支在5.2上也能编译,但要改一些头文件路径;而专门适配5.2的分支在5.3上会报MediaPlayer接口签名不一致的问题。所以最稳的路子是用跟你引擎版本完全匹配的release,别追求版本号新。

3. 从拉流地址到屏幕画面:媒体链路搭建与C++/蓝图调用要点

3.1 三件套:MediaPlayer目标、MediaTexture、材质

FFMPEGMedia插件的使用逻辑跟引擎自带媒体框架一致。要让RTSP流的画面显示出来,你得在内容浏览器里准备好三条资源链路:

  • 媒体播放器目标(Media Player Asset,插件提供)
  • 媒体纹理(Media Texture)
  • 用于显示画面的材质或UMG Image

实际操作中,我在内容浏览器里右键创建了Media Player资产,创建的时候勾选“关联MediaTexture”,引擎会自动把对应的纹理资产生成出来。这一步很关键,因为后面材质采样的就是这张自动生成的纹理;如果你手动单独创建MediaTexture,需要在细节面板里手动指定Source,容易漏。

之后材质这边,我用的方法是:新建一个Material,把MediaTexture的采样节点拖进来,连到Base Color和Emissive Color上,这样视频画面就能自发光显示,不受场景光照影响。对3D屏幕来说,这个材质直接赋给一个平面或者曲面网格就行。如果是做UI大屏,更简单——直接把MediaTexture拖到UMG的Image控件上作为Brush,不需要材质。UI这条链路在手机上更省性能,我在安卓上基本都走UI。

3.2 播放流程:Open、Play、Close

播放的调用节奏,先看最简单的情况。在Level蓝图或者Actor的蓝图事件里,拿这个Media Player资产,调用打开源节点的逻辑,填入RTSP地址,接着调用播放节点。就这么简单。

用C++侧写也一样,核心就是三个调用:

// 假设 FMediaPlayer 类型的对象已经创建 MediaPlayer->Open(URL); // URL是rtsp地址 MediaPlayer->Play(); // 开始播放 // 需要停止时 MediaPlayer->Close();

这里我要多说一句:虽然调用简单,但"什么时候调用"有讲究。我最初试过在BeginPlay同步调Open,结果低概率卡死或者黑屏。后面我改成在BeginPlay之后延迟一帧、确保渲染资源都初始化完再打开,就稳了很多。虽然看起来只是时序上的一步之差,实际原因是UE5的渲染线程初始化时序问题,越是在项目启动阶段碰多媒体资源越容易撞上。

3.3 场景里的实用扩展:定时重连、动态换源

单一播放不难,难的是多路播放和断线重连。我做过的项目里,摄像头网络偶尔抖动,RTSP流一断,画面容易卡在最后一帧。FFMPEGMedia本身没有自带的重连逻辑,但可以通过蓝图或者C++封装:在玩家播放器状态判断为Ended/Error后,延时3-5秒重新Open一次。重连前必须Close掉旧会话,否则一直开新的句柄,内存和句柄数都会上涨,最后整个项目卡死。

多路播放的做法是:创建多个MediaPlayer资产和多个MediaTexture,各自开不同的RTSP地址。CPU和带宽消耗是线性的,我实测过8路720p的时候PC还能顶住,但16路1080p已经开始明显卡顿。做多路之前,一定要先想清楚解码是走硬解还是软解。FFMPEGMedia默认软解为主,多路高码流时CPU立刻吃满。如果你要上8路以上,建议用子码流或者降低帧率,属于"项目前期就能定下来的优化",比后期改架构省事太多。

3.4 蓝图调用时容易漏掉的一步

蓝图操作里我吃过一个亏:打开源成功之后,直接调用Play,音视频都能起来,但是隔几秒画面就黑。后来发现是播放器目标资产勾了"播放时自动把AudioTrack选到了无输出",而FFmpeg在RTSP的音频轨上识别不友好,导致整个播放管线阻塞。处理方式很简单:在播放器细节面板上,把Audio Output Type设成None,用纯视频模式。毕竟监控场景基本不依赖摄像头自带的音频,而且摄像头麦克风的声音经常是噪声。

4. 实测萤石和海康取流:地址格式、延迟参数与调优顺序

4.1 各家RTSP取流地址长什么样

做素材准备时最头疼的是RTSP地址格式。行业里海康和萤石占了很大比例,它们的地址格式我直接列出来,方便抄作业:

  • 海康威视(老款):
rtsp://用户名:密码@IP:554/Streaming/Channels/101

其中101代表主码流,102代表子码流。我们项目里用102比较多,子码流的码率低,多路画面不卡。

  • 海康威视(新版本)+ H.265编码:
rtsp://用户名:密码@IP:554/Streaming/Channels/101?transportmode=unicast

H.265的流直接拉会有花屏风险,下文会说处理办法。

  • 萤石:
rtsp://用户名:密码@IP:554/h264/ch1/main/av_stream rtsp://用户名:密码@IP:554/h264/ch1/sub/av_stream
  • 大华:
rtsp://用户名:密码@IP:554/cam/realmonitor?channel=1&subtype=0

这些都是我在实测环境里验证过的通用格式,但你手上的摄像头如果固件版本特殊,老版本可能不生效。确认地址是否可用有个笨办法:先用VLC media player(电脑上装一个)输入RTSP地址来试试能不能出画面。VLC能出图,UE5插件一般也能播,但延迟表现另说;VLC都拉不出来,先怀疑地址或者设备侧权限,别急着找插件原因。

4.2 拉流协议选择:TCP还是UDP

RTSP默认的传输方式各家设备不一样。很多摄像头固件默认用UDP拉流,UDP在跨网段、网络不稳的时候经常丢包,表现出来就是画面马赛克、卡顿、偶尔花屏。FFMPEGMedia的Open接口支持传选项参数,可以在播放器打开源的时候带上:

rtsp_transport=tcp

这个参数会把RTP协议走TCP封装,虽然会多一点点延迟(同网段下几乎感觉不到),但稳定性大幅提升。我做项目时一律用TCP,问题率低很多。如果你发现画面偶尔碎块,优先加参数再排查其他。

4.3 低延迟设置与实测数据

这里直接放我实测的一组数据:局域网环境,海康摄像头,720p主码流,H.264编码,FFMPEGMedia的默认参数下,画面延迟大约在0.8-1.5秒;加上TCP传输后差不多也是这个量级。然后把播放器的缓冲策略调成低延迟、把FFmpeg的解码线程数以及丢帧策略打开(插件内部有对应的开关,部分版本叫"Low Latency"、部分版本叫AV_CODEC_FLAG_LOW_DELAY),延迟能压到0.3秒左右。具体能压到多少取决于设备和网络,但"开低延迟模式+T C P传输+子码流"这三个组合是通用有效的顺序。

安卓端我实测的延迟比PC高一点,同样的局域网大概多100到200毫秒,主要原因是安卓的硬件解码器和FFMPEGMedia的管线结合得没有Windows好。安卓上如果追求低延迟,我的建议是分辨率选子码流,帧率保持默认,同时开TCP,不要用4K大码流硬顶。

4.4 缓存RTSP流的两种思路

热搜词里有"安卓缓存RTSP流"这个话题,说明不少人被这个问题困扰。我这里先说结论:RTSP是实时的推送流,不适合像普通视频那样先下载到本地再播放。但实际项目里确实有一种"伪缓存"需求,比如断网回放或者减少重复拉流压力。我处理过两种做法:

一种是在服务端跑一个转推模块,用FFmpeg命令行把RTSP流转成HLS或者MP4切片,UE5端播放的其实是HTTP地址。这种做法优点是不依赖插件,U5原生播放器就能播;缺点是延迟会从几百毫秒飙到几秒,只能用于非实时场景。

另一种是在UE5端做断线缓存:播放前把最近几秒的关键帧存到本地文件,等网络恢复后从关键帧开始续播。这种方案实现成本高,一般项目没必要。至少对于监控类大屏,实时性优先,别把精力耗在缓存RTSP上。

5. 报错排查实录:lowlevelfatalerror、黑屏和花屏的处理链路

5.1 lowlevelfatalerror的触发条件与报错全貌

说到报错,热词里有一条极其典型:

lowlevelfatalerror [file:d:\build\++ue5\sync\engine\source\runtime\rendercor...

我在项目早期几乎每天跟它打照面。这个报错发生在UE5的渲染线程侧,从RenderCore的这句提示就能判断,问题出在渲染资源和解码线程的交叉操作上。FFMPEGMedia解码线程拿到一帧之后,会把数据提交到渲染线程更新MediaTexture。如果这时MediaTexture还没有完全初始化,或者正在被材质使用途中又被释放,渲染线程就会触发这个致命错误。

我排查这个问题的完整链路是这样的:

  • 第一步,复现。我每次都是启动项目、等待场景加载、出现画面后才崩溃,概率大概三成。只要是稳定复现,问题就好查。
  • 第二步,看崩溃调用栈。VS里附加到进程,崩溃栈指向了MediaTexture的更新逻辑。可以确认不是插件本身读取内存的问题,而是纹理资源生命周期。
  • 第三步,排除释放顺序。我的Actor在关卡结束或切换关卡时被销毁,销毁时没有显式让播放器停止。实际上播放器还握着MediaTexture,渲染线程还在提交帧,纹理却被销毁了。解决动作:在BeginDestroy里先关播放器,再销毁Actor。
  • 第四步,处理启动时序。像之前说的,初始化阶段打开播放器要延迟到渲染资源就绪之后。我对比测试后确认,这两个动作单独做都不会崩,但连在一起时概率性崩溃。

处理完这两条之后,lowlevelfatalerror在我项目里就再没出现过。所以如果你遇到这个报错,先按这个链路检查:播放器有没有在Actor销毁时关闭,播放打开时机是否在渲染初始化抢跑,MediaTexture是否被多个材质同时引用造成状态冲突。

5.2 黑屏:多半不是插件坏了,是链路断了

黑屏问题比崩溃更隐蔽,因为程序不报错,就是不显示画面。我的排查顺序是:

  1. 检查RTSP地址是否用VLC验证过。很多时候是摄像头不允许在线的第二个连接,VLC占着通道,UE这边去拉就被锁了。
  2. 检查MediaTexture是否被正确指到播放器资产上。这一步容易错在手动创建的MediaTexture没有指定Source。
  3. 检查材质里MediaTexture采样节点是否连到了Emissive或者Base Color。漏掉任何一路,画面都不会显示,但因为UI这种材质看不出来,你会以为视频流没通。
  4. 检查播放器是否真的处于Playing状态。如果网络差,打开源很慢,可以先监听状态变化事件再决定下一步。

黑屏十有八九是链路断的,不是解码失败。真解码失败的典型表现是花屏,而不是黑屏。

5.3 花屏与H.265/HEVC的坑

花屏的核心原因通常是编码格式和解码器不匹配。FFMPEGMedia某些预编译库没有带HEVC解码器,一旦摄像头输出H.265,画面就会出现色块和条纹。我遇到过一台新固件的摄像头,默认主码流走H.265,拉出来全是碎块,改成H.264之后立刻正常。

排查花屏时,可以先从摄像头后台把编码格式切到H.264,或者换一个支持HEVC的FFMPEGMedia版本。另外也要注意,老设备的子码流可能被迫用H.264,所以直接用/Streaming/Channels/102这类子码流地址也能绕开花屏。

5.4 线程问题:不要跨线程操作播放器

FFMPEGMedia内部用解码线程拉流,跨线程调用播放器的接口是大忌。我修过一位同事的崩溃,原因是在异步任务里调了Play(),结果直接崩溃。这类插件对外接口基本都要在GameThread上调用。如果你有需求要定时轮询拉流状态,可以用蓝图Timer或者AsyncTask里的ENamedThreads::GameThread包装一下。

6. 写在最后的几条经验

这个项目做下来,我最大的感受是:FFMPEGMedia插件本身不复杂,复杂的是跟引擎渲染线程、摄像头设备、网络环境三者之间的协调。插件只是把解码和拉流的问题解决了,后面的接入策略、生命周期管理、异常恢复,全得自己做。

给正准备入坑的朋友几条实际建议:第一,先花半天时间用VLC验证所有RTSP地址,把这步做的越扎实,后面找坑的时间越少。第二,播放器生命周期一定和Actor绑定,销毁前务必关闭播放器,这条能躲过一大半的随机崩溃。第三,优先用TCP拉流,默认UDP会给你挖各种暗坑。第四,多路视频时不要迷信主码流,子码流的画质对大屏和监控场景完全够用,但性能消耗差距是成倍的。

按照我个人的项目节奏,如果在管理端把"播放器状态监控和重连"做成一个通用组件,那么后续每接入一路新摄像头就是配置一个地址的事。这个插件能做到让我把精力集中在业务表现而不是解码协议上,已经算是物有所值了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询