ARTICLE DETAIL

资讯详情

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

UE5播放RTSP监控流:FFMPEGMedia插件接入与实战优化

UE5播放RTSP监控流:FFMPEGMedia插件接入与实战优化 做UE5项目做多了难免碰到要播放监控摄像头实时流的场景。本来以为用自带的MediaPlayer就行结果发现UE5默认根本不支持RTSP流折腾了几天最后是靠FFMPEGMedia这个开源插件解决的。这篇文章把整个接入过程、踩过的坑、关键参数和稳定性经验整理出来给同样被RTSP折磨的开发者一个可以直接照着做的参考。先说结论如果你要在UE5里播放海康、大华这些IP摄像头的RTSP流或者接入任何RTSP网络流FFMPEGMedia是目前最省事的方案。它把FFmpeg封装进了UE5的媒体框架你可以在蓝图里像操作普通视频一样播放RTSP流。文章内容主要针对有一定UE5基础、但没深入研究过媒体框架和流媒体协议的开发者下面从原理讲到实战每个环节都标注了容易出错的地方。1. 为什么UE5默认播不了RTSPFFMPEGMedia是怎么解决的1.1 RTSP协议和普通视频文件差在哪可能有人觉得RTSP不也是一种流媒体地址吗为什么UE5的MediaPlayer播不了这里面涉及一个关键区别普通视频文件比如MP4、WebM是一个有完整索引和解码信息的文件而RTSP是一种实时流媒体控制协议全称是Real Time Streaming Protocol。RTSP本身不负责传输视频数据它只负责会话管理客户端向服务器发送DESCRIBE请求获取SDP描述然后SETUP建立RTP传输通道最后PLAY让服务器开始推流。真正的视频数据走的是RTPReal-time Transport Protocol下面还有RTCP做质量控制和同步。这套流程下来播放器需要处理SDP解析、RTP/RTCP协议、H.264/H.265的RTP打包解包、缓冲控制等一系列底层问题。UE5自带媒体框架的重点方向是本地文件和适配标准媒体源对RTSP这种需要长连接、持续收包的流式协议支持很弱。打开一个RTSP地址MediaPlayer大概率卡在打开阶段直接失败。所以做安防项目、智慧城市大屏、机器人巡检这类项目时RTSP支持几乎是绕不开的需求。1.2 FFMPEGMedia在UE5媒体框架里的位置FFMPEGMedia插件做的事情本质上是在UE5的媒体框架里添加了一个新的媒体源支持。UE5把媒体播放抽象成了IMediaPlayer、IMediaTracks这些接口上层则是开发者熟悉的UMediaPlayer和UMediaTexture。FFMPEGMedia内部用FFmpeg的avformat_open_input打开RTSP地址用avcodec解码视频帧再通过RHI把图像上传到GPU纹理。上层的UMediaPlayer、UMediaTexture、UMediaSoundComponent一套组合拳就能像普通视频一样把RTSP流展示在UI上或者贴到3D物体上。这里有个理解上的关键点FFMPEGMedia不是一个单独的播放器它是UE5媒体框架的一个“适配器”。所以打开RTSP流之后你依然可以用蓝图里的媒体播放器节点来控制播放、暂停、获取时长等操作常规UE媒体流程不用学第二套。1.3 为什么选FFMPEGMedia而不是其他方案我在决定方案前对比过几条路简单列出来方案工作量稳定性适用场景自写RTSP插件极大要处理RTP/RTCP/SDP/H264分包初期很低后期维护成本高有专门协议团队的大厂基于GStreamer中Linux下成熟Windows打包痛苦中等依赖链长Linux环境为主的项目自写FFmpeg封装较大等于重新实现FFMPEGMedia中等调试周期长有特定格式需求的定制项目FFMPEGMedia插件很小熟悉蓝图即可较高社区用的人多绝大多数UE5 RTSP项目FFMPEGMedia最核心的优势是FFmpeg本身已经把所有难啃的骨头都啃下了RTSP客户端、RTP解包、H.264/H.265/MPEG4解码、音频解码全都在FFmpeg内部有成熟实现。插件把这些能力暴露给UE5媒体框架你只需要关注业务逻辑和显示效果不需要关心底层协议怎么拼包丢包。另外它的跨平台特性也不错我的项目在Windows上跑通了后续要往Linux服务器上部署同样可行只需要重新编译对应平台版本。所以综合来看这条路最适合大多数中小型UE5项目。2. 插件编译与FFmpeg依赖配置2.1 获取源码和准备工具链FFMPEGMedia插件需要自己拉源码编译它不是UE5自带的插件。GitHub上有多个同名或类似名称的仓库我建议选择最近还在维护、且明确支持UE5版本的仓库。把仓库克隆到项目的Plugins目录下目录结构大致是你的项目/ ├── Plugins/ │ └── FFMPEGMedia/ │ ├── FFMPEGMedia.uplugin │ ├── Source/ │ └── ThirdParty/ ├── YourProject.uproject └── ...然后确认本机的编译环境。UE5编译插件需要Visual Studio 2022安装时必须勾选“使用C的游戏开发”工作负载。准备好之后右键.uproject文件选择Generate Visual Studio project files生成项目工程。用VS打开生成出来的.sln文件解决方案配置选择Development Editor平台选择Win64右键FFMPEGMedia模块编译。这里想提醒一句先确认插件源码对应的引擎版本。有的旧版本插件在UE5.1、UE5.2上编译会报错因为引擎的API有改动。我用的是UE5.2找的也是标注支持UE5.2的分支编译过程很顺利。如果版本不匹配大概率第4节那些编译错误迟早会找上门。2.2 FFmpeg库的引用方式与路径配置FFMPEGMedia插件本身不携带FFmpeg的二进制文件需要自己准备FFmpeg的动态库和头文件。这也是新手最容易卡住的地方。我使用的方案是下载FFmpeg官方编译的shared版本里面包含avformat、avcodec、avutil、swscale等dll文件以及对应的include头文件和lib导入库。把这些文件按下面的方式放好FFMPEGMedia/ThirdParty/ ├── include/ │ ├── libavcodec/ │ ├── libavformat/ │ ├── libavutil/ │ └── ... ├── lib/ │ ├── avcodec.lib │ ├── avformat.lib │ └── ... └── bin/ ├── avcodec-60.dll ├── avformat-60.dll └── ...步骤不复杂但要注意版本一致性。FFmpeg的dll、lib、头文件必须来自同一个版本否则链接期会报一堆“未定义的外部符号”。可以查一下依赖的对应关系比如FFmpeg 6.0对应的库版本号就是avcodec-60、avformat-60。千万不要混用FFmpeg 5.x的头文件和6.x的dll我在这上面浪费了半天。插件模块的Build.cs里面需要显式引用这些第三方库确保编译时能找到头文件链接时能找到lib文件。如果插件源码里对FFmpeg库的引用路径已经写好了那你只需要按路径放文件如果没写好需要自己加一行类似这样的依赖模块配置PublicDependencyModuleNames.AddRange(new string[] { Core, CoreUObject, Engine, Media, MediaAssets, RenderCore, RHI, Slate, SlateCore });C模块这里我不展开太多但建议先把插件编译通过再开始做蓝图逻辑不然后面问题混在一起很难排查。2.3 编译期典型报错MSB3073的排查编译插件时最容易遇到的就是MSB3073错误。很多人一看到这个错误就觉得代码有问题但其实MSB3073往往不是代码语法错误而是项目构建过程中某个外部命令执行失败最常见的是RunUAT.bat打包或构建脚本执行失败。我遇到过一次典型的MSB3073错误信息大概是Error MSB3073: The command call C:\Program Files\Epic Games\UE_5.2\Engine\Build\BatchFiles\RunUAT.bat -Script... exited with code 2排查思路参考下面这个顺序先看完整构建日志定位是哪个步骤退出日志末尾往往有真实原因。检查FFmpeg的dll是否放到了插件期望的路径下。检查.uplugin的RuntimeDependencies里是否声明了dll文件如果没有声明运行时可能找不到依赖。确认VS版本和工具链版本是否和UE5要求一致UE5.2推荐VS2022 v143工具集。检查项目路径是不是带有中文或空格UE对这类路径的兼容性长期不好。如果上面都查过仍报MSB3073可以试试清掉Intermediate和Saved目录重新Generate project files再编译。UE的增量构建偶尔会留下一些陈旧的中间文件导致脚本执行异常。这个操作在构建体系抽风时非常有效。3. 蓝图接入从MediaPlayer到UI显示3.1 创建媒体播放器和媒体纹理编译通过之后剩下的工作基本都在蓝图里完成。在UE5中接入RTSP流的资源准备流程如下在内容浏览器中右键选择“媒体”分类下的“Media Player”创建一个媒体播放器资产。创建时会弹窗询问是否自动生成对应的Media Texture和Media Sound Component资产这一步建议选“是”能省不少事。在你的UI蓝图或者Actor蓝图里添加一个Image控件如果是UMG或者Plane模型如果是3D物体。把Image控件的Brush引用的Image指向刚才生成的Media Texture资产。把Media Texture资产的Media Player属性指向刚才生成的Media Player资产。要注意的是光创建资产还不够RTSP流不是自动播放的。你需要在蓝图的Event BeginPlay里调用MediaPlayer的“Open URL”节点输入RTSP地址。这里可以分成两种做法直接在蓝图里用Open URL节点简单粗暴。创建MediaSource资产在资源面板里写死地址再用Open Source节点播放。适合地址固定的场景便于资源化管理。我在实时预览类项目里通常直接用Open URL因为摄像头地址可能由服务器动态下发MediaSource资产写死地址反而不灵活。打开RTSP流之后可以用OnMediaOpened和OnMediaOpenFailed这两个事件来判断是否成功。OnMediaOpened触发时再去操作MediaTexture避免在媒体流还没就绪时就急着显示。3.2 RTSP地址怎么写海康/大华/测试流RTSP地址是很多问题的源头。地址搞错了后面所有操作都白做。海康威视摄像头的标准取流地址格式如下rtsp://用户名:密码IP地址:554/h264/ch1/main/av_stream这个地址末尾的main代表主码流想要低码率预览可以改成subrtsp://用户名:密码IP地址:554/h264/ch1/sub/av_stream大华摄像头的格式不太一样用的是带参数的URLrtsp://用户名:密码IP地址:554/cam/realmonitor?channel1subtype0其中subtype0是主码流subtype1是子码流。不同品牌的摄像头RTSP路径规则差别挺大海康的路径结尾有av_stream大华的结尾是realmonitor如果在项目中对接多品牌设备建议把地址格式做成可配置项放到服务器端下发。有一个细节特别容易踩坑RTSP地址里的密码如果包含特殊字符比如、/、:这些字符必须做URL编码。例如密码里含直接写进地址会导致解析错误需要转成%40。我分享给前端对接时都会特意强调这一点因为大多数“怎么都连不上”的问题都出在这。如果需要跑测试不建议每次都去拿真实摄像头。自己电脑上用MediaMTX原RtspSimpleServer加FFmpeg就能搭建一个本地RTSP测试源# 启动MediaMTX服务它会监听8554端口 ./mediamtx.exe # 另一个终端把本地视频文件推到RTSP服务器 ffmpeg -re -i sample.mp4 -c copy -f rtsp rtsp://localhost:8554/live/test然后用下面的地址在UE5里测试rtsp://localhost:8554/live/test这种自建测试源最大的好处是可控性强你可以随意替换视频文件、调整分辨率、限制码率用来验证插件在不同视频参数下的表现。比反复找公共测试流靠谱得多。3.3 用蓝图控制播放、暂停和双指触摸RTSP流接入后控制逻辑其实和普通视频播放一样。最常用的一批节点有Open URL打开网络流。Play播放。Pause暂停。Close关闭流。OnMediaOpened打开成功回调。OnMediaOpenFailed打开失败回调。需要注意RTSP实时流和普通视频在概念上有个区别你不能像播放MP4那样“暂停后继续播放”因为摄像头画面是实时的暂停后恢复播放会出现画面跳变或长时间卡住。真实项目里暂停功能往往做成“关闭流后重新打开”。关于热词里提到的“UE5双指触摸蓝图”其实是在移动端项目里用两个手指控制视频显示区域的缩放和移动。基于UMG的双指缩放手法比较通用获取两个Touch输入的位置实时计算两点的屏幕距离。初始距离记为startDistance当前距离记为currentDistance播放器显示层的缩放比例就是currentDistance除以startDistance。关键节点流程为在PlayerController里绑定InputTouch事件用获取触摸位置节点拿到Touch 1和Touch 2的屏幕坐标计算向量长度得到距离。把距离变化应用到目标Widget的RenderTransform Scale上。双指旋转同理计算两点连线的角度变化更新RenderTransform Angle。有几个实践经验要分享Touch事件绑定要区分手指索引别把同一根手指的移动当成双指操作。两指间距小于一定阈值比如50像素时不要缩放防止抖动。iPad和安卓平板的触摸采样率不同缩放系数的平滑处理建议用Lerp插值不要直接赋值。3.4 在3D场景里贴视频和做UI模糊效果RTSP视频不只在UI上展示很多时候要贴到3D物体上做可视化大屏、数字孪生、AR叠加等效果。把视频贴到3D平面的方法很简单创建Plane新建一个材质把Media Texture连接到材质节点的Base Color材质设置为Unlit模式再把材质赋给Plane。这里我最常犯的错误是忘记设置材质的Sampler Type或纹理采样方式。如果MediaTexture在UI上正常、在3D上却是黑屏优先检查材质的纹理节点是不是被Scene Color遮住了以及材质是否开了Unlit。对于视频贴图建议把材质的着色模式改为Unlit避免光照影响画面观感。热词里还有“UE5 3DUI模糊”。这个在RTSP场景里很常见大屏界面里视频预览窗口后面背景希望边缘有模糊效果。UE5默认UMG对背景模糊的支持比较弱如果是视频作为背景更好的做法是直接在背景层用同一路MediaTexture做低分辨率模糊材质或者后期用Post Process体积模糊。不要在UMG里硬做背景模糊否则性能开销大且效果不可控。我有一个实际经验视频墙项目中背景用同一路RTSP流的子码流做模糊放大处理前景叠加清晰的主码流小窗整体视觉层次比单纯拉伸画面好很多性能开销也可控。4. 运行期问题花屏、延迟、崩溃4.1 画面不出来或黑屏先查什么RTSP流接入后最常见的问题就是蓝图逻辑都对但画面黑屏。我一般按照下面的顺序排查用VLC或者ffprobe验证RTSP地址本身能否播放。VLC能放说明地址、账号、密码没问题VLC不能放就不要去UE里查问题。检查防火墙是否有554端口或大范围UDP端口拦截。很多公司网络对RTP传输使用的UDP端口有限制。在蓝图里监听OnMediaOpenFailed事件看回调里是否带了错误信息。确认MediaTexture是否真的收到了帧。可以在材质里把MediaTexture连到自发光通道排除UI Brush设置问题。检查摄像头的编码格式是不是H.265/HEVC。部分FFMPEGMedia版本对H.265支持依赖FFmpeg编译选项如果解码器打开失败画面也不会出现。每次做新项目我都会先用VLC验证地址这一步。看起来最简单但真的省掉过无数冤枉路。4.2 延迟高的原因与FFmpeg参数调整RTSP实时流延迟高是讨论度最高的一个问题。很多人反馈延迟有几秒甚至十几秒根本没法用。这里面有三个主要原因第一FFmpeg在打开网络流时默认会做较深度的流探测和缓冲用来保证媒体信息完整。实时流场景这是致命的。第二网络抖动缓冲器会积累一定量数据延迟和抗抖动永远是权衡关系。第三RTSP流如果走UDP传输丢包重传机制会对解码顺序造成影响进而增加缓冲。解决思路是在FFMPEGMedia插件源码里找到avformat_open_input的位置传入自定义的AVDictionary参数。我实际调试后常用的一组参数AVDictionary* options nullptr; av_dict_set(options, rtsp_transport, tcp, 0); av_dict_set(options, max_delay, 500000, 0); av_dict_set(options, fflags, nobuffer, 0); av_dict_set(options, probesize, 64, 0); av_dict_set(options, analyzeduration, 0, 0); avformat_open_input(formatContext, url.c_str(), nullptr, options);参数的取舍我解释一下rtsp_transport强制走TCP。TCP不会有UDP的NAT穿透问题也更稳定代价是延迟略高。局域网内TCP优势明显公网场景建议按需测试。max_delay单位是微秒500000表示缓冲区最多堆积0.5秒的数据。这个值是我试过以后比较平衡的设置画面不会频繁卡顿延迟可控制在700毫秒以内。probesize和analyzeduration设为极小值是为了跳过过深的流探测快速开始播放。要注意如果设得太小某些摄像头的SDP解析会失败这时需要适当调大probesize。改完这部分源码后重新编译插件延迟表现会有质的提升。我在一个项目里把延迟从4秒压到了1秒以内。4.3 LowLevelFatalError这类崩溃怎么定位热词里有“lowlevelfatalerror [file: d:\buildue5\sync\engine\source\runtime\rendercore...]”这类崩溃在播放RTSP流时确实容易出现。这个错误本身是RHI层在蓝图中断言失败常见的原因有几个最常见的是MediaTexture的尺寸为0或者非法值时RHI试图创建对应大小的纹理在RenderCore层直接触发Fatal Error。简单说就是播放器还没准备好你就让UI或材质强行使用了这张纹理Texture一上来就是个空尺寸。解决方法是不要在一开始就急着把MediaTexture赋值给Image或材质。等OnMediaOpened成功触发之后再动态设置UI显示层的Visibility或材质参数。如果项目里有循环调用打开流的逻辑也要保证在关闭后不会残留引用。另一个常见诱因是GPU驱动与引擎着色器缓存不匹配。可以尝试清掉Saved目录下的ShaderCache文件升级显卡驱动到新版本再重新构建着色器。如果上述都没解决打开项目日志文件检查崩溃点附近的C堆栈。虽然RTSP问题的堆栈信息往往不全但至少能定位是RHI纹理创建还是Shader编译阶段。日志信息里找Runtime/RenderCore附近的内容基本能判断方向。4.4 安卓上的RTSP缓存播放和其他平台限制安卓端播放RTSP流是另一个大坑。FFMPEGMedia在安卓上能否工作取决于你是否编译了Android版本的FFmpeg动态库而且移动端硬解适配很麻烦很多设备解码H.264只能走软解性能和发热都很难看。热词“安卓缓存RTSP流”背后是一种变通方案如果设备上的解码器不支持直接播放网络流先把RTSP流缓存到本地文件再让UE播放本地文件。具体做法是把RTSP流重新封装成TS或MP4格式写入缓存目录等到缓存了足够的数据再用MediaPlayer打开本地文件。这种方案的延迟会升高但稳定性和兼容性都大幅改善特别适合不强调实时性的场景比如历史录像回放。如果是实时预览要求高、必须在移动端播放我通常建议加上服务端转码摄像头RTSP推给服务器服务器转成HLS或者FLVUE走HTTP拉流。虽然多了一跳但适用性更强安卓/iOS都走得更稳。这已经超出插件本身范畴了但在真实项目中是非常重要的优化方向。5. 多路播放与性能优化5.1 带宽和分辨率怎么算主码流 vs 子码流安防项目里几乎不存在只播一路视频的情况。常见的是4路、8路、甚至16路视频墙这时候必须在项目开始前按带宽和性能做估算。拿海康摄像机举例子码流类型典型分辨率典型码率单路带宽主码流1920x10804 Mbps0.5 MB/s子码流640x3600.5 Mbps0.0625 MB/s子码流704x5760.8 Mbps0.1 MB/s如果视频墙有16路都开主码流需要的带宽约为8 MB/s这还没算协议开销和其他业务网络流量。普通千兆局域网的带宽足够但如果你用的是WiFi或者外网接入情况就完全不同了。所以多路项目里通常采用分级策略总览用子码流视频墙的大屏单画面点击放大后再切主码流。海康、大华的RTSP地址都支持随时切换主码流和子码流你只需要在点击放大时关闭当前子码流播放重新用主码流地址打开一个全屏播放器。带宽看似不是大问题但在真实的弱电项目中网络交换机、防火墙的转发能力、录像机本身的取流上限都会成为瓶颈提前算好码流数量是负责的做法。5.2 多路视频的内存和CPU控制每开一路RTSP流FFmpeg都要创建独立的解码上下文。解码后的图像数据从YUV转RGB后上传到GPU纹理这个过程中的内存开销不能忽略。一路1080p RGBA纹理的显存大约是1920x1080x4字节约8.3MB。如果16路都显示显存消耗是132MB。看起来还好但加上解码器内部的缓冲、YUV数据临时区、以及蓝图层面的缓存实际开销往往比理论值高出50%到一倍。CPU的消耗更值得关注。FFmpeg软解一路1080p30帧的H.264流在主流PC上大约要占用一个核心15%到30%的算力取决于CPU代际和视频内容复杂度。16路全用软解基本会把整台机器拖垮。我实际操作中的优化思路有这些视频墙总览界面上最多同时解码4路切出去的路立即Close释放解码器资源。每个网格用预加载机制提前1秒打开即将显示的流显示后不再重复创建MediaPlayer。分辨率优先选择子码流只在聚焦窗口使用主码流。音频不必要就别开很多RTSP流音频采样率格式五花八门解码也会消耗CPU。有人会想用GPU硬解来降低CPU负载但FFMPEGMedia在UE5里接入硬解需要额外移植NVDEC或者VideoToolbox门槛不低。一个transform反馈是先接受软解然后通过合理调度把并发路数压下来性价比远高于直接搞硬解。5.3 真实项目中的稳定策略多路RTSP播放项目最麻烦的不是“能不能播”而是“能不能长期稳定播”。我遇到过摄像头断电重启、网络丢包、流超时等各种情况总结出来一套适用性很强的稳定策略所有RTSP地址统一走TCP传输避免UDP穿透和丢包问题。每条流建立独立的失败重连机制用定时器每5秒检查播放状态如果发现媒体关闭或打开失败自动重试。摄像头断电恢复后摄像机重新上线的时间一般在30秒上下所以重连次数不要限制太紧连续重试1分钟左右是合理的。对于视频墙把16路播放器的开关逻辑抽成一个独立子蓝图方便统一管理连接状态。多媒体播放器在处理无法播放的视频或者无效地址时内存释放不总是及时的关闭时手动置空引用可以降低崩溃概率。FFMPEGMedia插件本身在Windows上的稳定性我实测下来是不错的。前提是不要在播放中频繁切换分辨率、频繁开关流否则底层FFmpeg的动态申请会积累内存碎片。我用了一套“切换主码流前先Close旧流等2秒再开新流”的策略之后稳定性明显提升。6. 一点个人经验与后续扩展最后再说一点我用下来的真实感受。第一次跑通FFMPEGMedia播放RTSP流时很多人都觉得“太麻烦了UE5怎么连这都不支持”。但理解了原理之后会发现UE5面向的不只是游戏场景它也承担大量数字孪生、智慧城市、工业仿真的需求而这些场景里视频接入几乎是标配。能用插件把范围扩大已经是很省事的选择。我现在的标准工作流是先用VLC验证RTSP地址再用ffprobe看视频编码格式然后才打开FFMPEGMedia工程测试。进入UE后第一时间把延迟参数调低再开始做UI。这套顺序最大的作用是把配置问题、解码问题和业务问题分开隔离定位起来非常快。如果后续项目中需要接入多路视频、动态地址下发或移动端播放我建议基于现有的FFMPEGMedia做二次封装把播放器逻辑封装成插件内的一个业务模块而不是在关卡蓝图里堆一堆节点。否则功能多了以后蓝图连线会变成一团乱麻。这篇文章写完时我手头项目里的16路RTSP视频墙已经跑了两个多星期没出过大问题。希望这篇记录的思路和参数能让你在实现RTSP播放时少走这些弯路。
返回列表